diff options
Diffstat (limited to 'drivers')
208 files changed, 14467 insertions, 5003 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 2673428cb6..6846d181aa 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/ obj-$(CONFIG_ALTERA_SDRAM) += ddr/altera/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/ power/pmic/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/regulator/ +obj-$(CONFIG_SPL_RESET_SUPPORT) += reset/ obj-$(CONFIG_SPL_MTD_SUPPORT) += mtd/ obj-$(CONFIG_SPL_ONENAND_SUPPORT) += mtd/onenand/ obj-$(CONFIG_SPL_UBI) += mtd/ubispl/ diff --git a/drivers/ata/dwc_ahci.c b/drivers/ata/dwc_ahci.c index 029b7784f6..6c7371e3ed 100644 --- a/drivers/ata/dwc_ahci.c +++ b/drivers/ata/dwc_ahci.c @@ -25,17 +25,18 @@ struct dwc_ahci_priv { void *wrapper_base; }; +static int dwc_ahci_bind(struct udevice *dev) +{ + struct udevice *scsi_dev; + + return ahci_bind_scsi(dev, &scsi_dev); +} + static int dwc_ahci_ofdata_to_platdata(struct udevice *dev) { struct dwc_ahci_priv *priv = dev_get_priv(dev); - struct scsi_platdata *plat = dev_get_uclass_platdata(dev); fdt_addr_t addr; - plat->max_id = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), - "max-id", CONFIG_SYS_SCSI_MAX_SCSI_ID); - plat->max_lun = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), - "max-lun", CONFIG_SYS_SCSI_MAX_LUN); - priv->base = map_physmem(devfdt_get_addr(dev), sizeof(void *), MAP_NOCACHE); @@ -81,11 +82,7 @@ static int dwc_ahci_probe(struct udevice *dev) writel(val, priv->wrapper_base + TI_SATA_SYSCONFIG); } - ret = ahci_init_dm(dev, priv->base); - if (ret) - return ret; - - return ahci_start_ports_dm(dev); + return ahci_probe_scsi(dev, (ulong)priv->base); } static const struct udevice_id dwc_ahci_ids[] = { @@ -95,11 +92,11 @@ static const struct udevice_id dwc_ahci_ids[] = { U_BOOT_DRIVER(dwc_ahci) = { .name = "dwc_ahci", - .id = UCLASS_SCSI, + .id = UCLASS_AHCI, .of_match = dwc_ahci_ids, + .bind = dwc_ahci_bind, .ofdata_to_platdata = dwc_ahci_ofdata_to_platdata, .ops = &scsi_ops, .probe = dwc_ahci_probe, .priv_auto_alloc_size = sizeof(struct dwc_ahci_priv), - .flags = DM_FLAG_ALLOC_PRIV_DMA, }; diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig index 73972b7f9c..d335ed14b9 100644 --- a/drivers/bootcount/Kconfig +++ b/drivers/bootcount/Kconfig @@ -34,6 +34,7 @@ config BOOTCOUNT_EXT config BOOTCOUNT_AM33XX bool "Boot counter in AM33XX RTC IP block" depends on AM33XX || SOC_DA8XX + select SPL_AM33XX_ENABLE_RTC32K_OSC if AM33XX help A bootcount driver for the RTC IP block found on many TI platforms. This requires the RTC clocks, etc, to be enabled prior to use and diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index cdfa052c16..c382e8865f 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -75,6 +75,14 @@ config CLK_ZYNQMP This clock driver adds support for clock realted settings for ZynqMP platform. +config CLK_STM32MP1 + bool "Enable RCC clock driver for STM32MP1" + depends on ARCH_STM32MP && CLK + default y + help + Enable the STM32 clock (RCC) driver. Enable support for + manipulating STM32MP1's on-SoC clocks. + source "drivers/clk/tegra/Kconfig" source "drivers/clk/uniphier/Kconfig" source "drivers/clk/exynos/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index dab106ab7f..e05c607223 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_CLK_EXYNOS) += exynos/ obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o obj-$(CONFIG_CLK_RENESAS) += renesas/ obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o +obj-$(CONFIG_CLK_STM32MP1) += clk_stm32mp1.o obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_CLK_ZYNQ) += clk_zynq.o obj-$(CONFIG_CLK_ZYNQMP) += clk_zynqmp.o diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig index fd56f200b9..8d482a2752 100644 --- a/drivers/clk/at91/Kconfig +++ b/drivers/clk/at91/Kconfig @@ -27,6 +27,14 @@ config AT91_UTMI fast crystal oscillator to meet the frequency accuracy required by USB. +config AT91_USB_CLK + bool "Support USB OHCI Input Clock" + depends on CLK_AT91 + help + This option is used to enable the USB Input Clock, from + the device tree, configure the USBS bit (PLLA or UTMI PLL) + and USBDIV field of the PMC_USB register. + config AT91_H32MX bool "Support H32MX 32-bit Matrix Clock" depends on CLK_AT91 diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index fbe3cb6581..8c197ff949 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -3,9 +3,10 @@ # obj-y += pmc.o sckc.o -obj-y += clk-slow.o clk-main.o clk-plla.o clk-master.o +obj-y += clk-slow.o clk-main.o clk-plla.o clk-plladiv.o clk-master.o obj-y += clk-system.o clk-peripheral.o obj-$(CONFIG_AT91_UTMI) += clk-utmi.o +obj-$(CONFIG_AT91_USB_CLK) += clk-usb.o obj-$(CONFIG_AT91_H32MX) += clk-h32mx.o obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generated.o diff --git a/drivers/clk/at91/clk-plladiv.c b/drivers/clk/at91/clk-plladiv.c new file mode 100644 index 0000000000..0599d2893b --- /dev/null +++ b/drivers/clk/at91/clk-plladiv.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 Microhip / Atmel Corporation + * Wenyou.Yang <wenyou.yang@microchip.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +static int at91_plladiv_clk_enable(struct clk *clk) +{ + return 0; +} + +static ulong at91_plladiv_clk_get_rate(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk source; + ulong clk_rate; + int ret; + + ret = clk_get_by_index(clk->dev, 0, &source); + if (ret) + return -EINVAL; + + clk_rate = clk_get_rate(&source); + if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2) + clk_rate /= 2; + + return clk_rate; +} + +static ulong at91_plladiv_clk_set_rate(struct clk *clk, ulong rate) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk source; + ulong parent_rate; + int ret; + + ret = clk_get_by_index(clk->dev, 0, &source); + if (ret) + return -EINVAL; + + parent_rate = clk_get_rate(&source); + if ((parent_rate != rate) && ((parent_rate) / 2 != rate)) + return -EINVAL; + + if (parent_rate != rate) { + writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2), + &pmc->mckr); + } + + return 0; +} + +static struct clk_ops at91_plladiv_clk_ops = { + .enable = at91_plladiv_clk_enable, + .get_rate = at91_plladiv_clk_get_rate, + .set_rate = at91_plladiv_clk_set_rate, +}; + +static int at91_plladiv_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id at91_plladiv_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-plldiv" }, + {} +}; + +U_BOOT_DRIVER(at91_plladiv_clk) = { + .name = "at91-plladiv-clk", + .id = UCLASS_CLK, + .of_match = at91_plladiv_clk_match, + .probe = at91_plladiv_clk_probe, + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &at91_plladiv_clk_ops, +}; diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index 24b271aa18..81fe47a9d7 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -44,6 +44,30 @@ static inline int is_pck(int id) return (id >= 8) && (id <= 15); } +static ulong system_clk_get_rate(struct clk *clk) +{ + struct clk clk_dev; + int ret; + + ret = clk_get_by_index(clk->dev, 0, &clk_dev); + if (ret) + return -EINVAL; + + return clk_get_rate(&clk_dev); +} + +static ulong system_clk_set_rate(struct clk *clk, ulong rate) +{ + struct clk clk_dev; + int ret; + + ret = clk_get_by_index(clk->dev, 0, &clk_dev); + if (ret) + return -EINVAL; + + return clk_set_rate(&clk_dev, rate); +} + static int system_clk_enable(struct clk *clk) { struct pmc_platdata *plat = dev_get_platdata(clk->dev); @@ -73,6 +97,8 @@ static int system_clk_enable(struct clk *clk) static struct clk_ops system_clk_ops = { .of_xlate = at91_clk_of_xlate, + .get_rate = system_clk_get_rate, + .set_rate = system_clk_set_rate, .enable = system_clk_enable, }; diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c new file mode 100644 index 0000000000..36622c09dc --- /dev/null +++ b/drivers/clk/at91/clk-usb.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2018 Microhip / Atmel Corporation + * Wenyou.Yang <wenyou.yang@microchip.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <linux/io.h> +#include <mach/at91_pmc.h> +#include "pmc.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define AT91_USB_CLK_SOURCE_MAX 2 +#define AT91_USB_CLK_MAX_DIV 15 + +struct at91_usb_clk_priv { + u32 num_clksource; +}; + +static ulong at91_usb_clk_get_rate(struct clk *clk) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct clk source; + u32 tmp, usbdiv; + u8 source_index; + int ret; + + tmp = readl(&pmc->pcr); + source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) & + AT91_PMC_USB_USBS_MASK; + usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK; + + ret = clk_get_by_index(clk->dev, source_index, &source); + if (ret) + return 0; + + return clk_get_rate(&source) / (usbdiv + 1); +} + +static ulong at91_usb_clk_set_rate(struct clk *clk, ulong rate) +{ + struct pmc_platdata *plat = dev_get_platdata(clk->dev); + struct at91_pmc *pmc = plat->reg_base; + struct at91_usb_clk_priv *priv = dev_get_priv(clk->dev); + struct clk source, best_source; + ulong tmp_rate, best_rate = rate, source_rate; + int tmp_diff, best_diff = -1; + u32 div, best_div = 0; + u8 best_source_index = 0; + u8 i; + u32 tmp; + int ret; + + for (i = 0; i < priv->num_clksource; i++) { + ret = clk_get_by_index(clk->dev, i, &source); + if (ret) + return ret; + + source_rate = clk_get_rate(&source); + if (IS_ERR_VALUE(source_rate)) + return source_rate; + + for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) { + tmp_rate = DIV_ROUND_CLOSEST(source_rate, div); + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + best_rate = tmp_rate; + best_diff = tmp_diff; + + best_div = div - 1; + best_source = source; + best_source_index = i; + } + + if (!best_diff || tmp_rate < rate) + break; + } + + if (!best_diff) + break; + } + + debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n", + best_source.dev->name, best_rate, best_div); + + ret = clk_enable(&best_source); + if (ret) + return ret; + + tmp = AT91_PMC_USB_USBS_(best_source_index) | + AT91_PMC_USB_DIV_(best_div); + writel(tmp, &pmc->usb); + + return 0; +} + +static struct clk_ops at91_usb_clk_ops = { + .get_rate = at91_usb_clk_get_rate, + .set_rate = at91_usb_clk_set_rate, +}; + +static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct at91_usb_clk_priv *priv = dev_get_priv(dev); + u32 cells[AT91_USB_CLK_SOURCE_MAX]; + u32 num_clksource; + + num_clksource = fdtdec_get_int_array_count(gd->fdt_blob, + dev_of_offset(dev), + "clocks", cells, + AT91_USB_CLK_SOURCE_MAX); + + if (!num_clksource) + return -1; + + priv->num_clksource = num_clksource; + + return 0; +} + +static int at91_usb_clk_probe(struct udevice *dev) +{ + return at91_pmc_core_probe(dev); +} + +static const struct udevice_id at91_usb_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-usb" }, + {} +}; + +U_BOOT_DRIVER(at91_usb_clk) = { + .name = "at91-usb-clk", + .id = UCLASS_CLK, + .of_match = at91_usb_clk_match, + .probe = at91_usb_clk_probe, + .ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv), + .platdata_auto_alloc_size = sizeof(struct pmc_platdata), + .ops = &at91_usb_clk_ops, +}; diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index ad763795d9..6e99b3b15d 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -104,6 +104,39 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) return clk_get_by_indexed_prop(dev, "clocks", index, clk); } +int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk) +{ + int i, ret, err, count; + + bulk->count = 0; + + count = dev_count_phandle_with_args(dev, "clocks", "#clock-cells"); + if (!count) + return 0; + + bulk->clks = devm_kcalloc(dev, count, sizeof(struct clk), GFP_KERNEL); + if (!bulk->clks) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = clk_get_by_index(dev, i, &bulk->clks[i]); + if (ret < 0) + goto bulk_get_err; + + ++bulk->count; + } + + return 0; + +bulk_get_err: + err = clk_release_all(bulk->clks, bulk->count); + if (err) + debug("%s: could release all clocks for %p\n", + __func__, dev); + + return ret; +} + static int clk_set_default_parents(struct udevice *dev) { struct clk clk, parent_clk; @@ -336,6 +369,19 @@ int clk_enable(struct clk *clk) return ops->enable(clk); } +int clk_enable_bulk(struct clk_bulk *bulk) +{ + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = clk_enable(&bulk->clks[i]); + if (ret < 0 && ret != -ENOSYS) + return ret; + } + + return 0; +} + int clk_disable(struct clk *clk) { const struct clk_ops *ops = clk_dev_ops(clk->dev); @@ -348,6 +394,19 @@ int clk_disable(struct clk *clk) return ops->disable(clk); } +int clk_disable_bulk(struct clk_bulk *bulk) +{ + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = clk_disable(&bulk->clks[i]); + if (ret < 0 && ret != -ENOSYS) + return ret; + } + + return 0; +} + UCLASS_DRIVER(clk) = { .id = UCLASS_CLK, .name = "clk", diff --git a/drivers/clk/clk_sandbox_test.c b/drivers/clk/clk_sandbox_test.c index 999100de9d..d0898815b3 100644 --- a/drivers/clk/clk_sandbox_test.c +++ b/drivers/clk/clk_sandbox_test.c @@ -11,6 +11,7 @@ struct sandbox_clk_test { struct clk clks[SANDBOX_CLK_TEST_ID_COUNT]; + struct clk_bulk bulk; }; static const char * const sandbox_clk_test_names[] = { @@ -34,6 +35,13 @@ int sandbox_clk_test_get(struct udevice *dev) return 0; } +int sandbox_clk_test_get_bulk(struct udevice *dev) +{ + struct sandbox_clk_test *sbct = dev_get_priv(dev); + + return clk_get_bulk(dev, &sbct->bulk); +} + ulong sandbox_clk_test_get_rate(struct udevice *dev, int id) { struct sandbox_clk_test *sbct = dev_get_priv(dev); @@ -64,6 +72,13 @@ int sandbox_clk_test_enable(struct udevice *dev, int id) return clk_enable(&sbct->clks[id]); } +int sandbox_clk_test_enable_bulk(struct udevice *dev) +{ + struct sandbox_clk_test *sbct = dev_get_priv(dev); + + return clk_enable_bulk(&sbct->bulk); +} + int sandbox_clk_test_disable(struct udevice *dev, int id) { struct sandbox_clk_test *sbct = dev_get_priv(dev); @@ -74,6 +89,13 @@ int sandbox_clk_test_disable(struct udevice *dev, int id) return clk_disable(&sbct->clks[id]); } +int sandbox_clk_test_disable_bulk(struct udevice *dev) +{ + struct sandbox_clk_test *sbct = dev_get_priv(dev); + + return clk_disable_bulk(&sbct->bulk); +} + int sandbox_clk_test_free(struct udevice *dev) { struct sandbox_clk_test *sbct = dev_get_priv(dev); @@ -88,6 +110,13 @@ int sandbox_clk_test_free(struct udevice *dev) return 0; } +int sandbox_clk_test_release_bulk(struct udevice *dev) +{ + struct sandbox_clk_test *sbct = dev_get_priv(dev); + + return clk_release_bulk(&sbct->bulk); +} + static const struct udevice_id sandbox_clk_test_ids[] = { { .compatible = "sandbox,clk-test" }, { } diff --git a/drivers/clk/clk_stm32f.c b/drivers/clk/clk_stm32f.c index 926b249ff3..d8eab1a88d 100644 --- a/drivers/clk/clk_stm32f.c +++ b/drivers/clk/clk_stm32f.c @@ -55,18 +55,27 @@ #define RCC_CFGR_PPRE1_SHIFT 10 #define RCC_CFGR_PPRE2_SHIFT 13 -#define RCC_PLLCFGR_PLLSAIN_MASK GENMASK(14, 6) -#define RCC_PLLCFGR_PLLSAIP_MASK GENMASK(17, 16) +#define RCC_PLLSAICFGR_PLLSAIN_MASK GENMASK(14, 6) +#define RCC_PLLSAICFGR_PLLSAIP_MASK GENMASK(17, 16) +#define RCC_PLLSAICFGR_PLLSAIQ_MASK GENMASK(27, 24) +#define RCC_PLLSAICFGR_PLLSAIR_MASK GENMASK(30, 28) #define RCC_PLLSAICFGR_PLLSAIN_SHIFT 6 #define RCC_PLLSAICFGR_PLLSAIP_SHIFT 16 +#define RCC_PLLSAICFGR_PLLSAIQ_SHIFT 24 +#define RCC_PLLSAICFGR_PLLSAIR_SHIFT 28 #define RCC_PLLSAICFGR_PLLSAIP_4 BIT(16) #define RCC_PLLSAICFGR_PLLSAIQ_4 BIT(26) -#define RCC_PLLSAICFGR_PLLSAIR_2 BIT(29) +#define RCC_PLLSAICFGR_PLLSAIR_3 BIT(29) | BIT(28) +#define RCC_DCKCFGRX_TIMPRE BIT(24) #define RCC_DCKCFGRX_CK48MSEL BIT(27) #define RCC_DCKCFGRX_SDMMC1SEL BIT(28) #define RCC_DCKCFGR2_SDMMC2SEL BIT(29) +#define RCC_DCKCFGR_PLLSAIDIVR_SHIFT 16 +#define RCC_DCKCFGR_PLLSAIDIVR_MASK GENMASK(17, 16) +#define RCC_DCKCFGR_PLLSAIDIVR_2 0 + /* * RCC AHB1ENR specific definitions */ @@ -86,8 +95,10 @@ #define RCC_APB2ENR_SYSCFGEN BIT(14) #define RCC_APB2ENR_SAI1EN BIT(22) -enum periph_clock { - TIMER2_CLOCK_CFG, +enum pllsai_div { + PLLSAIP, + PLLSAIQ, + PLLSAIR, }; static const struct stm32_clk_info stm32f4_clk_info = { @@ -125,13 +136,17 @@ struct stm32_clk { unsigned long hse_rate; }; +#ifdef CONFIG_VIDEO_STM32 +static const u8 plldivr_table[] = { 0, 0, 2, 3, 4, 5, 6, 7 }; +#endif +static const u8 pllsaidivr_table[] = { 2, 4, 8, 16 }; + static int configure_clocks(struct udevice *dev) { struct stm32_clk *priv = dev_get_priv(dev); struct stm32_rcc_regs *regs = priv->base; struct stm32_pwr_regs *pwr = priv->pwr_regs; struct pll_psc *sys_pll_psc = &priv->info.sys_pll_psc; - u32 pllsaicfgr = 0; /* Reset RCC configuration */ setbits_le32(®s->cr, RCC_CR_HSION); @@ -163,20 +178,10 @@ static int configure_clocks(struct udevice *dev) clrsetbits_le32(®s->pllcfgr, RCC_PLLCFGR_PLLQ_MASK, sys_pll_psc->pll_q << RCC_PLLCFGR_PLLQ_SHIFT); - /* Configure the SAI PLL to get a 48 MHz source */ - pllsaicfgr = RCC_PLLSAICFGR_PLLSAIR_2 | RCC_PLLSAICFGR_PLLSAIQ_4 | - RCC_PLLSAICFGR_PLLSAIP_4; - pllsaicfgr |= 192 << RCC_PLLSAICFGR_PLLSAIN_SHIFT; - writel(pllsaicfgr, ®s->pllsaicfgr); - - /* Enable the main PLL */ - setbits_le32(®s->cr, RCC_CR_PLLON); - while (!(readl(®s->cr) & RCC_CR_PLLRDY)) - ; - + /* configure SDMMC clock */ if (priv->info.v2) { /*stm32f7 case */ - /* select PLLSAI as 48MHz clock source */ - setbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_CK48MSEL); + /* select PLLQ as 48MHz clock source */ + clrbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_CK48MSEL); /* select 48MHz as SDMMC1 clock source */ clrbits_le32(®s->dckcfgr2, RCC_DCKCFGRX_SDMMC1SEL); @@ -184,18 +189,36 @@ static int configure_clocks(struct udevice *dev) /* select 48MHz as SDMMC2 clock source */ clrbits_le32(®s->dckcfgr2, RCC_DCKCFGR2_SDMMC2SEL); } else { /* stm32f4 case */ - /* select PLLSAI as 48MHz clock source */ - setbits_le32(®s->dckcfgr, RCC_DCKCFGRX_CK48MSEL); + /* select PLLQ as 48MHz clock source */ + clrbits_le32(®s->dckcfgr, RCC_DCKCFGRX_CK48MSEL); /* select 48MHz as SDMMC1 clock source */ clrbits_le32(®s->dckcfgr, RCC_DCKCFGRX_SDMMC1SEL); } - /* Enable the SAI PLL */ +#ifdef CONFIG_VIDEO_STM32 + /* + * Configure the SAI PLL to generate LTDC pixel clock + */ + clrsetbits_le32(®s->pllsaicfgr, RCC_PLLSAICFGR_PLLSAIR_MASK, + RCC_PLLSAICFGR_PLLSAIR_3); + clrsetbits_le32(®s->pllsaicfgr, RCC_PLLSAICFGR_PLLSAIN_MASK, + 195 << RCC_PLLSAICFGR_PLLSAIN_SHIFT); + + clrsetbits_le32(®s->dckcfgr, RCC_DCKCFGR_PLLSAIDIVR_MASK, + RCC_DCKCFGR_PLLSAIDIVR_2 << RCC_DCKCFGR_PLLSAIDIVR_SHIFT); +#endif + /* Enable the main PLL */ + setbits_le32(®s->cr, RCC_CR_PLLON); + while (!(readl(®s->cr) & RCC_CR_PLLRDY)) + ; + +#ifdef CONFIG_VIDEO_STM32 +/* Enable the SAI PLL */ setbits_le32(®s->cr, RCC_CR_PLLSAION); while (!(readl(®s->cr) & RCC_CR_PLLSAIRDY)) ; - +#endif setbits_le32(®s->apb1enr, RCC_APB1ENR_PWREN); if (priv->info.has_overdrive) { @@ -221,8 +244,6 @@ static int configure_clocks(struct udevice *dev) while ((readl(®s->cfgr) & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_PLL) ; - /* gate the SAI clock, needed for MMC 1&2 clocks */ - setbits_le32(®s->apb2enr, RCC_APB2ENR_SAI1EN); #ifdef CONFIG_ETH_DESIGNWARE /* gate the SYSCFG clock, needed to set RMII ethernet interface */ @@ -232,49 +253,145 @@ static int configure_clocks(struct udevice *dev) return 0; } -static unsigned long stm32_clk_pll48clk_rate(struct stm32_clk *priv, - u32 sysclk) +static bool stm32_clk_get_ck48msel(struct stm32_clk *priv) { struct stm32_rcc_regs *regs = priv->base; - u16 pllq, pllm, pllsain, pllsaip; - bool pllsai; - - pllq = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLQ_MASK) - >> RCC_PLLCFGR_PLLQ_SHIFT; if (priv->info.v2) /*stm32f7 case */ - pllsai = readl(®s->dckcfgr2) & RCC_DCKCFGRX_CK48MSEL; + return readl(®s->dckcfgr2) & RCC_DCKCFGRX_CK48MSEL; else - pllsai = readl(®s->dckcfgr) & RCC_DCKCFGRX_CK48MSEL; - if (pllsai) { - /* PLL48CLK is selected from PLLSAI, get PLLSAI value */ - pllm = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLM_MASK); - pllsain = ((readl(®s->pllsaicfgr) & RCC_PLLCFGR_PLLSAIN_MASK) - >> RCC_PLLSAICFGR_PLLSAIN_SHIFT); - pllsaip = ((((readl(®s->pllsaicfgr) & RCC_PLLCFGR_PLLSAIP_MASK) - >> RCC_PLLSAICFGR_PLLSAIP_SHIFT) + 1) << 1); - return ((priv->hse_rate / pllm) * pllsain) / pllsaip; + return readl(®s->dckcfgr) & RCC_DCKCFGRX_CK48MSEL; +} + +static unsigned long stm32_clk_get_pllsai_vco_rate(struct stm32_clk *priv) +{ + struct stm32_rcc_regs *regs = priv->base; + u16 pllm, pllsain; + + pllm = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLM_MASK); + pllsain = ((readl(®s->pllsaicfgr) & RCC_PLLSAICFGR_PLLSAIN_MASK) + >> RCC_PLLSAICFGR_PLLSAIN_SHIFT); + + return ((priv->hse_rate / pllm) * pllsain); +} + +static unsigned long stm32_clk_get_pllsai_rate(struct stm32_clk *priv, + enum pllsai_div output) +{ + struct stm32_rcc_regs *regs = priv->base; + u16 pll_div_output; + + switch (output) { + case PLLSAIP: + pll_div_output = ((((readl(®s->pllsaicfgr) + & RCC_PLLSAICFGR_PLLSAIP_MASK) + >> RCC_PLLSAICFGR_PLLSAIP_SHIFT) + 1) << 1); + break; + case PLLSAIQ: + pll_div_output = (readl(®s->pllsaicfgr) + & RCC_PLLSAICFGR_PLLSAIQ_MASK) + >> RCC_PLLSAICFGR_PLLSAIQ_SHIFT; + break; + case PLLSAIR: + pll_div_output = (readl(®s->pllsaicfgr) + & RCC_PLLSAICFGR_PLLSAIR_MASK) + >> RCC_PLLSAICFGR_PLLSAIR_SHIFT; + break; + default: + pr_err("incorrect PLLSAI output %d\n", output); + return -EINVAL; } - /* PLL48CLK is selected from PLLQ */ - return sysclk / pllq; + + return (stm32_clk_get_pllsai_vco_rate(priv) / pll_div_output); } -static unsigned long stm32_clk_get_rate(struct clk *clk) +static bool stm32_get_timpre(struct stm32_clk *priv) { - struct stm32_clk *priv = dev_get_priv(clk->dev); struct stm32_rcc_regs *regs = priv->base; - u32 sysclk = 0; - u32 shift = 0; - u16 pllm, plln, pllp; + u32 val; + + if (priv->info.v2) /*stm32f7 case */ + val = readl(®s->dckcfgr2); + else + val = readl(®s->dckcfgr); + /* get timer prescaler */ + return !!(val & RCC_DCKCFGRX_TIMPRE); +} + +static u32 stm32_get_hclk_rate(struct stm32_rcc_regs *regs, u32 sysclk) +{ + u8 shift; /* Prescaler table lookups for clock computation */ u8 ahb_psc_table[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9 }; + + shift = ahb_psc_table[( + (readl(®s->cfgr) & RCC_CFGR_AHB_PSC_MASK) + >> RCC_CFGR_HPRE_SHIFT)]; + + return sysclk >> shift; +}; + +static u8 stm32_get_apb_shift(struct stm32_rcc_regs *regs, enum apb apb) +{ + /* Prescaler table lookups for clock computation */ u8 apb_psc_table[8] = { 0, 0, 0, 0, 1, 2, 3, 4 }; + if (apb == APB1) + return apb_psc_table[( + (readl(®s->cfgr) & RCC_CFGR_APB1_PSC_MASK) + >> RCC_CFGR_PPRE1_SHIFT)]; + else /* APB2 */ + return apb_psc_table[( + (readl(®s->cfgr) & RCC_CFGR_APB2_PSC_MASK) + >> RCC_CFGR_PPRE2_SHIFT)]; +}; + +static u32 stm32_get_timer_rate(struct stm32_clk *priv, u32 sysclk, + enum apb apb) +{ + struct stm32_rcc_regs *regs = priv->base; + u8 shift = stm32_get_apb_shift(regs, apb); + + if (stm32_get_timpre(priv)) + /* + * if APB prescaler is configured to a + * division factor of 1, 2 or 4 + */ + switch (shift) { + case 0: + case 1: + case 2: + return stm32_get_hclk_rate(regs, sysclk); + default: + return (sysclk >> shift) * 4; + } + else + /* + * if APB prescaler is configured to a + * division factor of 1 + */ + if (shift == 0) + return sysclk; + else + return (sysclk >> shift) * 2; +}; + +static ulong stm32_clk_get_rate(struct clk *clk) +{ + struct stm32_clk *priv = dev_get_priv(clk->dev); + struct stm32_rcc_regs *regs = priv->base; + u32 sysclk = 0; + u32 vco; + u32 sdmmcxsel_bit; + u32 saidivr; + u32 pllsai_rate; + u16 pllm, plln, pllp, pllq; + if ((readl(®s->cfgr) & RCC_CFGR_SWS_MASK) == RCC_CFGR_SWS_PLL) { pllm = (readl(®s->pllcfgr) & RCC_PLLCFGR_PLLM_MASK); @@ -282,7 +399,10 @@ static unsigned long stm32_clk_get_rate(struct clk *clk) >> RCC_PLLCFGR_PLLN_SHIFT); pllp = ((((readl(®s->pllcfgr) & RCC_PLLCFGR_PLLP_MASK) >> RCC_PLLCFGR_PLLP_SHIFT) + 1) << 1); - sysclk = ((priv->hse_rate / pllm) * plln) / pllp; + pllq = ((readl(®s->pllcfgr) & RCC_PLLCFGR_PLLQ_MASK) + >> RCC_PLLCFGR_PLLQ_SHIFT); + vco = (priv->hse_rate / pllm) * plln; + sysclk = vco / pllp; } else { return -EINVAL; } @@ -293,44 +413,72 @@ static unsigned long stm32_clk_get_rate(struct clk *clk) * AHB1, AHB2 and AHB3 */ case STM32F7_AHB1_CLOCK(GPIOA) ... STM32F7_AHB3_CLOCK(QSPI): - shift = ahb_psc_table[( - (readl(®s->cfgr) & RCC_CFGR_AHB_PSC_MASK) - >> RCC_CFGR_HPRE_SHIFT)]; - return sysclk >>= shift; + return stm32_get_hclk_rate(regs, sysclk); /* APB1 CLOCK */ case STM32F7_APB1_CLOCK(TIM2) ... STM32F7_APB1_CLOCK(UART8): - shift = apb_psc_table[( - (readl(®s->cfgr) & RCC_CFGR_APB1_PSC_MASK) - >> RCC_CFGR_PPRE1_SHIFT)]; - return sysclk >>= shift; + /* For timer clock, an additionnal prescaler is used*/ + switch (clk->id) { + case STM32F7_APB1_CLOCK(TIM2): + case STM32F7_APB1_CLOCK(TIM3): + case STM32F7_APB1_CLOCK(TIM4): + case STM32F7_APB1_CLOCK(TIM5): + case STM32F7_APB1_CLOCK(TIM6): + case STM32F7_APB1_CLOCK(TIM7): + case STM32F7_APB1_CLOCK(TIM12): + case STM32F7_APB1_CLOCK(TIM13): + case STM32F7_APB1_CLOCK(TIM14): + return stm32_get_timer_rate(priv, sysclk, APB1); + } + return (sysclk >> stm32_get_apb_shift(regs, APB1)); + /* APB2 CLOCK */ - case STM32F7_APB2_CLOCK(TIM1) ... STM32F7_APB2_CLOCK(LTDC): + case STM32F7_APB2_CLOCK(TIM1) ... STM32F7_APB2_CLOCK(DSI): + switch (clk->id) { /* * particular case for SDMMC1 and SDMMC2 : * 48Mhz source clock can be from main PLL or from - * SAI PLL + * PLLSAIP */ - switch (clk->id) { case STM32F7_APB2_CLOCK(SDMMC1): - if (readl(®s->dckcfgr2) & RCC_DCKCFGRX_SDMMC1SEL) - /* System clock is selected as SDMMC1 clock */ - return sysclk; - else - return stm32_clk_pll48clk_rate(priv, sysclk); - break; case STM32F7_APB2_CLOCK(SDMMC2): - if (readl(®s->dckcfgr2) & RCC_DCKCFGR2_SDMMC2SEL) - /* System clock is selected as SDMMC2 clock */ + if (clk->id == STM32F7_APB2_CLOCK(SDMMC1)) + sdmmcxsel_bit = RCC_DCKCFGRX_SDMMC1SEL; + else + sdmmcxsel_bit = RCC_DCKCFGR2_SDMMC2SEL; + + if (readl(®s->dckcfgr2) & sdmmcxsel_bit) + /* System clock is selected as SDMMC1 clock */ return sysclk; + /* + * 48 MHz can be generated by either PLLSAIP + * or by PLLQ depending of CK48MSEL bit of RCC_DCKCFGR + */ + if (stm32_clk_get_ck48msel(priv)) + return stm32_clk_get_pllsai_rate(priv, PLLSAIP); else - return stm32_clk_pll48clk_rate(priv, sysclk); + return (vco / pllq); break; + + /* For timer clock, an additionnal prescaler is used*/ + case STM32F7_APB2_CLOCK(TIM1): + case STM32F7_APB2_CLOCK(TIM8): + case STM32F7_APB2_CLOCK(TIM9): + case STM32F7_APB2_CLOCK(TIM10): + case STM32F7_APB2_CLOCK(TIM11): + return stm32_get_timer_rate(priv, sysclk, APB2); + break; + + /* particular case for LTDC clock */ + case STM32F7_APB2_CLOCK(LTDC): + saidivr = readl(®s->dckcfgr); + saidivr = (saidivr & RCC_DCKCFGR_PLLSAIDIVR_MASK) + >> RCC_DCKCFGR_PLLSAIDIVR_SHIFT; + pllsai_rate = stm32_clk_get_pllsai_rate(priv, PLLSAIR); + + return pllsai_rate / pllsaidivr_table[saidivr]; } + return (sysclk >> stm32_get_apb_shift(regs, APB2)); - shift = apb_psc_table[( - (readl(®s->cfgr) & RCC_CFGR_APB2_PSC_MASK) - >> RCC_CFGR_PPRE2_SHIFT)]; - return sysclk >>= shift; default: pr_err("clock index %ld out of range\n", clk->id); return -EINVAL; @@ -339,7 +487,104 @@ static unsigned long stm32_clk_get_rate(struct clk *clk) static ulong stm32_set_rate(struct clk *clk, ulong rate) { +#ifdef CONFIG_VIDEO_STM32 + struct stm32_clk *priv = dev_get_priv(clk->dev); + struct stm32_rcc_regs *regs = priv->base; + u32 pllsair_rate, pllsai_vco_rate, current_rate; + u32 best_div, best_diff, diff; + u16 div; + u8 best_plldivr, best_pllsaidivr; + u8 i, j; + bool found = false; + + /* Only set_rate for LTDC clock is implemented */ + if (clk->id != STM32F7_APB2_CLOCK(LTDC)) { + pr_err("set_rate not implemented for clock index %ld\n", + clk->id); + return 0; + } + + if (rate == stm32_clk_get_rate(clk)) + /* already set to requested rate */ + return rate; + + /* get the current PLLSAIR output freq */ + pllsair_rate = stm32_clk_get_pllsai_rate(priv, PLLSAIR); + best_div = pllsair_rate / rate; + + /* look into pllsaidivr_table if this divider is available*/ + for (i = 0 ; i < sizeof(pllsaidivr_table); i++) + if (best_div == pllsaidivr_table[i]) { + /* set pll_saidivr with found value */ + clrsetbits_le32(®s->dckcfgr, + RCC_DCKCFGR_PLLSAIDIVR_MASK, + pllsaidivr_table[i]); + return rate; + } + + /* + * As no pllsaidivr value is suitable to obtain requested freq, + * test all combination of pllsaidivr * pllsair and find the one + * which give freq closest to requested rate. + */ + + pllsai_vco_rate = stm32_clk_get_pllsai_vco_rate(priv); + best_diff = ULONG_MAX; + best_pllsaidivr = 0; + best_plldivr = 0; + /* + * start at index 2 of plldivr_table as divider value at index 0 + * and 1 are 0) + */ + for (i = 2; i < sizeof(plldivr_table); i++) { + for (j = 0; j < sizeof(pllsaidivr_table); j++) { + div = plldivr_table[i] * pllsaidivr_table[j]; + current_rate = pllsai_vco_rate / div; + /* perfect combination is found ? */ + if (current_rate == rate) { + best_pllsaidivr = j; + best_plldivr = i; + found = true; + break; + } + + diff = (current_rate > rate) ? + current_rate - rate : rate - current_rate; + + /* found a better combination ? */ + if (diff < best_diff) { + best_diff = diff; + best_pllsaidivr = j; + best_plldivr = i; + } + } + + if (found) + break; + } + + /* Disable the SAI PLL */ + clrbits_le32(®s->cr, RCC_CR_PLLSAION); + + /* set pll_saidivr with found value */ + clrsetbits_le32(®s->dckcfgr, RCC_DCKCFGR_PLLSAIDIVR_MASK, + best_pllsaidivr << RCC_DCKCFGR_PLLSAIDIVR_SHIFT); + + /* set pllsair with found value */ + clrsetbits_le32(®s->pllsaicfgr, RCC_PLLSAICFGR_PLLSAIR_MASK, + plldivr_table[best_plldivr] + << RCC_PLLSAICFGR_PLLSAIR_SHIFT); + + /* Enable the SAI PLL */ + setbits_le32(®s->cr, RCC_CR_PLLSAION); + while (!(readl(®s->cr) & RCC_CR_PLLSAIRDY)) + ; + + div = plldivr_table[best_plldivr] * pllsaidivr_table[best_pllsaidivr]; + return pllsai_vco_rate / div; +#else return 0; +#endif } static int stm32_clk_enable(struct clk *clk) @@ -356,17 +601,6 @@ static int stm32_clk_enable(struct clk *clk) return 0; } -void clock_setup(int peripheral) -{ - switch (peripheral) { - case TIMER2_CLOCK_CFG: - setbits_le32(&STM32_RCC->apb1enr, RCC_APB1ENR_TIM2EN); - break; - default: - break; - } -} - static int stm32_clk_probe(struct udevice *dev) { struct ofnode_phandle_args args; diff --git a/drivers/clk/clk_stm32h7.c b/drivers/clk/clk_stm32h7.c index c9594d405a..9ee2e2e999 100644 --- a/drivers/clk/clk_stm32h7.c +++ b/drivers/clk/clk_stm32h7.c @@ -35,6 +35,7 @@ DECLARE_GLOBAL_DATA_PTR; #define RCC_CFGR_SW_CSI 1 #define RCC_CFGR_SW_HSE 2 #define RCC_CFGR_SW_PLL1 3 +#define RCC_CFGR_TIMPRE BIT(15) #define RCC_PLLCKSELR_PLLSRC_HSI 0 #define RCC_PLLCKSELR_PLLSRC_CSI 1 @@ -339,6 +340,11 @@ struct pll_psc sys_pll_psc = { .divr = 2, }; +enum apb { + APB1, + APB2, +}; + int configure_clocks(struct udevice *dev) { struct stm32_clk *priv = dev_get_priv(dev); @@ -562,13 +568,74 @@ static u32 stm32_get_PLL1_rate(struct stm32_rcc_regs *regs, return -EINVAL; } +static u32 stm32_get_apb_psc(struct stm32_rcc_regs *regs, enum apb apb) +{ + u16 prescaler_table[8] = {2, 4, 8, 16, 64, 128, 256, 512}; + u32 d2cfgr = readl(®s->d2cfgr); + + if (apb == APB1) { + if (d2cfgr & RCC_D2CFGR_D2PPRE1_DIVIDED) + /* get D2 domain APB1 prescaler */ + return prescaler_table[ + ((d2cfgr & RCC_D2CFGR_D2PPRE1_DIVIDER) + >> RCC_D2CFGR_D2PPRE1_SHIFT)]; + } else { /* APB2 */ + if (d2cfgr & RCC_D2CFGR_D2PPRE2_DIVIDED) + /* get D2 domain APB2 prescaler */ + return prescaler_table[ + ((d2cfgr & RCC_D2CFGR_D2PPRE2_DIVIDER) + >> RCC_D2CFGR_D2PPRE2_SHIFT)]; + } + + return 1; +}; + +static u32 stm32_get_timer_rate(struct stm32_clk *priv, u32 sysclk, + enum apb apb) +{ + struct stm32_rcc_regs *regs = priv->rcc_base; +u32 psc = stm32_get_apb_psc(regs, apb); + + if (readl(®s->cfgr) & RCC_CFGR_TIMPRE) + /* + * if APB prescaler is configured to a + * division factor of 1, 2 or 4 + */ + switch (psc) { + case 1: + case 2: + case 4: + return sysclk; + case 8: + return sysclk / 2; + case 16: + return sysclk / 4; + default: + pr_err("unexpected prescaler value (%d)\n", psc); + return 0; + } + else + switch (psc) { + case 1: + return sysclk; + case 2: + case 4: + case 8: + case 16: + return sysclk / psc; + default: + pr_err("unexpected prescaler value (%d)\n", psc); + return 0; + } +}; + static ulong stm32_clk_get_rate(struct clk *clk) { struct stm32_clk *priv = dev_get_priv(clk->dev); struct stm32_rcc_regs *regs = priv->rcc_base; ulong sysclk = 0; u32 gate_offset; - u32 d1cfgr; + u32 d1cfgr, d3cfgr; /* prescaler table lookups for clock computation */ u16 prescaler_table[8] = {2, 4, 8, 16, 64, 128, 256, 512}; u8 source, idx; @@ -645,9 +712,10 @@ static ulong stm32_clk_get_rate(struct clk *clk) break; case RCC_APB4ENR: - if (d1cfgr & RCC_D3CFGR_D3PPRE_DIVIDED) { + d3cfgr = readl(®s->d3cfgr); + if (d3cfgr & RCC_D3CFGR_D3PPRE_DIVIDED) { /* get D3 domain APB4 prescaler */ - idx = (d1cfgr & RCC_D3CFGR_D3PPRE_DIVIDER) >> + idx = (d3cfgr & RCC_D3CFGR_D3PPRE_DIVIDER) >> RCC_D3CFGR_D3PPRE_SHIFT; sysclk = sysclk / prescaler_table[idx]; } @@ -660,31 +728,42 @@ static ulong stm32_clk_get_rate(struct clk *clk) case RCC_APB1LENR: case RCC_APB1HENR: - if (d1cfgr & RCC_D2CFGR_D2PPRE1_DIVIDED) { - /* get D2 domain APB1 prescaler */ - idx = (d1cfgr & RCC_D2CFGR_D2PPRE1_DIVIDER) >> - RCC_D2CFGR_D2PPRE1_SHIFT; - sysclk = sysclk / prescaler_table[idx]; + /* special case for GPT timers */ + switch (clk->id) { + case TIM14_CK: + case TIM13_CK: + case TIM12_CK: + case TIM7_CK: + case TIM6_CK: + case TIM5_CK: + case TIM4_CK: + case TIM3_CK: + case TIM2_CK: + return stm32_get_timer_rate(priv, sysclk, APB1); } debug("%s system clock: freq after APB1 prescaler = %ld\n", __func__, sysclk); - return sysclk; + return (sysclk / stm32_get_apb_psc(regs, APB1)); break; case RCC_APB2ENR: - if (d1cfgr & RCC_D2CFGR_D2PPRE2_DIVIDED) { - /* get D2 domain APB1 prescaler */ - idx = (d1cfgr & RCC_D2CFGR_D2PPRE2_DIVIDER) >> - RCC_D2CFGR_D2PPRE2_SHIFT; - sysclk = sysclk / prescaler_table[idx]; + /* special case for timers */ + switch (clk->id) { + case TIM17_CK: + case TIM16_CK: + case TIM15_CK: + case TIM8_CK: + case TIM1_CK: + return stm32_get_timer_rate(priv, sysclk, APB2); } debug("%s system clock: freq after APB2 prescaler = %ld\n", __func__, sysclk); - return sysclk; + return (sysclk / stm32_get_apb_psc(regs, APB2)); + break; default: diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c new file mode 100644 index 0000000000..c67aa44473 --- /dev/null +++ b/drivers/clk/clk_stm32mp1.c @@ -0,0 +1,1777 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +#include <common.h> +#include <clk-uclass.h> +#include <div64.h> +#include <dm.h> +#include <regmap.h> +#include <spl.h> +#include <syscon.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <dt-bindings/clock/stm32mp1-clks.h> +#include <dt-bindings/clock/stm32mp1-clksrc.h> + +#if !defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD) +/* activate clock tree initialization in the driver */ +#define STM32MP1_CLOCK_TREE_INIT +#endif + +#define MAX_HSI_HZ 64000000 + +/* TIMEOUT */ +#define TIMEOUT_200MS 200000 +#define TIMEOUT_1S 1000000 + +/* STGEN registers */ +#define STGENC_CNTCR 0x00 +#define STGENC_CNTSR 0x04 +#define STGENC_CNTCVL 0x08 +#define STGENC_CNTCVU 0x0C +#define STGENC_CNTFID0 0x20 + +#define STGENC_CNTCR_EN BIT(0) + +/* RCC registers */ +#define RCC_OCENSETR 0x0C +#define RCC_OCENCLRR 0x10 +#define RCC_HSICFGR 0x18 +#define RCC_MPCKSELR 0x20 +#define RCC_ASSCKSELR 0x24 +#define RCC_RCK12SELR 0x28 +#define RCC_MPCKDIVR 0x2C +#define RCC_AXIDIVR 0x30 +#define RCC_APB4DIVR 0x3C +#define RCC_APB5DIVR 0x40 +#define RCC_RTCDIVR 0x44 +#define RCC_MSSCKSELR 0x48 +#define RCC_PLL1CR 0x80 +#define RCC_PLL1CFGR1 0x84 +#define RCC_PLL1CFGR2 0x88 +#define RCC_PLL1FRACR 0x8C +#define RCC_PLL1CSGR 0x90 +#define RCC_PLL2CR 0x94 +#define RCC_PLL2CFGR1 0x98 +#define RCC_PLL2CFGR2 0x9C +#define RCC_PLL2FRACR 0xA0 +#define RCC_PLL2CSGR 0xA4 +#define RCC_I2C46CKSELR 0xC0 +#define RCC_CPERCKSELR 0xD0 +#define RCC_STGENCKSELR 0xD4 +#define RCC_DDRITFCR 0xD8 +#define RCC_BDCR 0x140 +#define RCC_RDLSICR 0x144 +#define RCC_MP_APB4ENSETR 0x200 +#define RCC_MP_APB5ENSETR 0x208 +#define RCC_MP_AHB5ENSETR 0x210 +#define RCC_MP_AHB6ENSETR 0x218 +#define RCC_OCRDYR 0x808 +#define RCC_DBGCFGR 0x80C +#define RCC_RCK3SELR 0x820 +#define RCC_RCK4SELR 0x824 +#define RCC_MCUDIVR 0x830 +#define RCC_APB1DIVR 0x834 +#define RCC_APB2DIVR 0x838 +#define RCC_APB3DIVR 0x83C +#define RCC_PLL3CR 0x880 +#define RCC_PLL3CFGR1 0x884 +#define RCC_PLL3CFGR2 0x888 +#define RCC_PLL3FRACR 0x88C +#define RCC_PLL3CSGR 0x890 +#define RCC_PLL4CR 0x894 +#define RCC_PLL4CFGR1 0x898 +#define RCC_PLL4CFGR2 0x89C +#define RCC_PLL4FRACR 0x8A0 +#define RCC_PLL4CSGR 0x8A4 +#define RCC_I2C12CKSELR 0x8C0 +#define RCC_I2C35CKSELR 0x8C4 +#define RCC_UART6CKSELR 0x8E4 +#define RCC_UART24CKSELR 0x8E8 +#define RCC_UART35CKSELR 0x8EC +#define RCC_UART78CKSELR 0x8F0 +#define RCC_SDMMC12CKSELR 0x8F4 +#define RCC_SDMMC3CKSELR 0x8F8 +#define RCC_ETHCKSELR 0x8FC +#define RCC_QSPICKSELR 0x900 +#define RCC_FMCCKSELR 0x904 +#define RCC_USBCKSELR 0x91C +#define RCC_MP_APB1ENSETR 0xA00 +#define RCC_MP_APB2ENSETR 0XA08 +#define RCC_MP_AHB2ENSETR 0xA18 +#define RCC_MP_AHB4ENSETR 0xA28 + +/* used for most of SELR register */ +#define RCC_SELR_SRC_MASK GENMASK(2, 0) +#define RCC_SELR_SRCRDY BIT(31) + +/* Values of RCC_MPCKSELR register */ +#define RCC_MPCKSELR_HSI 0 +#define RCC_MPCKSELR_HSE 1 +#define RCC_MPCKSELR_PLL 2 +#define RCC_MPCKSELR_PLL_MPUDIV 3 + +/* Values of RCC_ASSCKSELR register */ +#define RCC_ASSCKSELR_HSI 0 +#define RCC_ASSCKSELR_HSE 1 +#define RCC_ASSCKSELR_PLL 2 + +/* Values of RCC_MSSCKSELR register */ +#define RCC_MSSCKSELR_HSI 0 +#define RCC_MSSCKSELR_HSE 1 +#define RCC_MSSCKSELR_CSI 2 +#define RCC_MSSCKSELR_PLL 3 + +/* Values of RCC_CPERCKSELR register */ +#define RCC_CPERCKSELR_HSI 0 +#define RCC_CPERCKSELR_CSI 1 +#define RCC_CPERCKSELR_HSE 2 + +/* used for most of DIVR register : max div for RTC */ +#define RCC_DIVR_DIV_MASK GENMASK(5, 0) +#define RCC_DIVR_DIVRDY BIT(31) + +/* Masks for specific DIVR registers */ +#define RCC_APBXDIV_MASK GENMASK(2, 0) +#define RCC_MPUDIV_MASK GENMASK(2, 0) +#define RCC_AXIDIV_MASK GENMASK(2, 0) +#define RCC_MCUDIV_MASK GENMASK(3, 0) + +/* offset between RCC_MP_xxxENSETR and RCC_MP_xxxENCLRR registers */ +#define RCC_MP_ENCLRR_OFFSET 4 + +/* Fields of RCC_BDCR register */ +#define RCC_BDCR_LSEON BIT(0) +#define RCC_BDCR_LSEBYP BIT(1) +#define RCC_BDCR_LSERDY BIT(2) +#define RCC_BDCR_LSEDRV_MASK GENMASK(5, 4) +#define RCC_BDCR_LSEDRV_SHIFT 4 +#define RCC_BDCR_LSECSSON BIT(8) +#define RCC_BDCR_RTCCKEN BIT(20) +#define RCC_BDCR_RTCSRC_MASK GENMASK(17, 16) +#define RCC_BDCR_RTCSRC_SHIFT 16 + +/* Fields of RCC_RDLSICR register */ +#define RCC_RDLSICR_LSION BIT(0) +#define RCC_RDLSICR_LSIRDY BIT(1) + +/* used for ALL PLLNCR registers */ +#define RCC_PLLNCR_PLLON BIT(0) +#define RCC_PLLNCR_PLLRDY BIT(1) +#define RCC_PLLNCR_DIVPEN BIT(4) +#define RCC_PLLNCR_DIVQEN BIT(5) +#define RCC_PLLNCR_DIVREN BIT(6) +#define RCC_PLLNCR_DIVEN_SHIFT 4 + +/* used for ALL PLLNCFGR1 registers */ +#define RCC_PLLNCFGR1_DIVM_SHIFT 16 +#define RCC_PLLNCFGR1_DIVM_MASK GENMASK(21, 16) +#define RCC_PLLNCFGR1_DIVN_SHIFT 0 +#define RCC_PLLNCFGR1_DIVN_MASK GENMASK(8, 0) +/* only for PLL3 and PLL4 */ +#define RCC_PLLNCFGR1_IFRGE_SHIFT 24 +#define RCC_PLLNCFGR1_IFRGE_MASK GENMASK(25, 24) + +/* used for ALL PLLNCFGR2 registers */ +#define RCC_PLLNCFGR2_DIVX_MASK GENMASK(6, 0) +#define RCC_PLLNCFGR2_DIVP_SHIFT 0 +#define RCC_PLLNCFGR2_DIVP_MASK GENMASK(6, 0) +#define RCC_PLLNCFGR2_DIVQ_SHIFT 8 +#define RCC_PLLNCFGR2_DIVQ_MASK GENMASK(14, 8) +#define RCC_PLLNCFGR2_DIVR_SHIFT 16 +#define RCC_PLLNCFGR2_DIVR_MASK GENMASK(22, 16) + +/* used for ALL PLLNFRACR registers */ +#define RCC_PLLNFRACR_FRACV_SHIFT 3 +#define RCC_PLLNFRACR_FRACV_MASK GENMASK(15, 3) +#define RCC_PLLNFRACR_FRACLE BIT(16) + +/* used for ALL PLLNCSGR registers */ +#define RCC_PLLNCSGR_INC_STEP_SHIFT 16 +#define RCC_PLLNCSGR_INC_STEP_MASK GENMASK(30, 16) +#define RCC_PLLNCSGR_MOD_PER_SHIFT 0 +#define RCC_PLLNCSGR_MOD_PER_MASK GENMASK(12, 0) +#define RCC_PLLNCSGR_SSCG_MODE_SHIFT 15 +#define RCC_PLLNCSGR_SSCG_MODE_MASK BIT(15) + +/* used for RCC_OCENSETR and RCC_OCENCLRR registers */ +#define RCC_OCENR_HSION BIT(0) +#define RCC_OCENR_CSION BIT(4) +#define RCC_OCENR_HSEON BIT(8) +#define RCC_OCENR_HSEBYP BIT(10) +#define RCC_OCENR_HSECSSON BIT(11) + +/* Fields of RCC_OCRDYR register */ +#define RCC_OCRDYR_HSIRDY BIT(0) +#define RCC_OCRDYR_HSIDIVRDY BIT(2) +#define RCC_OCRDYR_CSIRDY BIT(4) +#define RCC_OCRDYR_HSERDY BIT(8) + +/* Fields of DDRITFCR register */ +#define RCC_DDRITFCR_DDRCKMOD_MASK GENMASK(22, 20) +#define RCC_DDRITFCR_DDRCKMOD_SHIFT 20 +#define RCC_DDRITFCR_DDRCKMOD_SSR 0 + +/* Fields of RCC_HSICFGR register */ +#define RCC_HSICFGR_HSIDIV_MASK GENMASK(1, 0) + +/* used for MCO related operations */ +#define RCC_MCOCFG_MCOON BIT(12) +#define RCC_MCOCFG_MCODIV_MASK GENMASK(7, 4) +#define RCC_MCOCFG_MCODIV_SHIFT 4 +#define RCC_MCOCFG_MCOSRC_MASK GENMASK(2, 0) + +enum stm32mp1_parent_id { +/* + * _HSI, _HSE, _CSI, _LSI, _LSE should not be moved + * they are used as index in osc[] as entry point + */ + _HSI, + _HSE, + _CSI, + _LSI, + _LSE, + _I2S_CKIN, + _USB_PHY_48, + NB_OSC, + +/* other parent source */ + _HSI_KER = NB_OSC, + _HSE_KER, + _HSE_KER_DIV2, + _CSI_KER, + _PLL1_P, + _PLL1_Q, + _PLL1_R, + _PLL2_P, + _PLL2_Q, + _PLL2_R, + _PLL3_P, + _PLL3_Q, + _PLL3_R, + _PLL4_P, + _PLL4_Q, + _PLL4_R, + _ACLK, + _PCLK1, + _PCLK2, + _PCLK3, + _PCLK4, + _PCLK5, + _HCLK6, + _HCLK2, + _CK_PER, + _CK_MPU, + _CK_MCU, + _PARENT_NB, + _UNKNOWN_ID = 0xff, +}; + +enum stm32mp1_parent_sel { + _I2C12_SEL, + _I2C35_SEL, + _I2C46_SEL, + _UART6_SEL, + _UART24_SEL, + _UART35_SEL, + _UART78_SEL, + _SDMMC12_SEL, + _SDMMC3_SEL, + _ETH_SEL, + _QSPI_SEL, + _FMC_SEL, + _USBPHY_SEL, + _USBO_SEL, + _STGEN_SEL, + _PARENT_SEL_NB, + _UNKNOWN_SEL = 0xff, +}; + +enum stm32mp1_pll_id { + _PLL1, + _PLL2, + _PLL3, + _PLL4, + _PLL_NB +}; + +enum stm32mp1_div_id { + _DIV_P, + _DIV_Q, + _DIV_R, + _DIV_NB, +}; + +enum stm32mp1_clksrc_id { + CLKSRC_MPU, + CLKSRC_AXI, + CLKSRC_MCU, + CLKSRC_PLL12, + CLKSRC_PLL3, + CLKSRC_PLL4, + CLKSRC_RTC, + CLKSRC_MCO1, + CLKSRC_MCO2, + CLKSRC_NB +}; + +enum stm32mp1_clkdiv_id { + CLKDIV_MPU, + CLKDIV_AXI, + CLKDIV_MCU, + CLKDIV_APB1, + CLKDIV_APB2, + CLKDIV_APB3, + CLKDIV_APB4, + CLKDIV_APB5, + CLKDIV_RTC, + CLKDIV_MCO1, + CLKDIV_MCO2, + CLKDIV_NB +}; + +enum stm32mp1_pllcfg { + PLLCFG_M, + PLLCFG_N, + PLLCFG_P, + PLLCFG_Q, + PLLCFG_R, + PLLCFG_O, + PLLCFG_NB +}; + +enum stm32mp1_pllcsg { + PLLCSG_MOD_PER, + PLLCSG_INC_STEP, + PLLCSG_SSCG_MODE, + PLLCSG_NB +}; + +enum stm32mp1_plltype { + PLL_800, + PLL_1600, + PLL_TYPE_NB +}; + +struct stm32mp1_pll { + u8 refclk_min; + u8 refclk_max; + u8 divn_max; +}; + +struct stm32mp1_clk_gate { + u16 offset; + u8 bit; + u8 index; + u8 set_clr; + u8 sel; + u8 fixed; +}; + +struct stm32mp1_clk_sel { + u16 offset; + u8 src; + u8 msk; + u8 nb_parent; + const u8 *parent; +}; + +#define REFCLK_SIZE 4 +struct stm32mp1_clk_pll { + enum stm32mp1_plltype plltype; + u16 rckxselr; + u16 pllxcfgr1; + u16 pllxcfgr2; + u16 pllxfracr; + u16 pllxcr; + u16 pllxcsgr; + u8 refclk[REFCLK_SIZE]; +}; + +struct stm32mp1_clk_data { + const struct stm32mp1_clk_gate *gate; + const struct stm32mp1_clk_sel *sel; + const struct stm32mp1_clk_pll *pll; + const int nb_gate; +}; + +struct stm32mp1_clk_priv { + fdt_addr_t base; + const struct stm32mp1_clk_data *data; + ulong osc[NB_OSC]; + struct udevice *osc_dev[NB_OSC]; +}; + +#define STM32MP1_CLK(off, b, idx, s) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 0, \ + .sel = (s), \ + .fixed = _UNKNOWN_ID, \ + } + +#define STM32MP1_CLK_F(off, b, idx, f) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 0, \ + .sel = _UNKNOWN_SEL, \ + .fixed = (f), \ + } + +#define STM32MP1_CLK_SET_CLR(off, b, idx, s) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 1, \ + .sel = (s), \ + .fixed = _UNKNOWN_ID, \ + } + +#define STM32MP1_CLK_SET_CLR_F(off, b, idx, f) \ + { \ + .offset = (off), \ + .bit = (b), \ + .index = (idx), \ + .set_clr = 1, \ + .sel = _UNKNOWN_SEL, \ + .fixed = (f), \ + } + +#define STM32MP1_CLK_PARENT(idx, off, s, m, p) \ + [(idx)] = { \ + .offset = (off), \ + .src = (s), \ + .msk = (m), \ + .parent = (p), \ + .nb_parent = ARRAY_SIZE((p)) \ + } + +#define STM32MP1_CLK_PLL(idx, type, off1, off2, off3, off4, off5, off6,\ + p1, p2, p3, p4) \ + [(idx)] = { \ + .plltype = (type), \ + .rckxselr = (off1), \ + .pllxcfgr1 = (off2), \ + .pllxcfgr2 = (off3), \ + .pllxfracr = (off4), \ + .pllxcr = (off5), \ + .pllxcsgr = (off6), \ + .refclk[0] = (p1), \ + .refclk[1] = (p2), \ + .refclk[2] = (p3), \ + .refclk[3] = (p4), \ + } + +static const u8 stm32mp1_clks[][2] = { + {CK_PER, _CK_PER}, + {CK_MPU, _CK_MPU}, + {CK_AXI, _ACLK}, + {CK_MCU, _CK_MCU}, + {CK_HSE, _HSE}, + {CK_CSI, _CSI}, + {CK_LSI, _LSI}, + {CK_LSE, _LSE}, + {CK_HSI, _HSI}, + {CK_HSE_DIV2, _HSE_KER_DIV2}, +}; + +static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { + STM32MP1_CLK(RCC_DDRITFCR, 0, DDRC1, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 1, DDRC1LP, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 2, DDRC2, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 3, DDRC2LP, _UNKNOWN_SEL), + STM32MP1_CLK_F(RCC_DDRITFCR, 4, DDRPHYC, _PLL2_R), + STM32MP1_CLK(RCC_DDRITFCR, 5, DDRPHYCLP, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 6, DDRCAPB, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 7, DDRCAPBLP, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 8, AXIDCG, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 9, DDRPHYCAPB, _UNKNOWN_SEL), + STM32MP1_CLK(RCC_DDRITFCR, 10, DDRPHYCAPBLP, _UNKNOWN_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 14, USART2_K, _UART24_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 15, USART3_K, _UART35_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 16, UART4_K, _UART24_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 17, UART5_K, _UART35_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 18, UART7_K, _UART78_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 19, UART8_K, _UART78_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 21, I2C1_K, _I2C12_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 22, I2C2_K, _I2C12_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 23, I2C3_K, _I2C35_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB1ENSETR, 24, I2C5_K, _I2C35_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_APB2ENSETR, 13, USART6_K, _UART6_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 8, DDRPERFM, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 15, IWDG2, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 2, I2C4_K, _I2C46_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 20, STGEN_K, _STGEN_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_AHB2ENSETR, 8, USBO_K, _USBO_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB2ENSETR, 16, SDMMC3_K, _SDMMC3_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 0, GPIOA, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 1, GPIOB, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 2, GPIOC, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 3, GPIOD, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 4, GPIOE, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 5, GPIOF, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 6, GPIOG, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 7, GPIOH, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 8, GPIOI, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 9, GPIOJ, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 10, GPIOK, _UNKNOWN_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_AHB5ENSETR, 0, GPIOZ, _UNKNOWN_SEL), + + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 7, ETHCK, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 8, ETHTX, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 9, ETHRX, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 10, ETHMAC_K, _ETH_SEL), + STM32MP1_CLK_SET_CLR_F(RCC_MP_AHB6ENSETR, 10, ETHMAC, _ACLK), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 12, FMC_K, _FMC_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 14, QSPI_K, _QSPI_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 16, SDMMC1_K, _SDMMC12_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 17, SDMMC2_K, _SDMMC12_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 24, USBH, _UNKNOWN_SEL), + + STM32MP1_CLK(RCC_DBGCFGR, 8, CK_DBG, _UNKNOWN_SEL), +}; + +static const u8 i2c12_parents[] = {_PCLK1, _PLL4_R, _HSI_KER, _CSI_KER}; +static const u8 i2c35_parents[] = {_PCLK1, _PLL4_R, _HSI_KER, _CSI_KER}; +static const u8 i2c46_parents[] = {_PCLK5, _PLL3_Q, _HSI_KER, _CSI_KER}; +static const u8 uart6_parents[] = {_PCLK2, _PLL4_Q, _HSI_KER, _CSI_KER, + _HSE_KER}; +static const u8 uart24_parents[] = {_PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, + _HSE_KER}; +static const u8 uart35_parents[] = {_PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, + _HSE_KER}; +static const u8 uart78_parents[] = {_PCLK1, _PLL4_Q, _HSI_KER, _CSI_KER, + _HSE_KER}; +static const u8 sdmmc12_parents[] = {_HCLK6, _PLL3_R, _PLL4_P, _HSI_KER}; +static const u8 sdmmc3_parents[] = {_HCLK2, _PLL3_R, _PLL4_P, _HSI_KER}; +static const u8 eth_parents[] = {_PLL4_P, _PLL3_Q}; +static const u8 qspi_parents[] = {_ACLK, _PLL3_R, _PLL4_P, _CK_PER}; +static const u8 fmc_parents[] = {_ACLK, _PLL3_R, _PLL4_P, _CK_PER}; +static const u8 usbphy_parents[] = {_HSE_KER, _PLL4_R, _HSE_KER_DIV2}; +static const u8 usbo_parents[] = {_PLL4_R, _USB_PHY_48}; +static const u8 stgen_parents[] = {_HSI_KER, _HSE_KER}; + +static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { + STM32MP1_CLK_PARENT(_I2C12_SEL, RCC_I2C12CKSELR, 0, 0x7, i2c12_parents), + STM32MP1_CLK_PARENT(_I2C35_SEL, RCC_I2C35CKSELR, 0, 0x7, i2c35_parents), + STM32MP1_CLK_PARENT(_I2C46_SEL, RCC_I2C46CKSELR, 0, 0x7, i2c46_parents), + STM32MP1_CLK_PARENT(_UART6_SEL, RCC_UART6CKSELR, 0, 0x7, uart6_parents), + STM32MP1_CLK_PARENT(_UART24_SEL, RCC_UART24CKSELR, 0, 0x7, + uart24_parents), + STM32MP1_CLK_PARENT(_UART35_SEL, RCC_UART35CKSELR, 0, 0x7, + uart35_parents), + STM32MP1_CLK_PARENT(_UART78_SEL, RCC_UART78CKSELR, 0, 0x7, + uart78_parents), + STM32MP1_CLK_PARENT(_SDMMC12_SEL, RCC_SDMMC12CKSELR, 0, 0x7, + sdmmc12_parents), + STM32MP1_CLK_PARENT(_SDMMC3_SEL, RCC_SDMMC3CKSELR, 0, 0x7, + sdmmc3_parents), + STM32MP1_CLK_PARENT(_ETH_SEL, RCC_ETHCKSELR, 0, 0x3, eth_parents), + STM32MP1_CLK_PARENT(_QSPI_SEL, RCC_QSPICKSELR, 0, 0xf, qspi_parents), + STM32MP1_CLK_PARENT(_FMC_SEL, RCC_FMCCKSELR, 0, 0xf, fmc_parents), + STM32MP1_CLK_PARENT(_USBPHY_SEL, RCC_USBCKSELR, 0, 0x3, usbphy_parents), + STM32MP1_CLK_PARENT(_USBO_SEL, RCC_USBCKSELR, 4, 0x1, usbo_parents), + STM32MP1_CLK_PARENT(_STGEN_SEL, RCC_STGENCKSELR, 0, 0x3, stgen_parents), +}; + +#ifdef STM32MP1_CLOCK_TREE_INIT +/* define characteristic of PLL according type */ +#define DIVN_MIN 24 +static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = { + [PLL_800] = { + .refclk_min = 4, + .refclk_max = 16, + .divn_max = 99, + }, + [PLL_1600] = { + .refclk_min = 8, + .refclk_max = 16, + .divn_max = 199, + }, +}; +#endif /* STM32MP1_CLOCK_TREE_INIT */ + +static const struct stm32mp1_clk_pll stm32mp1_clk_pll[_PLL_NB] = { + STM32MP1_CLK_PLL(_PLL1, PLL_1600, + RCC_RCK12SELR, RCC_PLL1CFGR1, RCC_PLL1CFGR2, + RCC_PLL1FRACR, RCC_PLL1CR, RCC_PLL1CSGR, + _HSI, _HSE, _UNKNOWN_ID, _UNKNOWN_ID), + STM32MP1_CLK_PLL(_PLL2, PLL_1600, + RCC_RCK12SELR, RCC_PLL2CFGR1, RCC_PLL2CFGR2, + RCC_PLL2FRACR, RCC_PLL2CR, RCC_PLL2CSGR, + _HSI, _HSE, _UNKNOWN_ID, _UNKNOWN_ID), + STM32MP1_CLK_PLL(_PLL3, PLL_800, + RCC_RCK3SELR, RCC_PLL3CFGR1, RCC_PLL3CFGR2, + RCC_PLL3FRACR, RCC_PLL3CR, RCC_PLL3CSGR, + _HSI, _HSE, _CSI, _UNKNOWN_ID), + STM32MP1_CLK_PLL(_PLL4, PLL_800, + RCC_RCK4SELR, RCC_PLL4CFGR1, RCC_PLL4CFGR2, + RCC_PLL4FRACR, RCC_PLL4CR, RCC_PLL4CSGR, + _HSI, _HSE, _CSI, _I2S_CKIN), +}; + +/* Prescaler table lookups for clock computation */ +/* div = /1 /2 /4 /8 / 16 /64 /128 /512 */ +static const u8 stm32mp1_mcu_div[16] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +/* div = /1 /2 /4 /8 /16 : same divider for pmu and apbx*/ +#define stm32mp1_mpu_div stm32mp1_mpu_apbx_div +#define stm32mp1_apbx_div stm32mp1_mpu_apbx_div +static const u8 stm32mp1_mpu_apbx_div[8] = { + 0, 1, 2, 3, 4, 4, 4, 4 +}; + +/* div = /1 /2 /3 /4 */ +static const u8 stm32mp1_axi_div[8] = { + 1, 2, 3, 4, 4, 4, 4, 4 +}; + +#ifdef DEBUG +static const char * const stm32mp1_clk_parent_name[_PARENT_NB] = { + [_HSI] = "HSI", + [_HSE] = "HSE", + [_CSI] = "CSI", + [_LSI] = "LSI", + [_LSE] = "LSE", + [_I2S_CKIN] = "I2S_CKIN", + [_HSI_KER] = "HSI_KER", + [_HSE_KER] = "HSE_KER", + [_HSE_KER_DIV2] = "HSE_KER_DIV2", + [_CSI_KER] = "CSI_KER", + [_PLL1_P] = "PLL1_P", + [_PLL1_Q] = "PLL1_Q", + [_PLL1_R] = "PLL1_R", + [_PLL2_P] = "PLL2_P", + [_PLL2_Q] = "PLL2_Q", + [_PLL2_R] = "PLL2_R", + [_PLL3_P] = "PLL3_P", + [_PLL3_Q] = "PLL3_Q", + [_PLL3_R] = "PLL3_R", + [_PLL4_P] = "PLL4_P", + [_PLL4_Q] = "PLL4_Q", + [_PLL4_R] = "PLL4_R", + [_ACLK] = "ACLK", + [_PCLK1] = "PCLK1", + [_PCLK2] = "PCLK2", + [_PCLK3] = "PCLK3", + [_PCLK4] = "PCLK4", + [_PCLK5] = "PCLK5", + [_HCLK6] = "KCLK6", + [_HCLK2] = "HCLK2", + [_CK_PER] = "CK_PER", + [_CK_MPU] = "CK_MPU", + [_CK_MCU] = "CK_MCU", + [_USB_PHY_48] = "USB_PHY_48" +}; + +static const char * const stm32mp1_clk_parent_sel_name[_PARENT_SEL_NB] = { + [_I2C12_SEL] = "I2C12", + [_I2C35_SEL] = "I2C35", + [_I2C46_SEL] = "I2C46", + [_UART6_SEL] = "UART6", + [_UART24_SEL] = "UART24", + [_UART35_SEL] = "UART35", + [_UART78_SEL] = "UART78", + [_SDMMC12_SEL] = "SDMMC12", + [_SDMMC3_SEL] = "SDMMC3", + [_ETH_SEL] = "ETH", + [_QSPI_SEL] = "QSPI", + [_FMC_SEL] = "FMC", + [_USBPHY_SEL] = "USBPHY", + [_USBO_SEL] = "USBO", + [_STGEN_SEL] = "STGEN" +}; +#endif + +static const struct stm32mp1_clk_data stm32mp1_data = { + .gate = stm32mp1_clk_gate, + .sel = stm32mp1_clk_sel, + .pll = stm32mp1_clk_pll, + .nb_gate = ARRAY_SIZE(stm32mp1_clk_gate), +}; + +static ulong stm32mp1_clk_get_fixed(struct stm32mp1_clk_priv *priv, int idx) +{ + if (idx >= NB_OSC) { + debug("%s: clk id %d not found\n", __func__, idx); + return 0; + } + + debug("%s: clk id %d = %x : %ld kHz\n", __func__, idx, + (u32)priv->osc[idx], priv->osc[idx] / 1000); + + return priv->osc[idx]; +} + +static int stm32mp1_clk_get_id(struct stm32mp1_clk_priv *priv, unsigned long id) +{ + const struct stm32mp1_clk_gate *gate = priv->data->gate; + int i, nb_clks = priv->data->nb_gate; + + for (i = 0; i < nb_clks; i++) { + if (gate[i].index == id) + break; + } + + if (i == nb_clks) { + printf("%s: clk id %d not found\n", __func__, (u32)id); + return -EINVAL; + } + + return i; +} + +static int stm32mp1_clk_get_sel(struct stm32mp1_clk_priv *priv, + int i) +{ + const struct stm32mp1_clk_gate *gate = priv->data->gate; + + if (gate[i].sel > _PARENT_SEL_NB) { + printf("%s: parents for clk id %d not found\n", + __func__, i); + return -EINVAL; + } + + return gate[i].sel; +} + +static int stm32mp1_clk_get_fixed_parent(struct stm32mp1_clk_priv *priv, + int i) +{ + const struct stm32mp1_clk_gate *gate = priv->data->gate; + + if (gate[i].fixed == _UNKNOWN_ID) + return -ENOENT; + + return gate[i].fixed; +} + +static int stm32mp1_clk_get_parent(struct stm32mp1_clk_priv *priv, + unsigned long id) +{ + const struct stm32mp1_clk_sel *sel = priv->data->sel; + int i; + int s, p; + + for (i = 0; i < ARRAY_SIZE(stm32mp1_clks); i++) + if (stm32mp1_clks[i][0] == id) + return stm32mp1_clks[i][1]; + + i = stm32mp1_clk_get_id(priv, id); + if (i < 0) + return i; + + p = stm32mp1_clk_get_fixed_parent(priv, i); + if (p >= 0 && p < _PARENT_NB) + return p; + + s = stm32mp1_clk_get_sel(priv, i); + if (s < 0) + return s; + + p = (readl(priv->base + sel[s].offset) >> sel[s].src) & sel[s].msk; + + if (p < sel[s].nb_parent) { +#ifdef DEBUG + debug("%s: %s clock is the parent %s of clk id %d\n", __func__, + stm32mp1_clk_parent_name[sel[s].parent[p]], + stm32mp1_clk_parent_sel_name[s], + (u32)id); +#endif + return sel[s].parent[p]; + } + + pr_err("%s: no parents defined for clk id %d\n", + __func__, (u32)id); + + return -EINVAL; +} + +static ulong stm32mp1_read_pll_freq(struct stm32mp1_clk_priv *priv, + int pll_id, int div_id) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + int divm, divn, divy, src; + ulong refclk, dfout; + u32 selr, cfgr1, cfgr2, fracr; + const u8 shift[_DIV_NB] = { + [_DIV_P] = RCC_PLLNCFGR2_DIVP_SHIFT, + [_DIV_Q] = RCC_PLLNCFGR2_DIVQ_SHIFT, + [_DIV_R] = RCC_PLLNCFGR2_DIVR_SHIFT }; + + debug("%s(%d, %d)\n", __func__, pll_id, div_id); + if (div_id > _DIV_NB) + return 0; + + selr = readl(priv->base + pll[pll_id].rckxselr); + cfgr1 = readl(priv->base + pll[pll_id].pllxcfgr1); + cfgr2 = readl(priv->base + pll[pll_id].pllxcfgr2); + fracr = readl(priv->base + pll[pll_id].pllxfracr); + + debug("PLL%d : selr=%x cfgr1=%x cfgr2=%x fracr=%x\n", + pll_id, selr, cfgr1, cfgr2, fracr); + + divm = (cfgr1 & (RCC_PLLNCFGR1_DIVM_MASK)) >> RCC_PLLNCFGR1_DIVM_SHIFT; + divn = cfgr1 & RCC_PLLNCFGR1_DIVN_MASK; + divy = (cfgr2 >> shift[div_id]) & RCC_PLLNCFGR2_DIVX_MASK; + + debug(" DIVN=%d DIVM=%d DIVY=%d\n", divn, divm, divy); + + src = selr & RCC_SELR_SRC_MASK; + refclk = stm32mp1_clk_get_fixed(priv, pll[pll_id].refclk[src]); + + debug(" refclk = %d kHz\n", (u32)(refclk / 1000)); + + /* + * For: PLL1 & PLL2 => VCO is * 2 but ck_pll_y is also / 2 + * So same final result than PLL2 et 4 + * with FRACV : + * Fck_pll_y = Fck_ref * ((DIVN + 1) + FRACV / 2^13) + * / (DIVM + 1) * (DIVy + 1) + * without FRACV + * Fck_pll_y = Fck_ref * ((DIVN + 1) / (DIVM + 1) *(DIVy + 1) + */ + if (fracr & RCC_PLLNFRACR_FRACLE) { + u32 fracv = (fracr & RCC_PLLNFRACR_FRACV_MASK) + >> RCC_PLLNFRACR_FRACV_SHIFT; + dfout = (ulong)lldiv((unsigned long long)refclk * + (((divn + 1) << 13) + fracv), + ((unsigned long long)(divm + 1) * + (divy + 1)) << 13); + } else { + dfout = (ulong)(refclk * (divn + 1) / (divm + 1) * (divy + 1)); + } + debug(" => dfout = %d kHz\n", (u32)(dfout / 1000)); + + return dfout; +} + +static ulong stm32mp1_clk_get(struct stm32mp1_clk_priv *priv, int p) +{ + u32 reg; + ulong clock = 0; + + switch (p) { + case _CK_MPU: + /* MPU sub system */ + reg = readl(priv->base + RCC_MPCKSELR); + switch (reg & RCC_SELR_SRC_MASK) { + case RCC_MPCKSELR_HSI: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case RCC_MPCKSELR_HSE: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + break; + case RCC_MPCKSELR_PLL: + case RCC_MPCKSELR_PLL_MPUDIV: + clock = stm32mp1_read_pll_freq(priv, _PLL1, _DIV_P); + if (p == RCC_MPCKSELR_PLL_MPUDIV) { + reg = readl(priv->base + RCC_MPCKDIVR); + clock /= stm32mp1_mpu_div[reg & + RCC_MPUDIV_MASK]; + } + break; + } + break; + /* AXI sub system */ + case _ACLK: + case _HCLK2: + case _HCLK6: + case _PCLK4: + case _PCLK5: + reg = readl(priv->base + RCC_ASSCKSELR); + switch (reg & RCC_SELR_SRC_MASK) { + case RCC_ASSCKSELR_HSI: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case RCC_ASSCKSELR_HSE: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + break; + case RCC_ASSCKSELR_PLL: + clock = stm32mp1_read_pll_freq(priv, _PLL2, _DIV_P); + break; + } + + /* System clock divider */ + reg = readl(priv->base + RCC_AXIDIVR); + clock /= stm32mp1_axi_div[reg & RCC_AXIDIV_MASK]; + + switch (p) { + case _PCLK4: + reg = readl(priv->base + RCC_APB4DIVR); + clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK]; + break; + case _PCLK5: + reg = readl(priv->base + RCC_APB5DIVR); + clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK]; + break; + default: + break; + } + break; + /* MCU sub system */ + case _CK_MCU: + case _PCLK1: + case _PCLK2: + case _PCLK3: + reg = readl(priv->base + RCC_MSSCKSELR); + switch (reg & RCC_SELR_SRC_MASK) { + case RCC_MSSCKSELR_HSI: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case RCC_MSSCKSELR_HSE: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + break; + case RCC_MSSCKSELR_CSI: + clock = stm32mp1_clk_get_fixed(priv, _CSI); + break; + case RCC_MSSCKSELR_PLL: + clock = stm32mp1_read_pll_freq(priv, _PLL3, _DIV_P); + break; + } + + /* MCU clock divider */ + reg = readl(priv->base + RCC_MCUDIVR); + clock >>= stm32mp1_mcu_div[reg & RCC_MCUDIV_MASK]; + + switch (p) { + case _PCLK1: + reg = readl(priv->base + RCC_APB1DIVR); + clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK]; + break; + case _PCLK2: + reg = readl(priv->base + RCC_APB2DIVR); + clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK]; + break; + case _PCLK3: + reg = readl(priv->base + RCC_APB3DIVR); + clock >>= stm32mp1_apbx_div[reg & RCC_APBXDIV_MASK]; + break; + case _CK_MCU: + default: + break; + } + break; + case _CK_PER: + reg = readl(priv->base + RCC_CPERCKSELR); + switch (reg & RCC_SELR_SRC_MASK) { + case RCC_CPERCKSELR_HSI: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case RCC_CPERCKSELR_HSE: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + break; + case RCC_CPERCKSELR_CSI: + clock = stm32mp1_clk_get_fixed(priv, _CSI); + break; + } + break; + case _HSI: + case _HSI_KER: + clock = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case _CSI: + case _CSI_KER: + clock = stm32mp1_clk_get_fixed(priv, _CSI); + break; + case _HSE: + case _HSE_KER: + case _HSE_KER_DIV2: + clock = stm32mp1_clk_get_fixed(priv, _HSE); + if (p == _HSE_KER_DIV2) + clock >>= 1; + break; + case _LSI: + clock = stm32mp1_clk_get_fixed(priv, _LSI); + break; + case _LSE: + clock = stm32mp1_clk_get_fixed(priv, _LSE); + break; + /* PLL */ + case _PLL1_P: + case _PLL1_Q: + case _PLL1_R: + clock = stm32mp1_read_pll_freq(priv, _PLL1, p - _PLL1_P); + break; + case _PLL2_P: + case _PLL2_Q: + case _PLL2_R: + clock = stm32mp1_read_pll_freq(priv, _PLL2, p - _PLL2_P); + break; + case _PLL3_P: + case _PLL3_Q: + case _PLL3_R: + clock = stm32mp1_read_pll_freq(priv, _PLL3, p - _PLL3_P); + break; + case _PLL4_P: + case _PLL4_Q: + case _PLL4_R: + clock = stm32mp1_read_pll_freq(priv, _PLL4, p - _PLL4_P); + break; + /* other */ + case _USB_PHY_48: + clock = stm32mp1_clk_get_fixed(priv, _USB_PHY_48); + break; + + default: + break; + } + + debug("%s(%d) clock = %lx : %ld kHz\n", + __func__, p, clock, clock / 1000); + + return clock; +} + +static int stm32mp1_clk_enable(struct clk *clk) +{ + struct stm32mp1_clk_priv *priv = dev_get_priv(clk->dev); + const struct stm32mp1_clk_gate *gate = priv->data->gate; + int i = stm32mp1_clk_get_id(priv, clk->id); + + if (i < 0) + return i; + + if (gate[i].set_clr) + writel(BIT(gate[i].bit), priv->base + gate[i].offset); + else + setbits_le32(priv->base + gate[i].offset, BIT(gate[i].bit)); + + debug("%s: id clock %d has been enabled\n", __func__, (u32)clk->id); + + return 0; +} + +static int stm32mp1_clk_disable(struct clk *clk) +{ + struct stm32mp1_clk_priv *priv = dev_get_priv(clk->dev); + const struct stm32mp1_clk_gate *gate = priv->data->gate; + int i = stm32mp1_clk_get_id(priv, clk->id); + + if (i < 0) + return i; + + if (gate[i].set_clr) + writel(BIT(gate[i].bit), + priv->base + gate[i].offset + + RCC_MP_ENCLRR_OFFSET); + else + clrbits_le32(priv->base + gate[i].offset, BIT(gate[i].bit)); + + debug("%s: id clock %d has been disabled\n", __func__, (u32)clk->id); + + return 0; +} + +static ulong stm32mp1_clk_get_rate(struct clk *clk) +{ + struct stm32mp1_clk_priv *priv = dev_get_priv(clk->dev); + int p = stm32mp1_clk_get_parent(priv, clk->id); + ulong rate; + + if (p < 0) + return 0; + + rate = stm32mp1_clk_get(priv, p); + +#ifdef DEBUG + debug("%s: computed rate for id clock %d is %d (parent is %s)\n", + __func__, (u32)clk->id, (u32)rate, stm32mp1_clk_parent_name[p]); +#endif + return rate; +} + +#ifdef STM32MP1_CLOCK_TREE_INIT +static void stm32mp1_ls_osc_set(int enable, fdt_addr_t rcc, u32 offset, + u32 mask_on) +{ + u32 address = rcc + offset; + + if (enable) + setbits_le32(address, mask_on); + else + clrbits_le32(address, mask_on); +} + +static void stm32mp1_hs_ocs_set(int enable, fdt_addr_t rcc, u32 mask_on) +{ + if (enable) + setbits_le32(rcc + RCC_OCENSETR, mask_on); + else + setbits_le32(rcc + RCC_OCENCLRR, mask_on); +} + +static int stm32mp1_osc_wait(int enable, fdt_addr_t rcc, u32 offset, + u32 mask_rdy) +{ + u32 mask_test = 0; + u32 address = rcc + offset; + u32 val; + int ret; + + if (enable) + mask_test = mask_rdy; + + ret = readl_poll_timeout(address, val, + (val & mask_rdy) == mask_test, + TIMEOUT_1S); + + if (ret) + pr_err("OSC %x @ %x timeout for enable=%d : 0x%x\n", + mask_rdy, address, enable, readl(address)); + + return ret; +} + +static void stm32mp1_lse_enable(fdt_addr_t rcc, int bypass, int lsedrv) +{ + u32 value; + + if (bypass) + setbits_le32(rcc + RCC_BDCR, RCC_BDCR_LSEBYP); + + /* + * warning: not recommended to switch directly from "high drive" + * to "medium low drive", and vice-versa. + */ + value = (readl(rcc + RCC_BDCR) & RCC_BDCR_LSEDRV_MASK) + >> RCC_BDCR_LSEDRV_SHIFT; + + while (value != lsedrv) { + if (value > lsedrv) + value--; + else + value++; + + clrsetbits_le32(rcc + RCC_BDCR, + RCC_BDCR_LSEDRV_MASK, + value << RCC_BDCR_LSEDRV_SHIFT); + } + + stm32mp1_ls_osc_set(1, rcc, RCC_BDCR, RCC_BDCR_LSEON); +} + +static void stm32mp1_lse_wait(fdt_addr_t rcc) +{ + stm32mp1_osc_wait(1, rcc, RCC_BDCR, RCC_BDCR_LSERDY); +} + +static void stm32mp1_lsi_set(fdt_addr_t rcc, int enable) +{ + stm32mp1_ls_osc_set(enable, rcc, RCC_RDLSICR, RCC_RDLSICR_LSION); + stm32mp1_osc_wait(enable, rcc, RCC_RDLSICR, RCC_RDLSICR_LSIRDY); +} + +static void stm32mp1_hse_enable(fdt_addr_t rcc, int bypass, int css) +{ + if (bypass) + setbits_le32(rcc + RCC_OCENSETR, RCC_OCENR_HSEBYP); + + stm32mp1_hs_ocs_set(1, rcc, RCC_OCENR_HSEON); + stm32mp1_osc_wait(1, rcc, RCC_OCRDYR, RCC_OCRDYR_HSERDY); + + if (css) + setbits_le32(rcc + RCC_OCENSETR, RCC_OCENR_HSECSSON); +} + +static void stm32mp1_csi_set(fdt_addr_t rcc, int enable) +{ + stm32mp1_ls_osc_set(enable, rcc, RCC_OCENSETR, RCC_OCENR_CSION); + stm32mp1_osc_wait(enable, rcc, RCC_OCRDYR, RCC_OCRDYR_CSIRDY); +} + +static void stm32mp1_hsi_set(fdt_addr_t rcc, int enable) +{ + stm32mp1_hs_ocs_set(enable, rcc, RCC_OCENR_HSION); + stm32mp1_osc_wait(enable, rcc, RCC_OCRDYR, RCC_OCRDYR_HSIRDY); +} + +static int stm32mp1_set_hsidiv(fdt_addr_t rcc, u8 hsidiv) +{ + u32 address = rcc + RCC_OCRDYR; + u32 val; + int ret; + + clrsetbits_le32(rcc + RCC_HSICFGR, + RCC_HSICFGR_HSIDIV_MASK, + RCC_HSICFGR_HSIDIV_MASK & hsidiv); + + ret = readl_poll_timeout(address, val, + val & RCC_OCRDYR_HSIDIVRDY, + TIMEOUT_200MS); + if (ret) + pr_err("HSIDIV failed @ 0x%x: 0x%x\n", + address, readl(address)); + + return ret; +} + +static int stm32mp1_hsidiv(fdt_addr_t rcc, ulong hsifreq) +{ + u8 hsidiv; + u32 hsidivfreq = MAX_HSI_HZ; + + for (hsidiv = 0; hsidiv < 4; hsidiv++, + hsidivfreq = hsidivfreq / 2) + if (hsidivfreq == hsifreq) + break; + + if (hsidiv == 4) { + pr_err("clk-hsi frequency invalid"); + return -1; + } + + if (hsidiv > 0) + return stm32mp1_set_hsidiv(rcc, hsidiv); + + return 0; +} + +static void pll_start(struct stm32mp1_clk_priv *priv, int pll_id) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + + writel(RCC_PLLNCR_PLLON, priv->base + pll[pll_id].pllxcr); +} + +static int pll_output(struct stm32mp1_clk_priv *priv, int pll_id, int output) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + u32 pllxcr = priv->base + pll[pll_id].pllxcr; + u32 val; + int ret; + + ret = readl_poll_timeout(pllxcr, val, val & RCC_PLLNCR_PLLRDY, + TIMEOUT_200MS); + + if (ret) { + pr_err("PLL%d start failed @ 0x%x: 0x%x\n", + pll_id, pllxcr, readl(pllxcr)); + return ret; + } + + /* start the requested output */ + setbits_le32(pllxcr, output << RCC_PLLNCR_DIVEN_SHIFT); + + return 0; +} + +static int pll_stop(struct stm32mp1_clk_priv *priv, int pll_id) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + u32 pllxcr = priv->base + pll[pll_id].pllxcr; + u32 val; + + /* stop all output */ + clrbits_le32(pllxcr, + RCC_PLLNCR_DIVPEN | RCC_PLLNCR_DIVQEN | RCC_PLLNCR_DIVREN); + + /* stop PLL */ + clrbits_le32(pllxcr, RCC_PLLNCR_PLLON); + + /* wait PLL stopped */ + return readl_poll_timeout(pllxcr, val, (val & RCC_PLLNCR_PLLRDY) == 0, + TIMEOUT_200MS); +} + +static void pll_config_output(struct stm32mp1_clk_priv *priv, + int pll_id, u32 *pllcfg) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + fdt_addr_t rcc = priv->base; + u32 value; + + value = (pllcfg[PLLCFG_P] << RCC_PLLNCFGR2_DIVP_SHIFT) + & RCC_PLLNCFGR2_DIVP_MASK; + value |= (pllcfg[PLLCFG_Q] << RCC_PLLNCFGR2_DIVQ_SHIFT) + & RCC_PLLNCFGR2_DIVQ_MASK; + value |= (pllcfg[PLLCFG_R] << RCC_PLLNCFGR2_DIVR_SHIFT) + & RCC_PLLNCFGR2_DIVR_MASK; + writel(value, rcc + pll[pll_id].pllxcfgr2); +} + +static int pll_config(struct stm32mp1_clk_priv *priv, int pll_id, + u32 *pllcfg, u32 fracv) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + fdt_addr_t rcc = priv->base; + enum stm32mp1_plltype type = pll[pll_id].plltype; + int src; + ulong refclk; + u8 ifrge = 0; + u32 value; + + src = readl(priv->base + pll[pll_id].rckxselr) & RCC_SELR_SRC_MASK; + + refclk = stm32mp1_clk_get_fixed(priv, pll[pll_id].refclk[src]) / + (pllcfg[PLLCFG_M] + 1); + + if (refclk < (stm32mp1_pll[type].refclk_min * 1000000) || + refclk > (stm32mp1_pll[type].refclk_max * 1000000)) { + debug("invalid refclk = %x\n", (u32)refclk); + return -EINVAL; + } + if (type == PLL_800 && refclk >= 8000000) + ifrge = 1; + + value = (pllcfg[PLLCFG_N] << RCC_PLLNCFGR1_DIVN_SHIFT) + & RCC_PLLNCFGR1_DIVN_MASK; + value |= (pllcfg[PLLCFG_M] << RCC_PLLNCFGR1_DIVM_SHIFT) + & RCC_PLLNCFGR1_DIVM_MASK; + value |= (ifrge << RCC_PLLNCFGR1_IFRGE_SHIFT) + & RCC_PLLNCFGR1_IFRGE_MASK; + writel(value, rcc + pll[pll_id].pllxcfgr1); + + /* fractional configuration: load sigma-delta modulator (SDM) */ + + /* Write into FRACV the new fractional value , and FRACLE to 0 */ + writel(fracv << RCC_PLLNFRACR_FRACV_SHIFT, + rcc + pll[pll_id].pllxfracr); + + /* Write FRACLE to 1 : FRACV value is loaded into the SDM */ + setbits_le32(rcc + pll[pll_id].pllxfracr, + RCC_PLLNFRACR_FRACLE); + + pll_config_output(priv, pll_id, pllcfg); + + return 0; +} + +static void pll_csg(struct stm32mp1_clk_priv *priv, int pll_id, u32 *csg) +{ + const struct stm32mp1_clk_pll *pll = priv->data->pll; + u32 pllxcsg; + + pllxcsg = ((csg[PLLCSG_MOD_PER] << RCC_PLLNCSGR_MOD_PER_SHIFT) & + RCC_PLLNCSGR_MOD_PER_MASK) | + ((csg[PLLCSG_INC_STEP] << RCC_PLLNCSGR_INC_STEP_SHIFT) & + RCC_PLLNCSGR_INC_STEP_MASK) | + ((csg[PLLCSG_SSCG_MODE] << RCC_PLLNCSGR_SSCG_MODE_SHIFT) & + RCC_PLLNCSGR_SSCG_MODE_MASK); + + writel(pllxcsg, priv->base + pll[pll_id].pllxcsgr); +} + +static int set_clksrc(struct stm32mp1_clk_priv *priv, unsigned int clksrc) +{ + u32 address = priv->base + (clksrc >> 4); + u32 val; + int ret; + + clrsetbits_le32(address, RCC_SELR_SRC_MASK, clksrc & RCC_SELR_SRC_MASK); + ret = readl_poll_timeout(address, val, val & RCC_SELR_SRCRDY, + TIMEOUT_200MS); + if (ret) + pr_err("CLKSRC %x start failed @ 0x%x: 0x%x\n", + clksrc, address, readl(address)); + + return ret; +} + +static void stgen_config(struct stm32mp1_clk_priv *priv) +{ + int p; + u32 stgenc, cntfid0; + ulong rate; + + stgenc = (u32)syscon_get_first_range(STM32MP_SYSCON_STGEN); + + cntfid0 = readl(stgenc + STGENC_CNTFID0); + p = stm32mp1_clk_get_parent(priv, STGEN_K); + rate = stm32mp1_clk_get(priv, p); + + if (cntfid0 != rate) { + pr_debug("System Generic Counter (STGEN) update\n"); + clrbits_le32(stgenc + STGENC_CNTCR, STGENC_CNTCR_EN); + writel(0x0, stgenc + STGENC_CNTCVL); + writel(0x0, stgenc + STGENC_CNTCVU); + writel(rate, stgenc + STGENC_CNTFID0); + setbits_le32(stgenc + STGENC_CNTCR, STGENC_CNTCR_EN); + + __asm__ volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (rate)); + + /* need to update gd->arch.timer_rate_hz with new frequency */ + timer_init(); + pr_debug("gd->arch.timer_rate_hz = %x\n", + (u32)gd->arch.timer_rate_hz); + pr_debug("Tick = %x\n", (u32)(get_ticks())); + } +} + +static int set_clkdiv(unsigned int clkdiv, u32 address) +{ + u32 val; + int ret; + + clrsetbits_le32(address, RCC_DIVR_DIV_MASK, clkdiv & RCC_DIVR_DIV_MASK); + ret = readl_poll_timeout(address, val, val & RCC_DIVR_DIVRDY, + TIMEOUT_200MS); + if (ret) + pr_err("CLKDIV %x start failed @ 0x%x: 0x%x\n", + clkdiv, address, readl(address)); + + return ret; +} + +static void stm32mp1_mco_csg(struct stm32mp1_clk_priv *priv, + u32 clksrc, u32 clkdiv) +{ + u32 address = priv->base + (clksrc >> 4); + + /* + * binding clksrc : bit15-4 offset + * bit3: disable + * bit2-0: MCOSEL[2:0] + */ + if (clksrc & 0x8) { + clrbits_le32(address, RCC_MCOCFG_MCOON); + } else { + clrsetbits_le32(address, + RCC_MCOCFG_MCOSRC_MASK, + clksrc & RCC_MCOCFG_MCOSRC_MASK); + clrsetbits_le32(address, + RCC_MCOCFG_MCODIV_MASK, + clkdiv << RCC_MCOCFG_MCODIV_SHIFT); + setbits_le32(address, RCC_MCOCFG_MCOON); + } +} + +static void set_rtcsrc(struct stm32mp1_clk_priv *priv, + unsigned int clksrc, + int lse_css) +{ + u32 address = priv->base + RCC_BDCR; + + if (readl(address) & RCC_BDCR_RTCCKEN) + goto skip_rtc; + + if (clksrc == CLK_RTC_DISABLED) + goto skip_rtc; + + clrsetbits_le32(address, + RCC_BDCR_RTCSRC_MASK, + clksrc << RCC_BDCR_RTCSRC_SHIFT); + + setbits_le32(address, RCC_BDCR_RTCCKEN); + +skip_rtc: + if (lse_css) + setbits_le32(address, RCC_BDCR_LSECSSON); +} + +static void pkcs_config(struct stm32mp1_clk_priv *priv, u32 pkcs) +{ + u32 address = priv->base + ((pkcs >> 4) & 0xFFF); + u32 value = pkcs & 0xF; + u32 mask = 0xF; + + if (pkcs & BIT(31)) { + mask <<= 4; + value <<= 4; + } + clrsetbits_le32(address, mask, value); +} + +static int stm32mp1_clktree(struct udevice *dev) +{ + struct stm32mp1_clk_priv *priv = dev_get_priv(dev); + fdt_addr_t rcc = priv->base; + unsigned int clksrc[CLKSRC_NB]; + unsigned int clkdiv[CLKDIV_NB]; + unsigned int pllcfg[_PLL_NB][PLLCFG_NB]; + ofnode plloff[_PLL_NB]; + int ret; + int i, len; + int lse_css = 0; + const u32 *pkcs_cell; + + /* check mandatory field */ + ret = dev_read_u32_array(dev, "st,clksrc", clksrc, CLKSRC_NB); + if (ret < 0) { + debug("field st,clksrc invalid: error %d\n", ret); + return -FDT_ERR_NOTFOUND; + } + + ret = dev_read_u32_array(dev, "st,clkdiv", clkdiv, CLKDIV_NB); + if (ret < 0) { + debug("field st,clkdiv invalid: error %d\n", ret); + return -FDT_ERR_NOTFOUND; + } + + /* check mandatory field in each pll */ + for (i = 0; i < _PLL_NB; i++) { + char name[12]; + + sprintf(name, "st,pll@%d", i); + plloff[i] = dev_read_subnode(dev, name); + if (!ofnode_valid(plloff[i])) + continue; + ret = ofnode_read_u32_array(plloff[i], "cfg", + pllcfg[i], PLLCFG_NB); + if (ret < 0) { + debug("field cfg invalid: error %d\n", ret); + return -FDT_ERR_NOTFOUND; + } + } + + debug("configuration MCO\n"); + stm32mp1_mco_csg(priv, clksrc[CLKSRC_MCO1], clkdiv[CLKDIV_MCO1]); + stm32mp1_mco_csg(priv, clksrc[CLKSRC_MCO2], clkdiv[CLKDIV_MCO2]); + + debug("switch ON osillator\n"); + /* + * switch ON oscillator found in device-tree, + * HSI already ON after bootrom + */ + if (priv->osc[_LSI]) + stm32mp1_lsi_set(rcc, 1); + + if (priv->osc[_LSE]) { + int bypass; + int lsedrv; + struct udevice *dev = priv->osc_dev[_LSE]; + + bypass = dev_read_bool(dev, "st,bypass"); + lse_css = dev_read_bool(dev, "st,css"); + lsedrv = dev_read_u32_default(dev, "st,drive", + LSEDRV_MEDIUM_HIGH); + + stm32mp1_lse_enable(rcc, bypass, lsedrv); + } + + if (priv->osc[_HSE]) { + int bypass, css; + struct udevice *dev = priv->osc_dev[_HSE]; + + bypass = dev_read_bool(dev, "st,bypass"); + css = dev_read_bool(dev, "st,css"); + + stm32mp1_hse_enable(rcc, bypass, css); + } + /* CSI is mandatory for automatic I/O compensation (SYSCFG_CMPCR) + * => switch on CSI even if node is not present in device tree + */ + stm32mp1_csi_set(rcc, 1); + + /* come back to HSI */ + debug("come back to HSI\n"); + set_clksrc(priv, CLK_MPU_HSI); + set_clksrc(priv, CLK_AXI_HSI); + set_clksrc(priv, CLK_MCU_HSI); + + debug("pll stop\n"); + for (i = 0; i < _PLL_NB; i++) + pll_stop(priv, i); + + /* configure HSIDIV */ + debug("configure HSIDIV\n"); + if (priv->osc[_HSI]) { + stm32mp1_hsidiv(rcc, priv->osc[_HSI]); + stgen_config(priv); + } + + /* select DIV */ + debug("select DIV\n"); + /* no ready bit when MPUSRC != CLK_MPU_PLL1P_DIV, MPUDIV is disabled */ + writel(clkdiv[CLKDIV_MPU] & RCC_DIVR_DIV_MASK, rcc + RCC_MPCKDIVR); + set_clkdiv(clkdiv[CLKDIV_AXI], rcc + RCC_AXIDIVR); + set_clkdiv(clkdiv[CLKDIV_APB4], rcc + RCC_APB4DIVR); + set_clkdiv(clkdiv[CLKDIV_APB5], rcc + RCC_APB5DIVR); + set_clkdiv(clkdiv[CLKDIV_MCU], rcc + RCC_MCUDIVR); + set_clkdiv(clkdiv[CLKDIV_APB1], rcc + RCC_APB1DIVR); + set_clkdiv(clkdiv[CLKDIV_APB2], rcc + RCC_APB2DIVR); + set_clkdiv(clkdiv[CLKDIV_APB3], rcc + RCC_APB3DIVR); + + /* no ready bit for RTC */ + writel(clkdiv[CLKDIV_RTC] & RCC_DIVR_DIV_MASK, rcc + RCC_RTCDIVR); + + /* configure PLLs source */ + debug("configure PLLs source\n"); + set_clksrc(priv, clksrc[CLKSRC_PLL12]); + set_clksrc(priv, clksrc[CLKSRC_PLL3]); + set_clksrc(priv, clksrc[CLKSRC_PLL4]); + + /* configure and start PLLs */ + debug("configure PLLs\n"); + for (i = 0; i < _PLL_NB; i++) { + u32 fracv; + u32 csg[PLLCSG_NB]; + + debug("configure PLL %d @ %d\n", i, + ofnode_to_offset(plloff[i])); + if (!ofnode_valid(plloff[i])) + continue; + + fracv = ofnode_read_u32_default(plloff[i], "frac", 0); + pll_config(priv, i, pllcfg[i], fracv); + ret = ofnode_read_u32_array(plloff[i], "csg", csg, PLLCSG_NB); + if (!ret) { + pll_csg(priv, i, csg); + } else if (ret != -FDT_ERR_NOTFOUND) { + debug("invalid csg node for pll@%d res=%d\n", i, ret); + return ret; + } + pll_start(priv, i); + } + + /* wait and start PLLs ouptut when ready */ + for (i = 0; i < _PLL_NB; i++) { + if (!ofnode_valid(plloff[i])) + continue; + debug("output PLL %d\n", i); + pll_output(priv, i, pllcfg[i][PLLCFG_O]); + } + + /* wait LSE ready before to use it */ + if (priv->osc[_LSE]) + stm32mp1_lse_wait(rcc); + + /* configure with expected clock source */ + debug("CLKSRC\n"); + set_clksrc(priv, clksrc[CLKSRC_MPU]); + set_clksrc(priv, clksrc[CLKSRC_AXI]); + set_clksrc(priv, clksrc[CLKSRC_MCU]); + set_rtcsrc(priv, clksrc[CLKSRC_RTC], lse_css); + + /* configure PKCK */ + debug("PKCK\n"); + pkcs_cell = dev_read_prop(dev, "st,pkcs", &len); + if (pkcs_cell) { + bool ckper_disabled = false; + + for (i = 0; i < len / sizeof(u32); i++) { + u32 pkcs = (u32)fdt32_to_cpu(pkcs_cell[i]); + + if (pkcs == CLK_CKPER_DISABLED) { + ckper_disabled = true; + continue; + } + pkcs_config(priv, pkcs); + } + /* CKPER is source for some peripheral clock + * (FMC-NAND / QPSI-NOR) and switching source is allowed + * only if previous clock is still ON + * => deactivated CKPER only after switching clock + */ + if (ckper_disabled) + pkcs_config(priv, CLK_CKPER_DISABLED); + } + + /* STGEN clock source can change with CLK_STGEN_XXX */ + stgen_config(priv); + + debug("oscillator off\n"); + /* switch OFF HSI if not found in device-tree */ + if (!priv->osc[_HSI]) + stm32mp1_hsi_set(rcc, 0); + + /* Software Self-Refresh mode (SSR) during DDR initilialization */ + clrsetbits_le32(priv->base + RCC_DDRITFCR, + RCC_DDRITFCR_DDRCKMOD_MASK, + RCC_DDRITFCR_DDRCKMOD_SSR << + RCC_DDRITFCR_DDRCKMOD_SHIFT); + + return 0; +} +#endif /* STM32MP1_CLOCK_TREE_INIT */ + +static void stm32mp1_osc_clk_init(const char *name, + struct stm32mp1_clk_priv *priv, + int index) +{ + struct clk clk; + struct udevice *dev = NULL; + + priv->osc[index] = 0; + clk.id = 0; + if (!uclass_get_device_by_name(UCLASS_CLK, name, &dev)) { + if (clk_request(dev, &clk)) + pr_err("%s request", name); + else + priv->osc[index] = clk_get_rate(&clk); + } + priv->osc_dev[index] = dev; +} + +static void stm32mp1_osc_init(struct udevice *dev) +{ + struct stm32mp1_clk_priv *priv = dev_get_priv(dev); + int i; + const char *name[NB_OSC] = { + [_LSI] = "clk-lsi", + [_LSE] = "clk-lse", + [_HSI] = "clk-hsi", + [_HSE] = "clk-hse", + [_CSI] = "clk-csi", + [_I2S_CKIN] = "i2s_ckin", + [_USB_PHY_48] = "ck_usbo_48m"}; + + for (i = 0; i < NB_OSC; i++) { + stm32mp1_osc_clk_init(name[i], priv, i); + debug("%d: %s => %x\n", i, name[i], (u32)priv->osc[i]); + } +} + +static int stm32mp1_clk_probe(struct udevice *dev) +{ + int result = 0; + struct stm32mp1_clk_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr(dev->parent); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->data = (void *)&stm32mp1_data; + + if (!priv->data->gate || !priv->data->sel || + !priv->data->pll) + return -EINVAL; + + stm32mp1_osc_init(dev); + +#ifdef STM32MP1_CLOCK_TREE_INIT + /* clock tree init is done only one time, before relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + result = stm32mp1_clktree(dev); +#endif + + return result; +} + +static const struct clk_ops stm32mp1_clk_ops = { + .enable = stm32mp1_clk_enable, + .disable = stm32mp1_clk_disable, + .get_rate = stm32mp1_clk_get_rate, +}; + +static const struct udevice_id stm32mp1_clk_ids[] = { + { .compatible = "st,stm32mp1-rcc-clk" }, + { } +}; + +U_BOOT_DRIVER(stm32mp1_clock) = { + .name = "stm32mp1_clk", + .id = UCLASS_CLK, + .of_match = stm32mp1_clk_ids, + .ops = &stm32mp1_clk_ops, + .priv_auto_alloc_size = sizeof(struct stm32mp1_clk_priv), + .probe = stm32mp1_clk_probe, +}; diff --git a/drivers/clk/clk_zynq.c b/drivers/clk/clk_zynq.c index 50f2a65c20..3845e07309 100644 --- a/drivers/clk/clk_zynq.c +++ b/drivers/clk/clk_zynq.c @@ -394,7 +394,7 @@ static ulong zynq_clk_get_rate(struct clk *clk) return zynq_clk_get_peripheral_rate(priv, id, two_divs); case dma_clk: return zynq_clk_get_cpu_rate(priv, cpu_2x_clk); - case usb0_aper_clk ... smc_aper_clk: + case usb0_aper_clk ... swdt_clk: return zynq_clk_get_cpu_rate(priv, cpu_1x_clk); default: return -ENXIO; diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c index bcc62904f1..d0d6c898bc 100644 --- a/drivers/clk/clk_zynqmp.c +++ b/drivers/clk/clk_zynqmp.c @@ -226,6 +226,18 @@ static u32 zynqmp_clk_get_register(enum zynqmp_clk id) return CRL_APB_CAN0_REF_CTRL; case can1_ref: return CRL_APB_CAN1_REF_CTRL; + case pl0: + return CRL_APB_PL0_REF_CTRL; + case pl1: + return CRL_APB_PL1_REF_CTRL; + case pl2: + return CRL_APB_PL2_REF_CTRL; + case pl3: + return CRL_APB_PL3_REF_CTRL; + case wdt: + return CRF_APB_TOPSW_LSBUS_CTRL; + case iopll_to_fpd: + return CRL_APB_IOPLL_TO_FPD_CTRL; default: debug("Invalid clk id%d\n", id); } @@ -278,6 +290,22 @@ static enum zynqmp_clk zynqmp_clk_get_peripheral_pll(u32 clk_ctrl) } } +static enum zynqmp_clk zynqmp_clk_get_wdt_pll(u32 clk_ctrl) +{ + u32 srcsel = (clk_ctrl & CLK_CTRL_SRCSEL_MASK) >> + CLK_CTRL_SRCSEL_SHIFT; + + switch (srcsel) { + case 2: + return iopll_to_fpd; + case 3: + return dpll; + case 0 ... 1: + default: + return apll; + } +} + static ulong zynqmp_clk_get_pll_src(ulong clk_ctrl, struct zynqmp_clk_priv *priv, bool is_pre_src) @@ -420,6 +448,49 @@ static ulong zynqmp_clk_get_peripheral_rate(struct zynqmp_clk_priv *priv, DIV_ROUND_CLOSEST(pllrate, div0), div1); } +static ulong zynqmp_clk_get_wdt_rate(struct zynqmp_clk_priv *priv, + enum zynqmp_clk id, bool two_divs) +{ + enum zynqmp_clk pll; + u32 clk_ctrl, div0; + u32 div1 = 1; + int ret; + ulong pllrate; + + ret = zynqmp_mmio_read(zynqmp_clk_get_register(id), &clk_ctrl); + if (ret) { + printf("%d %s mio read fail\n", __LINE__, __func__); + return -EIO; + } + + div0 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + if (!div0) + div0 = 1; + + pll = zynqmp_clk_get_wdt_pll(clk_ctrl); + if (two_divs) { + ret = zynqmp_mmio_read(zynqmp_clk_get_register(pll), &clk_ctrl); + if (ret) { + printf("%d %s mio read fail\n", __LINE__, __func__); + return -EIO; + } + div1 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + if (!div1) + div1 = 1; + } + + if (pll == iopll_to_fpd) + pll = iopll; + + pllrate = zynqmp_clk_get_pll_rate(priv, pll); + if (IS_ERR_VALUE(pllrate)) + return pllrate; + + return + DIV_ROUND_CLOSEST( + DIV_ROUND_CLOSEST(pllrate, div0), div1); +} + static unsigned long zynqmp_clk_calc_peripheral_two_divs(ulong rate, ulong pll_rate, u32 *div0, u32 *div1) @@ -510,8 +581,12 @@ static ulong zynqmp_clk_get_rate(struct clk *clk) return zynqmp_clk_get_ddr_rate(priv); case gem0_ref ... gem3_ref: case qspi_ref ... can1_ref: + case pl0 ... pl3: two_divs = true; return zynqmp_clk_get_peripheral_rate(priv, id, two_divs); + case wdt: + two_divs = true; + return zynqmp_clk_get_wdt_rate(priv, id, two_divs); default: return -ENXIO; } @@ -627,6 +702,7 @@ static struct clk_ops zynqmp_clk_ops = { }; static const struct udevice_id zynqmp_clk_ids[] = { + { .compatible = "xlnx,zynqmp-clk" }, { .compatible = "xlnx,zynqmp-clkc" }, { } }; diff --git a/drivers/clk/renesas/r8a7790-cpg-mssr.c b/drivers/clk/renesas/r8a7790-cpg-mssr.c index 33ab9ad7cc..360c02c5fd 100644 --- a/drivers/clk/renesas/r8a7790-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7790-cpg-mssr.c @@ -40,7 +40,7 @@ enum clk_ids { MOD_CLK_BASE }; -static const struct cpg_core_clk r8a7790_core_clks[] __initconst = { +static const struct cpg_core_clk r8a7790_core_clks[] = { /* External Clock Inputs */ DEF_INPUT("extal", CLK_EXTAL), DEF_INPUT("usb_extal", CLK_USB_EXTAL), @@ -90,7 +90,7 @@ static const struct cpg_core_clk r8a7790_core_clks[] __initconst = { DEF_DIV6P1("ssprs", R8A7790_CLK_SSPRS, CLK_PLL1_DIV2, 0x24c), }; -static const struct mssr_mod_clk r8a7790_mod_clks[] __initconst = { +static const struct mssr_mod_clk r8a7790_mod_clks[] = { DEF_MOD("msiof0", 0, R8A7790_CLK_MP), DEF_MOD("vcp1", 100, R8A7790_CLK_ZS), DEF_MOD("vcp0", 101, R8A7790_CLK_ZS), @@ -209,10 +209,6 @@ static const struct mssr_mod_clk r8a7790_mod_clks[] __initconst = { DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)), }; -static const unsigned int r8a7790_crit_mod_clks[] __initconst = { - MOD_CLK_ID(408), /* INTC-SYS (GIC) */ -}; - /* * CPG Clock Data */ @@ -235,7 +231,7 @@ static const unsigned int r8a7790_crit_mod_clks[] __initconst = { #define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \ (((md) & BIT(13)) >> 12) | \ (((md) & BIT(19)) >> 19)) -static const struct rcar_gen2_cpg_pll_config cpg_pll_configs[8] __initconst = { +static const struct rcar_gen2_cpg_pll_config cpg_pll_configs[8] = { { 1, 208, 106 }, { 1, 208, 88 }, { 1, 156, 80 }, { 1, 156, 66 }, { 2, 240, 122 }, { 2, 240, 102 }, { 2, 208, 106 }, { 2, 208, 88 }, }; diff --git a/drivers/clk/rockchip/clk_rk3036.c b/drivers/clk/rockchip/clk_rk3036.c index 510a00a3aa..560222b96c 100644 --- a/drivers/clk/rockchip/clk_rk3036.c +++ b/drivers/clk/rockchip/clk_rk3036.c @@ -321,7 +321,7 @@ static int rk3036_clk_probe(struct udevice *dev) { struct rk3036_clk_priv *priv = dev_get_priv(dev); - priv->cru = (struct rk3036_cru *)devfdt_get_addr(dev); + priv->cru = dev_read_addr_ptr(dev); rkclk_init(priv->cru); return 0; diff --git a/drivers/clk/rockchip/clk_rk3188.c b/drivers/clk/rockchip/clk_rk3188.c index 6451c95a32..cfe6abe470 100644 --- a/drivers/clk/rockchip/clk_rk3188.c +++ b/drivers/clk/rockchip/clk_rk3188.c @@ -123,7 +123,7 @@ static int rkclk_configure_ddr(struct rk3188_cru *cru, struct rk3188_grf *grf, unsigned int hz, bool has_bwadj) { static const struct pll_div dpll_cfg[] = { - {.nf = 25, .nr = 2, .no = 1}, + {.nf = 75, .nr = 1, .no = 6}, {.nf = 400, .nr = 9, .no = 2}, {.nf = 500, .nr = 9, .no = 2}, {.nf = 100, .nr = 3, .no = 1}, @@ -541,7 +541,7 @@ static int rk3188_clk_ofdata_to_platdata(struct udevice *dev) #if !CONFIG_IS_ENABLED(OF_PLATDATA) struct rk3188_clk_priv *priv = dev_get_priv(dev); - priv->cru = (struct rk3188_cru *)devfdt_get_addr(dev); + priv->cru = dev_read_addr_ptr(dev); #endif return 0; diff --git a/drivers/clk/rockchip/clk_rk322x.c b/drivers/clk/rockchip/clk_rk322x.c index 4e6d2f0834..ebcab73002 100644 --- a/drivers/clk/rockchip/clk_rk322x.c +++ b/drivers/clk/rockchip/clk_rk322x.c @@ -475,7 +475,7 @@ static int rk322x_clk_ofdata_to_platdata(struct udevice *dev) { struct rk322x_clk_priv *priv = dev_get_priv(dev); - priv->cru = (struct rk322x_cru *)devfdt_get_addr(dev); + priv->cru = dev_read_addr_ptr(dev); return 0; } diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index 552a71a82f..3a36d04096 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -906,7 +906,7 @@ static int rk3288_clk_ofdata_to_platdata(struct udevice *dev) #if !CONFIG_IS_ENABLED(OF_PLATDATA) struct rk3288_clk_priv *priv = dev_get_priv(dev); - priv->cru = (struct rk3288_cru *)devfdt_get_addr(dev); + priv->cru = dev_read_addr_ptr(dev); #endif return 0; diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c index 2ccc79851c..046b4e4c2f 100644 --- a/drivers/clk/rockchip/clk_rk3328.c +++ b/drivers/clk/rockchip/clk_rk3328.c @@ -767,7 +767,7 @@ static int rk3328_clk_ofdata_to_platdata(struct udevice *dev) { struct rk3328_clk_priv *priv = dev_get_priv(dev); - priv->cru = (struct rk3328_cru *)devfdt_get_addr(dev); + priv->cru = dev_read_addr_ptr(dev); return 0; } diff --git a/drivers/clk/rockchip/clk_rv1108.c b/drivers/clk/rockchip/clk_rv1108.c index 224c81355e..958fc78592 100644 --- a/drivers/clk/rockchip/clk_rv1108.c +++ b/drivers/clk/rockchip/clk_rv1108.c @@ -213,7 +213,7 @@ static int rv1108_clk_probe(struct udevice *dev) { struct rv1108_clk_priv *priv = dev_get_priv(dev); - priv->cru = (struct rv1108_cru *)devfdt_get_addr(dev); + priv->cru = dev_read_addr_ptr(dev); rkclk_init(priv->cru); diff --git a/drivers/core/fdtaddr.c b/drivers/core/fdtaddr.c index 3847dd836e..9a3b4c312a 100644 --- a/drivers/core/fdtaddr.c +++ b/drivers/core/fdtaddr.c @@ -49,12 +49,17 @@ fdt_addr_t devfdt_get_addr_index(struct udevice *dev, int index) reg += index * (na + ns); - /* - * Use the full-fledged translate function for complex - * bus setups. - */ - addr = fdt_translate_address((void *)gd->fdt_blob, - dev_of_offset(dev), reg); + if (ns) { + /* + * Use the full-fledged translate function for complex + * bus setups. + */ + addr = fdt_translate_address((void *)gd->fdt_blob, + dev_of_offset(dev), reg); + } else { + /* Non translatable if #size-cells == 0 */ + addr = fdt_read_number(reg, na); + } } else { /* * Use the "simple" translate function for less complex diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 4e4532651f..5909a25f85 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -227,13 +227,16 @@ fdt_addr_t ofnode_get_addr_index(ofnode node, int index) uint flags; u64 size; int na; + int ns; prop_val = of_get_address(ofnode_to_np(node), index, &size, &flags); if (!prop_val) return FDT_ADDR_T_NONE; - if (IS_ENABLED(CONFIG_OF_TRANSLATE)) { + ns = of_n_size_cells(ofnode_to_np(node)); + + if (IS_ENABLED(CONFIG_OF_TRANSLATE) && ns > 0) { return of_translate_address(ofnode_to_np(node), prop_val); } else { na = of_n_addr_cells(ofnode_to_np(node)); diff --git a/drivers/core/root.c b/drivers/core/root.c index 3a426ab4b7..9000ed55ca 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -333,7 +333,8 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only) { - int node, ret; + int ret; + ofnode node; ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); if (ret) { @@ -342,13 +343,18 @@ int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only) } /* bind fixed-clock */ - node = ofnode_to_offset(ofnode_path("/clocks")); + node = ofnode_path("/clocks"); /* if no DT "clocks" node, no need to go further */ - if (node < 0) + if (!ofnode_valid(node)) return ret; - ret = dm_scan_fdt_node(gd->dm_root, gd->fdt_blob, node, - pre_reloc_only); +#if CONFIG_IS_ENABLED(OF_LIVE) + if (of_live_active()) + ret = dm_scan_fdt_live(gd->dm_root, node.np, pre_reloc_only); + else +#endif + ret = dm_scan_fdt_node(gd->dm_root, gd->fdt_blob, node.of_offset, + pre_reloc_only); if (ret) debug("dm_scan_fdt_node() failed: %d\n", ret); diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 1aedaa08f0..628e2e13ff 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -457,6 +457,32 @@ int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node, } #if CONFIG_IS_ENABLED(OF_CONTROL) +int uclass_get_device_by_phandle_id(enum uclass_id id, uint phandle_id, + struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + *devp = NULL; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + uint phandle; + + phandle = dev_read_phandle(dev); + + if (phandle == phandle_id) { + *devp = dev; + return uclass_get_device_tail(dev, ret, devp); + } + } + + return -ENODEV; +} + int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, struct udevice **devp) { diff --git a/drivers/cpu/bmips_cpu.c b/drivers/cpu/bmips_cpu.c index 4ad291a56e..6c612bacdc 100644 --- a/drivers/cpu/bmips_cpu.c +++ b/drivers/cpu/bmips_cpu.c @@ -50,6 +50,10 @@ DECLARE_GLOBAL_DATA_PTR; #define DMIPSPLLCFG_6358_N2_SHIFT 29 #define DMIPSPLLCFG_6358_N2_MASK (0x7 << DMIPSPLLCFG_6358_N2_SHIFT) +#define REG_BCM6362_MISC_STRAPBUS 0x1814 +#define STRAPBUS_6362_FCVO_SHIFT 1 +#define STRAPBUS_6362_FCVO_MASK (0x1f << STRAPBUS_6362_FCVO_SHIFT) + #define REG_BCM6368_DDR_DMIPSPLLCFG 0x12a0 #define DMIPSPLLCFG_6368_P1_SHIFT 0 #define DMIPSPLLCFG_6368_P1_MASK (0xf << DMIPSPLLCFG_6368_P1_SHIFT) @@ -194,6 +198,44 @@ static ulong bcm6358_get_cpu_freq(struct bmips_cpu_priv *priv) return (16 * 1000000 * n1 * n2) / m1; } +static ulong bcm6362_get_cpu_freq(struct bmips_cpu_priv *priv) +{ + unsigned int mips_pll_fcvo; + + mips_pll_fcvo = readl_be(priv->regs + REG_BCM6362_MISC_STRAPBUS); + mips_pll_fcvo = (mips_pll_fcvo & STRAPBUS_6362_FCVO_MASK) + >> STRAPBUS_6362_FCVO_SHIFT; + + switch (mips_pll_fcvo) { + case 0x03: + case 0x0b: + case 0x13: + case 0x1b: + return 240000000; + case 0x04: + case 0x0c: + case 0x14: + case 0x1c: + return 160000000; + case 0x05: + case 0x0e: + case 0x16: + case 0x1e: + case 0x1f: + return 400000000; + case 0x06: + return 440000000; + case 0x07: + case 0x17: + return 384000000; + case 0x15: + case 0x1d: + return 200000000; + default: + return 320000000; + } +} + static ulong bcm6368_get_cpu_freq(struct bmips_cpu_priv *priv) { unsigned int tmp, p1, p2, ndiv, m1; @@ -289,6 +331,12 @@ static const struct bmips_cpu_hw bmips_cpu_bcm6358 = { .get_cpu_count = bcm6358_get_cpu_count, }; +static const struct bmips_cpu_hw bmips_cpu_bcm6362 = { + .get_cpu_desc = bmips_short_cpu_desc, + .get_cpu_freq = bcm6362_get_cpu_freq, + .get_cpu_count = bcm6358_get_cpu_count, +}; + static const struct bmips_cpu_hw bmips_cpu_bcm6368 = { .get_cpu_desc = bmips_short_cpu_desc, .get_cpu_freq = bcm6368_get_cpu_freq, @@ -395,6 +443,9 @@ static const struct udevice_id bmips_cpu_ids[] = { .compatible = "brcm,bcm6358-cpu", .data = (ulong)&bmips_cpu_bcm6358, }, { + .compatible = "brcm,bcm6362-cpu", + .data = (ulong)&bmips_cpu_bcm6362, + }, { .compatible = "brcm,bcm6368-cpu", .data = (ulong)&bmips_cpu_bcm6368, }, { diff --git a/drivers/fpga/fpga.c b/drivers/fpga/fpga.c index 6aead27f16..ac01612d75 100644 --- a/drivers/fpga/fpga.c +++ b/drivers/fpga/fpga.c @@ -148,20 +148,21 @@ int fpga_add(fpga_type devtype, void *desc) { int devnum = FPGA_INVALID_DEVICE; + if (!desc) { + printf("%s: NULL device descriptor\n", __func__); + return devnum; + } + if (next_desc < 0) { printf("%s: FPGA support not initialized!\n", __func__); } else if ((devtype > fpga_min_type) && (devtype < fpga_undefined)) { - if (desc) { - if (next_desc < CONFIG_MAX_FPGA_DEVICES) { - devnum = next_desc; - desc_table[next_desc].devtype = devtype; - desc_table[next_desc++].devdesc = desc; - } else { - printf("%s: Exceeded Max FPGA device count\n", - __func__); - } + if (next_desc < CONFIG_MAX_FPGA_DEVICES) { + devnum = next_desc; + desc_table[next_desc].devtype = devtype; + desc_table[next_desc++].devdesc = desc; } else { - printf("%s: NULL device descriptor\n", __func__); + printf("%s: Exceeded Max FPGA device count\n", + __func__); } } else { printf("%s: Unsupported FPGA type %d\n", __func__, devtype); diff --git a/drivers/fpga/zynqmppl.c b/drivers/fpga/zynqmppl.c index 57a4e6c88e..43e8b2520e 100644 --- a/drivers/fpga/zynqmppl.c +++ b/drivers/fpga/zynqmppl.c @@ -11,6 +11,7 @@ #include <zynqmppl.h> #include <linux/sizes.h> #include <asm/arch/sys_proto.h> +#include <memalign.h> #define DUMMY_WORD 0xffffffff @@ -195,6 +196,7 @@ static int zynqmp_validate_bitstream(xilinx_desc *desc, const void *buf, static int zynqmp_load(xilinx_desc *desc, const void *buf, size_t bsize, bitstream_type bstype) { + ALLOC_CACHE_ALIGN_BUFFER(u32, bsizeptr, 1); u32 swap; ulong bin_buf; int ret; @@ -205,25 +207,37 @@ static int zynqmp_load(xilinx_desc *desc, const void *buf, size_t bsize, return FPGA_FAIL; bin_buf = zynqmp_align_dma_buffer((u32 *)buf, bsize, swap); + bsizeptr = (u32 *)&bsize; debug("%s called!\n", __func__); flush_dcache_range(bin_buf, bin_buf + bsize); - - if (bsize % 4) - bsize = bsize / 4 + 1; - else - bsize = bsize / 4; + flush_dcache_range((ulong)bsizeptr, (ulong)bsizeptr + sizeof(size_t)); buf_lo = (u32)bin_buf; buf_hi = upper_32_bits(bin_buf); - ret = invoke_smc(ZYNQMP_SIP_SVC_PM_FPGA_LOAD, buf_lo, buf_hi, bsize, - bstype, ret_payload); + bstype |= BIT(ZYNQMP_FPGA_BIT_NS); + ret = invoke_smc(ZYNQMP_SIP_SVC_PM_FPGA_LOAD, buf_lo, buf_hi, + (u32)(uintptr_t)bsizeptr, bstype, ret_payload); if (ret) debug("PL FPGA LOAD fail\n"); return ret; } +static int zynqmp_pcap_info(xilinx_desc *desc) +{ + int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + + ret = invoke_smc(ZYNQMP_SIP_SVC_PM_FPGA_STATUS, 0, 0, 0, + 0, ret_payload); + if (!ret) + printf("PCAP status\t0x%x\n", ret_payload[1]); + + return ret; +} + struct xilinx_fpga_op zynqmp_op = { .load = zynqmp_load, + .info = zynqmp_pcap_info, }; diff --git a/drivers/fpga/zynqpl.c b/drivers/fpga/zynqpl.c index 2ff716c252..db9bd12992 100644 --- a/drivers/fpga/zynqpl.c +++ b/drivers/fpga/zynqpl.c @@ -17,6 +17,7 @@ #include <asm/arch/sys_proto.h> #define DEVCFG_CTRL_PCFG_PROG_B 0x40000000 +#define DEVCFG_CTRL_PCFG_AES_EFUSE_MASK 0x00001000 #define DEVCFG_ISR_FATAL_ERROR_MASK 0x00740040 #define DEVCFG_ISR_ERROR_FLAGS_MASK 0x00340840 #define DEVCFG_ISR_RX_FIFO_OV 0x00040000 @@ -205,9 +206,24 @@ static int zynq_dma_xfer_init(bitstream_type bstype) /* Setting PCFG_PROG_B signal to high */ control = readl(&devcfg_base->ctrl); writel(control | DEVCFG_CTRL_PCFG_PROG_B, &devcfg_base->ctrl); + + /* + * Delay is required if AES efuse is selected as + * key source. + */ + if (control & DEVCFG_CTRL_PCFG_AES_EFUSE_MASK) + mdelay(5); + /* Setting PCFG_PROG_B signal to low */ writel(control & ~DEVCFG_CTRL_PCFG_PROG_B, &devcfg_base->ctrl); + /* + * Delay is required if AES efuse is selected as + * key source. + */ + if (control & DEVCFG_CTRL_PCFG_AES_EFUSE_MASK) + mdelay(5); + /* Polling the PCAP_INIT status for Reset */ ts = get_timer(0); while (readl(&devcfg_base->status) & DEVCFG_STATUS_PCFG_INIT) { diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index cc75aece6a..b7e4ffb09d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -234,7 +234,7 @@ config PIC32_GPIO config STM32F7_GPIO bool "ST STM32 GPIO driver" - depends on DM_GPIO && STM32 + depends on DM_GPIO && (STM32 || ARCH_STM32MP) default y help Device model driver support for STM32 GPIO controller. It should be diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 9faf3357af..1fbfdef477 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -655,7 +655,7 @@ static int gpio_request_tail(int ret, ofnode node, ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node, &desc->dev); if (ret) { - debug("%s: uclass_get_device_by_of_offset failed\n", __func__); + debug("%s: uclass_get_device_by_ofnode failed\n", __func__); goto err; } ret = gpio_find_and_xlate(desc, args); diff --git a/drivers/gpio/omap_gpio.c b/drivers/gpio/omap_gpio.c index 7243100219..559f29b801 100644 --- a/drivers/gpio/omap_gpio.c +++ b/drivers/gpio/omap_gpio.c @@ -345,6 +345,7 @@ U_BOOT_DRIVER(gpio_omap) = { .bind = omap_gpio_bind, .probe = omap_gpio_probe, .priv_auto_alloc_size = sizeof(struct gpio_bank), + .flags = DM_FLAG_PRE_RELOC, }; #endif /* CONFIG_DM_GPIO */ diff --git a/drivers/gpio/stm32f7_gpio.c b/drivers/gpio/stm32f7_gpio.c index a7cfb8c923..376e86cd69 100644 --- a/drivers/gpio/stm32f7_gpio.c +++ b/drivers/gpio/stm32f7_gpio.c @@ -16,14 +16,11 @@ #include <linux/errno.h> #include <linux/io.h> -#define MAX_SIZE_BANK_NAME 5 #define STM32_GPIOS_PER_BANK 16 #define MODE_BITS(gpio_pin) (gpio_pin * 2) #define MODE_BITS_MASK 3 #define IN_OUT_BIT_INDEX(gpio_pin) (1UL << (gpio_pin)) -DECLARE_GLOBAL_DATA_PTR; - static int stm32_gpio_direction_input(struct udevice *dev, unsigned offset) { struct stm32_gpio_priv *priv = dev_get_priv(dev); @@ -82,21 +79,19 @@ static int gpio_stm32_probe(struct udevice *dev) struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct stm32_gpio_priv *priv = dev_get_priv(dev); fdt_addr_t addr; - char *name; + const char *name; - addr = devfdt_get_addr(dev); + addr = dev_read_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; priv->regs = (struct stm32_gpio_regs *)addr; - name = (char *)fdtdec_locate_byte_array(gd->fdt_blob, - dev_of_offset(dev), - "st,bank-name", - MAX_SIZE_BANK_NAME); + name = dev_read_string(dev, "st,bank-name"); if (!name) return -EINVAL; uc_priv->bank_name = name; - uc_priv->gpio_count = STM32_GPIOS_PER_BANK; + uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", + STM32_GPIOS_PER_BANK); debug("%s, addr = 0x%p, bank_name = %s\n", __func__, (u32 *)priv->regs, uc_priv->bank_name); diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index 3cf01b6e36..ea6f3593b9 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -354,12 +354,15 @@ static const struct udevice_id sunxi_gpio_ids[] = { ID("allwinner,sun8i-a83t-pinctrl", a_all), ID("allwinner,sun8i-h3-pinctrl", a_all), ID("allwinner,sun8i-r40-pinctrl", a_all), + ID("allwinner,sun8i-v3s-pinctrl", a_all), ID("allwinner,sun9i-a80-pinctrl", a_all), + ID("allwinner,sun50i-a64-pinctrl", a_all), ID("allwinner,sun6i-a31-r-pinctrl", l_2), ID("allwinner,sun8i-a23-r-pinctrl", l_1), ID("allwinner,sun8i-a83t-r-pinctrl", l_1), ID("allwinner,sun8i-h3-r-pinctrl", l_1), ID("allwinner,sun9i-a80-r-pinctrl", l_3), + ID("allwinner,sun50i-a64-r-pinctrl", l_1), { } }; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 286ae487ca..7fb201d8e6 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -370,7 +370,7 @@ config SYS_I2C_S3C24X0 config SYS_I2C_STM32F7 bool "STMicroelectronics STM32F7 I2C support" - depends on (STM32F7 || STM32H7) && DM_I2C + depends on (STM32F7 || STM32H7 || ARCH_STM32MP) && DM_I2C help Enable this option to add support for STM32 I2C controller introduced with STM32F7/H7 SoCs. This I2C controller supports : diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index cb0f5ea233..450a91ded6 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -12,6 +12,7 @@ #include <i2c.h> /* Functional interface */ #include <asm/io.h> #include <asm/fsl_i2c.h> /* HW definitions */ +#include <clk.h> #include <dm.h> #include <mapmem.h> @@ -573,11 +574,9 @@ static int fsl_i2c_set_bus_speed(struct udevice *bus, uint speed) static int fsl_i2c_ofdata_to_platdata(struct udevice *bus) { struct fsl_i2c_dev *dev = dev_get_priv(bus); - fdt_addr_t addr; + struct clk clock; - addr = dev_read_u32_default(bus, "reg", -1); - - dev->base = map_sysmem(CONFIG_SYS_IMMR + addr, sizeof(struct fsl_i2c_base)); + dev->base = map_sysmem(dev_read_addr(bus), sizeof(struct fsl_i2c_base)); if (!dev->base) return -ENOMEM; @@ -587,7 +586,11 @@ static int fsl_i2c_ofdata_to_platdata(struct udevice *bus) 0x7f); dev->speed = dev_read_u32_default(bus, "clock-frequency", 400000); - dev->i2c_clk = dev->index ? gd->arch.i2c2_clk : gd->arch.i2c1_clk; + if (!clk_get_by_index(bus, 0, &clock)) + dev->i2c_clk = clk_get_rate(&clock); + else + dev->i2c_clk = dev->index ? gd->arch.i2c2_clk : + gd->arch.i2c1_clk; return 0; } diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 920811a075..4ac6ef84f5 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -11,9 +11,19 @@ #include <malloc.h> #include <dm/device-internal.h> #include <dm/lists.h> +#include <dm/pinctrl.h> +#ifdef CONFIG_DM_GPIO +#include <asm/gpio.h> +#endif #define I2C_MAX_OFFSET_LEN 4 +enum { + PIN_SDA = 0, + PIN_SCL, + PIN_COUNT, +}; + /* Useful debugging function */ void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs) { @@ -445,20 +455,110 @@ int i2c_get_chip_offset_len(struct udevice *dev) return chip->offset_len; } +#ifdef CONFIG_DM_GPIO +static void i2c_gpio_set_pin(struct gpio_desc *pin, int bit) +{ + if (bit) + dm_gpio_set_dir_flags(pin, GPIOD_IS_IN); + else + dm_gpio_set_dir_flags(pin, GPIOD_IS_OUT | + GPIOD_ACTIVE_LOW | + GPIOD_IS_OUT_ACTIVE); +} + +static int i2c_gpio_get_pin(struct gpio_desc *pin) +{ + return dm_gpio_get_value(pin); +} + +static int i2c_deblock_gpio_loop(struct gpio_desc *sda_pin, + struct gpio_desc *scl_pin) +{ + int counter = 9; + int ret = 0; + + i2c_gpio_set_pin(sda_pin, 1); + i2c_gpio_set_pin(scl_pin, 1); + udelay(5); + + /* Toggle SCL until slave release SDA */ + while (counter-- >= 0) { + i2c_gpio_set_pin(scl_pin, 1); + udelay(5); + i2c_gpio_set_pin(scl_pin, 0); + udelay(5); + if (i2c_gpio_get_pin(sda_pin)) + break; + } + + /* Then, send I2C stop */ + i2c_gpio_set_pin(sda_pin, 0); + udelay(5); + + i2c_gpio_set_pin(scl_pin, 1); + udelay(5); + + i2c_gpio_set_pin(sda_pin, 1); + udelay(5); + + if (!i2c_gpio_get_pin(sda_pin) || !i2c_gpio_get_pin(scl_pin)) + ret = -EREMOTEIO; + + return ret; +} + +static int i2c_deblock_gpio(struct udevice *bus) +{ + struct gpio_desc gpios[PIN_COUNT]; + int ret, ret0; + + ret = gpio_request_list_by_name(bus, "gpios", gpios, + ARRAY_SIZE(gpios), GPIOD_IS_IN); + if (ret != ARRAY_SIZE(gpios)) { + debug("%s: I2C Node '%s' has no 'gpios' property %s\n", + __func__, dev_read_name(bus), bus->name); + if (ret >= 0) { + gpio_free_list(bus, gpios, ret); + ret = -ENOENT; + } + goto out; + } + + ret = pinctrl_select_state(bus, "gpio"); + if (ret) { + debug("%s: I2C Node '%s' has no 'gpio' pinctrl state. %s\n", + __func__, dev_read_name(bus), bus->name); + goto out_no_pinctrl; + } + + ret0 = i2c_deblock_gpio_loop(&gpios[PIN_SDA], &gpios[PIN_SCL]); + + ret = pinctrl_select_state(bus, "default"); + if (ret) { + debug("%s: I2C Node '%s' has no 'default' pinctrl state. %s\n", + __func__, dev_read_name(bus), bus->name); + } + + ret = !ret ? ret0 : ret; + +out_no_pinctrl: + gpio_free_list(bus, gpios, ARRAY_SIZE(gpios)); +out: + return ret; +} +#else +static int i2c_deblock_gpio(struct udevice *bus) +{ + return -ENOSYS; +} +#endif // CONFIG_DM_GPIO + int i2c_deblock(struct udevice *bus) { struct dm_i2c_ops *ops = i2c_get_ops(bus); - /* - * We could implement a software deblocking here if we could get - * access to the GPIOs used by I2C, and switch them to GPIO mode - * and then back to I2C. This is somewhat beyond our powers in - * driver model at present, so for now just fail. - * - * See https://patchwork.ozlabs.org/patch/399040/ - */ if (!ops->deblock) - return -ENOSYS; + return i2c_deblock_gpio(bus); return ops->deblock(bus); } diff --git a/drivers/i2c/ihs_i2c.c b/drivers/i2c/ihs_i2c.c index 9298521220..82abb439f0 100644 --- a/drivers/i2c/ihs_i2c.c +++ b/drivers/i2c/ihs_i2c.c @@ -99,7 +99,8 @@ static int wait_for_int(bool read) #endif #ifdef CONFIG_DM_I2C - fpgamap_read16(fpga, priv->addr + REG_INTERRUPT_STATUS, &val); + fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, + FPGAMAP_SIZE_16); #else I2C_GET_REG(interrupt_status, &val); #endif @@ -110,7 +111,8 @@ static int wait_for_int(bool read) if (ctr++ > 5000) return 1; #ifdef CONFIG_DM_I2C - fpgamap_read16(fpga, priv->addr + REG_INTERRUPT_STATUS, &val); + fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, + FPGAMAP_SIZE_16); #else I2C_GET_REG(interrupt_status, &val); #endif @@ -128,6 +130,7 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, #endif { u16 val; + u16 data; #ifdef CONFIG_DM_I2C struct ihs_i2c_priv *priv = dev_get_priv(dev); struct udevice *fpga; @@ -136,13 +139,14 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, #endif /* Clear interrupt status */ + data = I2CINT_ERROR_EV | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV; #ifdef CONFIG_DM_I2C - fpgamap_write16(fpga, priv->addr + REG_INTERRUPT_STATUS, - I2CINT_ERROR_EV | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV); - fpgamap_read16(fpga, priv->addr + REG_INTERRUPT_STATUS, &val); + fpgamap_write(fpga, priv->addr + REG_INTERRUPT_STATUS, &data, + FPGAMAP_SIZE_16); + fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, + FPGAMAP_SIZE_16); #else - I2C_SET_REG(interrupt_status, I2CINT_ERROR_EV - | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV); + I2C_SET_REG(interrupt_status, data); I2C_GET_REG(interrupt_status, &val); #endif @@ -153,26 +157,24 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, if (len > 1) val |= buffer[1] << 8; #ifdef CONFIG_DM_I2C - fpgamap_write16(fpga, priv->addr + REG_WRITE_MAILBOX_EXT, val); + fpgamap_write(fpga, priv->addr + REG_WRITE_MAILBOX_EXT, &val, + FPGAMAP_SIZE_16); #else I2C_SET_REG(write_mailbox_ext, val); #endif } + data = I2CMB_NATIVE + | (read ? 0 : I2CMB_WRITE) + | (chip << 1) + | ((len > 1) ? I2CMB_2BYTE : 0) + | (is_last ? 0 : I2CMB_HOLD_BUS); + #ifdef CONFIG_DM_I2C - fpgamap_write16(fpga, priv->addr + REG_WRITE_MAILBOX, - I2CMB_NATIVE - | (read ? I2CMB_READ : I2CMB_WRITE) - | (chip << 1) - | ((len > 1) ? I2CMB_2BYTE : I2CMB_1BYTE) - | (!is_last ? I2CMB_HOLD_BUS : I2CMB_DONT_HOLD_BUS)); + fpgamap_write(fpga, priv->addr + REG_WRITE_MAILBOX, &data, + FPGAMAP_SIZE_16); #else - I2C_SET_REG(write_mailbox, - I2CMB_NATIVE - | (read ? 0 : I2CMB_WRITE) - | (chip << 1) - | ((len > 1) ? I2CMB_2BYTE : 0) - | (is_last ? 0 : I2CMB_HOLD_BUS)); + I2C_SET_REG(write_mailbox, data); #endif #ifdef CONFIG_DM_I2C @@ -185,7 +187,8 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, /* If we want to read, get the bytes from the mailbox */ if (read) { #ifdef CONFIG_DM_I2C - fpgamap_read16(fpga, priv->addr + REG_READ_MAILBOX_EXT, &val); + fpgamap_read(fpga, priv->addr + REG_READ_MAILBOX_EXT, &val, + FPGAMAP_SIZE_16); #else I2C_GET_REG(read_mailbox_ext, &val); #endif diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index de74e89efd..32d7809dba 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -156,7 +156,7 @@ static int bus_i2c_receive(struct imx_lpi2c_reg *regs, u8 *rxbuf, int len) static int bus_i2c_start(struct imx_lpi2c_reg *regs, u8 addr, u8 dir) { - lpi2c_status_t result = LPI2C_SUCESS; + lpi2c_status_t result; u32 val; result = imx_lpci2c_check_busy_bus(regs); @@ -184,7 +184,7 @@ static int bus_i2c_start(struct imx_lpi2c_reg *regs, u8 addr, u8 dir) static int bus_i2c_stop(struct imx_lpi2c_reg *regs) { - lpi2c_status_t result = LPI2C_SUCESS; + lpi2c_status_t result; u32 status; result = bus_i2c_wait_for_tx_ready(regs); @@ -213,7 +213,7 @@ static int bus_i2c_stop(struct imx_lpi2c_reg *regs) static int bus_i2c_read(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) { - lpi2c_status_t result = LPI2C_SUCESS; + lpi2c_status_t result; result = bus_i2c_start(regs, chip, 1); if (result) @@ -230,7 +230,7 @@ static int bus_i2c_read(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) static int bus_i2c_write(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) { - lpi2c_status_t result = LPI2C_SUCESS; + lpi2c_status_t result; result = bus_i2c_start(regs, chip, 0); if (result) @@ -354,7 +354,7 @@ static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip, u32 chip_flags) { struct imx_lpi2c_reg *regs; - lpi2c_status_t result = LPI2C_SUCESS; + lpi2c_status_t result; regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); result = bus_i2c_start(regs, chip, 0); diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index 4fd5551a22..cc9c5ef356 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -25,9 +25,6 @@ #include <asm/arch/gpio.h> #endif #endif -#if defined(CONFIG_8xx) -#include <asm/io.h> -#endif #include <i2c.h> #if defined(CONFIG_SOFT_I2C_GPIO_SCL) diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c index 86624878e2..81f061aecd 100644 --- a/drivers/i2c/stm32f7_i2c.c +++ b/drivers/i2c/stm32f7_i2c.c @@ -533,7 +533,7 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup, if (((sdadel >= sdadel_min) && (sdadel <= sdadel_max)) && (p != p_prev)) { - v = kmalloc(sizeof(*v), GFP_KERNEL); + v = calloc(1, sizeof(*v)); if (!v) return -ENOMEM; @@ -689,7 +689,7 @@ exit: /* Release list and memory */ list_for_each_entry_safe(v, _v, &solutions, node) { list_del(&v->node); - kfree(v); + free(v); } return ret; diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 9109ac6dba..ee7bfc4d2c 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -12,9 +12,5 @@ obj-$(CONFIG_TEGRA_KEYBOARD) += tegra-kbc.o obj-$(CONFIG_TWL4030_INPUT) += twl4030.o obj-$(CONFIG_TWL6030_INPUT) += twl6030.o obj-$(CONFIG_CROS_EC_KEYB) += cros_ec_keyb.o -ifdef CONFIG_PS2KBD -obj-y += keyboard.o pc_keyb.o -obj-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o -endif obj-y += input.o obj-$(CONFIG_$(SPL_)OF_CONTROL) += key_matrix.o diff --git a/drivers/input/keyboard.c b/drivers/input/keyboard.c deleted file mode 100644 index 84ee015cb3..0000000000 --- a/drivers/input/keyboard.c +++ /dev/null @@ -1,87 +0,0 @@ -/*********************************************************************** - * - * (C) Copyright 2004 - * DENX Software Engineering - * Wolfgang Denk, wd@denx.de - * - * Keyboard driver - * - ***********************************************************************/ - -#include <common.h> -#include <console.h> -#include <input.h> - -#include <stdio_dev.h> -#include <keyboard.h> -#include <stdio_dev.h> - -static struct input_config config; - -static int kbd_read_keys(struct input_config *config) -{ -#if defined(CONFIG_ARCH_MPC8540) || \ - defined(CONFIG_ARCH_MPC8541) || defined(CONFIG_ARCH_MPC8555) - /* no ISR is used, so received chars must be polled */ - ps2ser_check(); -#endif - - return 1; -} - -static int check_leds(int ret) -{ - int leds; - - leds = input_leds_changed(&config); - if (leds >= 0) - pckbd_leds(leds); - - return ret; -} - -/* test if a character is in the queue */ -static int kbd_testc(struct stdio_dev *dev) -{ - return check_leds(input_tstc(&config)); -} - -/* gets the character from the queue */ -static int kbd_getc(struct stdio_dev *dev) -{ - return check_leds(input_getc(&config)); -} - -void handle_scancode(unsigned char scan_code) -{ - bool release = false; - - /* Compare with i8042_kbd_check() in i8042.c if some logic is missing */ - if (scan_code & 0x80) { - scan_code &= 0x7f; - release = true; - } - - input_add_keycode(&config, scan_code, release); -} - -/* TODO: convert to driver model */ -int kbd_init (void) -{ - struct stdio_dev kbddev; - struct input_config *input = &config; - - if(kbd_init_hw()==-1) - return -1; - memset (&kbddev, 0, sizeof(kbddev)); - strcpy(kbddev.name, "kbd"); - kbddev.flags = DEV_FLAGS_INPUT; - kbddev.getc = kbd_getc; - kbddev.tstc = kbd_testc; - - input_init(input, 0); - input->read_keys = kbd_read_keys; - input_add_tables(input, true); - - return input_stdio_register(&kbddev); -} diff --git a/drivers/input/pc_keyb.c b/drivers/input/pc_keyb.c deleted file mode 100644 index 1606ab33ff..0000000000 --- a/drivers/input/pc_keyb.c +++ /dev/null @@ -1,251 +0,0 @@ -/*********************************************************************** - * - * (C) Copyright 2004 - * DENX Software Engineering - * Wolfgang Denk, wd@denx.de - * - * PS/2 keyboard driver - * - * Originally from linux source (drivers/char/pc_keyb.c) - * - ***********************************************************************/ - -#include <common.h> - -#include <keyboard.h> -#include <pc_keyb.h> - -#undef KBG_DEBUG - -#ifdef KBG_DEBUG -#define PRINTF(fmt,args...) printf (fmt ,##args) -#else -#define PRINTF(fmt,args...) -#endif - - -/* - * This reads the keyboard status port, and does the - * appropriate action. - * - */ -static unsigned char handle_kbd_event(void) -{ - unsigned char status = kbd_read_status(); - unsigned int work = 10000; - - while ((--work > 0) && (status & KBD_STAT_OBF)) { - unsigned char scancode; - - scancode = kbd_read_input(); - - /* Error bytes must be ignored to make the - Synaptics touchpads compaq use work */ - /* Ignore error bytes */ - if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) { - if (status & KBD_STAT_MOUSE_OBF) - ; /* not supported: handle_mouse_event(scancode); */ - else - handle_scancode(scancode); - } - status = kbd_read_status(); - } - if (!work) - PRINTF("pc_keyb: controller jammed (0x%02X).\n", status); - return status; -} - - -static int kbd_read_data(void) -{ - int val; - unsigned char status; - - val = -1; - status = kbd_read_status(); - if (status & KBD_STAT_OBF) { - val = kbd_read_input(); - if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) - val = -2; - } - return val; -} - -static int kbd_wait_for_input(void) -{ - unsigned long timeout; - int val; - - timeout = KBD_TIMEOUT; - val=kbd_read_data(); - while(val < 0) { - if(timeout--==0) - return -1; - udelay(1000); - val=kbd_read_data(); - } - return val; -} - - -static int kb_wait(void) -{ - unsigned long timeout = KBC_TIMEOUT * 10; - - do { - unsigned char status = handle_kbd_event(); - if (!(status & KBD_STAT_IBF)) - return 0; /* ok */ - udelay(1000); - timeout--; - } while (timeout); - return 1; -} - -static void kbd_write_command_w(int data) -{ - if(kb_wait()) - PRINTF("timeout in kbd_write_command_w\n"); - kbd_write_command(data); -} - -static void kbd_write_output_w(int data) -{ - if(kb_wait()) - PRINTF("timeout in kbd_write_output_w\n"); - kbd_write_output(data); -} - -static void kbd_send_data(unsigned char data) -{ - kbd_write_output_w(data); - kbd_wait_for_input(); -} - - -static char * kbd_initialize(void) -{ - int status; - - /* - * Test the keyboard interface. - * This seems to be the only way to get it going. - * If the test is successful a x55 is placed in the input buffer. - */ - kbd_write_command_w(KBD_CCMD_SELF_TEST); - if (kbd_wait_for_input() != 0x55) - return "Kbd: failed self test"; - /* - * Perform a keyboard interface test. This causes the controller - * to test the keyboard clock and data lines. The results of the - * test are placed in the input buffer. - */ - kbd_write_command_w(KBD_CCMD_KBD_TEST); - if (kbd_wait_for_input() != 0x00) - return "Kbd: interface failed self test"; - /* - * Enable the keyboard by allowing the keyboard clock to run. - */ - kbd_write_command_w(KBD_CCMD_KBD_ENABLE); - - /* - * Reset keyboard. If the read times out - * then the assumption is that no keyboard is - * plugged into the machine. - * This defaults the keyboard to scan-code set 2. - * - * Set up to try again if the keyboard asks for RESEND. - */ - do { - kbd_write_output_w(KBD_CMD_RESET); - status = kbd_wait_for_input(); - if (status == KBD_REPLY_ACK) - break; - if (status != KBD_REPLY_RESEND) { - PRINTF("status: %X\n",status); - return "Kbd: reset failed, no ACK"; - } - } while (1); - if (kbd_wait_for_input() != KBD_REPLY_POR) - return "Kbd: reset failed, no POR"; - - /* - * Set keyboard controller mode. During this, the keyboard should be - * in the disabled state. - * - * Set up to try again if the keyboard asks for RESEND. - */ - do { - kbd_write_output_w(KBD_CMD_DISABLE); - status = kbd_wait_for_input(); - if (status == KBD_REPLY_ACK) - break; - if (status != KBD_REPLY_RESEND) - return "Kbd: disable keyboard: no ACK"; - } while (1); - - kbd_write_command_w(KBD_CCMD_WRITE_MODE); - kbd_write_output_w(KBD_MODE_KBD_INT - | KBD_MODE_SYS - | KBD_MODE_DISABLE_MOUSE - | KBD_MODE_KCC); - - /* AMCC powerpc portables need this to use scan-code set 1 -- Cort */ - kbd_write_command_w(KBD_CCMD_READ_MODE); - if (!(kbd_wait_for_input() & KBD_MODE_KCC)) { - /* - * If the controller does not support conversion, - * Set the keyboard to scan-code set 1. - */ - kbd_write_output_w(0xF0); - kbd_wait_for_input(); - kbd_write_output_w(0x01); - kbd_wait_for_input(); - } - kbd_write_output_w(KBD_CMD_ENABLE); - if (kbd_wait_for_input() != KBD_REPLY_ACK) - return "Kbd: enable keyboard: no ACK"; - - /* - * Finally, set the typematic rate to maximum. - */ - kbd_write_output_w(KBD_CMD_SET_RATE); - if (kbd_wait_for_input() != KBD_REPLY_ACK) - return "Kbd: Set rate: no ACK"; - kbd_write_output_w(0x00); - if (kbd_wait_for_input() != KBD_REPLY_ACK) - return "Kbd: Set rate: no ACK"; - return NULL; -} - -static void kbd_interrupt(void *dev_id) -{ - handle_kbd_event(); -} - -/****************************************************************** - * Init - ******************************************************************/ - -int kbd_init_hw(void) -{ - char* result; - - kbd_request_region(); - - result=kbd_initialize(); - if (result==NULL) { - PRINTF("AT Keyboard initialized\n"); - kbd_request_irq(kbd_interrupt); - return (1); - } else { - printf("%s\n",result); - return (-1); - } -} - -void pckbd_leds(unsigned char leds) -{ - kbd_send_data(KBD_CMD_SET_LEDS); - kbd_send_data(leds); -} diff --git a/drivers/input/ps2mult.c b/drivers/input/ps2mult.c deleted file mode 100644 index ab749336bf..0000000000 --- a/drivers/input/ps2mult.c +++ /dev/null @@ -1,461 +0,0 @@ -/*********************************************************************** - * - * (C) Copyright 2004 - * DENX Software Engineering - * Wolfgang Denk, wd@denx.de - * - * PS/2 multiplexer driver - * - * Originally from linux source (drivers/char/ps2mult.c) - * - * Uses simple serial driver (ps2ser.c) to access the multiplexer - * Used by PS/2 keyboard driver (pc_keyb.c) - * - ***********************************************************************/ - -#include <common.h> - -#include <pc_keyb.h> -#include <asm/atomic.h> -#include <ps2mult.h> - -/* #define DEBUG_MULT */ -/* #define DEBUG_KEYB */ - -#define KBD_STAT_DEFAULT (KBD_STAT_SELFTEST | KBD_STAT_UNLOCKED) - -#define PRINTF(format, args...) printf("ps2mult.c: " format, ## args) - -#ifdef DEBUG_MULT -#define PRINTF_MULT(format, args...) printf("PS2MULT: " format, ## args) -#else -#define PRINTF_MULT(format, args...) -#endif - -#ifdef DEBUG_KEYB -#define PRINTF_KEYB(format, args...) printf("KEYB: " format, ## args) -#else -#define PRINTF_KEYB(format, args...) -#endif - - -static ulong start_time; -static int init_done = 0; - -static int received_escape = 0; -static int received_bsync = 0; -static int received_selector = 0; - -static int kbd_command_active = 0; -static int mouse_command_active = 0; -static int ctl_command_active = 0; - -static u_char command_byte = 0; - -static void (*keyb_handler)(void *dev_id); - -static u_char ps2mult_buf [PS2BUF_SIZE]; -static atomic_t ps2mult_buf_cnt; -static int ps2mult_buf_in_idx; -static int ps2mult_buf_out_idx; - -static u_char ps2mult_buf_status [PS2BUF_SIZE]; - -#ifndef CONFIG_BOARD_EARLY_INIT_R -#error #define CONFIG_BOARD_EARLY_INIT_R and call ps2mult_early_init() in board_early_init_r() -#endif -void ps2mult_early_init (void) -{ - start_time = get_timer(0); -} - -static void ps2mult_send_byte(u_char byte, u_char sel) -{ - ps2ser_putc(sel); - - if (sel == PS2MULT_KB_SELECTOR) { - PRINTF_MULT("0x%02x send KEYBOARD\n", byte); - kbd_command_active = 1; - } else { - PRINTF_MULT("0x%02x send MOUSE\n", byte); - mouse_command_active = 1; - } - - switch (byte) { - case PS2MULT_ESCAPE: - case PS2MULT_BSYNC: - case PS2MULT_KB_SELECTOR: - case PS2MULT_MS_SELECTOR: - case PS2MULT_SESSION_START: - case PS2MULT_SESSION_END: - ps2ser_putc(PS2MULT_ESCAPE); - break; - default: - break; - } - - ps2ser_putc(byte); -} - -static void ps2mult_receive_byte(u_char byte, u_char sel) -{ - u_char status = KBD_STAT_DEFAULT; - -#if 1 /* Ignore mouse in U-Boot */ - if (sel == PS2MULT_MS_SELECTOR) return; -#endif - - if (sel == PS2MULT_KB_SELECTOR) { - if (kbd_command_active) { - if (!received_bsync) { - PRINTF_MULT("0x%02x lost KEYBOARD !!!\n", byte); - return; - } else { - kbd_command_active = 0; - received_bsync = 0; - } - } - PRINTF_MULT("0x%02x receive KEYBOARD\n", byte); - status |= KBD_STAT_IBF | KBD_STAT_OBF; - } else { - if (mouse_command_active) { - if (!received_bsync) { - PRINTF_MULT("0x%02x lost MOUSE !!!\n", byte); - return; - } else { - mouse_command_active = 0; - received_bsync = 0; - } - } - PRINTF_MULT("0x%02x receive MOUSE\n", byte); - status |= KBD_STAT_IBF | KBD_STAT_OBF | KBD_STAT_MOUSE_OBF; - } - - if (atomic_read(&ps2mult_buf_cnt) < PS2BUF_SIZE) { - ps2mult_buf_status[ps2mult_buf_in_idx] = status; - ps2mult_buf[ps2mult_buf_in_idx++] = byte; - ps2mult_buf_in_idx &= (PS2BUF_SIZE - 1); - atomic_inc(&ps2mult_buf_cnt); - } else { - PRINTF("buffer overflow\n"); - } - - if (received_bsync) { - PRINTF("unexpected BSYNC\n"); - received_bsync = 0; - } -} - -void ps2mult_callback (int in_cnt) -{ - int i; - u_char byte; - static int keyb_handler_active = 0; - - if (!init_done) { - return; - } - - for (i = 0; i < in_cnt; i ++) { - byte = ps2ser_getc(); - - if (received_escape) { - ps2mult_receive_byte(byte, received_selector); - received_escape = 0; - } else switch (byte) { - case PS2MULT_ESCAPE: - PRINTF_MULT("ESCAPE receive\n"); - received_escape = 1; - break; - - case PS2MULT_BSYNC: - PRINTF_MULT("BSYNC receive\n"); - received_bsync = 1; - break; - - case PS2MULT_KB_SELECTOR: - case PS2MULT_MS_SELECTOR: - PRINTF_MULT("%s receive\n", - byte == PS2MULT_KB_SELECTOR ? "KB_SEL" : "MS_SEL"); - received_selector = byte; - break; - - case PS2MULT_SESSION_START: - case PS2MULT_SESSION_END: - PRINTF_MULT("%s receive\n", - byte == PS2MULT_SESSION_START ? - "SESSION_START" : "SESSION_END"); - break; - - default: - ps2mult_receive_byte(byte, received_selector); - } - } - - if (keyb_handler && !keyb_handler_active && - atomic_read(&ps2mult_buf_cnt)) { - keyb_handler_active = 1; - keyb_handler(NULL); - keyb_handler_active = 0; - } -} - -u_char ps2mult_read_status(void) -{ - u_char byte; - - if (atomic_read(&ps2mult_buf_cnt) == 0) { - ps2ser_check(); - } - - if (atomic_read(&ps2mult_buf_cnt)) { - byte = ps2mult_buf_status[ps2mult_buf_out_idx]; - } else { - byte = KBD_STAT_DEFAULT; - } - PRINTF_KEYB("read_status()=0x%02x\n", byte); - return byte; -} - -u_char ps2mult_read_input(void) -{ - u_char byte = 0; - - if (atomic_read(&ps2mult_buf_cnt) == 0) { - ps2ser_check(); - } - - if (atomic_read(&ps2mult_buf_cnt)) { - byte = ps2mult_buf[ps2mult_buf_out_idx++]; - ps2mult_buf_out_idx &= (PS2BUF_SIZE - 1); - atomic_dec(&ps2mult_buf_cnt); - } - PRINTF_KEYB("read_input()=0x%02x\n", byte); - return byte; -} - -void ps2mult_write_output(u_char val) -{ - int i; - - PRINTF_KEYB("write_output(0x%02x)\n", val); - - for (i = 0; i < KBD_TIMEOUT; i++) { - if (!kbd_command_active && !mouse_command_active) { - break; - } - udelay(1000); - ps2ser_check(); - } - - if (kbd_command_active) { - PRINTF("keyboard command not acknoledged\n"); - kbd_command_active = 0; - } - - if (mouse_command_active) { - PRINTF("mouse command not acknoledged\n"); - mouse_command_active = 0; - } - - if (ctl_command_active) { - switch (ctl_command_active) { - case KBD_CCMD_WRITE_MODE: - /* Scan code conversion not supported */ - command_byte = val & ~KBD_MODE_KCC; - break; - - case KBD_CCMD_WRITE_AUX_OBUF: - ps2mult_receive_byte(val, PS2MULT_MS_SELECTOR); - break; - - case KBD_CCMD_WRITE_MOUSE: - ps2mult_send_byte(val, PS2MULT_MS_SELECTOR); - break; - - default: - PRINTF("invalid controller command\n"); - break; - } - - ctl_command_active = 0; - return; - } - - ps2mult_send_byte(val, PS2MULT_KB_SELECTOR); -} - -void ps2mult_write_command(u_char val) -{ - ctl_command_active = 0; - - PRINTF_KEYB("write_command(0x%02x)\n", val); - - switch (val) { - case KBD_CCMD_READ_MODE: - ps2mult_receive_byte(command_byte, PS2MULT_KB_SELECTOR); - break; - - case KBD_CCMD_WRITE_MODE: - ctl_command_active = val; - break; - - case KBD_CCMD_MOUSE_DISABLE: - break; - - case KBD_CCMD_MOUSE_ENABLE: - break; - - case KBD_CCMD_SELF_TEST: - ps2mult_receive_byte(0x55, PS2MULT_KB_SELECTOR); - break; - - case KBD_CCMD_KBD_TEST: - ps2mult_receive_byte(0x00, PS2MULT_KB_SELECTOR); - break; - - case KBD_CCMD_KBD_DISABLE: - break; - - case KBD_CCMD_KBD_ENABLE: - break; - - case KBD_CCMD_WRITE_AUX_OBUF: - ctl_command_active = val; - break; - - case KBD_CCMD_WRITE_MOUSE: - ctl_command_active = val; - break; - - default: - PRINTF("invalid controller command\n"); - break; - } -} - -static int ps2mult_getc_w (void) -{ - int res = -1; - int i; - - for (i = 0; i < KBD_TIMEOUT; i++) { - if (ps2ser_check()) { - res = ps2ser_getc(); - break; - } - udelay(1000); - } - - switch (res) { - case PS2MULT_KB_SELECTOR: - case PS2MULT_MS_SELECTOR: - received_selector = res; - break; - default: - break; - } - - return res; -} - -int ps2mult_init (void) -{ - int byte; - int kbd_found = 0; - int mouse_found = 0; - - while (get_timer(start_time) < CONFIG_PS2MULT_DELAY); - - ps2ser_init(); - - ps2ser_putc(PS2MULT_SESSION_START); - - ps2ser_putc(PS2MULT_KB_SELECTOR); - ps2ser_putc(KBD_CMD_RESET); - - do { - byte = ps2mult_getc_w(); - } while (byte >= 0 && byte != KBD_REPLY_ACK); - - if (byte == KBD_REPLY_ACK) { - byte = ps2mult_getc_w(); - if (byte == 0xaa) { - kbd_found = 1; - puts("keyboard"); - } - } - - if (!kbd_found) { - while (byte >= 0) { - byte = ps2mult_getc_w(); - } - } - -#if 1 /* detect mouse */ - ps2ser_putc(PS2MULT_MS_SELECTOR); - ps2ser_putc(AUX_RESET); - - do { - byte = ps2mult_getc_w(); - } while (byte >= 0 && byte != AUX_ACK); - - if (byte == AUX_ACK) { - byte = ps2mult_getc_w(); - if (byte == 0xaa) { - byte = ps2mult_getc_w(); - if (byte == 0x00) { - mouse_found = 1; - puts(", mouse"); - } - } - } - - if (!mouse_found) { - while (byte >= 0) { - byte = ps2mult_getc_w(); - } - } -#endif - - if (mouse_found || kbd_found) { - if (!received_selector) { - if (mouse_found) { - received_selector = PS2MULT_MS_SELECTOR; - } else { - received_selector = PS2MULT_KB_SELECTOR; - } - } - - init_done = 1; - } else { - puts("No device found"); - } - - puts("\n"); - -#if 0 /* for testing */ - { - int i; - u_char key[] = { - 0x1f, 0x12, 0x14, 0x12, 0x31, 0x2f, 0x39, /* setenv */ - 0x1f, 0x14, 0x20, 0x17, 0x31, 0x39, /* stdin */ - 0x1f, 0x12, 0x13, 0x17, 0x1e, 0x26, 0x1c, /* serial */ - }; - - for (i = 0; i < sizeof (key); i++) { - ps2mult_receive_byte (key[i], PS2MULT_KB_SELECTOR); - ps2mult_receive_byte (key[i] | 0x80, PS2MULT_KB_SELECTOR); - } - } -#endif - - return init_done ? 0 : -1; -} - -int ps2mult_request_irq(void (*handler)(void *)) -{ - keyb_handler = handler; - - return 0; -} diff --git a/drivers/input/ps2ser.c b/drivers/input/ps2ser.c deleted file mode 100644 index 0b5ce06853..0000000000 --- a/drivers/input/ps2ser.c +++ /dev/null @@ -1,147 +0,0 @@ -/*********************************************************************** - * - * (C) Copyright 2004-2009 - * DENX Software Engineering - * Wolfgang Denk, wd@denx.de - * - * Simple 16550A serial driver - * - * Originally from linux source (drivers/char/ps2ser.c) - * - * Used by the PS/2 multiplexer driver (ps2mult.c) - * - ***********************************************************************/ - -#include <common.h> - -#include <asm/io.h> -#include <asm/atomic.h> -#include <ps2mult.h> -/* This is needed for ns16550.h */ -#ifndef CONFIG_SYS_NS16550_REG_SIZE -#define CONFIG_SYS_NS16550_REG_SIZE 1 -#endif -#include <ns16550.h> - -DECLARE_GLOBAL_DATA_PTR; - -/* #define DEBUG */ - -#define PS2SER_BAUD 57600 - -#if CONFIG_PS2SERIAL == 1 -#define COM_BASE (CONFIG_SYS_CCSRBAR+0x4500) -#elif CONFIG_PS2SERIAL == 2 -#define COM_BASE (CONFIG_SYS_CCSRBAR+0x4600) -#else -#error CONFIG_PS2SERIAL must be in 1 ... 2 -#endif - -static int ps2ser_getc_hw(void); -static void ps2ser_interrupt(void *dev_id); - -extern struct serial_state rs_table[]; /* in serial.c */ - -static u_char ps2buf[PS2BUF_SIZE]; -static atomic_t ps2buf_cnt; -static int ps2buf_in_idx; -static int ps2buf_out_idx; - -int ps2ser_init(void) -{ - NS16550_t com_port = (NS16550_t)COM_BASE; - - com_port->ier = 0x00; - com_port->lcr = UART_LCR_BKSE | UART_LCR_8N1; - com_port->dll = (CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) & 0xff; - com_port->dlm = ((CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) >> 8) & 0xff; - com_port->lcr = UART_LCR_8N1; - com_port->mcr = (UART_MCR_DTR | UART_MCR_RTS); - com_port->fcr = (UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR); - - return (0); -} - -void ps2ser_putc(int chr) -{ - NS16550_t com_port = (NS16550_t)COM_BASE; - debug(">>>> 0x%02x\n", chr); - - while ((com_port->lsr & UART_LSR_THRE) == 0); - com_port->thr = chr; -} - -static int ps2ser_getc_hw(void) -{ - NS16550_t com_port = (NS16550_t)COM_BASE; - int res = -1; - - if (com_port->lsr & UART_LSR_DR) { - res = com_port->rbr; - } - - return res; -} - -int ps2ser_getc(void) -{ - volatile int chr; - int flags; - - debug("<< "); - - flags = disable_interrupts(); - - do { - if (atomic_read(&ps2buf_cnt) != 0) { - chr = ps2buf[ps2buf_out_idx++]; - ps2buf_out_idx &= (PS2BUF_SIZE - 1); - atomic_dec(&ps2buf_cnt); - } else { - chr = ps2ser_getc_hw(); - } - } - while (chr < 0); - - if (flags) - enable_interrupts(); - - debug("0x%02x\n", chr); - - return chr; -} - -int ps2ser_check(void) -{ - int flags; - - flags = disable_interrupts(); - ps2ser_interrupt(NULL); - if (flags) enable_interrupts(); - - return atomic_read(&ps2buf_cnt); -} - -static void ps2ser_interrupt(void *dev_id) -{ - NS16550_t com_port = (NS16550_t)COM_BASE; - int chr; - int status; - - do { - chr = ps2ser_getc_hw(); - status = com_port->lsr; - if (chr < 0) continue; - - if (atomic_read(&ps2buf_cnt) < PS2BUF_SIZE) { - ps2buf[ps2buf_in_idx++] = chr; - ps2buf_in_idx &= (PS2BUF_SIZE - 1); - atomic_inc(&ps2buf_cnt); - } else { - printf ("ps2ser.c: buffer overflow\n"); - } - } while (status & UART_LSR_DR); - if (atomic_read(&ps2buf_cnt)) { - ps2mult_callback(atomic_read(&ps2buf_cnt)); - } -} diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 5f67e336db..6935da2177 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -267,13 +267,22 @@ config SH_SDHI Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform config MMC_UNIPHIER - bool "UniPhier/RCar SD/MMC Host Controller support" - depends on ARCH_UNIPHIER || ARCH_RMOBILE + bool "UniPhier SD/MMC Host Controller support" + depends on ARCH_UNIPHIER depends on BLK && DM_MMC depends on OF_CONTROL help This selects support for the Matsushita SD/MMC Host Controller on - SocioNext UniPhier and Renesas RCar SoCs. + SocioNext UniPhier SoCs. + +config RENESAS_SDHI + bool "Renesas R-Car SD/MMC Host Controller support" + depends on ARCH_RMOBILE + depends on BLK && DM_MMC + depends on OF_CONTROL + help + This selects support for the Matsushita SD/MMC Host Controller on + Renesas R-Car SoCs. config MMC_BCM2835 bool "BCM2835 family custom SD/MMC Host Controller support" @@ -523,18 +532,18 @@ config STM32_SDMMC2 If you have a board based on such a SoC and with a SD/MMC slot, say Y or M here. -config MMC_NDS32 - bool "Andestech SD/MMC controller support" - depends on DM_MMC && OF_CONTROL && BLK && FTSDC010 - help - This enables support for the Andestech SD/MMM controller, which is - based on Faraday IP. - config FTSDC010 bool "Ftsdc010 SD/MMC controller Support" help This SD/MMC controller is present in Andestech SoCs which is based on Faraday IP. +config FTSDC010_SDIO + bool "Support ftsdc010 sdio" + default n + depends on FTSDC010 + help + This can enable ftsdc010 sdio function. + endif config TEGRA124_MMC_DISABLE_EXT_LOOPBACK @@ -548,6 +557,12 @@ config TEGRA124_MMC_DISABLE_EXT_LOOPBACK TODO(marcel.ziswiler@toradex.com): Move to device tree controlled approach once proper kernel integration made it mainline. +config FSL_ESDHC + bool "Freescale/NXP eSDHC controller support" + help + This selects support for the eSDHC (enhanced secure digital host + controller) found on numerous Freescale/NXP SoCs. + endmenu config SYS_FSL_ERRATUM_ESDHC111 diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 42113e2603..cf46c332f1 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -42,7 +42,6 @@ obj-$(CONFIG_MMC_SANDBOX) += sandbox_mmc.o obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o -obj-$(CONFIG_MMC_NDS32) += nds32_mmc.o # SDHCI obj-$(CONFIG_MMC_SDHCI) += sdhci.o @@ -63,5 +62,6 @@ obj-$(CONFIG_MMC_SDHCI_XENON) += xenon_sdhci.o obj-$(CONFIG_MMC_SDHCI_ZYNQ) += zynq_sdhci.o obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o -obj-$(CONFIG_MMC_UNIPHIER) += uniphier-sd.o +obj-$(CONFIG_MMC_UNIPHIER) += tmio-common.o uniphier-sd.o +obj-$(CONFIG_RENESAS_SDHI) += tmio-common.o renesas-sdhi.o obj-$(CONFIG_MMC_BCM2835) += bcm2835_sdhost.o diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c index 3157354d2a..08bddd410e 100644 --- a/drivers/mmc/bcm2835_sdhci.c +++ b/drivers/mmc/bcm2835_sdhci.c @@ -183,7 +183,7 @@ static int bcm2835_sdhci_probe(struct udevice *dev) if (base == FDT_ADDR_T_NONE) return -EINVAL; - ret = bcm2835_get_mmc_clock(); + ret = bcm2835_get_mmc_clock(BCM2835_MBOX_CLOCK_ID_EMMC); if (ret < 0) { debug("%s: Failed to set MMC clock (err=%d)\n", __func__, ret); return ret; diff --git a/drivers/mmc/bcm2835_sdhost.c b/drivers/mmc/bcm2835_sdhost.c index 1bf52a3019..bccd182e50 100644 --- a/drivers/mmc/bcm2835_sdhost.c +++ b/drivers/mmc/bcm2835_sdhost.c @@ -35,6 +35,7 @@ #include <dm.h> #include <mmc.h> #include <asm/arch/msg.h> +#include <asm/arch/mbox.h> #include <asm/unaligned.h> #include <linux/compat.h> #include <linux/io.h> @@ -941,7 +942,7 @@ static int bcm2835_probe(struct udevice *dev) if (!host->ioaddr) return -ENOMEM; - host->max_clk = bcm2835_get_mmc_clock(); + host->max_clk = bcm2835_get_mmc_clock(BCM2835_MBOX_CLOCK_ID_CORE); bcm2835_add_host(host); diff --git a/drivers/mmc/ftsdc010_mci.c b/drivers/mmc/ftsdc010_mci.c index 6ac4f83bd1..9de3a1503d 100644 --- a/drivers/mmc/ftsdc010_mci.c +++ b/drivers/mmc/ftsdc010_mci.c @@ -4,23 +4,63 @@ * (C) Copyright 2010 Faraday Technology * Dante Su <dantesu@faraday-tech.com> * + * Copyright 2018 Andes Technology, Inc. + * Author: Rick Chen (rick@andestech.com) + * * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> +#include <clk.h> #include <malloc.h> #include <part.h> #include <mmc.h> - #include <linux/io.h> #include <linux/errno.h> #include <asm/byteorder.h> #include <faraday/ftsdc010.h> #include "ftsdc010_mci.h" +#include <dm.h> +#include <dt-structs.h> +#include <errno.h> +#include <mapmem.h> +#include <pwrseq.h> +#include <syscon.h> +#include <linux/err.h> + +DECLARE_GLOBAL_DATA_PTR; #define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 4) /* 250 ms */ #define CFG_RST_TIMEOUT CONFIG_SYS_HZ /* 1 sec reset timeout */ +#if CONFIG_IS_ENABLED(OF_PLATDATA) +struct ftsdc010 { + fdt32_t bus_width; + bool cap_mmc_highspeed; + bool cap_sd_highspeed; + fdt32_t clock_freq_min_max[2]; + struct phandle_2_cell clocks[4]; + fdt32_t fifo_depth; + fdt32_t reg[2]; +}; +#endif + +struct ftsdc010_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct ftsdc010 dtplat; +#endif + struct mmc_config cfg; + struct mmc mmc; +}; + +struct ftsdc_priv { + struct clk clk; + struct ftsdc010_chip chip; + int fifo_depth; + bool fifo_mode; + u32 minmax[2]; +}; + static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) { struct ftsdc010_chip *chip = mmc->priv; @@ -138,16 +178,10 @@ static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask) /* * u-boot mmc api */ -#ifdef CONFIG_DM_MMC static int ftsdc010_request(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct mmc *mmc = mmc_get_mmc_dev(dev); -#else -static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ -#endif int ret = -EOPNOTSUPP; uint32_t len = 0; struct ftsdc010_chip *chip = mmc->priv; @@ -248,14 +282,9 @@ static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd, return ret; } -#ifdef CONFIG_DM_MMC static int ftsdc010_set_ios(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); -#else -static int ftsdc010_set_ios(struct mmc *mmc) -{ -#endif struct ftsdc010_chip *chip = mmc->priv; struct ftsdc010_mmc __iomem *regs = chip->regs; @@ -277,27 +306,17 @@ static int ftsdc010_set_ios(struct mmc *mmc) return 0; } -#ifdef CONFIG_DM_MMC static int ftsdc010_get_cd(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); -#else -static int ftsdc010_get_cd(struct mmc *mmc) -{ -#endif struct ftsdc010_chip *chip = mmc->priv; struct ftsdc010_mmc __iomem *regs = chip->regs; return !(readl(®s->status) & FTSDC010_STATUS_CARD_DETECT); } -#ifdef CONFIG_DM_MMC static int ftsdc010_get_wp(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); -#else -static int ftsdc010_get_wp(struct mmc *mmc) -{ -#endif struct ftsdc010_chip *chip = mmc->priv; struct ftsdc010_mmc __iomem *regs = chip->regs; if (readl(®s->status) & FTSDC010_STATUS_WRITE_PROT) { @@ -337,31 +356,20 @@ static int ftsdc010_init(struct mmc *mmc) return 0; } -#ifdef CONFIG_DM_MMC -int ftsdc010_probe(struct udevice *dev) +static int ftsdc010_probe(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); return ftsdc010_init(mmc); } -const struct dm_mmc_ops dm_ftsdc010_ops = { +const struct dm_mmc_ops dm_ftsdc010_mmc_ops = { .send_cmd = ftsdc010_request, .set_ios = ftsdc010_set_ios, .get_cd = ftsdc010_get_cd, .get_wp = ftsdc010_get_wp, }; -#else -static const struct mmc_ops ftsdc010_ops = { - .send_cmd = ftsdc010_request, - .set_ios = ftsdc010_set_ios, - .getcd = ftsdc010_get_cd, - .getwp = ftsdc010_get_wp, - .init = ftsdc010_init, -}; -#endif - -void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, +static void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, uint caps, u32 max_clk, u32 min_clk) { cfg->name = name; @@ -380,73 +388,94 @@ void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; } -void set_bus_width(struct ftsdc010_mmc __iomem *regs, struct mmc_config *cfg) +static int ftsdc010_mmc_ofdata_to_platdata(struct udevice *dev) { - switch (readl(®s->bwr) & FTSDC010_BWR_CAPS_MASK) { - case FTSDC010_BWR_CAPS_4BIT: - cfg->host_caps |= MMC_MODE_4BIT; - break; - case FTSDC010_BWR_CAPS_8BIT: - cfg->host_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; - break; - default: - break; +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct ftsdc_priv *priv = dev_get_priv(dev); + struct ftsdc010_chip *chip = &priv->chip; + chip->name = dev->name; + chip->ioaddr = (void *)devfdt_get_addr(dev); + chip->buswidth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "bus-width", 4); + chip->priv = dev; + priv->fifo_depth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "fifo-depth", 0); + priv->fifo_mode = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "fifo-mode"); + if (fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev), + "clock-freq-min-max", priv->minmax, 2)) { + int val = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "max-frequency", -EINVAL); + if (val < 0) + return val; + + priv->minmax[0] = 400000; /* 400 kHz */ + priv->minmax[1] = val; + } else { + debug("%s: 'clock-freq-min-max' property was deprecated.\n", + __func__); } +#endif + chip->sclk = priv->minmax[1]; + chip->regs = chip->ioaddr; + return 0; } -#ifdef CONFIG_BLK -int ftsdc010_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg) +static int ftsdc010_mmc_probe(struct udevice *dev) { - return mmc_bind(dev, mmc, cfg); + struct ftsdc010_plat *plat = dev_get_platdata(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct ftsdc_priv *priv = dev_get_priv(dev); + struct ftsdc010_chip *chip = &priv->chip; + struct udevice *pwr_dev __maybe_unused; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + int ret; + struct ftsdc010 *dtplat = &plat->dtplat; + chip->name = dev->name; + chip->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); + chip->buswidth = dtplat->bus_width; + chip->priv = dev; + chip->dev_index = 1; + memcpy(priv->minmax, dtplat->clock_freq_min_max, sizeof(priv->minmax)); + ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk); + if (ret < 0) + return ret; +#endif + + if (dev_read_bool(dev, "cap-mmc-highspeed") || \ + dev_read_bool(dev, "cap-sd-highspeed")) + chip->caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz; + + ftsdc_setup_cfg(&plat->cfg, dev->name, chip->buswidth, chip->caps, + priv->minmax[1] , priv->minmax[0]); + chip->mmc = &plat->mmc; + chip->mmc->priv = &priv->chip; + chip->mmc->dev = dev; + upriv->mmc = chip->mmc; + return ftsdc010_probe(dev); } -#else -int ftsdc010_mmc_init(int devid) +int ftsdc010_mmc_bind(struct udevice *dev) { - struct mmc *mmc; - struct ftsdc010_chip *chip; - struct ftsdc010_mmc __iomem *regs; -#ifdef CONFIG_FTSDC010_BASE_LIST - uint32_t base_list[] = CONFIG_FTSDC010_BASE_LIST; - - if (devid < 0 || devid >= ARRAY_SIZE(base_list)) - return -1; - regs = (void __iomem *)base_list[devid]; -#else - regs = (void __iomem *)(CONFIG_FTSDC010_BASE + (devid << 20)); -#endif - - chip = malloc(sizeof(struct ftsdc010_chip)); - if (!chip) - return -ENOMEM; - memset(chip, 0, sizeof(struct ftsdc010_chip)); + struct ftsdc010_plat *plat = dev_get_platdata(dev); - chip->regs = regs; -#ifdef CONFIG_SYS_CLK_FREQ - chip->sclk = CONFIG_SYS_CLK_FREQ; -#else - chip->sclk = clk_get_rate("SDC"); -#endif + return mmc_bind(dev, &plat->mmc, &plat->cfg); +} - chip->cfg.name = "ftsdc010"; -#ifndef CONFIG_DM_MMC - chip->cfg.ops = &ftsdc010_ops; -#endif - chip->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz; - set_bus_width(regs , &chip->cfg); - chip->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; - chip->cfg.f_max = chip->sclk / 2; - chip->cfg.f_min = chip->sclk / 0x100; - - chip->cfg.part_type = PART_TYPE_DOS; - chip->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - - mmc = mmc_create(&chip->cfg, chip); - if (mmc == NULL) { - free(chip); - return -ENOMEM; - } +static const struct udevice_id ftsdc010_mmc_ids[] = { + { .compatible = "andestech,atsdc010" }, + { } +}; - return 0; -} -#endif +U_BOOT_DRIVER(ftsdc010_mmc) = { + .name = "ftsdc010_mmc", + .id = UCLASS_MMC, + .of_match = ftsdc010_mmc_ids, + .ofdata_to_platdata = ftsdc010_mmc_ofdata_to_platdata, + .ops = &dm_ftsdc010_mmc_ops, + .bind = ftsdc010_mmc_bind, + .probe = ftsdc010_mmc_probe, + .priv_auto_alloc_size = sizeof(struct ftsdc_priv), + .platdata_auto_alloc_size = sizeof(struct ftsdc010_plat), +}; diff --git a/drivers/mmc/ftsdc010_mci.h b/drivers/mmc/ftsdc010_mci.h index 31a27fd772..e417360662 100644 --- a/drivers/mmc/ftsdc010_mci.h +++ b/drivers/mmc/ftsdc010_mci.h @@ -35,19 +35,4 @@ struct ftsdc010_chip { bool fifo_mode; }; - -#ifdef CONFIG_DM_MMC -/* Export the operations to drivers */ -int ftsdc010_probe(struct udevice *dev); -extern const struct dm_mmc_ops dm_ftsdc010_ops; -#endif -void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, - uint caps, u32 max_clk, u32 min_clk); -void set_bus_width(struct ftsdc010_mmc __iomem *regs, struct mmc_config *cfg); - -#ifdef CONFIG_BLK -int ftsdc010_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg); -#endif - - #endif /* __FTSDC010_MCI_H */ diff --git a/drivers/mmc/nds32_mmc.c b/drivers/mmc/nds32_mmc.c deleted file mode 100644 index 6d3c8572e5..0000000000 --- a/drivers/mmc/nds32_mmc.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Andestech ATFSDC010 SD/MMC driver - * - * (C) Copyright 2017 - * Rick Chen, NDS32 Software Engineering, rick@andestech.com - - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <clk.h> -#include <dm.h> -#include <dt-structs.h> -#include <errno.h> -#include <mapmem.h> -#include <mmc.h> -#include <pwrseq.h> -#include <syscon.h> -#include <linux/err.h> -#include <faraday/ftsdc010.h> -#include "ftsdc010_mci.h" - -DECLARE_GLOBAL_DATA_PTR; - -#if CONFIG_IS_ENABLED(OF_PLATDATA) -struct nds_mmc { - fdt32_t bus_width; - bool cap_mmc_highspeed; - bool cap_sd_highspeed; - fdt32_t clock_freq_min_max[2]; - struct phandle_2_cell clocks[4]; - fdt32_t fifo_depth; - fdt32_t reg[2]; -}; -#endif - -struct nds_mmc_plat { -#if CONFIG_IS_ENABLED(OF_PLATDATA) - struct nds_mmc dtplat; -#endif - struct mmc_config cfg; - struct mmc mmc; -}; - -struct ftsdc_priv { - struct clk clk; - struct ftsdc010_chip chip; - int fifo_depth; - bool fifo_mode; - u32 minmax[2]; -}; - -static int nds32_mmc_ofdata_to_platdata(struct udevice *dev) -{ -#if !CONFIG_IS_ENABLED(OF_PLATDATA) - struct ftsdc_priv *priv = dev_get_priv(dev); - struct ftsdc010_chip *chip = &priv->chip; - chip->name = dev->name; - chip->ioaddr = (void *)devfdt_get_addr(dev); - chip->buswidth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "bus-width", 4); - chip->priv = dev; - priv->fifo_depth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "fifo-depth", 0); - priv->fifo_mode = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), - "fifo-mode"); - if (fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev), - "clock-freq-min-max", priv->minmax, 2)) { - int val = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "max-frequency", -EINVAL); - if (val < 0) - return val; - - priv->minmax[0] = 400000; /* 400 kHz */ - priv->minmax[1] = val; - } else { - debug("%s: 'clock-freq-min-max' property was deprecated.\n", - __func__); - } -#endif - chip->sclk = priv->minmax[1]; - chip->regs = chip->ioaddr; - return 0; -} - -static int nds32_mmc_probe(struct udevice *dev) -{ - struct nds_mmc_plat *plat = dev_get_platdata(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - struct ftsdc_priv *priv = dev_get_priv(dev); - struct ftsdc010_chip *chip = &priv->chip; - struct udevice *pwr_dev __maybe_unused; -#if CONFIG_IS_ENABLED(OF_PLATDATA) - int ret; - struct nds_mmc *dtplat = &plat->dtplat; - chip->name = dev->name; - chip->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); - chip->buswidth = dtplat->bus_width; - chip->priv = dev; - chip->dev_index = 1; - memcpy(priv->minmax, dtplat->clock_freq_min_max, sizeof(priv->minmax)); - ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk); - if (ret < 0) - return ret; -#endif - ftsdc_setup_cfg(&plat->cfg, dev->name, chip->buswidth, chip->caps, - priv->minmax[1] , priv->minmax[0]); - chip->mmc = &plat->mmc; - chip->mmc->priv = &priv->chip; - chip->mmc->dev = dev; - upriv->mmc = chip->mmc; - return ftsdc010_probe(dev); -} - -static int nds32_mmc_bind(struct udevice *dev) -{ - struct nds_mmc_plat *plat = dev_get_platdata(dev); - return ftsdc010_bind(dev, &plat->mmc, &plat->cfg); -} - -static const struct udevice_id nds32_mmc_ids[] = { - { .compatible = "andestech,atsdc010" }, - { } -}; - -U_BOOT_DRIVER(nds32_mmc_drv) = { - .name = "nds32_mmc", - .id = UCLASS_MMC, - .of_match = nds32_mmc_ids, - .ofdata_to_platdata = nds32_mmc_ofdata_to_platdata, - .ops = &dm_ftsdc010_ops, - .bind = nds32_mmc_bind, - .probe = nds32_mmc_probe, - .priv_auto_alloc_size = sizeof(struct ftsdc_priv), - .platdata_auto_alloc_size = sizeof(struct nds_mmc_plat), -}; diff --git a/drivers/mmc/pci_mmc.c b/drivers/mmc/pci_mmc.c index 05c0044a7a..b7a2ebfe3f 100644 --- a/drivers/mmc/pci_mmc.c +++ b/drivers/mmc/pci_mmc.c @@ -29,11 +29,10 @@ static int pci_mmc_probe(struct udevice *dev) struct pci_mmc_plat *plat = dev_get_platdata(dev); struct pci_mmc_priv *priv = dev_get_priv(dev); struct sdhci_host *host = &priv->host; - u32 ioaddr; int ret; - dm_pci_read_config32(dev, PCI_BASE_ADDRESS_0, &ioaddr); - host->ioaddr = map_sysmem(ioaddr, 0); + host->ioaddr = (void *)dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, + PCI_REGION_MEM); host->name = dev->name; ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); if (ret) diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c new file mode 100644 index 0000000000..56a43ca7d3 --- /dev/null +++ b/drivers/mmc/renesas-sdhi.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <fdtdec.h> +#include <mmc.h> +#include <dm.h> +#include <linux/compat.h> +#include <linux/dma-direction.h> +#include <linux/io.h> +#include <linux/sizes.h> +#include <power/regulator.h> +#include <asm/unaligned.h> + +#include "tmio-common.h" + +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + +/* SCC registers */ +#define RENESAS_SDHI_SCC_DTCNTL 0x800 +#define RENESAS_SDHI_SCC_DTCNTL_TAPEN BIT(0) +#define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 +#define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff +#define RENESAS_SDHI_SCC_TAPSET 0x804 +#define RENESAS_SDHI_SCC_DT2FF 0x808 +#define RENESAS_SDHI_SCC_CKSEL 0x80c +#define RENESAS_SDHI_SCC_CKSEL_DTSEL BIT(0) +#define RENESAS_SDHI_SCC_RVSCNTL 0x810 +#define RENESAS_SDHI_SCC_RVSCNTL_RVSEN BIT(0) +#define RENESAS_SDHI_SCC_RVSREQ 0x814 +#define RENESAS_SDHI_SCC_RVSREQ_RVSERR BIT(2) +#define RENESAS_SDHI_SCC_SMPCMP 0x818 +#define RENESAS_SDHI_SCC_TMPPORT2 0x81c + +#define RENESAS_SDHI_MAX_TAP 3 + +static unsigned int renesas_sdhi_init_tuning(struct tmio_sd_priv *priv) +{ + u32 reg; + + /* Initialize SCC */ + tmio_sd_writel(priv, 0, TMIO_SD_INFO1); + + reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); + reg &= ~TMIO_SD_CLKCTL_SCLKEN; + tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); + + /* Set sampling clock selection range */ + tmio_sd_writel(priv, 0x8 << RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT, + RENESAS_SDHI_SCC_DTCNTL); + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_DTCNTL); + reg |= RENESAS_SDHI_SCC_DTCNTL_TAPEN; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_DTCNTL); + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); + reg |= RENESAS_SDHI_SCC_CKSEL_DTSEL; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL); + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + + tmio_sd_writel(priv, 0x300 /* scc_tappos */, + RENESAS_SDHI_SCC_DT2FF); + + reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); + reg |= TMIO_SD_CLKCTL_SCLKEN; + tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); + + /* Read TAPNUM */ + return (tmio_sd_readl(priv, RENESAS_SDHI_SCC_DTCNTL) >> + RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & + RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK; +} + +static void renesas_sdhi_reset_tuning(struct tmio_sd_priv *priv) +{ + u32 reg; + + /* Reset SCC */ + reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); + reg &= ~TMIO_SD_CLKCTL_SCLKEN; + tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); + reg &= ~RENESAS_SDHI_SCC_CKSEL_DTSEL; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL); + + reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); + reg |= TMIO_SD_CLKCTL_SCLKEN; + tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); +} + +static void renesas_sdhi_prepare_tuning(struct tmio_sd_priv *priv, + unsigned long tap) +{ + /* Set sampling clock position */ + tmio_sd_writel(priv, tap, RENESAS_SDHI_SCC_TAPSET); +} + +static unsigned int renesas_sdhi_compare_scc_data(struct tmio_sd_priv *priv) +{ + /* Get comparison of sampling data */ + return tmio_sd_readl(priv, RENESAS_SDHI_SCC_SMPCMP); +} + +static int renesas_sdhi_select_tuning(struct tmio_sd_priv *priv, + unsigned int tap_num, unsigned int taps, + unsigned int smpcmp) +{ + unsigned long tap_cnt; /* counter of tuning success */ + unsigned long tap_set; /* tap position */ + unsigned long tap_start;/* start position of tuning success */ + unsigned long tap_end; /* end position of tuning success */ + unsigned long ntap; /* temporary counter of tuning success */ + unsigned long match_cnt;/* counter of matching data */ + unsigned long i; + bool select = false; + u32 reg; + + /* Clear SCC_RVSREQ */ + tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ); + + /* Merge the results */ + for (i = 0; i < tap_num * 2; i++) { + if (!(taps & BIT(i))) { + taps &= ~BIT(i % tap_num); + taps &= ~BIT((i % tap_num) + tap_num); + } + if (!(smpcmp & BIT(i))) { + smpcmp &= ~BIT(i % tap_num); + smpcmp &= ~BIT((i % tap_num) + tap_num); + } + } + + /* + * Find the longest consecutive run of successful probes. If that + * is more than RENESAS_SDHI_MAX_TAP probes long then use the + * center index as the tap. + */ + tap_cnt = 0; + ntap = 0; + tap_start = 0; + tap_end = 0; + for (i = 0; i < tap_num * 2; i++) { + if (taps & BIT(i)) + ntap++; + else { + if (ntap > tap_cnt) { + tap_start = i - ntap; + tap_end = i - 1; + tap_cnt = ntap; + } + ntap = 0; + } + } + + if (ntap > tap_cnt) { + tap_start = i - ntap; + tap_end = i - 1; + tap_cnt = ntap; + } + + /* + * If all of the TAP is OK, the sampling clock position is selected by + * identifying the change point of data. + */ + if (tap_cnt == tap_num * 2) { + match_cnt = 0; + ntap = 0; + tap_start = 0; + tap_end = 0; + for (i = 0; i < tap_num * 2; i++) { + if (smpcmp & BIT(i)) + ntap++; + else { + if (ntap > match_cnt) { + tap_start = i - ntap; + tap_end = i - 1; + match_cnt = ntap; + } + ntap = 0; + } + } + if (ntap > match_cnt) { + tap_start = i - ntap; + tap_end = i - 1; + match_cnt = ntap; + } + if (match_cnt) + select = true; + } else if (tap_cnt >= RENESAS_SDHI_MAX_TAP) + select = true; + + if (select) + tap_set = ((tap_start + tap_end) / 2) % tap_num; + else + return -EIO; + + /* Set SCC */ + tmio_sd_writel(priv, tap_set, RENESAS_SDHI_SCC_TAPSET); + + /* Enable auto re-tuning */ + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg |= RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + + return 0; +} + +int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; + unsigned int tap_num; + unsigned int taps = 0, smpcmp = 0; + int i, ret = 0; + u32 caps; + + /* Only supported on Renesas RCar */ + if (!(priv->caps & TMIO_SD_CAP_RCAR_UHS)) + return -EINVAL; + + /* clock tuning is not needed for upto 52MHz */ + if (!((mmc->selected_mode == MMC_HS_200) || + (mmc->selected_mode == UHS_SDR104) || + (mmc->selected_mode == UHS_SDR50))) + return 0; + + tap_num = renesas_sdhi_init_tuning(priv); + if (!tap_num) + /* Tuning is not supported */ + goto out; + + if (tap_num * 2 >= sizeof(taps) * 8) { + dev_err(dev, + "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n"); + goto out; + } + + /* Issue CMD19 twice for each tap */ + for (i = 0; i < 2 * tap_num; i++) { + renesas_sdhi_prepare_tuning(priv, i % tap_num); + + /* Force PIO for the tuning */ + caps = priv->caps; + priv->caps &= ~TMIO_SD_CAP_DMA_INTERNAL; + + ret = mmc_send_tuning(mmc, opcode, NULL); + + priv->caps = caps; + + if (ret == 0) + taps |= BIT(i); + + ret = renesas_sdhi_compare_scc_data(priv); + if (ret == 0) + smpcmp |= BIT(i); + + mdelay(1); + } + + ret = renesas_sdhi_select_tuning(priv, tap_num, taps, smpcmp); + +out: + if (ret < 0) { + dev_warn(dev, "Tuning procedure failed\n"); + renesas_sdhi_reset_tuning(priv); + } + + return ret; +} +#endif + +static int renesas_sdhi_set_ios(struct udevice *dev) +{ + int ret = tmio_sd_set_ios(dev); + + mdelay(10); + +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + struct tmio_sd_priv *priv = dev_get_priv(dev); + + renesas_sdhi_reset_tuning(priv); +#endif + + return ret; +} + +static const struct dm_mmc_ops renesas_sdhi_ops = { + .send_cmd = tmio_sd_send_cmd, + .set_ios = renesas_sdhi_set_ios, + .get_cd = tmio_sd_get_cd, +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + .execute_tuning = renesas_sdhi_execute_tuning, +#endif +}; + +#define RENESAS_GEN2_QUIRKS TMIO_SD_CAP_RCAR_GEN2 +#define RENESAS_GEN3_QUIRKS \ + TMIO_SD_CAP_64BIT | TMIO_SD_CAP_RCAR_GEN3 | TMIO_SD_CAP_RCAR_UHS + +static const struct udevice_id renesas_sdhi_match[] = { + { .compatible = "renesas,sdhi-r8a7790", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7791", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7792", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7793", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7794", .data = RENESAS_GEN2_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7795", .data = RENESAS_GEN3_QUIRKS }, + { .compatible = "renesas,sdhi-r8a7796", .data = RENESAS_GEN3_QUIRKS }, + { .compatible = "renesas,sdhi-r8a77965", .data = RENESAS_GEN3_QUIRKS }, + { .compatible = "renesas,sdhi-r8a77970", .data = RENESAS_GEN3_QUIRKS }, + { .compatible = "renesas,sdhi-r8a77995", .data = RENESAS_GEN3_QUIRKS }, + { /* sentinel */ } +}; + +static int renesas_sdhi_probe(struct udevice *dev) +{ + u32 quirks = dev_get_driver_data(dev); + struct fdt_resource reg_res; + DECLARE_GLOBAL_DATA_PTR; + int ret; + + if (quirks == RENESAS_GEN2_QUIRKS) { + ret = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), + "reg", 0, ®_res); + if (ret < 0) { + dev_err(dev, "\"reg\" resource not found, ret=%i\n", + ret); + return ret; + } + + if (fdt_resource_size(®_res) == 0x100) + quirks |= TMIO_SD_CAP_16BIT; + } + + ret = tmio_sd_probe(dev, quirks); +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + if (!ret) + renesas_sdhi_reset_tuning(dev_get_priv(dev)); +#endif + return ret; +} + +U_BOOT_DRIVER(renesas_sdhi) = { + .name = "renesas-sdhi", + .id = UCLASS_MMC, + .of_match = renesas_sdhi_match, + .bind = tmio_sd_bind, + .probe = renesas_sdhi_probe, + .priv_auto_alloc_size = sizeof(struct tmio_sd_priv), + .platdata_auto_alloc_size = sizeof(struct tmio_sd_plat), + .ops = &renesas_sdhi_ops, +}; diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index be6edb2eae..ab89be4764 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -62,6 +62,13 @@ static int arasan_sdhci_probe(struct udevice *dev) host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD; host->max_clk = max_frequency; + /* + * The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg + * doesn't allow us to clear MMC_MODE_4BIT. Consequently, we don't + * check for other bus-width values. + */ + if (host->bus_width == 8) + host->host_caps |= MMC_MODE_8BIT; ret = sdhci_setup_cfg(&plat->cfg, host, 0, EMMC_MIN_FREQ); @@ -82,6 +89,7 @@ static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev) host->name = dev->name; host->ioaddr = dev_read_addr_ptr(dev); + host->bus_width = dev_read_u32_default(dev, "bus-width", 4); #endif return 0; diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c index bd2200a05c..f5b21dd097 100644 --- a/drivers/mmc/stm32_sdmmc2.c +++ b/drivers/mmc/stm32_sdmmc2.c @@ -72,7 +72,10 @@ struct stm32_sdmmc2_ctx { #define SDMMC_CLKCR_HWFC_EN BIT(17) #define SDMMC_CLKCR_DDR BIT(18) #define SDMMC_CLKCR_BUSSPEED BIT(19) -#define SDMMC_CLKCR_SELCLKRX GENMASK(21, 20) +#define SDMMC_CLKCR_SELCLKRX_MASK GENMASK(21, 20) +#define SDMMC_CLKCR_SELCLKRX_CK 0 +#define SDMMC_CLKCR_SELCLKRX_CKIN BIT(20) +#define SDMMC_CLKCR_SELCLKRX_FBCK BIT(21) /* SDMMC_CMD register */ #define SDMMC_CMD_CMDINDEX GENMASK(5, 0) @@ -495,7 +498,8 @@ static int stm32_sdmmc2_set_ios(struct udevice *dev) if (mmc->bus_width == 8) clk |= SDMMC_CLKCR_WIDBUS_8; - writel(clk | priv->clk_reg_msk, priv->base + SDMMC_CLKCR); + writel(clk | priv->clk_reg_msk | SDMMC_CLKCR_HWFC_EN, + priv->base + SDMMC_CLKCR); return 0; } @@ -534,6 +538,8 @@ static int stm32_sdmmc2_probe(struct udevice *dev) priv->clk_reg_msk |= SDMMC_CLKCR_NEGEDGE; if (dev_read_bool(dev, "st,dirpol")) priv->pwr_reg_msk |= SDMMC_POWER_DIRPOL; + if (dev_read_bool(dev, "st,pin-ckin")) + priv->clk_reg_msk |= SDMMC_CLKCR_SELCLKRX_CKIN; ret = clk_get_by_index(dev, 0, &priv->clk); if (ret) diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 4edb4be46c..df6f32850e 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -30,6 +30,7 @@ struct sunxi_mmc_priv { uint32_t *mclkreg; unsigned fatal_err; struct gpio_desc cd_gpio; /* Change Detect GPIO */ + int cd_inverted; /* Inverted Card Detect */ struct sunxi_mmc *reg; struct mmc_config cfg; }; @@ -544,9 +545,11 @@ static int sunxi_mmc_getcd(struct udevice *dev) { struct sunxi_mmc_priv *priv = dev_get_priv(dev); - if (dm_gpio_is_valid(&priv->cd_gpio)) - return dm_gpio_get_value(&priv->cd_gpio); + if (dm_gpio_is_valid(&priv->cd_gpio)) { + int cd_state = dm_gpio_get_value(&priv->cd_gpio); + return cd_state ^ priv->cd_inverted; + } return 1; } @@ -610,6 +613,9 @@ static int sunxi_mmc_probe(struct udevice *dev) sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP); } + /* Check if card detect is inverted */ + priv->cd_inverted = dev_read_bool(dev, "cd-inverted"); + upriv->mmc = &plat->mmc; /* Reset controller */ diff --git a/drivers/mmc/tmio-common.c b/drivers/mmc/tmio-common.c new file mode 100644 index 0000000000..5f1c9c0bd4 --- /dev/null +++ b/drivers/mmc/tmio-common.c @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <fdtdec.h> +#include <mmc.h> +#include <dm.h> +#include <dm/pinctrl.h> +#include <linux/compat.h> +#include <linux/dma-direction.h> +#include <linux/io.h> +#include <linux/sizes.h> +#include <power/regulator.h> +#include <asm/unaligned.h> + +#include "tmio-common.h" + +DECLARE_GLOBAL_DATA_PTR; + +static u64 tmio_sd_readq(struct tmio_sd_priv *priv, unsigned int reg) +{ + return readq(priv->regbase + (reg << 1)); +} + +static void tmio_sd_writeq(struct tmio_sd_priv *priv, + u64 val, unsigned int reg) +{ + writeq(val, priv->regbase + (reg << 1)); +} + +static u16 tmio_sd_readw(struct tmio_sd_priv *priv, unsigned int reg) +{ + return readw(priv->regbase + (reg >> 1)); +} + +static void tmio_sd_writew(struct tmio_sd_priv *priv, + u16 val, unsigned int reg) +{ + writew(val, priv->regbase + (reg >> 1)); +} + +u32 tmio_sd_readl(struct tmio_sd_priv *priv, unsigned int reg) +{ + u32 val; + + if (priv->caps & TMIO_SD_CAP_64BIT) + return readl(priv->regbase + (reg << 1)); + else if (priv->caps & TMIO_SD_CAP_16BIT) { + val = readw(priv->regbase + (reg >> 1)) & 0xffff; + if ((reg == TMIO_SD_RSP10) || (reg == TMIO_SD_RSP32) || + (reg == TMIO_SD_RSP54) || (reg == TMIO_SD_RSP76)) { + val |= readw(priv->regbase + (reg >> 1) + 2) << 16; + } + return val; + } else + return readl(priv->regbase + reg); +} + +void tmio_sd_writel(struct tmio_sd_priv *priv, + u32 val, unsigned int reg) +{ + if (priv->caps & TMIO_SD_CAP_64BIT) + writel(val, priv->regbase + (reg << 1)); + else if (priv->caps & TMIO_SD_CAP_16BIT) { + writew(val & 0xffff, priv->regbase + (reg >> 1)); + if (reg == TMIO_SD_INFO1 || reg == TMIO_SD_INFO1_MASK || + reg == TMIO_SD_INFO2 || reg == TMIO_SD_INFO2_MASK || + reg == TMIO_SD_ARG) + writew(val >> 16, priv->regbase + (reg >> 1) + 2); + } else + writel(val, priv->regbase + reg); +} + +static dma_addr_t __dma_map_single(void *ptr, size_t size, + enum dma_data_direction dir) +{ + unsigned long addr = (unsigned long)ptr; + + if (dir == DMA_FROM_DEVICE) + invalidate_dcache_range(addr, addr + size); + else + flush_dcache_range(addr, addr + size); + + return addr; +} + +static void __dma_unmap_single(dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + if (dir != DMA_TO_DEVICE) + invalidate_dcache_range(addr, addr + size); +} + +static int tmio_sd_check_error(struct udevice *dev) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + u32 info2 = tmio_sd_readl(priv, TMIO_SD_INFO2); + + if (info2 & TMIO_SD_INFO2_ERR_RTO) { + /* + * TIMEOUT must be returned for unsupported command. Do not + * display error log since this might be a part of sequence to + * distinguish between SD and MMC. + */ + return -ETIMEDOUT; + } + + if (info2 & TMIO_SD_INFO2_ERR_TO) { + dev_err(dev, "timeout error\n"); + return -ETIMEDOUT; + } + + if (info2 & (TMIO_SD_INFO2_ERR_END | TMIO_SD_INFO2_ERR_CRC | + TMIO_SD_INFO2_ERR_IDX)) { + dev_err(dev, "communication out of sync\n"); + return -EILSEQ; + } + + if (info2 & (TMIO_SD_INFO2_ERR_ILA | TMIO_SD_INFO2_ERR_ILR | + TMIO_SD_INFO2_ERR_ILW)) { + dev_err(dev, "illegal access\n"); + return -EIO; + } + + return 0; +} + +static int tmio_sd_wait_for_irq(struct udevice *dev, unsigned int reg, + u32 flag) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + long wait = 1000000; + int ret; + + while (!(tmio_sd_readl(priv, reg) & flag)) { + if (wait-- < 0) { + dev_err(dev, "timeout\n"); + return -ETIMEDOUT; + } + + ret = tmio_sd_check_error(dev); + if (ret) + return ret; + + udelay(1); + } + + return 0; +} + +#define tmio_pio_read_fifo(__width, __suffix) \ +static void tmio_pio_read_fifo_##__width(struct tmio_sd_priv *priv, \ + char *pbuf, uint blksz) \ +{ \ + u##__width *buf = (u##__width *)pbuf; \ + int i; \ + \ + if (likely(IS_ALIGNED((uintptr_t)buf, ((__width) / 8)))) { \ + for (i = 0; i < blksz / ((__width) / 8); i++) { \ + *buf++ = tmio_sd_read##__suffix(priv, \ + TMIO_SD_BUF); \ + } \ + } else { \ + for (i = 0; i < blksz / ((__width) / 8); i++) { \ + u##__width data; \ + data = tmio_sd_read##__suffix(priv, \ + TMIO_SD_BUF); \ + put_unaligned(data, buf++); \ + } \ + } \ +} + +tmio_pio_read_fifo(64, q) +tmio_pio_read_fifo(32, l) +tmio_pio_read_fifo(16, w) + +static int tmio_sd_pio_read_one_block(struct udevice *dev, char *pbuf, + uint blocksize) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + int ret; + + /* wait until the buffer is filled with data */ + ret = tmio_sd_wait_for_irq(dev, TMIO_SD_INFO2, + TMIO_SD_INFO2_BRE); + if (ret) + return ret; + + /* + * Clear the status flag _before_ read the buffer out because + * TMIO_SD_INFO2_BRE is edge-triggered, not level-triggered. + */ + tmio_sd_writel(priv, 0, TMIO_SD_INFO2); + + if (priv->caps & TMIO_SD_CAP_64BIT) + tmio_pio_read_fifo_64(priv, pbuf, blocksize); + else if (priv->caps & TMIO_SD_CAP_16BIT) + tmio_pio_read_fifo_16(priv, pbuf, blocksize); + else + tmio_pio_read_fifo_32(priv, pbuf, blocksize); + + return 0; +} + +#define tmio_pio_write_fifo(__width, __suffix) \ +static void tmio_pio_write_fifo_##__width(struct tmio_sd_priv *priv, \ + const char *pbuf, uint blksz)\ +{ \ + const u##__width *buf = (const u##__width *)pbuf; \ + int i; \ + \ + if (likely(IS_ALIGNED((uintptr_t)buf, ((__width) / 8)))) { \ + for (i = 0; i < blksz / ((__width) / 8); i++) { \ + tmio_sd_write##__suffix(priv, *buf++, \ + TMIO_SD_BUF); \ + } \ + } else { \ + for (i = 0; i < blksz / ((__width) / 8); i++) { \ + u##__width data = get_unaligned(buf++); \ + tmio_sd_write##__suffix(priv, data, \ + TMIO_SD_BUF); \ + } \ + } \ +} + +tmio_pio_write_fifo(64, q) +tmio_pio_write_fifo(32, l) +tmio_pio_write_fifo(16, w) + +static int tmio_sd_pio_write_one_block(struct udevice *dev, + const char *pbuf, uint blocksize) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + int ret; + + /* wait until the buffer becomes empty */ + ret = tmio_sd_wait_for_irq(dev, TMIO_SD_INFO2, + TMIO_SD_INFO2_BWE); + if (ret) + return ret; + + tmio_sd_writel(priv, 0, TMIO_SD_INFO2); + + if (priv->caps & TMIO_SD_CAP_64BIT) + tmio_pio_write_fifo_64(priv, pbuf, blocksize); + else if (priv->caps & TMIO_SD_CAP_16BIT) + tmio_pio_write_fifo_16(priv, pbuf, blocksize); + else + tmio_pio_write_fifo_32(priv, pbuf, blocksize); + + return 0; +} + +static int tmio_sd_pio_xfer(struct udevice *dev, struct mmc_data *data) +{ + const char *src = data->src; + char *dest = data->dest; + int i, ret; + + for (i = 0; i < data->blocks; i++) { + if (data->flags & MMC_DATA_READ) + ret = tmio_sd_pio_read_one_block(dev, dest, + data->blocksize); + else + ret = tmio_sd_pio_write_one_block(dev, src, + data->blocksize); + if (ret) + return ret; + + if (data->flags & MMC_DATA_READ) + dest += data->blocksize; + else + src += data->blocksize; + } + + return 0; +} + +static void tmio_sd_dma_start(struct tmio_sd_priv *priv, + dma_addr_t dma_addr) +{ + u32 tmp; + + tmio_sd_writel(priv, 0, TMIO_SD_DMA_INFO1); + tmio_sd_writel(priv, 0, TMIO_SD_DMA_INFO2); + + /* enable DMA */ + tmp = tmio_sd_readl(priv, TMIO_SD_EXTMODE); + tmp |= TMIO_SD_EXTMODE_DMA_EN; + tmio_sd_writel(priv, tmp, TMIO_SD_EXTMODE); + + tmio_sd_writel(priv, dma_addr & U32_MAX, TMIO_SD_DMA_ADDR_L); + + /* suppress the warning "right shift count >= width of type" */ + dma_addr >>= min_t(int, 32, 8 * sizeof(dma_addr)); + + tmio_sd_writel(priv, dma_addr & U32_MAX, TMIO_SD_DMA_ADDR_H); + + tmio_sd_writel(priv, TMIO_SD_DMA_CTL_START, TMIO_SD_DMA_CTL); +} + +static int tmio_sd_dma_wait_for_irq(struct udevice *dev, u32 flag, + unsigned int blocks) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + long wait = 1000000 + 10 * blocks; + + while (!(tmio_sd_readl(priv, TMIO_SD_DMA_INFO1) & flag)) { + if (wait-- < 0) { + dev_err(dev, "timeout during DMA\n"); + return -ETIMEDOUT; + } + + udelay(10); + } + + if (tmio_sd_readl(priv, TMIO_SD_DMA_INFO2)) { + dev_err(dev, "error during DMA\n"); + return -EIO; + } + + return 0; +} + +static int tmio_sd_dma_xfer(struct udevice *dev, struct mmc_data *data) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + size_t len = data->blocks * data->blocksize; + void *buf; + enum dma_data_direction dir; + dma_addr_t dma_addr; + u32 poll_flag, tmp; + int ret; + + tmp = tmio_sd_readl(priv, TMIO_SD_DMA_MODE); + + if (data->flags & MMC_DATA_READ) { + buf = data->dest; + dir = DMA_FROM_DEVICE; + /* + * The DMA READ completion flag position differs on Socionext + * and Renesas SoCs. It is bit 20 on Socionext SoCs and using + * bit 17 is a hardware bug and forbidden. It is bit 17 on + * Renesas SoCs and bit 20 does not work on them. + */ + poll_flag = (priv->caps & TMIO_SD_CAP_RCAR) ? + TMIO_SD_DMA_INFO1_END_RD : + TMIO_SD_DMA_INFO1_END_RD2; + tmp |= TMIO_SD_DMA_MODE_DIR_RD; + } else { + buf = (void *)data->src; + dir = DMA_TO_DEVICE; + poll_flag = TMIO_SD_DMA_INFO1_END_WR; + tmp &= ~TMIO_SD_DMA_MODE_DIR_RD; + } + + tmio_sd_writel(priv, tmp, TMIO_SD_DMA_MODE); + + dma_addr = __dma_map_single(buf, len, dir); + + tmio_sd_dma_start(priv, dma_addr); + + ret = tmio_sd_dma_wait_for_irq(dev, poll_flag, data->blocks); + + __dma_unmap_single(dma_addr, len, dir); + + return ret; +} + +/* check if the address is DMA'able */ +static bool tmio_sd_addr_is_dmaable(unsigned long addr) +{ + if (!IS_ALIGNED(addr, TMIO_SD_DMA_MINALIGN)) + return false; + +#if defined(CONFIG_ARCH_UNIPHIER) && !defined(CONFIG_ARM64) && \ + defined(CONFIG_SPL_BUILD) + /* + * For UniPhier ARMv7 SoCs, the stack is allocated in the locked ways + * of L2, which is unreachable from the DMA engine. + */ + if (addr < CONFIG_SPL_STACK) + return false; +#endif + + return true; +} + +int tmio_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + int ret; + u32 tmp; + + if (tmio_sd_readl(priv, TMIO_SD_INFO2) & TMIO_SD_INFO2_CBSY) { + dev_err(dev, "command busy\n"); + return -EBUSY; + } + + /* clear all status flags */ + tmio_sd_writel(priv, 0, TMIO_SD_INFO1); + tmio_sd_writel(priv, 0, TMIO_SD_INFO2); + + /* disable DMA once */ + tmp = tmio_sd_readl(priv, TMIO_SD_EXTMODE); + tmp &= ~TMIO_SD_EXTMODE_DMA_EN; + tmio_sd_writel(priv, tmp, TMIO_SD_EXTMODE); + + tmio_sd_writel(priv, cmd->cmdarg, TMIO_SD_ARG); + + tmp = cmd->cmdidx; + + if (data) { + tmio_sd_writel(priv, data->blocksize, TMIO_SD_SIZE); + tmio_sd_writel(priv, data->blocks, TMIO_SD_SECCNT); + + /* Do not send CMD12 automatically */ + tmp |= TMIO_SD_CMD_NOSTOP | TMIO_SD_CMD_DATA; + + if (data->blocks > 1) + tmp |= TMIO_SD_CMD_MULTI; + + if (data->flags & MMC_DATA_READ) + tmp |= TMIO_SD_CMD_RD; + } + + /* + * Do not use the response type auto-detection on this hardware. + * CMD8, for example, has different response types on SD and eMMC, + * while this controller always assumes the response type for SD. + * Set the response type manually. + */ + switch (cmd->resp_type) { + case MMC_RSP_NONE: + tmp |= TMIO_SD_CMD_RSP_NONE; + break; + case MMC_RSP_R1: + tmp |= TMIO_SD_CMD_RSP_R1; + break; + case MMC_RSP_R1b: + tmp |= TMIO_SD_CMD_RSP_R1B; + break; + case MMC_RSP_R2: + tmp |= TMIO_SD_CMD_RSP_R2; + break; + case MMC_RSP_R3: + tmp |= TMIO_SD_CMD_RSP_R3; + break; + default: + dev_err(dev, "unknown response type\n"); + return -EINVAL; + } + + dev_dbg(dev, "sending CMD%d (SD_CMD=%08x, SD_ARG=%08x)\n", + cmd->cmdidx, tmp, cmd->cmdarg); + tmio_sd_writel(priv, tmp, TMIO_SD_CMD); + + ret = tmio_sd_wait_for_irq(dev, TMIO_SD_INFO1, + TMIO_SD_INFO1_RSP); + if (ret) + return ret; + + if (cmd->resp_type & MMC_RSP_136) { + u32 rsp_127_104 = tmio_sd_readl(priv, TMIO_SD_RSP76); + u32 rsp_103_72 = tmio_sd_readl(priv, TMIO_SD_RSP54); + u32 rsp_71_40 = tmio_sd_readl(priv, TMIO_SD_RSP32); + u32 rsp_39_8 = tmio_sd_readl(priv, TMIO_SD_RSP10); + + cmd->response[0] = ((rsp_127_104 & 0x00ffffff) << 8) | + ((rsp_103_72 & 0xff000000) >> 24); + cmd->response[1] = ((rsp_103_72 & 0x00ffffff) << 8) | + ((rsp_71_40 & 0xff000000) >> 24); + cmd->response[2] = ((rsp_71_40 & 0x00ffffff) << 8) | + ((rsp_39_8 & 0xff000000) >> 24); + cmd->response[3] = (rsp_39_8 & 0xffffff) << 8; + } else { + /* bit 39-8 */ + cmd->response[0] = tmio_sd_readl(priv, TMIO_SD_RSP10); + } + + if (data) { + /* use DMA if the HW supports it and the buffer is aligned */ + if (priv->caps & TMIO_SD_CAP_DMA_INTERNAL && + tmio_sd_addr_is_dmaable((long)data->src)) + ret = tmio_sd_dma_xfer(dev, data); + else + ret = tmio_sd_pio_xfer(dev, data); + + ret = tmio_sd_wait_for_irq(dev, TMIO_SD_INFO1, + TMIO_SD_INFO1_CMP); + if (ret) + return ret; + } + + tmio_sd_wait_for_irq(dev, TMIO_SD_INFO2, TMIO_SD_INFO2_SCLKDIVEN); + + return ret; +} + +static int tmio_sd_set_bus_width(struct tmio_sd_priv *priv, + struct mmc *mmc) +{ + u32 val, tmp; + + switch (mmc->bus_width) { + case 0: + case 1: + val = TMIO_SD_OPTION_WIDTH_1; + break; + case 4: + val = TMIO_SD_OPTION_WIDTH_4; + break; + case 8: + val = TMIO_SD_OPTION_WIDTH_8; + break; + default: + return -EINVAL; + } + + tmp = tmio_sd_readl(priv, TMIO_SD_OPTION); + tmp &= ~TMIO_SD_OPTION_WIDTH_MASK; + tmp |= val; + tmio_sd_writel(priv, tmp, TMIO_SD_OPTION); + + return 0; +} + +static void tmio_sd_set_ddr_mode(struct tmio_sd_priv *priv, + struct mmc *mmc) +{ + u32 tmp; + + tmp = tmio_sd_readl(priv, TMIO_SD_IF_MODE); + if (mmc->ddr_mode) + tmp |= TMIO_SD_IF_MODE_DDR; + else + tmp &= ~TMIO_SD_IF_MODE_DDR; + tmio_sd_writel(priv, tmp, TMIO_SD_IF_MODE); +} + +static void tmio_sd_set_clk_rate(struct tmio_sd_priv *priv, + struct mmc *mmc) +{ + unsigned int divisor; + u32 val, tmp; + + if (!mmc->clock) + return; + + divisor = DIV_ROUND_UP(priv->mclk, mmc->clock); + + if (divisor <= 1) + val = (priv->caps & TMIO_SD_CAP_RCAR) ? + TMIO_SD_CLKCTL_RCAR_DIV1 : TMIO_SD_CLKCTL_DIV1; + else if (divisor <= 2) + val = TMIO_SD_CLKCTL_DIV2; + else if (divisor <= 4) + val = TMIO_SD_CLKCTL_DIV4; + else if (divisor <= 8) + val = TMIO_SD_CLKCTL_DIV8; + else if (divisor <= 16) + val = TMIO_SD_CLKCTL_DIV16; + else if (divisor <= 32) + val = TMIO_SD_CLKCTL_DIV32; + else if (divisor <= 64) + val = TMIO_SD_CLKCTL_DIV64; + else if (divisor <= 128) + val = TMIO_SD_CLKCTL_DIV128; + else if (divisor <= 256) + val = TMIO_SD_CLKCTL_DIV256; + else if (divisor <= 512 || !(priv->caps & TMIO_SD_CAP_DIV1024)) + val = TMIO_SD_CLKCTL_DIV512; + else + val = TMIO_SD_CLKCTL_DIV1024; + + tmp = tmio_sd_readl(priv, TMIO_SD_CLKCTL); + if (tmp & TMIO_SD_CLKCTL_SCLKEN && + (tmp & TMIO_SD_CLKCTL_DIV_MASK) == val) + return; + + /* stop the clock before changing its rate to avoid a glitch signal */ + tmp &= ~TMIO_SD_CLKCTL_SCLKEN; + tmio_sd_writel(priv, tmp, TMIO_SD_CLKCTL); + + tmp &= ~TMIO_SD_CLKCTL_DIV_MASK; + tmp |= val | TMIO_SD_CLKCTL_OFFEN; + tmio_sd_writel(priv, tmp, TMIO_SD_CLKCTL); + + tmp |= TMIO_SD_CLKCTL_SCLKEN; + tmio_sd_writel(priv, tmp, TMIO_SD_CLKCTL); + + udelay(1000); +} + +static void tmio_sd_set_pins(struct udevice *dev) +{ + __maybe_unused struct mmc *mmc = mmc_get_mmc_dev(dev); + +#ifdef CONFIG_DM_REGULATOR + struct tmio_sd_priv *priv = dev_get_priv(dev); + + if (priv->vqmmc_dev) { + if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) + regulator_set_value(priv->vqmmc_dev, 1800000); + else + regulator_set_value(priv->vqmmc_dev, 3300000); + regulator_set_enable(priv->vqmmc_dev, true); + } +#endif + +#ifdef CONFIG_PINCTRL + switch (mmc->selected_mode) { + case MMC_LEGACY: + case SD_LEGACY: + case MMC_HS: + case SD_HS: + case MMC_HS_52: + case MMC_DDR_52: + pinctrl_select_state(dev, "default"); + break; + case UHS_SDR12: + case UHS_SDR25: + case UHS_SDR50: + case UHS_DDR50: + case UHS_SDR104: + case MMC_HS_200: + pinctrl_select_state(dev, "state_uhs"); + break; + default: + break; + } +#endif +} + +int tmio_sd_set_ios(struct udevice *dev) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + struct mmc *mmc = mmc_get_mmc_dev(dev); + int ret; + + dev_dbg(dev, "clock %uHz, DDRmode %d, width %u\n", + mmc->clock, mmc->ddr_mode, mmc->bus_width); + + ret = tmio_sd_set_bus_width(priv, mmc); + if (ret) + return ret; + tmio_sd_set_ddr_mode(priv, mmc); + tmio_sd_set_clk_rate(priv, mmc); + tmio_sd_set_pins(dev); + + return 0; +} + +int tmio_sd_get_cd(struct udevice *dev) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + + if (priv->caps & TMIO_SD_CAP_NONREMOVABLE) + return 1; + + return !!(tmio_sd_readl(priv, TMIO_SD_INFO1) & + TMIO_SD_INFO1_CD); +} + +static void tmio_sd_host_init(struct tmio_sd_priv *priv) +{ + u32 tmp; + + /* soft reset of the host */ + tmp = tmio_sd_readl(priv, TMIO_SD_SOFT_RST); + tmp &= ~TMIO_SD_SOFT_RST_RSTX; + tmio_sd_writel(priv, tmp, TMIO_SD_SOFT_RST); + tmp |= TMIO_SD_SOFT_RST_RSTX; + tmio_sd_writel(priv, tmp, TMIO_SD_SOFT_RST); + + /* FIXME: implement eMMC hw_reset */ + + tmio_sd_writel(priv, TMIO_SD_STOP_SEC, TMIO_SD_STOP); + + /* + * Connected to 32bit AXI. + * This register dropped backward compatibility at version 0x10. + * Write an appropriate value depending on the IP version. + */ + if (priv->version >= 0x10) + tmio_sd_writel(priv, 0x101, TMIO_SD_HOST_MODE); + else + tmio_sd_writel(priv, 0x0, TMIO_SD_HOST_MODE); + + if (priv->caps & TMIO_SD_CAP_DMA_INTERNAL) { + tmp = tmio_sd_readl(priv, TMIO_SD_DMA_MODE); + tmp |= TMIO_SD_DMA_MODE_ADDR_INC; + tmio_sd_writel(priv, tmp, TMIO_SD_DMA_MODE); + } +} + +int tmio_sd_bind(struct udevice *dev) +{ + struct tmio_sd_plat *plat = dev_get_platdata(dev); + + return mmc_bind(dev, &plat->mmc, &plat->cfg); +} + +int tmio_sd_probe(struct udevice *dev, u32 quirks) +{ + struct tmio_sd_plat *plat = dev_get_platdata(dev); + struct tmio_sd_priv *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + fdt_addr_t base; + struct clk clk; + int ret; + + base = devfdt_get_addr(dev); + if (base == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regbase = devm_ioremap(dev, base, SZ_2K); + if (!priv->regbase) + return -ENOMEM; + +#ifdef CONFIG_DM_REGULATOR + device_get_supply_regulator(dev, "vqmmc-supply", &priv->vqmmc_dev); +#endif + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) { + dev_err(dev, "failed to get host clock\n"); + return ret; + } + + /* set to max rate */ + priv->mclk = clk_set_rate(&clk, ULONG_MAX); + if (IS_ERR_VALUE(priv->mclk)) { + dev_err(dev, "failed to set rate for host clock\n"); + clk_free(&clk); + return priv->mclk; + } + + ret = clk_enable(&clk); + clk_free(&clk); + if (ret) { + dev_err(dev, "failed to enable host clock\n"); + return ret; + } + + ret = mmc_of_parse(dev, &plat->cfg); + if (ret < 0) { + dev_err(dev, "failed to parse host caps\n"); + return ret; + } + + plat->cfg.name = dev->name; + plat->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + + if (quirks) + priv->caps = quirks; + + priv->version = tmio_sd_readl(priv, TMIO_SD_VERSION) & + TMIO_SD_VERSION_IP; + dev_dbg(dev, "version %x\n", priv->version); + if (priv->version >= 0x10) { + priv->caps |= TMIO_SD_CAP_DMA_INTERNAL; + priv->caps |= TMIO_SD_CAP_DIV1024; + } + + if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), "non-removable", + NULL)) + priv->caps |= TMIO_SD_CAP_NONREMOVABLE; + + tmio_sd_host_init(priv); + + plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34; + plat->cfg.f_min = priv->mclk / + (priv->caps & TMIO_SD_CAP_DIV1024 ? 1024 : 512); + plat->cfg.f_max = priv->mclk; + plat->cfg.b_max = U32_MAX; /* max value of TMIO_SD_SECCNT */ + + upriv->mmc = &plat->mmc; + + return 0; +} diff --git a/drivers/mmc/tmio-common.h b/drivers/mmc/tmio-common.h new file mode 100644 index 0000000000..ef9404409d --- /dev/null +++ b/drivers/mmc/tmio-common.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2016 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __TMIO_COMMON_H__ +#define __TMIO_COMMON_H__ + +#define TMIO_SD_CMD 0x000 /* command */ +#define TMIO_SD_CMD_NOSTOP BIT(14) /* No automatic CMD12 issue */ +#define TMIO_SD_CMD_MULTI BIT(13) /* multiple block transfer */ +#define TMIO_SD_CMD_RD BIT(12) /* 1: read, 0: write */ +#define TMIO_SD_CMD_DATA BIT(11) /* data transfer */ +#define TMIO_SD_CMD_APP BIT(6) /* ACMD preceded by CMD55 */ +#define TMIO_SD_CMD_NORMAL (0 << 8)/* auto-detect of resp-type */ +#define TMIO_SD_CMD_RSP_NONE (3 << 8)/* response: none */ +#define TMIO_SD_CMD_RSP_R1 (4 << 8)/* response: R1, R5, R6, R7 */ +#define TMIO_SD_CMD_RSP_R1B (5 << 8)/* response: R1b, R5b */ +#define TMIO_SD_CMD_RSP_R2 (6 << 8)/* response: R2 */ +#define TMIO_SD_CMD_RSP_R3 (7 << 8)/* response: R3, R4 */ +#define TMIO_SD_ARG 0x008 /* command argument */ +#define TMIO_SD_STOP 0x010 /* stop action control */ +#define TMIO_SD_STOP_SEC BIT(8) /* use sector count */ +#define TMIO_SD_STOP_STP BIT(0) /* issue CMD12 */ +#define TMIO_SD_SECCNT 0x014 /* sector counter */ +#define TMIO_SD_RSP10 0x018 /* response[39:8] */ +#define TMIO_SD_RSP32 0x020 /* response[71:40] */ +#define TMIO_SD_RSP54 0x028 /* response[103:72] */ +#define TMIO_SD_RSP76 0x030 /* response[127:104] */ +#define TMIO_SD_INFO1 0x038 /* IRQ status 1 */ +#define TMIO_SD_INFO1_CD BIT(5) /* state of card detect */ +#define TMIO_SD_INFO1_INSERT BIT(4) /* card inserted */ +#define TMIO_SD_INFO1_REMOVE BIT(3) /* card removed */ +#define TMIO_SD_INFO1_CMP BIT(2) /* data complete */ +#define TMIO_SD_INFO1_RSP BIT(0) /* response complete */ +#define TMIO_SD_INFO2 0x03c /* IRQ status 2 */ +#define TMIO_SD_INFO2_ERR_ILA BIT(15) /* illegal access err */ +#define TMIO_SD_INFO2_CBSY BIT(14) /* command busy */ +#define TMIO_SD_INFO2_SCLKDIVEN BIT(13) /* command setting reg ena */ +#define TMIO_SD_INFO2_BWE BIT(9) /* write buffer ready */ +#define TMIO_SD_INFO2_BRE BIT(8) /* read buffer ready */ +#define TMIO_SD_INFO2_DAT0 BIT(7) /* SDDAT0 */ +#define TMIO_SD_INFO2_ERR_RTO BIT(6) /* response time out */ +#define TMIO_SD_INFO2_ERR_ILR BIT(5) /* illegal read err */ +#define TMIO_SD_INFO2_ERR_ILW BIT(4) /* illegal write err */ +#define TMIO_SD_INFO2_ERR_TO BIT(3) /* time out error */ +#define TMIO_SD_INFO2_ERR_END BIT(2) /* END bit error */ +#define TMIO_SD_INFO2_ERR_CRC BIT(1) /* CRC error */ +#define TMIO_SD_INFO2_ERR_IDX BIT(0) /* cmd index error */ +#define TMIO_SD_INFO1_MASK 0x040 +#define TMIO_SD_INFO2_MASK 0x044 +#define TMIO_SD_CLKCTL 0x048 /* clock divisor */ +#define TMIO_SD_CLKCTL_DIV_MASK 0x104ff +#define TMIO_SD_CLKCTL_DIV1024 BIT(16) /* SDCLK = CLK / 1024 */ +#define TMIO_SD_CLKCTL_DIV512 BIT(7) /* SDCLK = CLK / 512 */ +#define TMIO_SD_CLKCTL_DIV256 BIT(6) /* SDCLK = CLK / 256 */ +#define TMIO_SD_CLKCTL_DIV128 BIT(5) /* SDCLK = CLK / 128 */ +#define TMIO_SD_CLKCTL_DIV64 BIT(4) /* SDCLK = CLK / 64 */ +#define TMIO_SD_CLKCTL_DIV32 BIT(3) /* SDCLK = CLK / 32 */ +#define TMIO_SD_CLKCTL_DIV16 BIT(2) /* SDCLK = CLK / 16 */ +#define TMIO_SD_CLKCTL_DIV8 BIT(1) /* SDCLK = CLK / 8 */ +#define TMIO_SD_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */ +#define TMIO_SD_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */ +#define TMIO_SD_CLKCTL_DIV1 BIT(10) /* SDCLK = CLK */ +#define TMIO_SD_CLKCTL_RCAR_DIV1 0xff /* SDCLK = CLK (RCar ver.) */ +#define TMIO_SD_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */ +#define TMIO_SD_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */ +#define TMIO_SD_SIZE 0x04c /* block size */ +#define TMIO_SD_OPTION 0x050 +#define TMIO_SD_OPTION_WIDTH_MASK (5 << 13) +#define TMIO_SD_OPTION_WIDTH_1 (4 << 13) +#define TMIO_SD_OPTION_WIDTH_4 (0 << 13) +#define TMIO_SD_OPTION_WIDTH_8 (1 << 13) +#define TMIO_SD_BUF 0x060 /* read/write buffer */ +#define TMIO_SD_EXTMODE 0x1b0 +#define TMIO_SD_EXTMODE_DMA_EN BIT(1) /* transfer 1: DMA, 0: pio */ +#define TMIO_SD_SOFT_RST 0x1c0 +#define TMIO_SD_SOFT_RST_RSTX BIT(0) /* reset deassert */ +#define TMIO_SD_VERSION 0x1c4 /* version register */ +#define TMIO_SD_VERSION_IP 0xff /* IP version */ +#define TMIO_SD_HOST_MODE 0x1c8 +#define TMIO_SD_IF_MODE 0x1cc +#define TMIO_SD_IF_MODE_DDR BIT(0) /* DDR mode */ +#define TMIO_SD_VOLT 0x1e4 /* voltage switch */ +#define TMIO_SD_VOLT_MASK (3 << 0) +#define TMIO_SD_VOLT_OFF (0 << 0) +#define TMIO_SD_VOLT_330 (1 << 0)/* 3.3V signal */ +#define TMIO_SD_VOLT_180 (2 << 0)/* 1.8V signal */ +#define TMIO_SD_DMA_MODE 0x410 +#define TMIO_SD_DMA_MODE_DIR_RD BIT(16) /* 1: from device, 0: to dev */ +#define TMIO_SD_DMA_MODE_ADDR_INC BIT(0) /* 1: address inc, 0: fixed */ +#define TMIO_SD_DMA_CTL 0x414 +#define TMIO_SD_DMA_CTL_START BIT(0) /* start DMA (auto cleared) */ +#define TMIO_SD_DMA_RST 0x418 +#define TMIO_SD_DMA_RST_RD BIT(9) +#define TMIO_SD_DMA_RST_WR BIT(8) +#define TMIO_SD_DMA_INFO1 0x420 +#define TMIO_SD_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete (uniphier) */ +#define TMIO_SD_DMA_INFO1_END_RD BIT(17) /* DMA from device is complete (renesas) */ +#define TMIO_SD_DMA_INFO1_END_WR BIT(16) /* DMA to device is complete */ +#define TMIO_SD_DMA_INFO1_MASK 0x424 +#define TMIO_SD_DMA_INFO2 0x428 +#define TMIO_SD_DMA_INFO2_ERR_RD BIT(17) +#define TMIO_SD_DMA_INFO2_ERR_WR BIT(16) +#define TMIO_SD_DMA_INFO2_MASK 0x42c +#define TMIO_SD_DMA_ADDR_L 0x440 +#define TMIO_SD_DMA_ADDR_H 0x444 + +/* alignment required by the DMA engine of this controller */ +#define TMIO_SD_DMA_MINALIGN 0x10 + +struct tmio_sd_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +struct tmio_sd_priv { + void __iomem *regbase; + unsigned long mclk; + unsigned int version; + u32 caps; +#define TMIO_SD_CAP_NONREMOVABLE BIT(0) /* Nonremovable e.g. eMMC */ +#define TMIO_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ +#define TMIO_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ +#define TMIO_SD_CAP_64BIT BIT(3) /* Controller is 64bit */ +#define TMIO_SD_CAP_16BIT BIT(4) /* Controller is 16bit */ +#define TMIO_SD_CAP_RCAR_GEN2 BIT(5) /* Renesas RCar version of IP */ +#define TMIO_SD_CAP_RCAR_GEN3 BIT(6) /* Renesas RCar version of IP */ +#define TMIO_SD_CAP_RCAR_UHS BIT(7) /* Renesas RCar UHS/SDR modes */ +#define TMIO_SD_CAP_RCAR \ + (TMIO_SD_CAP_RCAR_GEN2 | TMIO_SD_CAP_RCAR_GEN3) +#ifdef CONFIG_DM_REGULATOR + struct udevice *vqmmc_dev; +#endif +}; + +int tmio_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data); +int tmio_sd_set_ios(struct udevice *dev); +int tmio_sd_get_cd(struct udevice *dev); + +int tmio_sd_bind(struct udevice *dev); +int tmio_sd_probe(struct udevice *dev, u32 quirks); + +u32 tmio_sd_readl(struct tmio_sd_priv *priv, unsigned int reg); +void tmio_sd_writel(struct tmio_sd_priv *priv, + u32 val, unsigned int reg); + +#endif /* __TMIO_COMMON_H__ */ diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 525b1702b9..47379b0328 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -17,857 +17,31 @@ #include <power/regulator.h> #include <asm/unaligned.h> -DECLARE_GLOBAL_DATA_PTR; - -#define UNIPHIER_SD_CMD 0x000 /* command */ -#define UNIPHIER_SD_CMD_NOSTOP BIT(14) /* No automatic CMD12 issue */ -#define UNIPHIER_SD_CMD_MULTI BIT(13) /* multiple block transfer */ -#define UNIPHIER_SD_CMD_RD BIT(12) /* 1: read, 0: write */ -#define UNIPHIER_SD_CMD_DATA BIT(11) /* data transfer */ -#define UNIPHIER_SD_CMD_APP BIT(6) /* ACMD preceded by CMD55 */ -#define UNIPHIER_SD_CMD_NORMAL (0 << 8)/* auto-detect of resp-type */ -#define UNIPHIER_SD_CMD_RSP_NONE (3 << 8)/* response: none */ -#define UNIPHIER_SD_CMD_RSP_R1 (4 << 8)/* response: R1, R5, R6, R7 */ -#define UNIPHIER_SD_CMD_RSP_R1B (5 << 8)/* response: R1b, R5b */ -#define UNIPHIER_SD_CMD_RSP_R2 (6 << 8)/* response: R2 */ -#define UNIPHIER_SD_CMD_RSP_R3 (7 << 8)/* response: R3, R4 */ -#define UNIPHIER_SD_ARG 0x008 /* command argument */ -#define UNIPHIER_SD_STOP 0x010 /* stop action control */ -#define UNIPHIER_SD_STOP_SEC BIT(8) /* use sector count */ -#define UNIPHIER_SD_STOP_STP BIT(0) /* issue CMD12 */ -#define UNIPHIER_SD_SECCNT 0x014 /* sector counter */ -#define UNIPHIER_SD_RSP10 0x018 /* response[39:8] */ -#define UNIPHIER_SD_RSP32 0x020 /* response[71:40] */ -#define UNIPHIER_SD_RSP54 0x028 /* response[103:72] */ -#define UNIPHIER_SD_RSP76 0x030 /* response[127:104] */ -#define UNIPHIER_SD_INFO1 0x038 /* IRQ status 1 */ -#define UNIPHIER_SD_INFO1_CD BIT(5) /* state of card detect */ -#define UNIPHIER_SD_INFO1_INSERT BIT(4) /* card inserted */ -#define UNIPHIER_SD_INFO1_REMOVE BIT(3) /* card removed */ -#define UNIPHIER_SD_INFO1_CMP BIT(2) /* data complete */ -#define UNIPHIER_SD_INFO1_RSP BIT(0) /* response complete */ -#define UNIPHIER_SD_INFO2 0x03c /* IRQ status 2 */ -#define UNIPHIER_SD_INFO2_ERR_ILA BIT(15) /* illegal access err */ -#define UNIPHIER_SD_INFO2_CBSY BIT(14) /* command busy */ -#define UNIPHIER_SD_INFO2_BWE BIT(9) /* write buffer ready */ -#define UNIPHIER_SD_INFO2_BRE BIT(8) /* read buffer ready */ -#define UNIPHIER_SD_INFO2_DAT0 BIT(7) /* SDDAT0 */ -#define UNIPHIER_SD_INFO2_ERR_RTO BIT(6) /* response time out */ -#define UNIPHIER_SD_INFO2_ERR_ILR BIT(5) /* illegal read err */ -#define UNIPHIER_SD_INFO2_ERR_ILW BIT(4) /* illegal write err */ -#define UNIPHIER_SD_INFO2_ERR_TO BIT(3) /* time out error */ -#define UNIPHIER_SD_INFO2_ERR_END BIT(2) /* END bit error */ -#define UNIPHIER_SD_INFO2_ERR_CRC BIT(1) /* CRC error */ -#define UNIPHIER_SD_INFO2_ERR_IDX BIT(0) /* cmd index error */ -#define UNIPHIER_SD_INFO1_MASK 0x040 -#define UNIPHIER_SD_INFO2_MASK 0x044 -#define UNIPHIER_SD_CLKCTL 0x048 /* clock divisor */ -#define UNIPHIER_SD_CLKCTL_DIV_MASK 0x104ff -#define UNIPHIER_SD_CLKCTL_DIV1024 BIT(16) /* SDCLK = CLK / 1024 */ -#define UNIPHIER_SD_CLKCTL_DIV512 BIT(7) /* SDCLK = CLK / 512 */ -#define UNIPHIER_SD_CLKCTL_DIV256 BIT(6) /* SDCLK = CLK / 256 */ -#define UNIPHIER_SD_CLKCTL_DIV128 BIT(5) /* SDCLK = CLK / 128 */ -#define UNIPHIER_SD_CLKCTL_DIV64 BIT(4) /* SDCLK = CLK / 64 */ -#define UNIPHIER_SD_CLKCTL_DIV32 BIT(3) /* SDCLK = CLK / 32 */ -#define UNIPHIER_SD_CLKCTL_DIV16 BIT(2) /* SDCLK = CLK / 16 */ -#define UNIPHIER_SD_CLKCTL_DIV8 BIT(1) /* SDCLK = CLK / 8 */ -#define UNIPHIER_SD_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */ -#define UNIPHIER_SD_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */ -#define UNIPHIER_SD_CLKCTL_DIV1 BIT(10) /* SDCLK = CLK */ -#define UNIPHIER_SD_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */ -#define UNIPHIER_SD_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */ -#define UNIPHIER_SD_SIZE 0x04c /* block size */ -#define UNIPHIER_SD_OPTION 0x050 -#define UNIPHIER_SD_OPTION_WIDTH_MASK (5 << 13) -#define UNIPHIER_SD_OPTION_WIDTH_1 (4 << 13) -#define UNIPHIER_SD_OPTION_WIDTH_4 (0 << 13) -#define UNIPHIER_SD_OPTION_WIDTH_8 (1 << 13) -#define UNIPHIER_SD_BUF 0x060 /* read/write buffer */ -#define UNIPHIER_SD_EXTMODE 0x1b0 -#define UNIPHIER_SD_EXTMODE_DMA_EN BIT(1) /* transfer 1: DMA, 0: pio */ -#define UNIPHIER_SD_SOFT_RST 0x1c0 -#define UNIPHIER_SD_SOFT_RST_RSTX BIT(0) /* reset deassert */ -#define UNIPHIER_SD_VERSION 0x1c4 /* version register */ -#define UNIPHIER_SD_VERSION_IP 0xff /* IP version */ -#define UNIPHIER_SD_HOST_MODE 0x1c8 -#define UNIPHIER_SD_IF_MODE 0x1cc -#define UNIPHIER_SD_IF_MODE_DDR BIT(0) /* DDR mode */ -#define UNIPHIER_SD_VOLT 0x1e4 /* voltage switch */ -#define UNIPHIER_SD_VOLT_MASK (3 << 0) -#define UNIPHIER_SD_VOLT_OFF (0 << 0) -#define UNIPHIER_SD_VOLT_330 (1 << 0)/* 3.3V signal */ -#define UNIPHIER_SD_VOLT_180 (2 << 0)/* 1.8V signal */ -#define UNIPHIER_SD_DMA_MODE 0x410 -#define UNIPHIER_SD_DMA_MODE_DIR_RD BIT(16) /* 1: from device, 0: to dev */ -#define UNIPHIER_SD_DMA_MODE_ADDR_INC BIT(0) /* 1: address inc, 0: fixed */ -#define UNIPHIER_SD_DMA_CTL 0x414 -#define UNIPHIER_SD_DMA_CTL_START BIT(0) /* start DMA (auto cleared) */ -#define UNIPHIER_SD_DMA_RST 0x418 -#define UNIPHIER_SD_DMA_RST_RD BIT(9) -#define UNIPHIER_SD_DMA_RST_WR BIT(8) -#define UNIPHIER_SD_DMA_INFO1 0x420 -#define UNIPHIER_SD_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete*/ -#define UNIPHIER_SD_DMA_INFO1_END_RD BIT(17) /* Don't use! Hardware bug */ -#define UNIPHIER_SD_DMA_INFO1_END_WR BIT(16) /* DMA to device is complete */ -#define UNIPHIER_SD_DMA_INFO1_MASK 0x424 -#define UNIPHIER_SD_DMA_INFO2 0x428 -#define UNIPHIER_SD_DMA_INFO2_ERR_RD BIT(17) -#define UNIPHIER_SD_DMA_INFO2_ERR_WR BIT(16) -#define UNIPHIER_SD_DMA_INFO2_MASK 0x42c -#define UNIPHIER_SD_DMA_ADDR_L 0x440 -#define UNIPHIER_SD_DMA_ADDR_H 0x444 - -/* alignment required by the DMA engine of this controller */ -#define UNIPHIER_SD_DMA_MINALIGN 0x10 - -struct uniphier_sd_plat { - struct mmc_config cfg; - struct mmc mmc; -}; - -struct uniphier_sd_priv { - void __iomem *regbase; - unsigned long mclk; - unsigned int version; - u32 caps; -#define UNIPHIER_SD_CAP_NONREMOVABLE BIT(0) /* Nonremovable e.g. eMMC */ -#define UNIPHIER_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ -#define UNIPHIER_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ -#define UNIPHIER_SD_CAP_64BIT BIT(3) /* Controller is 64bit */ -}; - -static u64 uniphier_sd_readq(struct uniphier_sd_priv *priv, unsigned int reg) -{ - if (priv->caps & UNIPHIER_SD_CAP_64BIT) - return readq(priv->regbase + (reg << 1)); - else - return readq(priv->regbase + reg); -} - -static void uniphier_sd_writeq(struct uniphier_sd_priv *priv, - u64 val, unsigned int reg) -{ - if (priv->caps & UNIPHIER_SD_CAP_64BIT) - writeq(val, priv->regbase + (reg << 1)); - else - writeq(val, priv->regbase + reg); -} - -static u32 uniphier_sd_readl(struct uniphier_sd_priv *priv, unsigned int reg) -{ - if (priv->caps & UNIPHIER_SD_CAP_64BIT) - return readl(priv->regbase + (reg << 1)); - else - return readl(priv->regbase + reg); -} - -static void uniphier_sd_writel(struct uniphier_sd_priv *priv, - u32 val, unsigned int reg) -{ - if (priv->caps & UNIPHIER_SD_CAP_64BIT) - writel(val, priv->regbase + (reg << 1)); - else - writel(val, priv->regbase + reg); -} - -static dma_addr_t __dma_map_single(void *ptr, size_t size, - enum dma_data_direction dir) -{ - unsigned long addr = (unsigned long)ptr; - - if (dir == DMA_FROM_DEVICE) - invalidate_dcache_range(addr, addr + size); - else - flush_dcache_range(addr, addr + size); - - return addr; -} - -static void __dma_unmap_single(dma_addr_t addr, size_t size, - enum dma_data_direction dir) -{ - if (dir != DMA_TO_DEVICE) - invalidate_dcache_range(addr, addr + size); -} - -static int uniphier_sd_check_error(struct udevice *dev) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - u32 info2 = uniphier_sd_readl(priv, UNIPHIER_SD_INFO2); - - if (info2 & UNIPHIER_SD_INFO2_ERR_RTO) { - /* - * TIMEOUT must be returned for unsupported command. Do not - * display error log since this might be a part of sequence to - * distinguish between SD and MMC. - */ - return -ETIMEDOUT; - } - - if (info2 & UNIPHIER_SD_INFO2_ERR_TO) { - dev_err(dev, "timeout error\n"); - return -ETIMEDOUT; - } - - if (info2 & (UNIPHIER_SD_INFO2_ERR_END | UNIPHIER_SD_INFO2_ERR_CRC | - UNIPHIER_SD_INFO2_ERR_IDX)) { - dev_err(dev, "communication out of sync\n"); - return -EILSEQ; - } - - if (info2 & (UNIPHIER_SD_INFO2_ERR_ILA | UNIPHIER_SD_INFO2_ERR_ILR | - UNIPHIER_SD_INFO2_ERR_ILW)) { - dev_err(dev, "illegal access\n"); - return -EIO; - } - - return 0; -} - -static int uniphier_sd_wait_for_irq(struct udevice *dev, unsigned int reg, - u32 flag) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - long wait = 1000000; - int ret; - - while (!(uniphier_sd_readl(priv, reg) & flag)) { - if (wait-- < 0) { - dev_err(dev, "timeout\n"); - return -ETIMEDOUT; - } - - ret = uniphier_sd_check_error(dev); - if (ret) - return ret; - - udelay(1); - } - - return 0; -} - -static int uniphier_sd_pio_read_one_block(struct udevice *dev, char *pbuf, - uint blocksize) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - int i, ret; - - /* wait until the buffer is filled with data */ - ret = uniphier_sd_wait_for_irq(dev, UNIPHIER_SD_INFO2, - UNIPHIER_SD_INFO2_BRE); - if (ret) - return ret; - - /* - * Clear the status flag _before_ read the buffer out because - * UNIPHIER_SD_INFO2_BRE is edge-triggered, not level-triggered. - */ - uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO2); - - if (priv->caps & UNIPHIER_SD_CAP_64BIT) { - u64 *buf = (u64 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 8))) { - for (i = 0; i < blocksize / 8; i++) { - *buf++ = uniphier_sd_readq(priv, - UNIPHIER_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 8; i++) { - u64 data; - data = uniphier_sd_readq(priv, - UNIPHIER_SD_BUF); - put_unaligned(data, buf++); - } - } - } else { - u32 *buf = (u32 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 4))) { - for (i = 0; i < blocksize / 4; i++) { - *buf++ = uniphier_sd_readl(priv, - UNIPHIER_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 4; i++) { - u32 data; - data = uniphier_sd_readl(priv, UNIPHIER_SD_BUF); - put_unaligned(data, buf++); - } - } - } - - return 0; -} - -static int uniphier_sd_pio_write_one_block(struct udevice *dev, - const char *pbuf, uint blocksize) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - int i, ret; - - /* wait until the buffer becomes empty */ - ret = uniphier_sd_wait_for_irq(dev, UNIPHIER_SD_INFO2, - UNIPHIER_SD_INFO2_BWE); - if (ret) - return ret; - - uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO2); - - if (priv->caps & UNIPHIER_SD_CAP_64BIT) { - const u64 *buf = (const u64 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 8))) { - for (i = 0; i < blocksize / 8; i++) { - uniphier_sd_writeq(priv, *buf++, - UNIPHIER_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 8; i++) { - u64 data = get_unaligned(buf++); - uniphier_sd_writeq(priv, data, - UNIPHIER_SD_BUF); - } - } - } else { - const u32 *buf = (const u32 *)pbuf; - if (likely(IS_ALIGNED((uintptr_t)buf, 4))) { - for (i = 0; i < blocksize / 4; i++) { - uniphier_sd_writel(priv, *buf++, - UNIPHIER_SD_BUF); - } - } else { - for (i = 0; i < blocksize / 4; i++) { - u32 data = get_unaligned(buf++); - uniphier_sd_writel(priv, data, - UNIPHIER_SD_BUF); - } - } - } - - return 0; -} - -static int uniphier_sd_pio_xfer(struct udevice *dev, struct mmc_data *data) -{ - const char *src = data->src; - char *dest = data->dest; - int i, ret; - - for (i = 0; i < data->blocks; i++) { - if (data->flags & MMC_DATA_READ) - ret = uniphier_sd_pio_read_one_block(dev, dest, - data->blocksize); - else - ret = uniphier_sd_pio_write_one_block(dev, src, - data->blocksize); - if (ret) - return ret; - - if (data->flags & MMC_DATA_READ) - dest += data->blocksize; - else - src += data->blocksize; - } - - return 0; -} - -static void uniphier_sd_dma_start(struct uniphier_sd_priv *priv, - dma_addr_t dma_addr) -{ - u32 tmp; - - uniphier_sd_writel(priv, 0, UNIPHIER_SD_DMA_INFO1); - uniphier_sd_writel(priv, 0, UNIPHIER_SD_DMA_INFO2); - - /* enable DMA */ - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_EXTMODE); - tmp |= UNIPHIER_SD_EXTMODE_DMA_EN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_EXTMODE); - - uniphier_sd_writel(priv, dma_addr & U32_MAX, UNIPHIER_SD_DMA_ADDR_L); - - /* suppress the warning "right shift count >= width of type" */ - dma_addr >>= min_t(int, 32, 8 * sizeof(dma_addr)); - - uniphier_sd_writel(priv, dma_addr & U32_MAX, UNIPHIER_SD_DMA_ADDR_H); - - uniphier_sd_writel(priv, UNIPHIER_SD_DMA_CTL_START, UNIPHIER_SD_DMA_CTL); -} - -static int uniphier_sd_dma_wait_for_irq(struct udevice *dev, u32 flag, - unsigned int blocks) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - long wait = 1000000 + 10 * blocks; - - while (!(uniphier_sd_readl(priv, UNIPHIER_SD_DMA_INFO1) & flag)) { - if (wait-- < 0) { - dev_err(dev, "timeout during DMA\n"); - return -ETIMEDOUT; - } - - udelay(10); - } - - if (uniphier_sd_readl(priv, UNIPHIER_SD_DMA_INFO2)) { - dev_err(dev, "error during DMA\n"); - return -EIO; - } - - return 0; -} - -static int uniphier_sd_dma_xfer(struct udevice *dev, struct mmc_data *data) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - size_t len = data->blocks * data->blocksize; - void *buf; - enum dma_data_direction dir; - dma_addr_t dma_addr; - u32 poll_flag, tmp; - int ret; - - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_DMA_MODE); - - if (data->flags & MMC_DATA_READ) { - buf = data->dest; - dir = DMA_FROM_DEVICE; - poll_flag = UNIPHIER_SD_DMA_INFO1_END_RD2; - tmp |= UNIPHIER_SD_DMA_MODE_DIR_RD; - } else { - buf = (void *)data->src; - dir = DMA_TO_DEVICE; - poll_flag = UNIPHIER_SD_DMA_INFO1_END_WR; - tmp &= ~UNIPHIER_SD_DMA_MODE_DIR_RD; - } - - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_DMA_MODE); - - dma_addr = __dma_map_single(buf, len, dir); - - uniphier_sd_dma_start(priv, dma_addr); - - ret = uniphier_sd_dma_wait_for_irq(dev, poll_flag, data->blocks); - - __dma_unmap_single(dma_addr, len, dir); - - return ret; -} - -/* check if the address is DMA'able */ -static bool uniphier_sd_addr_is_dmaable(unsigned long addr) -{ - if (!IS_ALIGNED(addr, UNIPHIER_SD_DMA_MINALIGN)) - return false; - -#if defined(CONFIG_ARCH_UNIPHIER) && !defined(CONFIG_ARM64) && \ - defined(CONFIG_SPL_BUILD) - /* - * For UniPhier ARMv7 SoCs, the stack is allocated in the locked ways - * of L2, which is unreachable from the DMA engine. - */ - if (addr < CONFIG_SPL_STACK) - return false; -#endif - - return true; -} - -static int uniphier_sd_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - int ret; - u32 tmp; - - if (uniphier_sd_readl(priv, UNIPHIER_SD_INFO2) & UNIPHIER_SD_INFO2_CBSY) { - dev_err(dev, "command busy\n"); - return -EBUSY; - } - - /* clear all status flags */ - uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO1); - uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO2); - - /* disable DMA once */ - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_EXTMODE); - tmp &= ~UNIPHIER_SD_EXTMODE_DMA_EN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_EXTMODE); - - uniphier_sd_writel(priv, cmd->cmdarg, UNIPHIER_SD_ARG); - - tmp = cmd->cmdidx; - - if (data) { - uniphier_sd_writel(priv, data->blocksize, UNIPHIER_SD_SIZE); - uniphier_sd_writel(priv, data->blocks, UNIPHIER_SD_SECCNT); - - /* Do not send CMD12 automatically */ - tmp |= UNIPHIER_SD_CMD_NOSTOP | UNIPHIER_SD_CMD_DATA; - - if (data->blocks > 1) - tmp |= UNIPHIER_SD_CMD_MULTI; - - if (data->flags & MMC_DATA_READ) - tmp |= UNIPHIER_SD_CMD_RD; - } - - /* - * Do not use the response type auto-detection on this hardware. - * CMD8, for example, has different response types on SD and eMMC, - * while this controller always assumes the response type for SD. - * Set the response type manually. - */ - switch (cmd->resp_type) { - case MMC_RSP_NONE: - tmp |= UNIPHIER_SD_CMD_RSP_NONE; - break; - case MMC_RSP_R1: - tmp |= UNIPHIER_SD_CMD_RSP_R1; - break; - case MMC_RSP_R1b: - tmp |= UNIPHIER_SD_CMD_RSP_R1B; - break; - case MMC_RSP_R2: - tmp |= UNIPHIER_SD_CMD_RSP_R2; - break; - case MMC_RSP_R3: - tmp |= UNIPHIER_SD_CMD_RSP_R3; - break; - default: - dev_err(dev, "unknown response type\n"); - return -EINVAL; - } - - dev_dbg(dev, "sending CMD%d (SD_CMD=%08x, SD_ARG=%08x)\n", - cmd->cmdidx, tmp, cmd->cmdarg); - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_CMD); - - ret = uniphier_sd_wait_for_irq(dev, UNIPHIER_SD_INFO1, - UNIPHIER_SD_INFO1_RSP); - if (ret) - return ret; - - if (cmd->resp_type & MMC_RSP_136) { - u32 rsp_127_104 = uniphier_sd_readl(priv, UNIPHIER_SD_RSP76); - u32 rsp_103_72 = uniphier_sd_readl(priv, UNIPHIER_SD_RSP54); - u32 rsp_71_40 = uniphier_sd_readl(priv, UNIPHIER_SD_RSP32); - u32 rsp_39_8 = uniphier_sd_readl(priv, UNIPHIER_SD_RSP10); - - cmd->response[0] = ((rsp_127_104 & 0x00ffffff) << 8) | - ((rsp_103_72 & 0xff000000) >> 24); - cmd->response[1] = ((rsp_103_72 & 0x00ffffff) << 8) | - ((rsp_71_40 & 0xff000000) >> 24); - cmd->response[2] = ((rsp_71_40 & 0x00ffffff) << 8) | - ((rsp_39_8 & 0xff000000) >> 24); - cmd->response[3] = (rsp_39_8 & 0xffffff) << 8; - } else { - /* bit 39-8 */ - cmd->response[0] = uniphier_sd_readl(priv, UNIPHIER_SD_RSP10); - } - - if (data) { - /* use DMA if the HW supports it and the buffer is aligned */ - if (priv->caps & UNIPHIER_SD_CAP_DMA_INTERNAL && - uniphier_sd_addr_is_dmaable((long)data->src)) - ret = uniphier_sd_dma_xfer(dev, data); - else - ret = uniphier_sd_pio_xfer(dev, data); - - ret = uniphier_sd_wait_for_irq(dev, UNIPHIER_SD_INFO1, - UNIPHIER_SD_INFO1_CMP); - if (ret) - return ret; - } - - return ret; -} - -static int uniphier_sd_set_bus_width(struct uniphier_sd_priv *priv, - struct mmc *mmc) -{ - u32 val, tmp; - - switch (mmc->bus_width) { - case 1: - val = UNIPHIER_SD_OPTION_WIDTH_1; - break; - case 4: - val = UNIPHIER_SD_OPTION_WIDTH_4; - break; - case 8: - val = UNIPHIER_SD_OPTION_WIDTH_8; - break; - default: - return -EINVAL; - } - - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_OPTION); - tmp &= ~UNIPHIER_SD_OPTION_WIDTH_MASK; - tmp |= val; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_OPTION); - - return 0; -} - -static void uniphier_sd_set_ddr_mode(struct uniphier_sd_priv *priv, - struct mmc *mmc) -{ - u32 tmp; - - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_IF_MODE); - if (mmc->ddr_mode) - tmp |= UNIPHIER_SD_IF_MODE_DDR; - else - tmp &= ~UNIPHIER_SD_IF_MODE_DDR; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_IF_MODE); -} - -static void uniphier_sd_set_clk_rate(struct uniphier_sd_priv *priv, - struct mmc *mmc) -{ - unsigned int divisor; - u32 val, tmp; - - if (!mmc->clock) - return; - - divisor = DIV_ROUND_UP(priv->mclk, mmc->clock); - - if (divisor <= 1) - val = UNIPHIER_SD_CLKCTL_DIV1; - else if (divisor <= 2) - val = UNIPHIER_SD_CLKCTL_DIV2; - else if (divisor <= 4) - val = UNIPHIER_SD_CLKCTL_DIV4; - else if (divisor <= 8) - val = UNIPHIER_SD_CLKCTL_DIV8; - else if (divisor <= 16) - val = UNIPHIER_SD_CLKCTL_DIV16; - else if (divisor <= 32) - val = UNIPHIER_SD_CLKCTL_DIV32; - else if (divisor <= 64) - val = UNIPHIER_SD_CLKCTL_DIV64; - else if (divisor <= 128) - val = UNIPHIER_SD_CLKCTL_DIV128; - else if (divisor <= 256) - val = UNIPHIER_SD_CLKCTL_DIV256; - else if (divisor <= 512 || !(priv->caps & UNIPHIER_SD_CAP_DIV1024)) - val = UNIPHIER_SD_CLKCTL_DIV512; - else - val = UNIPHIER_SD_CLKCTL_DIV1024; - - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_CLKCTL); - if (tmp & UNIPHIER_SD_CLKCTL_SCLKEN && - (tmp & UNIPHIER_SD_CLKCTL_DIV_MASK) == val) - return; - - /* stop the clock before changing its rate to avoid a glitch signal */ - tmp &= ~UNIPHIER_SD_CLKCTL_SCLKEN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_CLKCTL); - - tmp &= ~UNIPHIER_SD_CLKCTL_DIV_MASK; - tmp |= val | UNIPHIER_SD_CLKCTL_OFFEN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_CLKCTL); - - tmp |= UNIPHIER_SD_CLKCTL_SCLKEN; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_CLKCTL); - - udelay(1000); -} - -static int uniphier_sd_set_ios(struct udevice *dev) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - struct mmc *mmc = mmc_get_mmc_dev(dev); - int ret; - - dev_dbg(dev, "clock %uHz, DDRmode %d, width %u\n", - mmc->clock, mmc->ddr_mode, mmc->bus_width); - - ret = uniphier_sd_set_bus_width(priv, mmc); - if (ret) - return ret; - uniphier_sd_set_ddr_mode(priv, mmc); - uniphier_sd_set_clk_rate(priv, mmc); - - return 0; -} - -static int uniphier_sd_get_cd(struct udevice *dev) -{ - struct uniphier_sd_priv *priv = dev_get_priv(dev); - - if (priv->caps & UNIPHIER_SD_CAP_NONREMOVABLE) - return 1; - - return !!(uniphier_sd_readl(priv, UNIPHIER_SD_INFO1) & - UNIPHIER_SD_INFO1_CD); -} +#include "tmio-common.h" static const struct dm_mmc_ops uniphier_sd_ops = { - .send_cmd = uniphier_sd_send_cmd, - .set_ios = uniphier_sd_set_ios, - .get_cd = uniphier_sd_get_cd, + .send_cmd = tmio_sd_send_cmd, + .set_ios = tmio_sd_set_ios, + .get_cd = tmio_sd_get_cd, }; -static void uniphier_sd_host_init(struct uniphier_sd_priv *priv) -{ - u32 tmp; - - /* soft reset of the host */ - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_SOFT_RST); - tmp &= ~UNIPHIER_SD_SOFT_RST_RSTX; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_SOFT_RST); - tmp |= UNIPHIER_SD_SOFT_RST_RSTX; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_SOFT_RST); - - /* FIXME: implement eMMC hw_reset */ - - uniphier_sd_writel(priv, UNIPHIER_SD_STOP_SEC, UNIPHIER_SD_STOP); - - /* - * Connected to 32bit AXI. - * This register dropped backward compatibility at version 0x10. - * Write an appropriate value depending on the IP version. - */ - uniphier_sd_writel(priv, priv->version >= 0x10 ? 0x00000101 : 0x00000000, - UNIPHIER_SD_HOST_MODE); - - if (priv->caps & UNIPHIER_SD_CAP_DMA_INTERNAL) { - tmp = uniphier_sd_readl(priv, UNIPHIER_SD_DMA_MODE); - tmp |= UNIPHIER_SD_DMA_MODE_ADDR_INC; - uniphier_sd_writel(priv, tmp, UNIPHIER_SD_DMA_MODE); - } -} - -static int uniphier_sd_bind(struct udevice *dev) -{ - struct uniphier_sd_plat *plat = dev_get_platdata(dev); - - return mmc_bind(dev, &plat->mmc, &plat->cfg); -} - -static int uniphier_sd_probe(struct udevice *dev) -{ - struct uniphier_sd_plat *plat = dev_get_platdata(dev); - struct uniphier_sd_priv *priv = dev_get_priv(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); - const u32 quirks = dev_get_driver_data(dev); - fdt_addr_t base; - struct clk clk; - int ret; -#ifdef CONFIG_DM_REGULATOR - struct udevice *vqmmc_dev; -#endif - - base = devfdt_get_addr(dev); - if (base == FDT_ADDR_T_NONE) - return -EINVAL; - - priv->regbase = devm_ioremap(dev, base, SZ_2K); - if (!priv->regbase) - return -ENOMEM; - -#ifdef CONFIG_DM_REGULATOR - ret = device_get_supply_regulator(dev, "vqmmc-supply", &vqmmc_dev); - if (!ret) { - /* Set the regulator to 3.3V until we support 1.8V modes */ - regulator_set_value(vqmmc_dev, 3300000); - regulator_set_enable(vqmmc_dev, true); - } -#endif - - ret = clk_get_by_index(dev, 0, &clk); - if (ret < 0) { - dev_err(dev, "failed to get host clock\n"); - return ret; - } - - /* set to max rate */ - priv->mclk = clk_set_rate(&clk, ULONG_MAX); - if (IS_ERR_VALUE(priv->mclk)) { - dev_err(dev, "failed to set rate for host clock\n"); - clk_free(&clk); - return priv->mclk; - } - - ret = clk_enable(&clk); - clk_free(&clk); - if (ret) { - dev_err(dev, "failed to enable host clock\n"); - return ret; - } - - plat->cfg.name = dev->name; - plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; - - switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width", - 1)) { - case 8: - plat->cfg.host_caps |= MMC_MODE_8BIT; - break; - case 4: - plat->cfg.host_caps |= MMC_MODE_4BIT; - break; - case 1: - break; - default: - dev_err(dev, "Invalid \"bus-width\" value\n"); - return -EINVAL; - } - - if (quirks) { - priv->caps = quirks; - } else { - priv->version = uniphier_sd_readl(priv, UNIPHIER_SD_VERSION) & - UNIPHIER_SD_VERSION_IP; - dev_dbg(dev, "version %x\n", priv->version); - if (priv->version >= 0x10) { - priv->caps |= UNIPHIER_SD_CAP_DMA_INTERNAL; - priv->caps |= UNIPHIER_SD_CAP_DIV1024; - } - } - - if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), "non-removable", - NULL)) - priv->caps |= UNIPHIER_SD_CAP_NONREMOVABLE; - - uniphier_sd_host_init(priv); - - plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34; - plat->cfg.f_min = priv->mclk / - (priv->caps & UNIPHIER_SD_CAP_DIV1024 ? 1024 : 512); - plat->cfg.f_max = priv->mclk; - plat->cfg.b_max = U32_MAX; /* max value of UNIPHIER_SD_SECCNT */ - - upriv->mmc = &plat->mmc; - - return 0; -} - static const struct udevice_id uniphier_sd_match[] = { - { .compatible = "renesas,sdhi-r8a7790", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7791", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7792", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7793", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7794", .data = 0 }, - { .compatible = "renesas,sdhi-r8a7795", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a7796", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77965", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77970", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a77995", .data = UNIPHIER_SD_CAP_64BIT }, { .compatible = "socionext,uniphier-sdhc", .data = 0 }, { /* sentinel */ } }; +static int uniphier_sd_probe(struct udevice *dev) +{ + return tmio_sd_probe(dev, 0); +} + U_BOOT_DRIVER(uniphier_mmc) = { .name = "uniphier-mmc", .id = UCLASS_MMC, .of_match = uniphier_sd_match, - .bind = uniphier_sd_bind, + .bind = tmio_sd_bind, .probe = uniphier_sd_probe, - .priv_auto_alloc_size = sizeof(struct uniphier_sd_priv), - .platdata_auto_alloc_size = sizeof(struct uniphier_sd_plat), + .priv_auto_alloc_size = sizeof(struct tmio_sd_priv), + .platdata_auto_alloc_size = sizeof(struct tmio_sd_plat), .ops = &uniphier_sd_ops, }; diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 19579801d2..707359dca1 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -40,6 +40,13 @@ config FLASH_PIC32 This enables access to Microchip PIC32 internal non-CFI flash chips through PIC32 Non-Volatile-Memory Controller. +config RENESAS_RPC_HF + bool "Renesas RCar Gen3 RPC Hyperflash driver" + depends on RCAR_GEN3 && MTD + help + This enables access to Hyperflash memory through the Renesas + RCar Gen3 RPC controller. + endmenu source "drivers/mtd/nand/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 20c0d0af44..e46cbd8837 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o obj-$(CONFIG_ST_SMI) += st_smi.o obj-$(CONFIG_STM32_FLASH) += stm32_flash.o +obj-$(CONFIG_RENESAS_RPC_HF) += renesas_rpc_hf.o diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index a820af61ce..94fbf89e4b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -95,9 +95,11 @@ config NAND_PXA3XX config NAND_SUNXI bool "Support for NAND on Allwinner SoCs" - depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I + default ARCH_SUNXI + depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I select SYS_NAND_SELF_INIT select SYS_NAND_U_BOOT_LOCATIONS + select SPL_NAND_SUPPORT imply CMD_NAND ---help--- Enable support for NAND. This option enables the standard and @@ -166,6 +168,28 @@ config NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS comment "Generic NAND options" +config SYS_NAND_BLOCK_SIZE + hex "NAND chip eraseblock size" + depends on ARCH_SUNXI + help + Number of data bytes in one eraseblock for the NAND chip on the + board. This is the multiple of NAND_PAGE_SIZE and the number of + pages. + +config SYS_NAND_PAGE_SIZE + hex "NAND chip page size" + depends on ARCH_SUNXI + help + Number of data bytes in one page for the NAND chip on the + board, not including the OOB area. + +config SYS_NAND_OOBSIZE + hex "NAND chip OOB size" + depends on ARCH_SUNXI + help + Number of bytes in the Out-Of-Band area for the NAND chip on + the board. + # Enhance depends when converting drivers to Kconfig which use this config # option (mxc_nand, ndfc, omap_gpmc). config SYS_NAND_BUSWIDTH_16BIT diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 9f7d9d6ff7..332d905a3a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -57,7 +57,6 @@ obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o obj-$(CONFIG_NAND_MXC) += mxc_nand.o obj-$(CONFIG_NAND_MXS) += mxs_nand.o -obj-$(CONFIG_NAND_NDFC) += ndfc.o obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o obj-$(CONFIG_NAND_SPEAR) += spr_nand.o obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o diff --git a/drivers/mtd/nand/arasan_nfc.c b/drivers/mtd/nand/arasan_nfc.c index 3c9a0215c5..3be66efb73 100644 --- a/drivers/mtd/nand/arasan_nfc.c +++ b/drivers/mtd/nand/arasan_nfc.c @@ -795,10 +795,11 @@ static int arasan_nand_erase(struct arasan_nand_command_format *curr_cmd, writel(reg_val, &arasan_nand_base->cmd_reg); - page = (page_addr << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & - ARASAN_NAND_MEM_ADDR1_PAGE_MASK; + page = (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & + ARASAN_NAND_MEM_ADDR1_COL_MASK; column = page_addr & ARASAN_NAND_MEM_ADDR1_COL_MASK; - writel(page | column, &arasan_nand_base->memadr_reg1); + writel(column | (page << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT), + &arasan_nand_base->memadr_reg1); reg_val = readl(&arasan_nand_base->memadr_reg2); reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK; diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 8b548b204d..13a6535bd5 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -29,11 +29,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand_ecc.h> -/* The PPC4xx NDFC uses Smart Media (SMC) bytes order */ -#ifdef CONFIG_NAND_NDFC -#define CONFIG_MTD_NAND_ECC_SMC -#endif - /* * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), * only nand_correct_data() is needed @@ -110,13 +105,8 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ /* Calculate final ECC code */ -#ifdef CONFIG_MTD_NAND_ECC_SMC - ecc_code[0] = ~tmp2; - ecc_code[1] = ~tmp1; -#else ecc_code[0] = ~tmp1; ecc_code[1] = ~tmp2; -#endif ecc_code[2] = ((~reg1) << 2) | 0x03; return 0; @@ -146,15 +136,9 @@ int nand_correct_data(struct mtd_info *mtd, u_char *dat, { uint8_t s0, s1, s2; -#ifdef CONFIG_MTD_NAND_ECC_SMC - s0 = calc_ecc[0] ^ read_ecc[0]; - s1 = calc_ecc[1] ^ read_ecc[1]; - s2 = calc_ecc[2] ^ read_ecc[2]; -#else s1 = calc_ecc[0] ^ read_ecc[0]; s0 = calc_ecc[1] ^ read_ecc[1]; s2 = calc_ecc[2] ^ read_ecc[2]; -#endif if ((s0 | s1 | s2) == 0) return 0; diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c deleted file mode 100644 index 0a9849e9bc..0000000000 --- a/drivers/mtd/nand/ndfc.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Overview: - * Platform independent driver for NDFC (NanD Flash Controller) - * integrated into IBM/AMCC PPC4xx cores - * - * (C) Copyright 2006-2009 - * Stefan Roese, DENX Software Engineering, sr@denx.de. - * - * Based on original work by - * Thomas Gleixner - * Copyright 2006 IBM - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <nand.h> -#include <linux/mtd/ndfc.h> -#include <linux/mtd/nand_ecc.h> -#include <asm/processor.h> -#include <asm/io.h> -#include <asm/ppc4xx.h> - -#ifndef CONFIG_SYS_NAND_BCR -#define CONFIG_SYS_NAND_BCR 0x80002222 -#endif -#ifndef CONFIG_SYS_NDFC_EBC0_CFG -#define CONFIG_SYS_NDFC_EBC0_CFG 0xb8400000 -#endif - -/* - * We need to store the info, which chip-select (CS) is used for the - * chip number. For example on Sequoia NAND chip #0 uses - * CS #3. - */ -static int ndfc_cs[NDFC_MAX_BANKS]; - -static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - out_8((u8 *)(base + NDFC_CMD), cmd & 0xFF); - else - out_8((u8 *)(base + NDFC_ALE), cmd & 0xFF); -} - -static int ndfc_dev_ready(struct mtd_info *mtdinfo) -{ - struct nand_chip *this = mtd_to_nand(mtdinfo); - ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; - - return (in_be32((u32 *)(base + NDFC_STAT)) & NDFC_STAT_IS_READY); -} - -static void ndfc_enable_hwecc(struct mtd_info *mtdinfo, int mode) -{ - struct nand_chip *this = mtd_to_nand(mtdinfo); - ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; - u32 ccr; - - ccr = in_be32((u32 *)(base + NDFC_CCR)); - ccr |= NDFC_CCR_RESET_ECC; - out_be32((u32 *)(base + NDFC_CCR), ccr); -} - -static int ndfc_calculate_ecc(struct mtd_info *mtdinfo, - const u_char *dat, u_char *ecc_code) -{ - struct nand_chip *this = mtd_to_nand(mtdinfo); - ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; - u32 ecc; - u8 *p = (u8 *)&ecc; - - ecc = in_be32((u32 *)(base + NDFC_ECC)); - - /* The NDFC uses Smart Media (SMC) bytes order - */ - ecc_code[0] = p[1]; - ecc_code[1] = p[2]; - ecc_code[2] = p[3]; - - return 0; -} - -/* - * Speedups for buffer read/write/verify - * - * NDFC allows 32bit read/write of data. So we can speed up the buffer - * functions. No further checking, as nand_base will always read/write - * page aligned. - */ -static void ndfc_read_buf(struct mtd_info *mtdinfo, uint8_t *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtdinfo); - ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; - uint32_t *p = (uint32_t *) buf; - - for (;len > 0; len -= 4) - *p++ = in_be32((u32 *)(base + NDFC_DATA)); -} - -/* - * Don't use these speedup functions in NAND boot image, since the image - * has to fit into 4kByte. - */ -static void ndfc_write_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len) -{ - struct nand_chip *this = mtd_to_nand(mtdinfo); - ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; - uint32_t *p = (uint32_t *) buf; - - for (; len > 0; len -= 4) - out_be32((u32 *)(base + NDFC_DATA), *p++); -} - -/* - * Read a byte from the NDFC. - */ -static uint8_t ndfc_read_byte(struct mtd_info *mtd) -{ - - struct nand_chip *chip = mtd_to_nand(mtd); - -#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT - return (uint8_t) readw(chip->IO_ADDR_R); -#else - return readb(chip->IO_ADDR_R); -#endif - -} - -void board_nand_select_device(struct nand_chip *nand, int chip) -{ - /* - * Don't use "chip" to address the NAND device, - * generate the cs from the address where it is encoded. - */ - ulong base = (ulong)nand->IO_ADDR_W & 0xffffff00; - int cs = ndfc_cs[chip]; - - /* Set NandFlash Core Configuration Register */ - /* 1 col x 2 rows */ - out_be32((u32 *)(base + NDFC_CCR), 0x00000000 | (cs << 24)); - out_be32((u32 *)(base + NDFC_BCFG0 + (cs << 2)), CONFIG_SYS_NAND_BCR); -} - -static void ndfc_select_chip(struct mtd_info *mtd, int chip) -{ - /* - * Nothing to do here! - */ -} - -int board_nand_init(struct nand_chip *nand) -{ - int cs = (ulong)nand->IO_ADDR_W & 0x00000003; - ulong base = (ulong)nand->IO_ADDR_W & 0xffffff00; - static int chip = 0; - - /* - * Save chip-select for this chip # - */ - ndfc_cs[chip] = cs; - - /* - * Select required NAND chip in NDFC - */ - board_nand_select_device(nand, chip); - - nand->IO_ADDR_R = (void __iomem *)(base + NDFC_DATA); - nand->IO_ADDR_W = (void __iomem *)(base + NDFC_DATA); - nand->cmd_ctrl = ndfc_hwcontrol; - nand->chip_delay = 50; - nand->read_buf = ndfc_read_buf; - nand->dev_ready = ndfc_dev_ready; - nand->ecc.correct = nand_correct_data; - nand->ecc.hwctl = ndfc_enable_hwecc; - nand->ecc.calculate = ndfc_calculate_ecc; - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = 256; - nand->ecc.bytes = 3; - nand->ecc.strength = 1; - nand->select_chip = ndfc_select_chip; - -#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT - nand->options |= NAND_BUSWIDTH_16; -#endif - - nand->write_buf = ndfc_write_buf; - nand->read_byte = ndfc_read_byte; - - chip++; - - return 0; -} diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 532e03cd84..37160aaec2 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1407,8 +1407,14 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, /* Add ECC info retrieval from DT */ for (i = 0; i < ARRAY_SIZE(strengths); i++) { - if (ecc->strength <= strengths[i]) + if (ecc->strength <= strengths[i]) { + /* + * Update ecc->strength value with the actual strength + * that will be used by the ECC engine. + */ + ecc->strength = strengths[i]; break; + } } if (i >= ARRAY_SIZE(strengths)) { diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c index eed4472bdc..7241e9a374 100644 --- a/drivers/mtd/nand/sunxi_nand_spl.c +++ b/drivers/mtd/nand/sunxi_nand_spl.c @@ -10,6 +10,7 @@ #include <common.h> #include <config.h> #include <nand.h> +#include <linux/ctype.h> /* registers */ #define NFC_CTL 0x00000000 @@ -55,7 +56,7 @@ #define NFC_ADDR_NUM_OFFSET 16 -#define NFC_SEND_ADR (1 << 19) +#define NFC_SEND_ADDR (1 << 19) #define NFC_ACCESS_DIR (1 << 20) #define NFC_DATA_TRANS (1 << 21) #define NFC_SEND_CMD1 (1 << 22) @@ -67,10 +68,12 @@ #define NFC_SEND_CMD3 (1 << 28) #define NFC_SEND_CMD4 (1 << 29) #define NFC_RAW_CMD (0 << 30) +#define NFC_ECC_CMD (1 << 30) #define NFC_PAGE_CMD (2 << 30) #define NFC_ST_CMD_INT_FLAG (1 << 1) #define NFC_ST_DMA_INT_FLAG (1 << 2) +#define NFC_ST_CMD_FIFO_STAT (1 << 3) #define NFC_READ_CMD_OFFSET 0 #define NFC_RANDOM_READ_CMD0_OFFSET 8 @@ -80,22 +83,6 @@ #define NFC_CMD_RNDOUT 0x05 #define NFC_CMD_READSTART 0x30 -#define SUNXI_DMA_CFG_REG0 0x300 -#define SUNXI_DMA_SRC_START_ADDR_REG0 0x304 -#define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308 -#define SUNXI_DMA_DDMA_BC_REG0 0x30C -#define SUNXI_DMA_DDMA_PARA_REG0 0x318 - -#define SUNXI_DMA_DDMA_CFG_REG_LOADING (1 << 31) -#define SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25) -#define SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM (1 << 16) -#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9) -#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5) -#define SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0) - -#define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0) -#define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8) - struct nfc_config { int page_size; int ecc_strength; @@ -155,6 +142,42 @@ static inline int check_value_negated(int offset, int unexpected_bits, return check_value_inner(offset, unexpected_bits, timeout_us, 1); } +static int nand_wait_cmd_fifo_empty(void) +{ + if (!check_value_negated(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_FIFO_STAT, + DEFAULT_TIMEOUT_US)) { + printf("nand: timeout waiting for empty cmd FIFO\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int nand_wait_int(void) +{ + if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, + DEFAULT_TIMEOUT_US)) { + printf("nand: timeout waiting for interruption\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int nand_exec_cmd(u32 cmd) +{ + int ret; + + ret = nand_wait_cmd_fifo_empty(); + if (ret) + return ret; + + writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); + writel(cmd, SUNXI_NFC_BASE + NFC_CMD); + + return nand_wait_int(); +} + void nand_init(void) { uint32_t val; @@ -172,22 +195,15 @@ void nand_init(void) } /* reset NAND */ - writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); - writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET, - SUNXI_NFC_BASE + NFC_CMD); - - if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, - DEFAULT_TIMEOUT_US)) { - printf("Error timeout waiting for nand reset\n"); - return; - } - writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); + nand_exec_cmd(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET); } static void nand_apply_config(const struct nfc_config *conf) { u32 val; + nand_wait_cmd_fifo_empty(); + val = readl(SUNXI_NFC_BASE + NFC_CTL); val &= ~NFC_CTL_PAGE_SIZE_MASK; writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size), @@ -206,128 +222,111 @@ static int nand_load_page(const struct nfc_config *conf, u32 offs) SUNXI_NFC_BASE + NFC_RCMD_SET); writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW); writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); - writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); - writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | NFC_WAIT_FLAG | - ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADR, - SUNXI_NFC_BASE + NFC_CMD); - - if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, - DEFAULT_TIMEOUT_US)) { - printf("Error while initializing dma interrupt\n"); - return -EIO; - } - return 0; + return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | + NFC_SEND_ADDR | NFC_WAIT_FLAG | + ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET)); } -static int nand_reset_column(void) +static int nand_change_column(u16 column) { + int ret; + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), SUNXI_NFC_BASE + NFC_RCMD_SET); - writel(0, SUNXI_NFC_BASE + NFC_ADDR_LOW); - writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | - (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADR | NFC_CMD_RNDOUT, - SUNXI_NFC_BASE + NFC_CMD); + writel(column, SUNXI_NFC_BASE + NFC_ADDR_LOW); - if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, - DEFAULT_TIMEOUT_US)) { - printf("Error while initializing dma interrupt\n"); - return -1; - } + ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | + (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR | + NFC_CMD_RNDOUT); + if (ret) + return ret; + + /* Ensure tCCS has passed before reading data */ + udelay(1); return 0; } +static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116}; + static int nand_read_page(const struct nfc_config *conf, u32 offs, void *dest, int len) { - dma_addr_t dst = (dma_addr_t)dest; int nsectors = len / conf->ecc_size; u16 rand_seed = 0; - u32 val; - int page; - - page = offs / conf->page_size; + int oob_chunk_sz = ecc_bytes[conf->ecc_strength]; + int page = offs / conf->page_size; + u32 ecc_st; + int i; if (offs % conf->page_size || len % conf->ecc_size || len > conf->page_size || len < 0) return -EINVAL; - /* clear ecc status */ - writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); - /* Choose correct seed if randomized */ if (conf->randomize) rand_seed = random_seed[page % conf->nseeds]; - writel((rand_seed << 16) | (conf->ecc_strength << 12) | - (conf->randomize ? NFC_ECC_RANDOM_EN : 0) | - (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) | - NFC_ECC_EN | NFC_ECC_PIPELINE | NFC_ECC_EXCEPTION, - SUNXI_NFC_BASE + NFC_ECC_CTL); - - flush_dcache_range(dst, ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN)); - - /* SUNXI_DMA */ - writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */ - /* read from REG_IO_DATA */ - writel(SUNXI_NFC_BASE + NFC_IO_DATA, - SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0); - /* read to RAM */ - writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0); - writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC | - SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE, - SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0); - writel(len, SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0); - writel(SUNXI_DMA_DDMA_CFG_REG_LOADING | - SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 | - SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM | - SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 | - SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO | - SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC, - SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); - - writel(nsectors, SUNXI_NFC_BASE + NFC_SECTOR_NUM); - writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); - writel(NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_DATA_SWAP_METHOD, - SUNXI_NFC_BASE + NFC_CMD); - - if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG, - DEFAULT_TIMEOUT_US)) { - printf("Error while initializing dma interrupt\n"); - return -EIO; - } - writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); + /* Retrieve data from SRAM (PIO) */ + for (i = 0; i < nsectors; i++) { + int data_off = i * conf->ecc_size; + int oob_off = conf->page_size + (i * oob_chunk_sz); + u8 *data = dest + data_off; + + /* Clear ECC status and restart ECC engine */ + writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); + writel((rand_seed << 16) | (conf->ecc_strength << 12) | + (conf->randomize ? NFC_ECC_RANDOM_EN : 0) | + (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) | + NFC_ECC_EN | NFC_ECC_EXCEPTION, + SUNXI_NFC_BASE + NFC_ECC_CTL); + + /* Move the data in SRAM */ + nand_change_column(data_off); + writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); + nand_exec_cmd(NFC_DATA_TRANS); - if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0, - SUNXI_DMA_DDMA_CFG_REG_LOADING, - DEFAULT_TIMEOUT_US)) { - printf("Error while waiting for dma transfer to finish\n"); - return -EIO; - } + /* + * Let the ECC engine consume the ECC bytes and possibly correct + * the data. + */ + nand_change_column(oob_off); + nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD); + + /* Get the ECC status */ + ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST); + + /* ECC error detected. */ + if (ecc_st & 0xffff) + return -EIO; + + /* + * Return 1 if the first chunk is empty (needed for + * configuration detection). + */ + if (!i && (ecc_st & 0x10000)) + return 1; - invalidate_dcache_range(dst, - ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN)); + /* Retrieve the data from SRAM */ + memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE, + conf->ecc_size); - val = readl(SUNXI_NFC_BASE + NFC_ECC_ST); + /* Stop the ECC engine */ + writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN, + SUNXI_NFC_BASE + NFC_ECC_CTL); - /* ECC error detected. */ - if (val & 0xffff) - return -EIO; + if (data_off + conf->ecc_size >= len) + break; + } - /* - * Return 1 if the page is empty. - * We consider the page as empty if the first ECC block is marked - * empty. - */ - return (val & 0x10000) ? 1 : 0; + return 0; } static int nand_max_ecc_strength(struct nfc_config *conf) { - static const int ecc_bytes[] = { 32, 46, 54, 60, 74, 88, 102, 110, 116 }; int max_oobsize, max_ecc_bytes; int nsectors = conf->page_size / conf->ecc_size; int i; @@ -393,7 +392,7 @@ static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs, conf->ecc_strength >= 0; conf->ecc_strength--) { conf->randomize = false; - if (nand_reset_column()) + if (nand_change_column(0)) return -EIO; /* @@ -413,7 +412,7 @@ static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs, conf->randomize = true; conf->nseeds = ARRAY_SIZE(random_seed); do { - if (nand_reset_column()) + if (nand_change_column(0)) return -EIO; if (!nand_read_page(conf, offs, dest, @@ -475,11 +474,12 @@ static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) static int nand_read_buffer(struct nfc_config *conf, uint32_t offs, unsigned int size, void *dest) { - int first_seed, page, ret; + int first_seed = 0, page, ret; size = ALIGN(size, conf->page_size); page = offs / conf->page_size; - first_seed = page % conf->nseeds; + if (conf->randomize) + first_seed = page % conf->nseeds; for (; size; size -= conf->page_size) { if (nand_load_page(conf, offs)) @@ -504,7 +504,7 @@ static int nand_read_buffer(struct nfc_config *conf, uint32_t offs, /* Try to adjust ->nseeds and read the page again... */ conf->nseeds = cur_seed; - if (nand_reset_column()) + if (nand_change_column(0)) return -EIO; /* ... it still fails => it's a real corruption. */ diff --git a/drivers/mtd/renesas_rpc_hf.c b/drivers/mtd/renesas_rpc_hf.c new file mode 100644 index 0000000000..1ba6e354a0 --- /dev/null +++ b/drivers/mtd/renesas_rpc_hf.c @@ -0,0 +1,398 @@ +/* + * Renesas RCar Gen3 RPC Hyperflash driver + * + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2016 Cogent Embedded, Inc. + * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/of_access.h> +#include <errno.h> +#include <fdt_support.h> +#include <flash.h> +#include <mtd.h> +#include <wait_bit.h> +#include <mtd/cfi_flash.h> + +#define RPC_CMNCR 0x0000 /* R/W */ +#define RPC_CMNCR_MD BIT(31) +#define RPC_CMNCR_MOIIO0(val) (((val) & 0x3) << 16) +#define RPC_CMNCR_MOIIO1(val) (((val) & 0x3) << 18) +#define RPC_CMNCR_MOIIO2(val) (((val) & 0x3) << 20) +#define RPC_CMNCR_MOIIO3(val) (((val) & 0x3) << 22) +#define RPC_CMNCR_MOIIO_HIZ (RPC_CMNCR_MOIIO0(3) | RPC_CMNCR_MOIIO1(3) | \ + RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3)) +#define RPC_CMNCR_IO0FV(val) (((val) & 0x3) << 8) +#define RPC_CMNCR_IO2FV(val) (((val) & 0x3) << 12) +#define RPC_CMNCR_IO3FV(val) (((val) & 0x3) << 14) +#define RPC_CMNCR_IOFV_HIZ (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | \ + RPC_CMNCR_IO3FV(3)) +#define RPC_CMNCR_BSZ(val) (((val) & 0x3) << 0) + +#define RPC_SSLDR 0x0004 /* R/W */ +#define RPC_SSLDR_SPNDL(d) (((d) & 0x7) << 16) +#define RPC_SSLDR_SLNDL(d) (((d) & 0x7) << 8) +#define RPC_SSLDR_SCKDL(d) (((d) & 0x7) << 0) + +#define RPC_DRCR 0x000C /* R/W */ +#define RPC_DRCR_SSLN BIT(24) +#define RPC_DRCR_RBURST(v) (((v) & 0x1F) << 16) +#define RPC_DRCR_RCF BIT(9) +#define RPC_DRCR_RBE BIT(8) +#define RPC_DRCR_SSLE BIT(0) + +#define RPC_DRCMR 0x0010 /* R/W */ +#define RPC_DRCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPC_DRCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPC_DREAR 0x0014 /* R/W */ +#define RPC_DREAR_EAV(v) (((v) & 0xFF) << 16) +#define RPC_DREAR_EAC(v) (((v) & 0x7) << 0) + +#define RPC_DROPR 0x0018 /* R/W */ +#define RPC_DROPR_OPD3(o) (((o) & 0xFF) << 24) +#define RPC_DROPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPC_DROPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPC_DROPR_OPD0(o) (((o) & 0xFF) << 0) + +#define RPC_DRENR 0x001C /* R/W */ +#define RPC_DRENR_CDB(o) (u32)((((o) & 0x3) << 30)) +#define RPC_DRENR_OCDB(o) (((o) & 0x3) << 28) +#define RPC_DRENR_ADB(o) (((o) & 0x3) << 24) +#define RPC_DRENR_OPDB(o) (((o) & 0x3) << 20) +#define RPC_DRENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPC_DRENR_DME BIT(15) +#define RPC_DRENR_CDE BIT(14) +#define RPC_DRENR_OCDE BIT(12) +#define RPC_DRENR_ADE(v) (((v) & 0xF) << 8) +#define RPC_DRENR_OPDE(v) (((v) & 0xF) << 4) + +#define RPC_SMCR 0x0020 /* R/W */ +#define RPC_SMCR_SSLKP BIT(8) +#define RPC_SMCR_SPIRE BIT(2) +#define RPC_SMCR_SPIWE BIT(1) +#define RPC_SMCR_SPIE BIT(0) + +#define RPC_SMCMR 0x0024 /* R/W */ +#define RPC_SMCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPC_SMCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPC_SMADR 0x0028 /* R/W */ +#define RPC_SMOPR 0x002C /* R/W */ +#define RPC_SMOPR_OPD0(o) (((o) & 0xFF) << 0) +#define RPC_SMOPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPC_SMOPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPC_SMOPR_OPD3(o) (((o) & 0xFF) << 24) + +#define RPC_SMENR 0x0030 /* R/W */ +#define RPC_SMENR_CDB(o) (((o) & 0x3) << 30) +#define RPC_SMENR_OCDB(o) (((o) & 0x3) << 28) +#define RPC_SMENR_ADB(o) (((o) & 0x3) << 24) +#define RPC_SMENR_OPDB(o) (((o) & 0x3) << 20) +#define RPC_SMENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPC_SMENR_DME BIT(15) +#define RPC_SMENR_CDE BIT(14) +#define RPC_SMENR_OCDE BIT(12) +#define RPC_SMENR_ADE(v) (((v) & 0xF) << 8) +#define RPC_SMENR_OPDE(v) (((v) & 0xF) << 4) +#define RPC_SMENR_SPIDE(v) (((v) & 0xF) << 0) + +#define RPC_SMRDR0 0x0038 /* R */ +#define RPC_SMRDR1 0x003C /* R */ +#define RPC_SMWDR0 0x0040 /* R/W */ +#define RPC_SMWDR1 0x0044 /* R/W */ +#define RPC_CMNSR 0x0048 /* R */ +#define RPC_CMNSR_SSLF BIT(1) +#define RPC_CMNSR_TEND BIT(0) + +#define RPC_DRDMCR 0x0058 /* R/W */ +#define RPC_DRDMCR_DMCYC(v) (((v) & 0xF) << 0) + +#define RPC_DRDRENR 0x005C /* R/W */ +#define RPC_DRDRENR_HYPE (0x5 << 12) +#define RPC_DRDRENR_ADDRE BIT(8) +#define RPC_DRDRENR_OPDRE BIT(4) +#define RPC_DRDRENR_DRDRE BIT(0) + +#define RPC_SMDMCR 0x0060 /* R/W */ +#define RPC_SMDMCR_DMCYC(v) (((v) & 0xF) << 0) + +#define RPC_SMDRENR 0x0064 /* R/W */ +#define RPC_SMDRENR_HYPE (0x5 << 12) +#define RPC_SMDRENR_ADDRE BIT(8) +#define RPC_SMDRENR_OPDRE BIT(4) +#define RPC_SMDRENR_SPIDRE BIT(0) + +#define RPC_PHYCNT 0x007C /* R/W */ +#define RPC_PHYCNT_CAL BIT(31) +#define PRC_PHYCNT_OCTA_AA BIT(22) +#define PRC_PHYCNT_OCTA_SA BIT(23) +#define PRC_PHYCNT_EXDS BIT(21) +#define RPC_PHYCNT_OCT BIT(20) +#define RPC_PHYCNT_WBUF2 BIT(4) +#define RPC_PHYCNT_WBUF BIT(2) +#define RPC_PHYCNT_MEM(v) (((v) & 0x3) << 0) + +#define RPC_PHYINT 0x0088 /* R/W */ +#define RPC_PHYINT_RSTEN BIT(18) +#define RPC_PHYINT_WPEN BIT(17) +#define RPC_PHYINT_INTEN BIT(16) +#define RPC_PHYINT_RST BIT(2) +#define RPC_PHYINT_WP BIT(1) +#define RPC_PHYINT_INT BIT(0) + +#define RPC_WBUF 0x8000 /* R/W size=4/8/16/32/64Bytes */ +#define RPC_WBUF_SIZE 0x100 + +static phys_addr_t rpc_base; + +enum rpc_hf_size { + RPC_HF_SIZE_16BIT = RPC_SMENR_SPIDE(0x8), + RPC_HF_SIZE_32BIT = RPC_SMENR_SPIDE(0xC), + RPC_HF_SIZE_64BIT = RPC_SMENR_SPIDE(0xF), +}; + +static int rpc_hf_wait_tend(void) +{ + void __iomem *reg = (void __iomem *)rpc_base + RPC_CMNSR; + return wait_for_bit_le32(reg, RPC_CMNSR_TEND, true, 1000, 0); +} + +static int rpc_hf_mode(bool man) +{ + int ret; + + ret = rpc_hf_wait_tend(); + if (ret) + return ret; + + clrsetbits_le32(rpc_base + RPC_PHYCNT, + RPC_PHYCNT_WBUF | RPC_PHYCNT_WBUF2 | + RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3), + RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3)); + + clrsetbits_le32(rpc_base + RPC_CMNCR, + RPC_CMNCR_MD | RPC_CMNCR_BSZ(3), + RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ | + (man ? RPC_CMNCR_MD : 0) | RPC_CMNCR_BSZ(1)); + + if (man) + return 0; + + writel(RPC_DRCR_RBURST(0x1F) | RPC_DRCR_RCF | RPC_DRCR_RBE, + rpc_base + RPC_DRCR); + + writel(RPC_DRCMR_CMD(0xA0), rpc_base + RPC_DRCMR); + writel(RPC_DRENR_CDB(2) | RPC_DRENR_OCDB(2) | RPC_DRENR_ADB(2) | + RPC_DRENR_SPIDB(2) | RPC_DRENR_CDE | RPC_DRENR_OCDE | + RPC_DRENR_ADE(4), rpc_base + RPC_DRENR); + writel(RPC_DRDMCR_DMCYC(0xE), rpc_base + RPC_DRDMCR); + writel(RPC_DRDRENR_HYPE | RPC_DRDRENR_ADDRE | RPC_DRDRENR_DRDRE, + rpc_base + RPC_DRDRENR); + + /* Dummy read */ + readl(rpc_base + RPC_DRCR); + + return 0; +} + +static int rpc_hf_xfer(void *addr, u64 wdata, u64 *rdata, + enum rpc_hf_size size, bool write) +{ + int ret; + u32 val; + + ret = rpc_hf_mode(1); + if (ret) + return ret; + + /* Submit HF address, SMCMR CMD[7] ~= CA Bit# 47 (R/nW) */ + writel(write ? 0 : RPC_SMCMR_CMD(0x80), rpc_base + RPC_SMCMR); + writel((uintptr_t)addr >> 1, rpc_base + RPC_SMADR); + writel(0x0, rpc_base + RPC_SMOPR); + + writel(RPC_SMDRENR_HYPE | RPC_SMDRENR_ADDRE | RPC_SMDRENR_SPIDRE, + rpc_base + RPC_SMDRENR); + + val = RPC_SMENR_CDB(2) | RPC_SMENR_OCDB(2) | + RPC_SMENR_ADB(2) | RPC_SMENR_SPIDB(2) | + RPC_SMENR_CDE | RPC_SMENR_OCDE | RPC_SMENR_ADE(4) | size; + + if (write) { + writel(val, rpc_base + RPC_SMENR); + + if (size == RPC_HF_SIZE_64BIT) + writeq(cpu_to_be64(wdata), rpc_base + RPC_SMWDR0); + else + writel(cpu_to_be32(wdata), rpc_base + RPC_SMWDR0); + + writel(RPC_SMCR_SPIWE | RPC_SMCR_SPIE, rpc_base + RPC_SMCR); + } else { + val |= RPC_SMENR_DME; + + writel(RPC_SMDMCR_DMCYC(0xE), rpc_base + RPC_SMDMCR); + + writel(val, rpc_base + RPC_SMENR); + + writel(RPC_SMCR_SPIRE | RPC_SMCR_SPIE, rpc_base + RPC_SMCR); + + ret = rpc_hf_wait_tend(); + if (ret) + return ret; + + if (size == RPC_HF_SIZE_64BIT) + *rdata = be64_to_cpu(readq(rpc_base + RPC_SMRDR0)); + else + *rdata = be32_to_cpu(readl(rpc_base + RPC_SMRDR0)); + } + + return rpc_hf_mode(0); +} + +static void rpc_hf_write_cmd(void *addr, u64 wdata, enum rpc_hf_size size) +{ + int ret; + + ret = rpc_hf_xfer(addr, wdata, NULL, size, 1); + if (ret) + printf("RPC: Write failed, ret=%i\n", ret); +} + +static u64 rpc_hf_read_reg(void *addr, enum rpc_hf_size size) +{ + u64 rdata = 0; + int ret; + + ret = rpc_hf_xfer(addr, 0, &rdata, size, 0); + if (ret) + printf("RPC: Read failed, ret=%i\n", ret); + + return rdata; +} + +void flash_write8(u8 value, void *addr) +{ + rpc_hf_write_cmd(addr, value, RPC_HF_SIZE_16BIT); +} + +void flash_write16(u16 value, void *addr) +{ + rpc_hf_write_cmd(addr, value, RPC_HF_SIZE_16BIT); +} + +void flash_write32(u32 value, void *addr) +{ + rpc_hf_write_cmd(addr, value, RPC_HF_SIZE_32BIT); +} + +void flash_write64(u64 value, void *addr) +{ + rpc_hf_write_cmd(addr, value, RPC_HF_SIZE_64BIT); +} + +u8 flash_read8(void *addr) +{ + return rpc_hf_read_reg(addr, RPC_HF_SIZE_16BIT); +} + +u16 flash_read16(void *addr) +{ + return rpc_hf_read_reg(addr, RPC_HF_SIZE_16BIT); +} + +u32 flash_read32(void *addr) +{ + return rpc_hf_read_reg(addr, RPC_HF_SIZE_32BIT); +} + +u64 flash_read64(void *addr) +{ + return rpc_hf_read_reg(addr, RPC_HF_SIZE_64BIT); +} + +static int rpc_hf_bind(struct udevice *parent) +{ + const void *fdt = gd->fdt_blob; + ofnode node; + int ret, off; + + /* + * Check if there are any SPI NOR child nodes, if so, do NOT bind + * as this controller will be operated by the QSPI driver instead. + */ + dev_for_each_subnode(node, parent) { + off = ofnode_to_offset(node); + + ret = fdt_node_check_compatible(fdt, off, "spi-flash"); + if (!ret) + return -ENODEV; + + ret = fdt_node_check_compatible(fdt, off, "jedec,spi-nor"); + if (!ret) + return -ENODEV; + } + + return 0; +} + +static int rpc_hf_probe(struct udevice *dev) +{ + void *blob = (void *)gd->fdt_blob; + const fdt32_t *cell; + int node = dev_of_offset(dev); + int parent, addrc, sizec, len, ret; + struct clk clk; + phys_addr_t flash_base; + + parent = fdt_parent_offset(blob, node); + fdt_support_default_count_cells(blob, parent, &addrc, &sizec); + cell = fdt_getprop(blob, node, "reg", &len); + if (!cell) + return -ENOENT; + + if (addrc != 2 || sizec != 2) + return -EINVAL; + + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) { + dev_err(dev, "Failed to get RPC clock\n"); + return ret; + } + + ret = clk_enable(&clk); + clk_free(&clk); + if (ret) { + dev_err(dev, "Failed to enable RPC clock\n"); + return ret; + } + + rpc_base = fdt_translate_address(blob, node, cell); + flash_base = fdt_translate_address(blob, node, cell + addrc + sizec); + + flash_info[0].dev = dev; + flash_info[0].base = flash_base; + cfi_flash_num_flash_banks = 1; + gd->bd->bi_flashstart = flash_base; + + return 0; +} + +static const struct udevice_id rpc_hf_ids[] = { + { .compatible = "renesas,rpc" }, + {} +}; + +U_BOOT_DRIVER(rpc_hf) = { + .name = "rpc_hf", + .id = UCLASS_MTD, + .of_match = rpc_hf_ids, + .bind = rpc_hf_bind, + .probe = rpc_hf_probe, +}; diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 6ba255d676..4484cf8195 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -135,17 +135,4 @@ config SPI_FLASH_MTD If unsure, say N -if SPL - -config SPL_SPI_SUNXI - bool "Support for SPI Flash on Allwinner SoCs in SPL" - depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 || MACH_SUN50I - select SPL_SPI_FLASH_SUPPORT - ---help--- - Enable support for SPI Flash. This option allows SPL to read from - sunxi SPI Flash. It uses the same method as the boot ROM, so does - not need any extra configuration. - -endif - endmenu # menu "SPI Flash Support" diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index fcda023412..4be6e9b15f 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_DM_SPI_FLASH) += sf-uclass.o ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o -obj-$(CONFIG_SPL_SPI_SUNXI) += sunxi_spi_spl.o endif obj-$(CONFIG_SPI_FLASH) += sf_probe.o spi_flash.o spi_flash_ids.o sf.o diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 294d9f9d79..2e61685d3e 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -320,7 +320,7 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) erase_size = flash->erase_size; if (offset % erase_size || len % erase_size) { - debug("SF: Erase offset/length not multiple of erase size\n"); + printf("SF: Erase offset/length not multiple of erase size\n"); return -1; } diff --git a/drivers/mtd/spi/sunxi_spi_spl.c b/drivers/mtd/spi/sunxi_spi_spl.c deleted file mode 100644 index fa22981316..0000000000 --- a/drivers/mtd/spi/sunxi_spi_spl.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <spl.h> -#include <asm/gpio.h> -#include <asm/io.h> -#include <linux/libfdt.h> - -#ifdef CONFIG_SPL_OS_BOOT -#error CONFIG_SPL_OS_BOOT is not supported yet -#endif - -/* - * This is a very simple U-Boot image loading implementation, trying to - * replicate what the boot ROM is doing when loading the SPL. Because we - * know the exact pins where the SPI Flash is connected and also know - * that the Read Data Bytes (03h) command is supported, the hardware - * configuration is very simple and we don't need the extra flexibility - * of the SPI framework. Moreover, we rely on the default settings of - * the SPI controler hardware registers and only adjust what needs to - * be changed. This is good for the code size and this implementation - * adds less than 400 bytes to the SPL. - * - * There are two variants of the SPI controller in Allwinner SoCs: - * A10/A13/A20 (sun4i variant) and everything else (sun6i variant). - * Both of them are supported. - * - * The pin mixing part is SoC specific and only A10/A13/A20/H3/A64 are - * supported at the moment. - */ - -/*****************************************************************************/ -/* SUN4I variant of the SPI controller */ -/*****************************************************************************/ - -#define SUN4I_SPI0_CCTL (0x01C05000 + 0x1C) -#define SUN4I_SPI0_CTL (0x01C05000 + 0x08) -#define SUN4I_SPI0_RX (0x01C05000 + 0x00) -#define SUN4I_SPI0_TX (0x01C05000 + 0x04) -#define SUN4I_SPI0_FIFO_STA (0x01C05000 + 0x28) -#define SUN4I_SPI0_BC (0x01C05000 + 0x20) -#define SUN4I_SPI0_TC (0x01C05000 + 0x24) - -#define SUN4I_CTL_ENABLE BIT(0) -#define SUN4I_CTL_MASTER BIT(1) -#define SUN4I_CTL_TF_RST BIT(8) -#define SUN4I_CTL_RF_RST BIT(9) -#define SUN4I_CTL_XCH BIT(10) - -/*****************************************************************************/ -/* SUN6I variant of the SPI controller */ -/*****************************************************************************/ - -#define SUN6I_SPI0_CCTL (0x01C68000 + 0x24) -#define SUN6I_SPI0_GCR (0x01C68000 + 0x04) -#define SUN6I_SPI0_TCR (0x01C68000 + 0x08) -#define SUN6I_SPI0_FIFO_STA (0x01C68000 + 0x1C) -#define SUN6I_SPI0_MBC (0x01C68000 + 0x30) -#define SUN6I_SPI0_MTC (0x01C68000 + 0x34) -#define SUN6I_SPI0_BCC (0x01C68000 + 0x38) -#define SUN6I_SPI0_TXD (0x01C68000 + 0x200) -#define SUN6I_SPI0_RXD (0x01C68000 + 0x300) - -#define SUN6I_CTL_ENABLE BIT(0) -#define SUN6I_CTL_MASTER BIT(1) -#define SUN6I_CTL_SRST BIT(31) -#define SUN6I_TCR_XCH BIT(31) - -/*****************************************************************************/ - -#define CCM_AHB_GATING0 (0x01C20000 + 0x60) -#define CCM_SPI0_CLK (0x01C20000 + 0xA0) -#define SUN6I_BUS_SOFT_RST_REG0 (0x01C20000 + 0x2C0) - -#define AHB_RESET_SPI0_SHIFT 20 -#define AHB_GATE_OFFSET_SPI0 20 - -#define SPI0_CLK_DIV_BY_2 0x1000 -#define SPI0_CLK_DIV_BY_4 0x1001 - -/*****************************************************************************/ - -/* - * Allwinner A10/A20 SoCs were using pins PC0,PC1,PC2,PC23 for booting - * from SPI Flash, everything else is using pins PC0,PC1,PC2,PC3. - */ -static void spi0_pinmux_setup(unsigned int pin_function) -{ - unsigned int pin; - - for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(2); pin++) - sunxi_gpio_set_cfgpin(pin, pin_function); - - if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I)) - sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_function); - else - sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_function); -} - -/* - * Setup 6 MHz from OSC24M (because the BROM is doing the same). - */ -static void spi0_enable_clock(void) -{ - /* Deassert SPI0 reset on SUN6I */ - if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) - setbits_le32(SUN6I_BUS_SOFT_RST_REG0, - (1 << AHB_RESET_SPI0_SHIFT)); - - /* Open the SPI0 gate */ - setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0)); - - /* Divide by 4 */ - writel(SPI0_CLK_DIV_BY_4, IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) ? - SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL); - /* 24MHz from OSC24M */ - writel((1 << 31), CCM_SPI0_CLK); - - if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) { - /* Enable SPI in the master mode and do a soft reset */ - setbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER | - SUN6I_CTL_ENABLE | - SUN6I_CTL_SRST); - /* Wait for completion */ - while (readl(SUN6I_SPI0_GCR) & SUN6I_CTL_SRST) - ; - } else { - /* Enable SPI in the master mode and reset FIFO */ - setbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER | - SUN4I_CTL_ENABLE | - SUN4I_CTL_TF_RST | - SUN4I_CTL_RF_RST); - } -} - -static void spi0_disable_clock(void) -{ - /* Disable the SPI0 controller */ - if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) - clrbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER | - SUN6I_CTL_ENABLE); - else - clrbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER | - SUN4I_CTL_ENABLE); - - /* Disable the SPI0 clock */ - writel(0, CCM_SPI0_CLK); - - /* Close the SPI0 gate */ - clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0)); - - /* Assert SPI0 reset on SUN6I */ - if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) - clrbits_le32(SUN6I_BUS_SOFT_RST_REG0, - (1 << AHB_RESET_SPI0_SHIFT)); -} - -static void spi0_init(void) -{ - unsigned int pin_function = SUNXI_GPC_SPI0; - - if (IS_ENABLED(CONFIG_MACH_SUN50I)) - pin_function = SUN50I_GPC_SPI0; - - spi0_pinmux_setup(pin_function); - spi0_enable_clock(); -} - -static void spi0_deinit(void) -{ - /* New SoCs can disable pins, older could only set them as input */ - unsigned int pin_function = SUNXI_GPIO_INPUT; - if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) - pin_function = SUNXI_GPIO_DISABLE; - - spi0_disable_clock(); - spi0_pinmux_setup(pin_function); -} - -/*****************************************************************************/ - -#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */ - -static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize, - ulong spi_ctl_reg, - ulong spi_ctl_xch_bitmask, - ulong spi_fifo_reg, - ulong spi_tx_reg, - ulong spi_rx_reg, - ulong spi_bc_reg, - ulong spi_tc_reg, - ulong spi_bcc_reg) -{ - writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */ - writel(4, spi_tc_reg); /* Transfer counter (bytes to send) */ - if (spi_bcc_reg) - writel(4, spi_bcc_reg); /* SUN6I also needs this */ - - /* Send the Read Data Bytes (03h) command header */ - writeb(0x03, spi_tx_reg); - writeb((u8)(addr >> 16), spi_tx_reg); - writeb((u8)(addr >> 8), spi_tx_reg); - writeb((u8)(addr), spi_tx_reg); - - /* Start the data transfer */ - setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask); - - /* Wait until everything is received in the RX FIFO */ - while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize) - ; - - /* Skip 4 bytes */ - readl(spi_rx_reg); - - /* Read the data */ - while (bufsize-- > 0) - *buf++ = readb(spi_rx_reg); - - /* tSHSL time is up to 100 ns in various SPI flash datasheets */ - udelay(1); -} - -static void spi0_read_data(void *buf, u32 addr, u32 len) -{ - u8 *buf8 = buf; - u32 chunk_len; - - while (len > 0) { - chunk_len = len; - if (chunk_len > SPI_READ_MAX_SIZE) - chunk_len = SPI_READ_MAX_SIZE; - - if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) { - sunxi_spi0_read_data(buf8, addr, chunk_len, - SUN6I_SPI0_TCR, - SUN6I_TCR_XCH, - SUN6I_SPI0_FIFO_STA, - SUN6I_SPI0_TXD, - SUN6I_SPI0_RXD, - SUN6I_SPI0_MBC, - SUN6I_SPI0_MTC, - SUN6I_SPI0_BCC); - } else { - sunxi_spi0_read_data(buf8, addr, chunk_len, - SUN4I_SPI0_CTL, - SUN4I_CTL_XCH, - SUN4I_SPI0_FIFO_STA, - SUN4I_SPI0_TX, - SUN4I_SPI0_RX, - SUN4I_SPI0_BC, - SUN4I_SPI0_TC, - 0); - } - - len -= chunk_len; - buf8 += chunk_len; - addr += chunk_len; - } -} - -static ulong spi_load_read(struct spl_load_info *load, ulong sector, - ulong count, void *buf) -{ - spi0_read_data(buf, sector, count); - - return count; -} - -/*****************************************************************************/ - -static int spl_spi_load_image(struct spl_image_info *spl_image, - struct spl_boot_device *bootdev) -{ - int ret = 0; - struct image_header *header; - header = (struct image_header *)(CONFIG_SYS_TEXT_BASE); - - spi0_init(); - - spi0_read_data((void *)header, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40); - - if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && - image_get_magic(header) == FDT_MAGIC) { - struct spl_load_info load; - - debug("Found FIT image\n"); - load.dev = NULL; - load.priv = NULL; - load.filename = NULL; - load.bl_len = 1; - load.read = spi_load_read; - ret = spl_load_simple_fit(spl_image, &load, - CONFIG_SYS_SPI_U_BOOT_OFFS, header); - } else { - ret = spl_parse_image_header(spl_image, header); - if (ret) - return ret; - - spi0_read_data((void *)spl_image->load_addr, - CONFIG_SYS_SPI_U_BOOT_OFFS, spl_image->size); - } - - spi0_deinit(); - - return ret; -} -/* Use priorty 0 to override the default if it happens to be linked in */ -SPL_LOAD_IMAGE_METHOD("sunxi SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image); diff --git a/drivers/mtd/stm32_flash.c b/drivers/mtd/stm32_flash.c index 472499d83c..a82814272e 100644 --- a/drivers/mtd/stm32_flash.c +++ b/drivers/mtd/stm32_flash.c @@ -12,7 +12,7 @@ flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; -#define STM32_FLASH ((struct stm32_flash_regs *)FLASH_CNTL_BASE) +#define STM32_FLASH ((struct stm32_flash_regs *)STM32_FLASH_CNTL_BASE) void stm32_flash_latency_cfg(int latency) { diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index caa5197df5..cf84783356 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -1,5 +1,12 @@ menu "UBI support" +config CONFIG_UBI_SILENCE_MSG + bool "UBI silence verbose messages" + default ENV_IS_IN_UBI + help + Make the verbose messages from UBI stop printing. This leaves + warnings and errors enabled. + config MTD_UBI bool "Enable UBI - Unsorted block images" select CRC32 diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index de1947ccc1..3a374d8871 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1,4 +1,5 @@ source "drivers/net/phy/Kconfig" +source "drivers/net/pfe_eth/Kconfig" config DM_ETH bool "Enable Driver Model for Ethernet drivers" @@ -10,6 +11,13 @@ config DM_ETH This is currently implemented in net/eth.c Look in include/net.h for details. +config DRIVER_TI_CPSW + bool "TI Common Platform Ethernet Switch" + select PHYLIB + help + This driver supports the TI three port switch gigabit ethernet + subsystem found in the TI SoCs. + menuconfig NETDEVICES bool "Network device support" depends on NET @@ -147,9 +155,20 @@ config ETHOC help This MAC is present in OpenRISC and Xtensa XTFPGA boards. +config FEC_MXC_SHARE_MDIO + bool "Share the MDIO bus for FEC controller" + depends on FEC_MXC + +config FEC_MXC_MDIO_BASE + hex "MDIO base address for the FEC controller" + depends on FEC_MXC_SHARE_MDIO + help + This specifies the MDIO registers base address. It is used when + two FEC controllers share MDIO bus. + config FEC_MXC bool "FEC Ethernet controller" - depends on MX5 || MX6 + depends on MX5 || MX6 || MX7 help This driver supports the 10/100 Fast Ethernet controller for NXP i.MX processors. @@ -330,7 +349,7 @@ config RENESAS_RAVB config MPC8XX_FEC bool "Fast Ethernet Controller on MPC8XX" - depends on 8xx + depends on MPC8xx select MII help This driver implements support for the Fast Ethernet Controller @@ -410,4 +429,11 @@ config SYS_DPAA_QBMAN help QBman fixups to allow deep sleep in DPAA 1 SOCs +config TSEC_ENET + select PHYLIB + bool "Enable Three-Speed Ethernet Controller" + help + This driver implements support for the (Enhanced) Three-Speed + Ethernet Controller found on Freescale SoCs. + endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 4a16c62bac..2687fbbdb2 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_E1000_SPI) += e1000_spi.o obj-$(CONFIG_EEPRO100) += eepro100.o obj-$(CONFIG_SUN4I_EMAC) += sunxi_emac.o obj-$(CONFIG_SUN8I_EMAC) += sun8i_emac.o -obj-$(CONFIG_ENC28J60) += enc28j60.o obj-$(CONFIG_EP93XX) += ep93xx_eth.o obj-$(CONFIG_ETHOC) += ethoc.o obj-$(CONFIG_FEC_MXC) += fec_mxc.o @@ -73,3 +72,4 @@ obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o obj-$(CONFIG_VSC9953) += vsc9953.o obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o +obj-$(CONFIG_FSL_PFE) += pfe_eth/ diff --git a/drivers/net/cpsw-common.c b/drivers/net/cpsw-common.c index 0dc83ab820..7bd312a6c0 100644 --- a/drivers/net/cpsw-common.c +++ b/drivers/net/cpsw-common.c @@ -8,6 +8,7 @@ #include <common.h> #include <dm.h> +#include <environment.h> #include <fdt_support.h> #include <asm/io.h> #include <cpsw.h> diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index b72258f83b..e2395dbeb9 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -949,7 +949,7 @@ static int _cpsw_recv(struct cpsw_priv *priv, uchar **pkt) { void *buffer; int len; - int ret = -EAGAIN; + int ret; ret = cpdma_process(priv, &priv->rx_chan, &buffer, &len); if (ret < 0) diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c deleted file mode 100644 index 588a84d7a9..0000000000 --- a/drivers/net/enc28j60.c +++ /dev/null @@ -1,959 +0,0 @@ -/* - * (C) Copyright 2010 - * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de - * Martin Krause, Martin.Krause@tqs.de - * reworked original enc28j60.c - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <net.h> -#include <spi.h> -#include <malloc.h> -#include <netdev.h> -#include <miiphy.h> -#include "enc28j60.h" - -/* - * IMPORTANT: spi_claim_bus() and spi_release_bus() - * are called at begin and end of each of the following functions: - * enc_miiphy_read(), enc_miiphy_write(), enc_write_hwaddr(), - * enc_init(), enc_recv(), enc_send(), enc_halt() - * ALL other functions assume that the bus has already been claimed! - * Since net_process_received_packet() might call enc_send() in return, the bus - * must be released, net_process_received_packet() called and claimed again. - */ - -/* - * Controller memory layout. - * We only allow 1 frame for transmission and reserve the rest - * for reception to handle as many broadcast packets as possible. - * Also use the memory from 0x0000 for receiver buffer. See errata pt. 5 - * 0x0000 - 0x19ff 6656 bytes receive buffer - * 0x1a00 - 0x1fff 1536 bytes transmit buffer = - * control(1)+frame(1518)+status(7)+reserve(10). - */ -#define ENC_RX_BUF_START 0x0000 -#define ENC_RX_BUF_END 0x19ff -#define ENC_TX_BUF_START 0x1a00 -#define ENC_TX_BUF_END 0x1fff -#define ENC_MAX_FRM_LEN 1518 -#define RX_RESET_COUNTER 1000 - -/* - * For non data transfer functions, like phy read/write, set hwaddr, init - * we do not need a full, time consuming init including link ready wait. - * This enum helps to bring the chip through the minimum necessary inits. - */ -enum enc_initstate {none=0, setupdone, linkready}; -typedef struct enc_device { - struct eth_device *dev; /* back pointer */ - struct spi_slave *slave; - int rx_reset_counter; - u16 next_pointer; - u8 bank; /* current bank in enc28j60 */ - enum enc_initstate initstate; -} enc_dev_t; - -/* - * enc_bset: set bits in a common register - * enc_bclr: clear bits in a common register - * - * making the reg parameter u8 will give a compile time warning if the - * functions are called with a register not accessible in all Banks - */ -static void enc_bset(enc_dev_t *enc, const u8 reg, const u8 data) -{ - u8 dout[2]; - - dout[0] = CMD_BFS(reg); - dout[1] = data; - spi_xfer(enc->slave, 2 * 8, dout, NULL, - SPI_XFER_BEGIN | SPI_XFER_END); -} - -static void enc_bclr(enc_dev_t *enc, const u8 reg, const u8 data) -{ - u8 dout[2]; - - dout[0] = CMD_BFC(reg); - dout[1] = data; - spi_xfer(enc->slave, 2 * 8, dout, NULL, - SPI_XFER_BEGIN | SPI_XFER_END); -} - -/* - * high byte of the register contains bank number: - * 0: no bank switch necessary - * 1: switch to bank 0 - * 2: switch to bank 1 - * 3: switch to bank 2 - * 4: switch to bank 3 - */ -static void enc_set_bank(enc_dev_t *enc, const u16 reg) -{ - u8 newbank = reg >> 8; - - if (newbank == 0 || newbank == enc->bank) - return; - switch (newbank) { - case 1: - enc_bclr(enc, CTL_REG_ECON1, - ENC_ECON1_BSEL0 | ENC_ECON1_BSEL1); - break; - case 2: - enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_BSEL0); - enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_BSEL1); - break; - case 3: - enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_BSEL0); - enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_BSEL1); - break; - case 4: - enc_bset(enc, CTL_REG_ECON1, - ENC_ECON1_BSEL0 | ENC_ECON1_BSEL1); - break; - } - enc->bank = newbank; -} - -/* - * local functions to access SPI - * - * reg: register inside ENC28J60 - * data: 8/16 bits to write - * c: number of retries - * - * enc_r8: read 8 bits - * enc_r16: read 16 bits - * enc_w8: write 8 bits - * enc_w16: write 16 bits - * enc_w8_retry: write 8 bits, verify and retry - * enc_rbuf: read from ENC28J60 into buffer - * enc_wbuf: write from buffer into ENC28J60 - */ - -/* - * MAC and MII registers need a 3 byte SPI transfer to read, - * all other registers need a 2 byte SPI transfer. - */ -static int enc_reg2nbytes(const u16 reg) -{ - /* check if MAC or MII register */ - return ((reg >= CTL_REG_MACON1 && reg <= CTL_REG_MIRDH) || - (reg >= CTL_REG_MAADR1 && reg <= CTL_REG_MAADR4) || - (reg == CTL_REG_MISTAT)) ? 3 : 2; -} - -/* - * Read a byte register - */ -static u8 enc_r8(enc_dev_t *enc, const u16 reg) -{ - u8 dout[3]; - u8 din[3]; - int nbytes = enc_reg2nbytes(reg); - - enc_set_bank(enc, reg); - dout[0] = CMD_RCR(reg); - spi_xfer(enc->slave, nbytes * 8, dout, din, - SPI_XFER_BEGIN | SPI_XFER_END); - return din[nbytes-1]; -} - -/* - * Read a L/H register pair and return a word. - * Must be called with the L register's address. - */ -static u16 enc_r16(enc_dev_t *enc, const u16 reg) -{ - u8 dout[3]; - u8 din[3]; - u16 result; - int nbytes = enc_reg2nbytes(reg); - - enc_set_bank(enc, reg); - dout[0] = CMD_RCR(reg); - spi_xfer(enc->slave, nbytes * 8, dout, din, - SPI_XFER_BEGIN | SPI_XFER_END); - result = din[nbytes-1]; - dout[0]++; /* next register */ - spi_xfer(enc->slave, nbytes * 8, dout, din, - SPI_XFER_BEGIN | SPI_XFER_END); - result |= din[nbytes-1] << 8; - return result; -} - -/* - * Write a byte register - */ -static void enc_w8(enc_dev_t *enc, const u16 reg, const u8 data) -{ - u8 dout[2]; - - enc_set_bank(enc, reg); - dout[0] = CMD_WCR(reg); - dout[1] = data; - spi_xfer(enc->slave, 2 * 8, dout, NULL, - SPI_XFER_BEGIN | SPI_XFER_END); -} - -/* - * Write a L/H register pair. - * Must be called with the L register's address. - */ -static void enc_w16(enc_dev_t *enc, const u16 reg, const u16 data) -{ - u8 dout[2]; - - enc_set_bank(enc, reg); - dout[0] = CMD_WCR(reg); - dout[1] = data; - spi_xfer(enc->slave, 2 * 8, dout, NULL, - SPI_XFER_BEGIN | SPI_XFER_END); - dout[0]++; /* next register */ - dout[1] = data >> 8; - spi_xfer(enc->slave, 2 * 8, dout, NULL, - SPI_XFER_BEGIN | SPI_XFER_END); -} - -/* - * Write a byte register, verify and retry - */ -static void enc_w8_retry(enc_dev_t *enc, const u16 reg, const u8 data, const int c) -{ - u8 dout[2]; - u8 readback; - int i; - - enc_set_bank(enc, reg); - for (i = 0; i < c; i++) { - dout[0] = CMD_WCR(reg); - dout[1] = data; - spi_xfer(enc->slave, 2 * 8, dout, NULL, - SPI_XFER_BEGIN | SPI_XFER_END); - readback = enc_r8(enc, reg); - if (readback == data) - break; - /* wait 1ms */ - udelay(1000); - } - if (i == c) { - printf("%s: write reg 0x%03x failed\n", enc->dev->name, reg); - } -} - -/* - * Read ENC RAM into buffer - */ -static void enc_rbuf(enc_dev_t *enc, const u16 length, u8 *buf) -{ - u8 dout[1]; - - dout[0] = CMD_RBM; - spi_xfer(enc->slave, 8, dout, NULL, SPI_XFER_BEGIN); - spi_xfer(enc->slave, length * 8, NULL, buf, SPI_XFER_END); -#ifdef DEBUG - puts("Rx:\n"); - print_buffer(0, buf, 1, length, 0); -#endif -} - -/* - * Write buffer into ENC RAM - */ -static void enc_wbuf(enc_dev_t *enc, const u16 length, const u8 *buf, const u8 control) -{ - u8 dout[2]; - dout[0] = CMD_WBM; - dout[1] = control; - spi_xfer(enc->slave, 2 * 8, dout, NULL, SPI_XFER_BEGIN); - spi_xfer(enc->slave, length * 8, buf, NULL, SPI_XFER_END); -#ifdef DEBUG - puts("Tx:\n"); - print_buffer(0, buf, 1, length, 0); -#endif -} - -/* - * Try to claim the SPI bus. - * Print error message on failure. - */ -static int enc_claim_bus(enc_dev_t *enc) -{ - int rc = spi_claim_bus(enc->slave); - if (rc) - printf("%s: failed to claim SPI bus\n", enc->dev->name); - return rc; -} - -/* - * Release previously claimed SPI bus. - * This function is mainly for symmetry to enc_claim_bus(). - * Let the toolchain decide to inline it... - */ -static void enc_release_bus(enc_dev_t *enc) -{ - spi_release_bus(enc->slave); -} - -/* - * Read PHY register - */ -static u16 enc_phy_read(enc_dev_t *enc, const u8 addr) -{ - uint64_t etime; - u8 status; - - enc_w8(enc, CTL_REG_MIREGADR, addr); - enc_w8(enc, CTL_REG_MICMD, ENC_MICMD_MIIRD); - /* 1 second timeout - only happens on hardware problem */ - etime = get_ticks() + get_tbclk(); - /* poll MISTAT.BUSY bit until operation is complete */ - do - { - status = enc_r8(enc, CTL_REG_MISTAT); - } while (get_ticks() <= etime && (status & ENC_MISTAT_BUSY)); - if (status & ENC_MISTAT_BUSY) { - printf("%s: timeout reading phy\n", enc->dev->name); - return 0; - } - enc_w8(enc, CTL_REG_MICMD, 0); - return enc_r16(enc, CTL_REG_MIRDL); -} - -/* - * Write PHY register - */ -static void enc_phy_write(enc_dev_t *enc, const u8 addr, const u16 data) -{ - uint64_t etime; - u8 status; - - enc_w8(enc, CTL_REG_MIREGADR, addr); - enc_w16(enc, CTL_REG_MIWRL, data); - /* 1 second timeout - only happens on hardware problem */ - etime = get_ticks() + get_tbclk(); - /* poll MISTAT.BUSY bit until operation is complete */ - do - { - status = enc_r8(enc, CTL_REG_MISTAT); - } while (get_ticks() <= etime && (status & ENC_MISTAT_BUSY)); - if (status & ENC_MISTAT_BUSY) { - printf("%s: timeout writing phy\n", enc->dev->name); - return; - } -} - -/* - * Verify link status, wait if necessary - * - * Note: with a 10 MBit/s only PHY there is no autonegotiation possible, - * half/full duplex is a pure setup matter. For the time being, this driver - * will setup in half duplex mode only. - */ -static int enc_phy_link_wait(enc_dev_t *enc) -{ - u16 status; - int duplex; - uint64_t etime; - -#ifdef CONFIG_ENC_SILENTLINK - /* check if we have a link, then just return */ - status = enc_phy_read(enc, PHY_REG_PHSTAT1); - if (status & ENC_PHSTAT1_LLSTAT) - return 0; -#endif - - /* wait for link with 1 second timeout */ - etime = get_ticks() + get_tbclk(); - while (get_ticks() <= etime) { - status = enc_phy_read(enc, PHY_REG_PHSTAT1); - if (status & ENC_PHSTAT1_LLSTAT) { - /* now we have a link */ - status = enc_phy_read(enc, PHY_REG_PHSTAT2); - duplex = (status & ENC_PHSTAT2_DPXSTAT) ? 1 : 0; - printf("%s: link up, 10Mbps %s-duplex\n", - enc->dev->name, duplex ? "full" : "half"); - return 0; - } - udelay(1000); - } - - /* timeout occurred */ - printf("%s: link down\n", enc->dev->name); - return 1; -} - -/* - * This function resets the receiver only. - */ -static void enc_reset_rx(enc_dev_t *enc) -{ - u8 econ1; - - econ1 = enc_r8(enc, CTL_REG_ECON1); - if ((econ1 & ENC_ECON1_RXRST) == 0) { - enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXRST); - enc->rx_reset_counter = RX_RESET_COUNTER; - } -} - -/* - * Reset receiver and reenable it. - */ -static void enc_reset_rx_call(enc_dev_t *enc) -{ - enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_RXRST); - enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXEN); -} - -/* - * Copy a packet from the receive ring and forward it to - * the protocol stack. - */ -static void enc_receive(enc_dev_t *enc) -{ - u8 *packet = (u8 *)net_rx_packets[0]; - u16 pkt_len; - u16 copy_len; - u16 status; - u8 pkt_cnt = 0; - u16 rxbuf_rdpt; - u8 hbuf[6]; - - enc_w16(enc, CTL_REG_ERDPTL, enc->next_pointer); - do { - enc_rbuf(enc, 6, hbuf); - enc->next_pointer = hbuf[0] | (hbuf[1] << 8); - pkt_len = hbuf[2] | (hbuf[3] << 8); - status = hbuf[4] | (hbuf[5] << 8); - debug("next_pointer=$%04x pkt_len=%u status=$%04x\n", - enc->next_pointer, pkt_len, status); - if (pkt_len <= ENC_MAX_FRM_LEN) - copy_len = pkt_len; - else - copy_len = 0; - if ((status & (1L << 7)) == 0) /* check Received Ok bit */ - copy_len = 0; - /* check if next pointer is resonable */ - if (enc->next_pointer >= ENC_TX_BUF_START) - copy_len = 0; - if (copy_len > 0) { - enc_rbuf(enc, copy_len, packet); - } - /* advance read pointer to next pointer */ - enc_w16(enc, CTL_REG_ERDPTL, enc->next_pointer); - /* decrease packet counter */ - enc_bset(enc, CTL_REG_ECON2, ENC_ECON2_PKTDEC); - /* - * Only odd values should be written to ERXRDPTL, - * see errata B4 pt.13 - */ - rxbuf_rdpt = enc->next_pointer - 1; - if ((rxbuf_rdpt < enc_r16(enc, CTL_REG_ERXSTL)) || - (rxbuf_rdpt > enc_r16(enc, CTL_REG_ERXNDL))) { - enc_w16(enc, CTL_REG_ERXRDPTL, - enc_r16(enc, CTL_REG_ERXNDL)); - } else { - enc_w16(enc, CTL_REG_ERXRDPTL, rxbuf_rdpt); - } - /* read pktcnt */ - pkt_cnt = enc_r8(enc, CTL_REG_EPKTCNT); - if (copy_len == 0) { - (void)enc_r8(enc, CTL_REG_EIR); - enc_reset_rx(enc); - printf("%s: receive copy_len=0\n", enc->dev->name); - continue; - } - /* - * Because net_process_received_packet() might call enc_send(), - * we need to release the SPI bus, call - * net_process_received_packet(), reclaim the bus. - */ - enc_release_bus(enc); - net_process_received_packet(packet, pkt_len); - if (enc_claim_bus(enc)) - return; - (void)enc_r8(enc, CTL_REG_EIR); - } while (pkt_cnt); - /* Use EPKTCNT not EIR.PKTIF flag, see errata pt. 6 */ -} - -/* - * Poll for completely received packets. - */ -static void enc_poll(enc_dev_t *enc) -{ - u8 eir_reg; - u8 pkt_cnt; - - (void)enc_r8(enc, CTL_REG_ESTAT); - eir_reg = enc_r8(enc, CTL_REG_EIR); - if (eir_reg & ENC_EIR_TXIF) { - /* clear TXIF bit in EIR */ - enc_bclr(enc, CTL_REG_EIR, ENC_EIR_TXIF); - } - /* We have to use pktcnt and not pktif bit, see errata pt. 6 */ - pkt_cnt = enc_r8(enc, CTL_REG_EPKTCNT); - if (pkt_cnt > 0) { - if ((eir_reg & ENC_EIR_PKTIF) == 0) { - debug("enc_poll: pkt cnt > 0, but pktif not set\n"); - } - enc_receive(enc); - /* - * clear PKTIF bit in EIR, this should not need to be done - * but it seems like we get problems if we do not - */ - enc_bclr(enc, CTL_REG_EIR, ENC_EIR_PKTIF); - } - if (eir_reg & ENC_EIR_RXERIF) { - printf("%s: rx error\n", enc->dev->name); - enc_bclr(enc, CTL_REG_EIR, ENC_EIR_RXERIF); - } - if (eir_reg & ENC_EIR_TXERIF) { - printf("%s: tx error\n", enc->dev->name); - enc_bclr(enc, CTL_REG_EIR, ENC_EIR_TXERIF); - } -} - -/* - * Completely Reset the ENC - */ -static void enc_reset(enc_dev_t *enc) -{ - u8 dout[1]; - - dout[0] = CMD_SRC; - spi_xfer(enc->slave, 8, dout, NULL, - SPI_XFER_BEGIN | SPI_XFER_END); - /* sleep 1 ms. See errata pt. 2 */ - udelay(1000); -} - -/* - * Initialisation data for most of the ENC registers - */ -static const u16 enc_initdata[] = { - /* - * Setup the buffer space. The reset values are valid for the - * other pointers. - * - * We shall not write to ERXST, see errata pt. 5. Instead we - * have to make sure that ENC_RX_BUS_START is 0. - */ - CTL_REG_ERXSTL, ENC_RX_BUF_START, - CTL_REG_ERXSTH, ENC_RX_BUF_START >> 8, - CTL_REG_ERXNDL, ENC_RX_BUF_END, - CTL_REG_ERXNDH, ENC_RX_BUF_END >> 8, - CTL_REG_ERDPTL, ENC_RX_BUF_START, - CTL_REG_ERDPTH, ENC_RX_BUF_START >> 8, - /* - * Set the filter to receive only good-CRC, unicast and broadcast - * frames. - * Note: some DHCP servers return their answers as broadcasts! - * So its unwise to remove broadcast from this. This driver - * might incur receiver overruns with packet loss on a broadcast - * flooded network. - */ - CTL_REG_ERXFCON, ENC_RFR_BCEN | ENC_RFR_UCEN | ENC_RFR_CRCEN, - - /* enable MAC to receive frames */ - CTL_REG_MACON1, - ENC_MACON1_MARXEN | ENC_MACON1_TXPAUS | ENC_MACON1_RXPAUS, - - /* configure pad, tx-crc and duplex */ - CTL_REG_MACON3, - ENC_MACON3_PADCFG0 | ENC_MACON3_TXCRCEN | - ENC_MACON3_FRMLNEN, - - /* Allow infinite deferals if the medium is continously busy */ - CTL_REG_MACON4, ENC_MACON4_DEFER, - - /* Late collisions occur beyond 63 bytes */ - CTL_REG_MACLCON2, 63, - - /* - * Set (low byte) Non-Back-to_Back Inter-Packet Gap. - * Recommended 0x12 - */ - CTL_REG_MAIPGL, 0x12, - - /* - * Set (high byte) Non-Back-to_Back Inter-Packet Gap. - * Recommended 0x0c for half-duplex. Nothing for full-duplex - */ - CTL_REG_MAIPGH, 0x0C, - - /* set maximum frame length */ - CTL_REG_MAMXFLL, ENC_MAX_FRM_LEN, - CTL_REG_MAMXFLH, ENC_MAX_FRM_LEN >> 8, - - /* - * Set MAC back-to-back inter-packet gap. - * Recommended 0x12 for half duplex - * and 0x15 for full duplex. - */ - CTL_REG_MABBIPG, 0x12, - - /* end of table */ - 0xffff -}; - -/* - * Wait for the XTAL oscillator to become ready - */ -static int enc_clock_wait(enc_dev_t *enc) -{ - uint64_t etime; - - /* one second timeout */ - etime = get_ticks() + get_tbclk(); - - /* - * Wait for CLKRDY to become set (i.e., check that we can - * communicate with the ENC) - */ - do - { - if (enc_r8(enc, CTL_REG_ESTAT) & ENC_ESTAT_CLKRDY) - return 0; - } while (get_ticks() <= etime); - - printf("%s: timeout waiting for CLKRDY\n", enc->dev->name); - return -1; -} - -/* - * Write the MAC address into the ENC - */ -static int enc_write_macaddr(enc_dev_t *enc) -{ - unsigned char *p = enc->dev->enetaddr; - - enc_w8_retry(enc, CTL_REG_MAADR5, *p++, 5); - enc_w8_retry(enc, CTL_REG_MAADR4, *p++, 5); - enc_w8_retry(enc, CTL_REG_MAADR3, *p++, 5); - enc_w8_retry(enc, CTL_REG_MAADR2, *p++, 5); - enc_w8_retry(enc, CTL_REG_MAADR1, *p++, 5); - enc_w8_retry(enc, CTL_REG_MAADR0, *p, 5); - return 0; -} - -/* - * Setup most of the ENC registers - */ -static int enc_setup(enc_dev_t *enc) -{ - u16 phid1 = 0; - u16 phid2 = 0; - const u16 *tp; - - /* reset enc struct values */ - enc->next_pointer = ENC_RX_BUF_START; - enc->rx_reset_counter = RX_RESET_COUNTER; - enc->bank = 0xff; /* invalidate current bank in enc28j60 */ - - /* verify PHY identification */ - phid1 = enc_phy_read(enc, PHY_REG_PHID1); - phid2 = enc_phy_read(enc, PHY_REG_PHID2) & ENC_PHID2_MASK; - if (phid1 != ENC_PHID1_VALUE || phid2 != ENC_PHID2_VALUE) { - printf("%s: failed to identify PHY. Found %04x:%04x\n", - enc->dev->name, phid1, phid2); - return -1; - } - - /* now program registers */ - for (tp = enc_initdata; *tp != 0xffff; tp += 2) - enc_w8_retry(enc, tp[0], tp[1], 10); - - /* - * Prevent automatic loopback of data beeing transmitted by setting - * ENC_PHCON2_HDLDIS - */ - enc_phy_write(enc, PHY_REG_PHCON2, (1<<8)); - - /* - * LEDs configuration - * LEDA: LACFG = 0100 -> display link status - * LEDB: LBCFG = 0111 -> display TX & RX activity - * STRCH = 1 -> LED pulses - */ - enc_phy_write(enc, PHY_REG_PHLCON, 0x0472); - - /* Reset PDPXMD-bit => half duplex */ - enc_phy_write(enc, PHY_REG_PHCON1, 0); - - return 0; -} - -/* - * Check if ENC has been initialized. - * If not, try to initialize it. - * Remember initialized state in struct. - */ -static int enc_initcheck(enc_dev_t *enc, const enum enc_initstate requiredstate) -{ - if (enc->initstate >= requiredstate) - return 0; - - if (enc->initstate < setupdone) { - /* Initialize the ENC only */ - enc_reset(enc); - /* if any of functions fails, skip the rest and return an error */ - if (enc_clock_wait(enc) || enc_setup(enc) || enc_write_macaddr(enc)) { - return -1; - } - enc->initstate = setupdone; - } - /* if that's all we need, return here */ - if (enc->initstate >= requiredstate) - return 0; - - /* now wait for link ready condition */ - if (enc_phy_link_wait(enc)) { - return -1; - } - enc->initstate = linkready; - return 0; -} - -#if defined(CONFIG_CMD_MII) -/* - * Read a PHY register. - * - * This function is registered with miiphy_register(). - */ -int enc_miiphy_read(struct mii_dev *bus, int phy_adr, int devad, int reg) -{ - u16 value = 0; - struct eth_device *dev = eth_get_dev_by_name(bus->name); - enc_dev_t *enc; - - if (!dev || phy_adr != 0) - return -1; - - enc = dev->priv; - if (enc_claim_bus(enc)) - return -1; - if (enc_initcheck(enc, setupdone)) { - enc_release_bus(enc); - return -1; - } - value = enc_phy_read(enc, reg); - enc_release_bus(enc); - return value; -} - -/* - * Write a PHY register. - * - * This function is registered with miiphy_register(). - */ -int enc_miiphy_write(struct mii_dev *bus, int phy_adr, int devad, int reg, - u16 value) -{ - struct eth_device *dev = eth_get_dev_by_name(bus->name); - enc_dev_t *enc; - - if (!dev || phy_adr != 0) - return -1; - - enc = dev->priv; - if (enc_claim_bus(enc)) - return -1; - if (enc_initcheck(enc, setupdone)) { - enc_release_bus(enc); - return -1; - } - enc_phy_write(enc, reg, value); - enc_release_bus(enc); - return 0; -} -#endif - -/* - * Write hardware (MAC) address. - * - * This function entered into eth_device structure. - */ -static int enc_write_hwaddr(struct eth_device *dev) -{ - enc_dev_t *enc = dev->priv; - - if (enc_claim_bus(enc)) - return -1; - if (enc_initcheck(enc, setupdone)) { - enc_release_bus(enc); - return -1; - } - enc_release_bus(enc); - return 0; -} - -/* - * Initialize ENC28J60 for use. - * - * This function entered into eth_device structure. - */ -static int enc_init(struct eth_device *dev, bd_t *bis) -{ - enc_dev_t *enc = dev->priv; - - if (enc_claim_bus(enc)) - return -1; - if (enc_initcheck(enc, linkready)) { - enc_release_bus(enc); - return -1; - } - /* enable receive */ - enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXEN); - enc_release_bus(enc); - return 0; -} - -/* - * Check for received packets. - * - * This function entered into eth_device structure. - */ -static int enc_recv(struct eth_device *dev) -{ - enc_dev_t *enc = dev->priv; - - if (enc_claim_bus(enc)) - return -1; - if (enc_initcheck(enc, linkready)) { - enc_release_bus(enc); - return -1; - } - /* Check for dead receiver */ - if (enc->rx_reset_counter > 0) - enc->rx_reset_counter--; - else - enc_reset_rx_call(enc); - enc_poll(enc); - enc_release_bus(enc); - return 0; -} - -/* - * Send a packet. - * - * This function entered into eth_device structure. - * - * Should we wait here until we have a Link? Or shall we leave that to - * protocol retries? - */ -static int enc_send( - struct eth_device *dev, - void *packet, - int length) -{ - enc_dev_t *enc = dev->priv; - - if (enc_claim_bus(enc)) - return -1; - if (enc_initcheck(enc, linkready)) { - enc_release_bus(enc); - return -1; - } - /* setup transmit pointers */ - enc_w16(enc, CTL_REG_EWRPTL, ENC_TX_BUF_START); - enc_w16(enc, CTL_REG_ETXNDL, length + ENC_TX_BUF_START); - enc_w16(enc, CTL_REG_ETXSTL, ENC_TX_BUF_START); - /* write packet to ENC */ - enc_wbuf(enc, length, (u8 *) packet, 0x00); - /* - * Check that the internal transmit logic has not been altered - * by excessive collisions. Reset transmitter if so. - * See Errata B4 12 and 14. - */ - if (enc_r8(enc, CTL_REG_EIR) & ENC_EIR_TXERIF) { - enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_TXRST); - enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_TXRST); - } - enc_bclr(enc, CTL_REG_EIR, (ENC_EIR_TXERIF | ENC_EIR_TXIF)); - /* start transmitting */ - enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_TXRTS); - enc_release_bus(enc); - return 0; -} - -/* - * Finish use of ENC. - * - * This function entered into eth_device structure. - */ -static void enc_halt(struct eth_device *dev) -{ - enc_dev_t *enc = dev->priv; - - if (enc_claim_bus(enc)) - return; - /* Just disable receiver */ - enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_RXEN); - enc_release_bus(enc); -} - -/* - * This is the only exported function. - * - * It may be called several times with different bus:cs combinations. - */ -int enc28j60_initialize(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) -{ - struct eth_device *dev; - enc_dev_t *enc; - - /* try to allocate, check and clear eth_device object */ - dev = malloc(sizeof(*dev)); - if (!dev) { - return -1; - } - memset(dev, 0, sizeof(*dev)); - - /* try to allocate, check and clear enc_dev_t object */ - enc = malloc(sizeof(*enc)); - if (!enc) { - free(dev); - return -1; - } - memset(enc, 0, sizeof(*enc)); - - /* try to setup the SPI slave */ - enc->slave = spi_setup_slave(bus, cs, max_hz, mode); - if (!enc->slave) { - printf("enc28j60: invalid SPI device %i:%i\n", bus, cs); - free(enc); - free(dev); - return -1; - } - - enc->dev = dev; - /* now fill the eth_device object */ - dev->priv = enc; - dev->init = enc_init; - dev->halt = enc_halt; - dev->send = enc_send; - dev->recv = enc_recv; - dev->write_hwaddr = enc_write_hwaddr; - sprintf(dev->name, "enc%i.%i", bus, cs); - eth_register(dev); -#if defined(CONFIG_CMD_MII) - int retval; - struct mii_dev *mdiodev = mdio_alloc(); - if (!mdiodev) - return -ENOMEM; - strncpy(mdiodev->name, dev->name, MDIO_NAME_LEN); - mdiodev->read = enc_miiphy_read; - mdiodev->write = enc_miiphy_write; - - retval = mdio_register(mdiodev); - if (retval < 0) - return retval; -#endif - return 0; -} diff --git a/drivers/net/enc28j60.h b/drivers/net/enc28j60.h deleted file mode 100644 index 289e41288e..0000000000 --- a/drivers/net/enc28j60.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * (X) extracted from enc28j60.c - * Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#ifndef _enc28j60_h -#define _enc28j60_h - -/* - * SPI Commands - * - * Bits 7-5: Command - * Bits 4-0: Register - */ -#define CMD_RCR(x) (0x00+((x)&0x1f)) /* Read Control Register */ -#define CMD_RBM 0x3a /* Read Buffer Memory */ -#define CMD_WCR(x) (0x40+((x)&0x1f)) /* Write Control Register */ -#define CMD_WBM 0x7a /* Write Buffer Memory */ -#define CMD_BFS(x) (0x80+((x)&0x1f)) /* Bit Field Set */ -#define CMD_BFC(x) (0xa0+((x)&0x1f)) /* Bit Field Clear */ -#define CMD_SRC 0xff /* System Reset Command */ - -/* NEW: encode (bank number+1) in upper byte */ - -/* Common Control Registers accessible in all Banks */ -#define CTL_REG_EIE 0x01B -#define CTL_REG_EIR 0x01C -#define CTL_REG_ESTAT 0x01D -#define CTL_REG_ECON2 0x01E -#define CTL_REG_ECON1 0x01F - -/* Control Registers accessible in Bank 0 */ -#define CTL_REG_ERDPTL 0x100 -#define CTL_REG_ERDPTH 0x101 -#define CTL_REG_EWRPTL 0x102 -#define CTL_REG_EWRPTH 0x103 -#define CTL_REG_ETXSTL 0x104 -#define CTL_REG_ETXSTH 0x105 -#define CTL_REG_ETXNDL 0x106 -#define CTL_REG_ETXNDH 0x107 -#define CTL_REG_ERXSTL 0x108 -#define CTL_REG_ERXSTH 0x109 -#define CTL_REG_ERXNDL 0x10A -#define CTL_REG_ERXNDH 0x10B -#define CTL_REG_ERXRDPTL 0x10C -#define CTL_REG_ERXRDPTH 0x10D -#define CTL_REG_ERXWRPTL 0x10E -#define CTL_REG_ERXWRPTH 0x10F -#define CTL_REG_EDMASTL 0x110 -#define CTL_REG_EDMASTH 0x111 -#define CTL_REG_EDMANDL 0x112 -#define CTL_REG_EDMANDH 0x113 -#define CTL_REG_EDMADSTL 0x114 -#define CTL_REG_EDMADSTH 0x115 -#define CTL_REG_EDMACSL 0x116 -#define CTL_REG_EDMACSH 0x117 - -/* Control Registers accessible in Bank 1 */ -#define CTL_REG_EHT0 0x200 -#define CTL_REG_EHT1 0x201 -#define CTL_REG_EHT2 0x202 -#define CTL_REG_EHT3 0x203 -#define CTL_REG_EHT4 0x204 -#define CTL_REG_EHT5 0x205 -#define CTL_REG_EHT6 0x206 -#define CTL_REG_EHT7 0x207 -#define CTL_REG_EPMM0 0x208 -#define CTL_REG_EPMM1 0x209 -#define CTL_REG_EPMM2 0x20A -#define CTL_REG_EPMM3 0x20B -#define CTL_REG_EPMM4 0x20C -#define CTL_REG_EPMM5 0x20D -#define CTL_REG_EPMM6 0x20E -#define CTL_REG_EPMM7 0x20F -#define CTL_REG_EPMCSL 0x210 -#define CTL_REG_EPMCSH 0x211 -#define CTL_REG_EPMOL 0x214 -#define CTL_REG_EPMOH 0x215 -#define CTL_REG_EWOLIE 0x216 -#define CTL_REG_EWOLIR 0x217 -#define CTL_REG_ERXFCON 0x218 -#define CTL_REG_EPKTCNT 0x219 - -/* Control Registers accessible in Bank 2 */ -#define CTL_REG_MACON1 0x300 -#define CTL_REG_MACON2 0x301 -#define CTL_REG_MACON3 0x302 -#define CTL_REG_MACON4 0x303 -#define CTL_REG_MABBIPG 0x304 -#define CTL_REG_MAIPGL 0x306 -#define CTL_REG_MAIPGH 0x307 -#define CTL_REG_MACLCON1 0x308 -#define CTL_REG_MACLCON2 0x309 -#define CTL_REG_MAMXFLL 0x30A -#define CTL_REG_MAMXFLH 0x30B -#define CTL_REG_MAPHSUP 0x30D -#define CTL_REG_MICON 0x311 -#define CTL_REG_MICMD 0x312 -#define CTL_REG_MIREGADR 0x314 -#define CTL_REG_MIWRL 0x316 -#define CTL_REG_MIWRH 0x317 -#define CTL_REG_MIRDL 0x318 -#define CTL_REG_MIRDH 0x319 - -/* Control Registers accessible in Bank 3 */ -#define CTL_REG_MAADR1 0x400 -#define CTL_REG_MAADR0 0x401 -#define CTL_REG_MAADR3 0x402 -#define CTL_REG_MAADR2 0x403 -#define CTL_REG_MAADR5 0x404 -#define CTL_REG_MAADR4 0x405 -#define CTL_REG_EBSTSD 0x406 -#define CTL_REG_EBSTCON 0x407 -#define CTL_REG_EBSTCSL 0x408 -#define CTL_REG_EBSTCSH 0x409 -#define CTL_REG_MISTAT 0x40A -#define CTL_REG_EREVID 0x412 -#define CTL_REG_ECOCON 0x415 -#define CTL_REG_EFLOCON 0x417 -#define CTL_REG_EPAUSL 0x418 -#define CTL_REG_EPAUSH 0x419 - -/* PHY Register */ -#define PHY_REG_PHCON1 0x00 -#define PHY_REG_PHSTAT1 0x01 -#define PHY_REG_PHID1 0x02 -#define PHY_REG_PHID2 0x03 -#define PHY_REG_PHCON2 0x10 -#define PHY_REG_PHSTAT2 0x11 -#define PHY_REG_PHLCON 0x14 - -/* Receive Filter Register (ERXFCON) bits */ -#define ENC_RFR_UCEN 0x80 -#define ENC_RFR_ANDOR 0x40 -#define ENC_RFR_CRCEN 0x20 -#define ENC_RFR_PMEN 0x10 -#define ENC_RFR_MPEN 0x08 -#define ENC_RFR_HTEN 0x04 -#define ENC_RFR_MCEN 0x02 -#define ENC_RFR_BCEN 0x01 - -/* ECON1 Register Bits */ -#define ENC_ECON1_TXRST 0x80 -#define ENC_ECON1_RXRST 0x40 -#define ENC_ECON1_DMAST 0x20 -#define ENC_ECON1_CSUMEN 0x10 -#define ENC_ECON1_TXRTS 0x08 -#define ENC_ECON1_RXEN 0x04 -#define ENC_ECON1_BSEL1 0x02 -#define ENC_ECON1_BSEL0 0x01 - -/* ECON2 Register Bits */ -#define ENC_ECON2_AUTOINC 0x80 -#define ENC_ECON2_PKTDEC 0x40 -#define ENC_ECON2_PWRSV 0x20 -#define ENC_ECON2_VRPS 0x08 - -/* EIR Register Bits */ -#define ENC_EIR_PKTIF 0x40 -#define ENC_EIR_DMAIF 0x20 -#define ENC_EIR_LINKIF 0x10 -#define ENC_EIR_TXIF 0x08 -#define ENC_EIR_WOLIF 0x04 -#define ENC_EIR_TXERIF 0x02 -#define ENC_EIR_RXERIF 0x01 - -/* ESTAT Register Bits */ -#define ENC_ESTAT_INT 0x80 -#define ENC_ESTAT_LATECOL 0x10 -#define ENC_ESTAT_RXBUSY 0x04 -#define ENC_ESTAT_TXABRT 0x02 -#define ENC_ESTAT_CLKRDY 0x01 - -/* EIE Register Bits */ -#define ENC_EIE_INTIE 0x80 -#define ENC_EIE_PKTIE 0x40 -#define ENC_EIE_DMAIE 0x20 -#define ENC_EIE_LINKIE 0x10 -#define ENC_EIE_TXIE 0x08 -#define ENC_EIE_WOLIE 0x04 -#define ENC_EIE_TXERIE 0x02 -#define ENC_EIE_RXERIE 0x01 - -/* MACON1 Register Bits */ -#define ENC_MACON1_LOOPBK 0x10 -#define ENC_MACON1_TXPAUS 0x08 -#define ENC_MACON1_RXPAUS 0x04 -#define ENC_MACON1_PASSALL 0x02 -#define ENC_MACON1_MARXEN 0x01 - -/* MACON2 Register Bits */ -#define ENC_MACON2_MARST 0x80 -#define ENC_MACON2_RNDRST 0x40 -#define ENC_MACON2_MARXRST 0x08 -#define ENC_MACON2_RFUNRST 0x04 -#define ENC_MACON2_MATXRST 0x02 -#define ENC_MACON2_TFUNRST 0x01 - -/* MACON3 Register Bits */ -#define ENC_MACON3_PADCFG2 0x80 -#define ENC_MACON3_PADCFG1 0x40 -#define ENC_MACON3_PADCFG0 0x20 -#define ENC_MACON3_TXCRCEN 0x10 -#define ENC_MACON3_PHDRLEN 0x08 -#define ENC_MACON3_HFRMEN 0x04 -#define ENC_MACON3_FRMLNEN 0x02 -#define ENC_MACON3_FULDPX 0x01 - -/* MACON4 Register Bits */ -#define ENC_MACON4_DEFER 0x40 - -/* MICMD Register Bits */ -#define ENC_MICMD_MIISCAN 0x02 -#define ENC_MICMD_MIIRD 0x01 - -/* MISTAT Register Bits */ -#define ENC_MISTAT_NVALID 0x04 -#define ENC_MISTAT_SCAN 0x02 -#define ENC_MISTAT_BUSY 0x01 - -/* PHID1 and PHID2 values */ -#define ENC_PHID1_VALUE 0x0083 -#define ENC_PHID2_VALUE 0x1400 -#define ENC_PHID2_MASK 0xFC00 - -/* PHCON1 values */ -#define ENC_PHCON1_PDPXMD 0x0100 - -/* PHSTAT1 values */ -#define ENC_PHSTAT1_LLSTAT 0x0004 - -/* PHSTAT2 values */ -#define ENC_PHSTAT2_LSTAT 0x0400 -#define ENC_PHSTAT2_DPXSTAT 0x0200 - -#endif diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index ff7ad91116..0076d6323e 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -10,6 +10,7 @@ #include <common.h> #include <dm.h> +#include <environment.h> #include <malloc.h> #include <memalign.h> #include <miiphy.h> @@ -806,7 +807,16 @@ static int fec_recv(struct eth_device *dev) uint16_t bd_status; ulong addr, size, end; int i; + +#ifdef CONFIG_DM_ETH + *packetp = memalign(ARCH_DMA_MINALIGN, FEC_MAX_PKT_SIZE); + if (*packetp == 0) { + printf("%s: error allocating packetp\n", __func__); + return -ENOMEM; + } +#else ALLOC_CACHE_ALIGN_BUFFER(uchar, buff, FEC_MAX_PKT_SIZE); +#endif /* Check if any critical events have happened */ ievent = readl(&fec->eth->ievent); @@ -882,8 +892,13 @@ static int fec_recv(struct eth_device *dev) #ifdef CONFIG_FEC_MXC_SWAP_PACKET swap_packet((uint32_t *)addr, frame_length); #endif + +#ifdef CONFIG_DM_ETH + memcpy(*packetp, (char *)addr, frame_length); +#else memcpy(buff, (char *)addr, frame_length); net_process_received_packet(buff, frame_length); +#endif len = frame_length; } else { if (bd_status & FEC_RBD_ERR) @@ -997,18 +1012,9 @@ 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 +struct mii_dev *fec_get_miibus(ulong base_addr, int dev_id) { -#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 *)(ulong)base_addr; -#endif + struct ethernet_regs *eth = (struct ethernet_regs *)base_addr; struct mii_dev *bus; int ret; @@ -1140,12 +1146,12 @@ int fecmxc_initialize_multi(bd_t *bd, int dev_id, int phy_id, uint32_t addr) #endif int ret; -#ifdef CONFIG_MX28 +#ifdef CONFIG_FEC_MXC_MDIO_BASE /* * The i.MX28 has two ethernet interfaces, but they are not equal. * Only the first one can access the MDIO bus. */ - base_mii = MXS_ENET0_BASE; + base_mii = CONFIG_FEC_MXC_MDIO_BASE; #else base_mii = addr; #endif @@ -1201,10 +1207,19 @@ static int fecmxc_read_rom_hwaddr(struct udevice *dev) return fec_get_hwaddr(priv->dev_id, pdata->enetaddr); } +static int fecmxc_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + if (packet) + free(packet); + + return 0; +} + static const struct eth_ops fecmxc_ops = { .start = fecmxc_init, .send = fecmxc_send, .recv = fecmxc_recv, + .free_pkt = fecmxc_free_pkt, .stop = fecmxc_halt, .write_hwaddr = fecmxc_set_hwaddr, .read_rom_hwaddr = fecmxc_read_rom_hwaddr, @@ -1236,7 +1251,6 @@ static int fecmxc_probe(struct udevice *dev) struct eth_pdata *pdata = dev_get_platdata(dev); struct fec_priv *priv = dev_get_priv(dev); struct mii_dev *bus = NULL; - int dev_id = -1; uint32_t start; int ret; @@ -1257,9 +1271,13 @@ static int fecmxc_probe(struct udevice *dev) } fec_reg_setup(priv); - priv->dev_id = (dev_id == -1) ? 0 : dev_id; - bus = fec_get_miibus(dev, dev_id); + priv->dev_id = dev->seq; +#ifdef CONFIG_FEC_MXC_MDIO_BASE + bus = fec_get_miibus((ulong)CONFIG_FEC_MXC_MDIO_BASE, dev->seq); +#else + bus = fec_get_miibus((ulong)priv->eth, dev->seq); +#endif if (!bus) { ret = -ENOMEM; goto err_mii; @@ -1274,12 +1292,11 @@ static int fecmxc_probe(struct udevice *dev) return 0; -err_timeout: - free(priv->phydev); err_phy: mdio_unregister(bus); free(bus); err_mii: +err_timeout: fec_free_descs(priv); return ret; } @@ -1325,6 +1342,9 @@ static int fecmxc_ofdata_to_platdata(struct udevice *dev) static const struct udevice_id fecmxc_ids[] = { { .compatible = "fsl,imx6q-fec" }, + { .compatible = "fsl,imx6sl-fec" }, + { .compatible = "fsl,imx6sx-fec" }, + { .compatible = "fsl,imx6ul-fec" }, { } }; diff --git a/drivers/net/fsl_mcdmafec.c b/drivers/net/fsl_mcdmafec.c index 2d89cea4a3..00d905c299 100644 --- a/drivers/net/fsl_mcdmafec.c +++ b/drivers/net/fsl_mcdmafec.c @@ -9,6 +9,7 @@ */ #include <common.h> +#include <environment.h> #include <malloc.h> #include <command.h> #include <config.h> diff --git a/drivers/net/macb.c b/drivers/net/macb.c index e62aefcd0d..fe370bf728 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -915,7 +915,7 @@ static int macb_recv(struct eth_device *netdev) if (length >= 0) { net_process_received_packet(packet, length); reclaim_rx_buffers(macb, macb->next_rx_tail); - } else if (length < 0) { + } else { return length; } } diff --git a/drivers/net/mcffec.c b/drivers/net/mcffec.c index ebcbed941a..505a2d1bee 100644 --- a/drivers/net/mcffec.c +++ b/drivers/net/mcffec.c @@ -9,6 +9,7 @@ */ #include <common.h> +#include <environment.h> #include <malloc.h> #include <command.h> diff --git a/drivers/net/mpc8xx_fec.c b/drivers/net/mpc8xx_fec.c index 71fe984a5d..1dd41df18b 100644 --- a/drivers/net/mpc8xx_fec.c +++ b/drivers/net/mpc8xx_fec.c @@ -7,10 +7,10 @@ #include <common.h> #include <command.h> -#include <commproc.h> #include <malloc.h> #include <net.h> #include <netdev.h> +#include <asm/cpm_8xx.h> #include <asm/io.h> #include <phy.h> diff --git a/drivers/net/mvneta.c b/drivers/net/mvneta.c index 83e3153768..f2e9acfd1f 100644 --- a/drivers/net/mvneta.c +++ b/drivers/net/mvneta.c @@ -890,6 +890,15 @@ static void mvneta_mac_addr_set(struct mvneta_port *pp, unsigned char *addr, mvneta_set_ucast_addr(pp, addr[5], queue); } +static int mvneta_write_hwaddr(struct udevice *dev) +{ + mvneta_mac_addr_set(dev_get_priv(dev), + ((struct eth_pdata *)dev_get_platdata(dev))->enetaddr, + rxq_def); + + return 0; +} + /* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */ static void mvneta_rx_desc_fill(struct mvneta_rx_desc *rx_desc, u32 phys_addr, u32 cookie) @@ -1753,6 +1762,7 @@ static const struct eth_ops mvneta_ops = { .send = mvneta_send, .recv = mvneta_recv, .stop = mvneta_stop, + .write_hwaddr = mvneta_write_hwaddr, }; static int mvneta_ofdata_to_platdata(struct udevice *dev) diff --git a/drivers/net/mvpp2.c b/drivers/net/mvpp2.c index e3d31a560d..62c0c2be06 100644 --- a/drivers/net/mvpp2.c +++ b/drivers/net/mvpp2.c @@ -5598,6 +5598,10 @@ static int mvpp2_base_bind(struct udevice *parent) id += base_id_add; name = calloc(1, 16); + if (!name) { + free(plat); + return -ENOMEM; + } sprintf(name, "mvpp2-%d", id); /* Create child device UCLASS_ETH and bind it */ diff --git a/drivers/net/ne2000_base.c b/drivers/net/ne2000_base.c index fb088e06a4..421aa20ea6 100644 --- a/drivers/net/ne2000_base.c +++ b/drivers/net/ne2000_base.c @@ -74,6 +74,7 @@ Add SNMP #include <common.h> #include <command.h> +#include <environment.h> #include <net.h> #include <malloc.h> #include <linux/compiler.h> diff --git a/drivers/net/pfe_eth/Kconfig b/drivers/net/pfe_eth/Kconfig new file mode 100644 index 0000000000..a13b331a50 --- /dev/null +++ b/drivers/net/pfe_eth/Kconfig @@ -0,0 +1,12 @@ +menuconfig FSL_PFE + bool "NXP PFE Ethernet driver" + help + This driver provides support for NXP's Packet Forwarding Engine. + +if FSL_PFE + +config SYS_FSL_PFE_ADDR + hex "PFE base address" + default 0x04000000 + +endif diff --git a/drivers/net/pfe_eth/Makefile b/drivers/net/pfe_eth/Makefile new file mode 100644 index 0000000000..6b5248f659 --- /dev/null +++ b/drivers/net/pfe_eth/Makefile @@ -0,0 +1,12 @@ +# Copyright 2015-2016 Freescale Semiconductor, Inc. +# Copyright 2017 NXP +# +# SPDX-License-Identifier:GPL-2.0+ + +# Layerscape PFE driver +obj-y += pfe_cmd.o \ + pfe_driver.o \ + pfe_eth.o \ + pfe_firmware.o \ + pfe_hw.o \ + pfe_mdio.o diff --git a/drivers/net/pfe_eth/pfe_cmd.c b/drivers/net/pfe_eth/pfe_cmd.c new file mode 100644 index 0000000000..822dc0f141 --- /dev/null +++ b/drivers/net/pfe_eth/pfe_cmd.c @@ -0,0 +1,497 @@ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * @file + * @brief PFE utility commands + */ + +#include <net/pfe_eth/pfe_eth.h> + +static inline void pfe_command_help(void) +{ + printf("Usage: pfe [pe | status | expt ] <options>\n"); +} + +static void pfe_command_pe(int argc, char * const argv[]) +{ + if (argc >= 3 && strcmp(argv[2], "pmem") == 0) { + if (argc >= 4 && strcmp(argv[3], "read") == 0) { + int i; + int num; + int id; + u32 addr; + u32 size; + u32 val; + + if (argc == 7) { + num = simple_strtoul(argv[6], NULL, 0); + } else if (argc == 6) { + num = 1; + } else { + printf("Usage: pfe pe pmem read <id> <addr> [<num>]\n"); + return; + } + + id = simple_strtoul(argv[4], NULL, 0); + addr = simple_strtoul(argv[5], NULL, 16); + size = 4; + + for (i = 0; i < num; i++, addr += 4) { + val = pe_pmem_read(id, addr, size); + val = be32_to_cpu(val); + if (!(i & 3)) + printf("%08x: ", addr); + printf("%08x%s", val, i == num - 1 || (i & 3) + == 3 ? "\n" : " "); + } + + } else { + printf("Usage: pfe pe pmem read <parameters>\n"); + } + } else if (argc >= 3 && strcmp(argv[2], "dmem") == 0) { + if (argc >= 4 && strcmp(argv[3], "read") == 0) { + int i; + int num; + int id; + u32 addr; + u32 size; + u32 val; + + if (argc == 7) { + num = simple_strtoul(argv[6], NULL, 0); + } else if (argc == 6) { + num = 1; + } else { + printf("Usage: pfe pe dmem read <id> <addr> [<num>]\n"); + return; + } + + id = simple_strtoul(argv[4], NULL, 0); + addr = simple_strtoul(argv[5], NULL, 16); + size = 4; + + for (i = 0; i < num; i++, addr += 4) { + val = pe_dmem_read(id, addr, size); + val = be32_to_cpu(val); + if (!(i & 3)) + printf("%08x: ", addr); + printf("%08x%s", val, i == num - 1 || (i & 3) + == 3 ? "\n" : " "); + } + + } else if (argc >= 4 && strcmp(argv[3], "write") == 0) { + int id; + u32 val; + u32 addr; + u32 size; + + if (argc != 7) { + printf("Usage: pfe pe dmem write <id> <val> <addr>\n"); + return; + } + + id = simple_strtoul(argv[4], NULL, 0); + val = simple_strtoul(argv[5], NULL, 16); + val = cpu_to_be32(val); + addr = simple_strtoul(argv[6], NULL, 16); + size = 4; + pe_dmem_write(id, val, addr, size); + } else { + printf("Usage: pfe pe dmem [read | write] <parameters>\n"); + } + } else if (argc >= 3 && strcmp(argv[2], "lmem") == 0) { + if (argc >= 4 && strcmp(argv[3], "read") == 0) { + int i; + int num; + u32 val; + u32 offset; + + if (argc == 6) { + num = simple_strtoul(argv[5], NULL, 0); + } else if (argc == 5) { + num = 1; + } else { + printf("Usage: pfe pe lmem read <offset> [<num>]\n"); + return; + } + + offset = simple_strtoul(argv[4], NULL, 16); + + for (i = 0; i < num; i++, offset += 4) { + pe_lmem_read(&val, 4, offset); + val = be32_to_cpu(val); + printf("%08x%s", val, i == num - 1 || (i & 7) + == 7 ? "\n" : " "); + } + + } else if (argc >= 4 && strcmp(argv[3], "write") == 0) { + u32 val; + u32 offset; + + if (argc != 6) { + printf("Usage: pfe pe lmem write <val> <offset>\n"); + return; + } + + val = simple_strtoul(argv[4], NULL, 16); + val = cpu_to_be32(val); + offset = simple_strtoul(argv[5], NULL, 16); + pe_lmem_write(&val, 4, offset); + } else { + printf("Usage: pfe pe lmem [read | write] <parameters>\n"); + } + } else { + if (strcmp(argv[2], "help") != 0) + printf("Unknown option: %s\n", argv[2]); + + printf("Usage: pfe pe <parameters>\n"); + } +} + +#define NUM_QUEUES 16 + +/* + * qm_read_drop_stat + * This function is used to read the drop statistics from the TMU + * hw drop counter. Since the hw counter is always cleared afer + * reading, this function maintains the previous drop count, and + * adds the new value to it. That value can be retrieved by + * passing a pointer to it with the total_drops arg. + * + * @param tmu TMU number (0 - 3) + * @param queue queue number (0 - 15) + * @param total_drops pointer to location to store total drops (or NULL) + * @param do_reset if TRUE, clear total drops after updating + * + */ +u32 qm_read_drop_stat(u32 tmu, u32 queue, u32 *total_drops, int do_reset) +{ + static u32 qtotal[TMU_MAX_ID + 1][NUM_QUEUES]; + u32 val; + + writel((tmu << 8) | queue, TMU_TEQ_CTRL); + writel((tmu << 8) | queue, TMU_LLM_CTRL); + val = readl(TMU_TEQ_DROP_STAT); + qtotal[tmu][queue] += val; + if (total_drops) + *total_drops = qtotal[tmu][queue]; + if (do_reset) + qtotal[tmu][queue] = 0; + return val; +} + +static ssize_t tmu_queue_stats(char *buf, int tmu, int queue) +{ + ssize_t len = 0; + u32 drops; + + printf("%d-%02d, ", tmu, queue); + + drops = qm_read_drop_stat(tmu, queue, NULL, 0); + + /* Select queue */ + writel((tmu << 8) | queue, TMU_TEQ_CTRL); + writel((tmu << 8) | queue, TMU_LLM_CTRL); + + printf("(teq) drop: %10u, tx: %10u (llm) head: %08x, tail: %08x, drop: %10u\n", + drops, readl(TMU_TEQ_TRANS_STAT), + readl(TMU_LLM_QUE_HEADPTR), readl(TMU_LLM_QUE_TAILPTR), + readl(TMU_LLM_QUE_DROPCNT)); + + return len; +} + +static ssize_t tmu_queues(char *buf, int tmu) +{ + ssize_t len = 0; + int queue; + + for (queue = 0; queue < 16; queue++) + len += tmu_queue_stats(buf + len, tmu, queue); + + return len; +} + +static inline void hif_status(void) +{ + printf("hif:\n"); + + printf(" tx curr bd: %x\n", readl(HIF_TX_CURR_BD_ADDR)); + printf(" tx status: %x\n", readl(HIF_TX_STATUS)); + printf(" tx dma status: %x\n", readl(HIF_TX_DMA_STATUS)); + + printf(" rx curr bd: %x\n", readl(HIF_RX_CURR_BD_ADDR)); + printf(" rx status: %x\n", readl(HIF_RX_STATUS)); + printf(" rx dma status: %x\n", readl(HIF_RX_DMA_STATUS)); + + printf("hif nocopy:\n"); + + printf(" tx curr bd: %x\n", readl(HIF_NOCPY_TX_CURR_BD_ADDR)); + printf(" tx status: %x\n", readl(HIF_NOCPY_TX_STATUS)); + printf(" tx dma status: %x\n", readl(HIF_NOCPY_TX_DMA_STATUS)); + + printf(" rx curr bd: %x\n", readl(HIF_NOCPY_RX_CURR_BD_ADDR)); + printf(" rx status: %x\n", readl(HIF_NOCPY_RX_STATUS)); + printf(" rx dma status: %x\n", readl(HIF_NOCPY_RX_DMA_STATUS)); +} + +static void gpi(int id, void *base) +{ + u32 val; + + printf("%s%d:\n", __func__, id); + + printf(" tx under stick: %x\n", readl(base + GPI_FIFO_STATUS)); + val = readl(base + GPI_FIFO_DEBUG); + printf(" tx pkts: %x\n", (val >> 23) & 0x3f); + printf(" rx pkts: %x\n", (val >> 18) & 0x3f); + printf(" tx bytes: %x\n", (val >> 9) & 0x1ff); + printf(" rx bytes: %x\n", (val >> 0) & 0x1ff); + printf(" overrun: %x\n", readl(base + GPI_OVERRUN_DROPCNT)); +} + +static void bmu(int id, void *base) +{ + printf("%s%d:\n", __func__, id); + + printf(" buf size: %x\n", (1 << readl(base + BMU_BUF_SIZE))); + printf(" buf count: %x\n", readl(base + BMU_BUF_CNT)); + printf(" buf rem: %x\n", readl(base + BMU_REM_BUF_CNT)); + printf(" buf curr: %x\n", readl(base + BMU_CURR_BUF_CNT)); + printf(" free err: %x\n", readl(base + BMU_FREE_ERR_ADDR)); +} + +#define PESTATUS_ADDR_CLASS 0x800 +#define PEMBOX_ADDR_CLASS 0x890 +#define PESTATUS_ADDR_TMU 0x80 +#define PEMBOX_ADDR_TMU 0x290 +#define PESTATUS_ADDR_UTIL 0x0 + +static void pfe_pe_status(int argc, char * const argv[]) +{ + int do_clear = 0; + u32 id; + u32 dmem_addr; + u32 cpu_state; + u32 activity_counter; + u32 rx; + u32 tx; + u32 drop; + char statebuf[5]; + u32 class_debug_reg = 0; + + if (argc == 4 && strcmp(argv[3], "clear") == 0) + do_clear = 1; + + for (id = CLASS0_ID; id < MAX_PE; id++) { + if (id >= TMU0_ID) { + if (id == TMU2_ID) + continue; + if (id == TMU0_ID) + printf("tmu:\n"); + dmem_addr = PESTATUS_ADDR_TMU; + } else { + if (id == CLASS0_ID) + printf("class:\n"); + dmem_addr = PESTATUS_ADDR_CLASS; + class_debug_reg = readl(CLASS_PE0_DEBUG + id * 4); + } + + cpu_state = pe_dmem_read(id, dmem_addr, 4); + dmem_addr += 4; + memcpy(statebuf, (char *)&cpu_state, 4); + statebuf[4] = '\0'; + activity_counter = pe_dmem_read(id, dmem_addr, 4); + dmem_addr += 4; + rx = pe_dmem_read(id, dmem_addr, 4); + if (do_clear) + pe_dmem_write(id, 0, dmem_addr, 4); + dmem_addr += 4; + tx = pe_dmem_read(id, dmem_addr, 4); + if (do_clear) + pe_dmem_write(id, 0, dmem_addr, 4); + dmem_addr += 4; + drop = pe_dmem_read(id, dmem_addr, 4); + if (do_clear) + pe_dmem_write(id, 0, dmem_addr, 4); + dmem_addr += 4; + + if (id >= TMU0_ID) { + printf("%d: state=%4s ctr=%08x rx=%x qstatus=%x\n", + id - TMU0_ID, statebuf, + cpu_to_be32(activity_counter), + cpu_to_be32(rx), cpu_to_be32(tx)); + } else { + printf("%d: pc=1%04x ldst=%04x state=%4s ctr=%08x rx=%x tx=%x drop=%x\n", + id - CLASS0_ID, class_debug_reg & 0xFFFF, + class_debug_reg >> 16, + statebuf, cpu_to_be32(activity_counter), + cpu_to_be32(rx), cpu_to_be32(tx), + cpu_to_be32(drop)); + } + } +} + +static void pfe_command_status(int argc, char * const argv[]) +{ + if (argc >= 3 && strcmp(argv[2], "pe") == 0) { + pfe_pe_status(argc, argv); + } else if (argc == 3 && strcmp(argv[2], "bmu") == 0) { + bmu(1, BMU1_BASE_ADDR); + bmu(2, BMU2_BASE_ADDR); + } else if (argc == 3 && strcmp(argv[2], "hif") == 0) { + hif_status(); + } else if (argc == 3 && strcmp(argv[2], "gpi") == 0) { + gpi(0, EGPI1_BASE_ADDR); + gpi(1, EGPI2_BASE_ADDR); + gpi(3, HGPI_BASE_ADDR); + } else if (argc == 3 && strcmp(argv[2], "tmu0_queues") == 0) { + tmu_queues(NULL, 0); + } else if (argc == 3 && strcmp(argv[2], "tmu1_queues") == 0) { + tmu_queues(NULL, 1); + } else if (argc == 3 && strcmp(argv[2], "tmu3_queues") == 0) { + tmu_queues(NULL, 3); + } else { + printf("Usage: pfe status [pe <clear> | bmu | gpi | hif | tmuX_queues ]\n"); + } +} + +#define EXPT_DUMP_ADDR 0x1fa8 +#define EXPT_REG_COUNT 20 +static const char *register_names[EXPT_REG_COUNT] = { + " pc", "ECAS", " EID", " ED", + " sp", " r1", " r2", " r3", + " r4", " r5", " r6", " r7", + " r8", " r9", " r10", " r11", + " r12", " r13", " r14", " r15" +}; + +static void pfe_command_expt(int argc, char * const argv[]) +{ + unsigned int id, i, val, addr; + + if (argc == 3) { + id = simple_strtoul(argv[2], NULL, 0); + addr = EXPT_DUMP_ADDR; + printf("Exception information for PE %d:\n", id); + for (i = 0; i < EXPT_REG_COUNT; i++) { + val = pe_dmem_read(id, addr, 4); + val = be32_to_cpu(val); + printf("%s:%08x%s", register_names[i], val, + (i & 3) == 3 ? "\n" : " "); + addr += 4; + } + } else { + printf("Usage: pfe expt <id>\n"); + } +} + +#ifdef PFE_RESET_WA +/*This function sends a dummy packet to HIF through TMU3 */ +static void send_dummy_pkt_to_hif(void) +{ + u32 buf; + static u32 dummy_pkt[] = { + 0x4200800a, 0x01000003, 0x00018100, 0x00000000, + 0x33221100, 0x2b785544, 0xd73093cb, 0x01000608, + 0x04060008, 0x2b780200, 0xd73093cb, 0x0a01a8c0, + 0x33221100, 0xa8c05544, 0x00000301, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xbe86c51f }; + + /*Allocate BMU2 buffer */ + buf = readl(BMU2_BASE_ADDR + BMU_ALLOC_CTRL); + + debug("Sending a dummy pkt to HIF %x\n", buf); + buf += 0x80; + memcpy((void *)DDR_PFE_TO_VIRT(buf), dummy_pkt, sizeof(dummy_pkt)); + + /*Write length and pkt to TMU*/ + writel(0x03000042, TMU_PHY_INQ_PKTPTR); + writel(buf, TMU_PHY_INQ_PKTINFO); +} + +static void pfe_command_stop(int argc, char * const argv[]) +{ + int pfe_pe_id, hif_stop_loop = 10; + u32 rx_status; + + printf("Stopping PFE...\n"); + + /*Mark all descriptors as LAST_BD */ + hif_rx_desc_disable(); + + /*If HIF Rx BDP is busy send a dummy packet */ + do { + rx_status = readl(HIF_RX_STATUS); + if (rx_status & BDP_CSR_RX_DMA_ACTV) + send_dummy_pkt_to_hif(); + udelay(10); + } while (hif_stop_loop--); + + if (readl(HIF_RX_STATUS) & BDP_CSR_RX_DMA_ACTV) + printf("Unable to stop HIF\n"); + + /*Disable Class PEs */ + for (pfe_pe_id = CLASS0_ID; pfe_pe_id <= CLASS_MAX_ID; pfe_pe_id++) { + /*Inform PE to stop */ + pe_dmem_write(pfe_pe_id, cpu_to_be32(1), PEMBOX_ADDR_CLASS, 4); + udelay(10); + + /*Read status */ + if (!pe_dmem_read(pfe_pe_id, PEMBOX_ADDR_CLASS + 4, 4)) + printf("Failed to stop PE%d\n", pfe_pe_id); + } + + /*Disable TMU PEs */ + for (pfe_pe_id = TMU0_ID; pfe_pe_id <= TMU_MAX_ID; pfe_pe_id++) { + if (pfe_pe_id == TMU2_ID) + continue; + + /*Inform PE to stop */ + pe_dmem_write(pfe_pe_id, 1, PEMBOX_ADDR_TMU, 4); + udelay(10); + + /*Read status */ + if (!pe_dmem_read(pfe_pe_id, PEMBOX_ADDR_TMU + 4, 4)) + printf("Failed to stop PE%d\n", pfe_pe_id); + } +} +#endif + +static int pfe_command(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (argc == 1 || strcmp(argv[1], "help") == 0) { + pfe_command_help(); + return CMD_RET_SUCCESS; + } + + if (strcmp(argv[1], "pe") == 0) { + pfe_command_pe(argc, argv); + } else if (strcmp(argv[1], "status") == 0) { + pfe_command_status(argc, argv); + } else if (strcmp(argv[1], "expt") == 0) { + pfe_command_expt(argc, argv); +#ifdef PFE_RESET_WA + } else if (strcmp(argv[1], "stop") == 0) { + pfe_command_stop(argc, argv); +#endif + } else { + printf("Unknown option: %s\n", argv[1]); + pfe_command_help(); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + pfe, 7, 1, pfe_command, + "Performs PFE lib utility functions", + "Usage:\n" + "pfe <options>" +); diff --git a/drivers/net/pfe_eth/pfe_driver.c b/drivers/net/pfe_eth/pfe_driver.c new file mode 100644 index 0000000000..a9991f5c2b --- /dev/null +++ b/drivers/net/pfe_eth/pfe_driver.c @@ -0,0 +1,643 @@ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <net/pfe_eth/pfe_eth.h> +#include <net/pfe_eth/pfe_firmware.h> + +static struct tx_desc_s *g_tx_desc; +static struct rx_desc_s *g_rx_desc; + +/* + * HIF Rx interface function + * Reads the rx descriptor from the current location (rx_to_read). + * - If the descriptor has a valid data/pkt, then get the data pointer + * - check for the input rx phy number + * - increment the rx data pointer by pkt_head_room_size + * - decrement the data length by pkt_head_room_size + * - handover the packet to caller. + * + * @param[out] pkt_ptr - Pointer to store rx packet + * @param[out] phy_port - Pointer to store recv phy port + * + * @return -1 if no packet, else return length of packet. + */ +int pfe_recv(uchar **pkt_ptr, int *phy_port) +{ + struct rx_desc_s *rx_desc = g_rx_desc; + struct buf_desc *bd; + int len = 0; + + struct hif_header_s *hif_header; + + bd = rx_desc->rx_base + rx_desc->rx_to_read; + + if (readl(&bd->ctrl) & BD_CTRL_DESC_EN) + return len; /* No pending Rx packet */ + + /* this len include hif_header(8 bytes) */ + len = readl(&bd->ctrl) & 0xFFFF; + + hif_header = (struct hif_header_s *)DDR_PFE_TO_VIRT(readl(&bd->data)); + + /* Get the receive port info from the packet */ + debug("Pkt received:"); + debug(" Pkt ptr(%p), len(%d), gemac_port(%d) status(%08x)\n", + hif_header, len, hif_header->port_no, readl(&bd->status)); +#ifdef DEBUG + { + int i; + unsigned char *p = (unsigned char *)hif_header; + + for (i = 0; i < len; i++) { + if (!(i % 16)) + printf("\n"); + printf(" %02x", p[i]); + } + printf("\n"); + } +#endif + + *pkt_ptr = (uchar *)(hif_header + 1); + *phy_port = hif_header->port_no; + len -= sizeof(struct hif_header_s); + + return len; +} + +/* + * HIF function to check the Rx done + * This function will check the rx done indication of the current rx_to_read + * locations + * if success, moves the rx_to_read to next location. + */ +int pfe_eth_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct rx_desc_s *rx_desc = g_rx_desc; + struct buf_desc *bd; + + debug("%s:rx_base: %p, rx_to_read: %d\n", __func__, rx_desc->rx_base, + rx_desc->rx_to_read); + + bd = rx_desc->rx_base + rx_desc->rx_to_read; + + /* reset the control field */ + writel((MAX_FRAME_SIZE | BD_CTRL_LIFM | BD_CTRL_DESC_EN + | BD_CTRL_DIR), &bd->ctrl); + writel(0, &bd->status); + + debug("Rx Done : status: %08x, ctrl: %08x\n", readl(&bd->status), + readl(&bd->ctrl)); + + /* Give START_STROBE to BDP to fetch the descriptor __NOW__, + * BDP need not wait for rx_poll_cycle time to fetch the descriptor, + * In idle state (ie., no rx pkt), BDP will not fetch + * the descriptor even if strobe is given. + */ + writel((readl(HIF_RX_CTRL) | HIF_CTRL_BDP_CH_START_WSTB), HIF_RX_CTRL); + + /* increment the rx_to_read index to next location */ + rx_desc->rx_to_read = (rx_desc->rx_to_read + 1) + & (rx_desc->rx_ring_size - 1); + + debug("Rx next pkt location: %d\n", rx_desc->rx_to_read); + + return 0; +} + +/* + * HIF Tx interface function + * This function sends a single packet to PFE from HIF interface. + * - No interrupt indication on tx completion. + * - Data is copied to tx buffers before tx descriptor is updated + * and TX DMA is enabled. + * + * @param[in] phy_port Phy port number to send out this packet + * @param[in] data Pointer to the data + * @param[in] length Length of the ethernet packet to be transferred. + * + * @return -1 if tx Q is full, else returns the tx location where the pkt is + * placed. + */ +int pfe_send(int phy_port, void *data, int length) +{ + struct tx_desc_s *tx_desc = g_tx_desc; + struct buf_desc *bd; + struct hif_header_s hif_header; + u8 *tx_buf_va; + + debug("%s:pkt: %p, len: %d, tx_base: %p, tx_to_send: %d\n", __func__, + data, length, tx_desc->tx_base, tx_desc->tx_to_send); + + bd = tx_desc->tx_base + tx_desc->tx_to_send; + + /* check queue-full condition */ + if (readl(&bd->ctrl) & BD_CTRL_DESC_EN) + return -1; + + /* PFE checks for min pkt size */ + if (length < MIN_PKT_SIZE) + length = MIN_PKT_SIZE; + + tx_buf_va = (void *)DDR_PFE_TO_VIRT(readl(&bd->data)); + debug("%s: tx_buf_va: %p, tx_buf_pa: %08x\n", __func__, tx_buf_va, + readl(&bd->data)); + + /* Fill the gemac/phy port number to send this packet out */ + memset(&hif_header, 0, sizeof(struct hif_header_s)); + hif_header.port_no = phy_port; + + memcpy(tx_buf_va, (u8 *)&hif_header, sizeof(struct hif_header_s)); + memcpy(tx_buf_va + sizeof(struct hif_header_s), data, length); + length += sizeof(struct hif_header_s); + +#ifdef DEBUG + { + int i; + unsigned char *p = (unsigned char *)tx_buf_va; + + for (i = 0; i < length; i++) { + if (!(i % 16)) + printf("\n"); + printf("%02x ", p[i]); + } + } +#endif + + debug("Tx Done: status: %08x, ctrl: %08x\n", readl(&bd->status), + readl(&bd->ctrl)); + + /* fill the tx desc */ + writel((u32)(BD_CTRL_DESC_EN | BD_CTRL_LIFM | (length & 0xFFFF)), + &bd->ctrl); + writel(0, &bd->status); + + writel((HIF_CTRL_DMA_EN | HIF_CTRL_BDP_CH_START_WSTB), HIF_TX_CTRL); + + udelay(100); + + return tx_desc->tx_to_send; +} + +/* + * HIF function to check the Tx done + * This function will check the tx done indication of the current tx_to_send + * locations + * if success, moves the tx_to_send to next location. + * + * @return -1 if TX ownership bit is not cleared by hw. + * else on success (tx done completion) return zero. + */ +int pfe_tx_done(void) +{ + struct tx_desc_s *tx_desc = g_tx_desc; + struct buf_desc *bd; + + debug("%s:tx_base: %p, tx_to_send: %d\n", __func__, tx_desc->tx_base, + tx_desc->tx_to_send); + + bd = tx_desc->tx_base + tx_desc->tx_to_send; + + /* check queue-full condition */ + if (readl(&bd->ctrl) & BD_CTRL_DESC_EN) + return -1; + + /* reset the control field */ + writel(0, &bd->ctrl); + writel(0, &bd->status); + + debug("Tx Done : status: %08x, ctrl: %08x\n", readl(&bd->status), + readl(&bd->ctrl)); + + /* increment the txtosend index to next location */ + tx_desc->tx_to_send = (tx_desc->tx_to_send + 1) + & (tx_desc->tx_ring_size - 1); + + debug("Tx next pkt location: %d\n", tx_desc->tx_to_send); + + return 0; +} + +/* + * Helper function to dump Rx descriptors. + */ +static inline void hif_rx_desc_dump(void) +{ + struct buf_desc *bd_va; + int i; + struct rx_desc_s *rx_desc; + + if (!g_rx_desc) { + printf("%s: HIF Rx desc no init\n", __func__); + return; + } + + rx_desc = g_rx_desc; + bd_va = rx_desc->rx_base; + + debug("HIF rx desc: base_va: %p, base_pa: %08x\n", rx_desc->rx_base, + rx_desc->rx_base_pa); + for (i = 0; i < rx_desc->rx_ring_size; i++) { + debug("status: %08x, ctrl: %08x, data: %08x, next: 0x%08x\n", + readl(&bd_va->status), + readl(&bd_va->ctrl), + readl(&bd_va->data), + readl(&bd_va->next)); + bd_va++; + } +} + +/* + * This function mark all Rx descriptors as LAST_BD. + */ +void hif_rx_desc_disable(void) +{ + int i; + struct rx_desc_s *rx_desc; + struct buf_desc *bd_va; + + if (!g_rx_desc) { + printf("%s: HIF Rx desc not initialized\n", __func__); + return; + } + + rx_desc = g_rx_desc; + bd_va = rx_desc->rx_base; + + for (i = 0; i < rx_desc->rx_ring_size; i++) { + writel(readl(&bd_va->ctrl) | BD_CTRL_LAST_BD, &bd_va->ctrl); + bd_va++; + } +} + +/* + * HIF Rx Desc initialization function. + */ +static int hif_rx_desc_init(struct pfe_ddr_address *pfe_addr) +{ + u32 ctrl; + struct buf_desc *bd_va; + struct buf_desc *bd_pa; + struct rx_desc_s *rx_desc; + u32 rx_buf_pa; + int i; + + /* sanity check */ + if (g_rx_desc) { + printf("%s: HIF Rx desc re-init request\n", __func__); + return 0; + } + + rx_desc = (struct rx_desc_s *)malloc(sizeof(struct rx_desc_s)); + if (!rx_desc) { + printf("%s: Memory allocation failure\n", __func__); + return -ENOMEM; + } + memset(rx_desc, 0, sizeof(struct rx_desc_s)); + + /* init: Rx ring buffer */ + rx_desc->rx_ring_size = HIF_RX_DESC_NT; + + /* NOTE: must be 64bit aligned */ + bd_va = (struct buf_desc *)(pfe_addr->ddr_pfe_baseaddr + + RX_BD_BASEADDR); + bd_pa = (struct buf_desc *)(pfe_addr->ddr_pfe_phys_baseaddr + + RX_BD_BASEADDR); + + rx_desc->rx_base = bd_va; + rx_desc->rx_base_pa = (unsigned long)bd_pa; + + rx_buf_pa = pfe_addr->ddr_pfe_phys_baseaddr + HIF_RX_PKT_DDR_BASEADDR; + + debug("%s: Rx desc base: %p, base_pa: %08x, desc_count: %d\n", + __func__, rx_desc->rx_base, rx_desc->rx_base_pa, + rx_desc->rx_ring_size); + + memset(bd_va, 0, sizeof(struct buf_desc) * rx_desc->rx_ring_size); + + ctrl = (MAX_FRAME_SIZE | BD_CTRL_DESC_EN | BD_CTRL_DIR | BD_CTRL_LIFM); + + for (i = 0; i < rx_desc->rx_ring_size; i++) { + writel((unsigned long)(bd_pa + 1), &bd_va->next); + writel(ctrl, &bd_va->ctrl); + writel(rx_buf_pa + (i * MAX_FRAME_SIZE), &bd_va->data); + bd_va++; + bd_pa++; + } + --bd_va; + writel((u32)rx_desc->rx_base_pa, &bd_va->next); + + writel(rx_desc->rx_base_pa, HIF_RX_BDP_ADDR); + writel((readl(HIF_RX_CTRL) | HIF_CTRL_BDP_CH_START_WSTB), HIF_RX_CTRL); + + g_rx_desc = rx_desc; + + return 0; +} + +/* + * Helper function to dump Tx Descriptors. + */ +static inline void hif_tx_desc_dump(void) +{ + struct tx_desc_s *tx_desc; + int i; + struct buf_desc *bd_va; + + if (!g_tx_desc) { + printf("%s: HIF Tx desc no init\n", __func__); + return; + } + + tx_desc = g_tx_desc; + bd_va = tx_desc->tx_base; + + debug("HIF tx desc: base_va: %p, base_pa: %08x\n", tx_desc->tx_base, + tx_desc->tx_base_pa); + + for (i = 0; i < tx_desc->tx_ring_size; i++) + bd_va++; +} + +/* + * HIF Tx descriptor initialization function. + */ +static int hif_tx_desc_init(struct pfe_ddr_address *pfe_addr) +{ + struct buf_desc *bd_va; + struct buf_desc *bd_pa; + int i; + struct tx_desc_s *tx_desc; + u32 tx_buf_pa; + + /* sanity check */ + if (g_tx_desc) { + printf("%s: HIF Tx desc re-init request\n", __func__); + return 0; + } + + tx_desc = (struct tx_desc_s *)malloc(sizeof(struct tx_desc_s)); + if (!tx_desc) { + printf("%s:%d:Memory allocation failure\n", __func__, + __LINE__); + return -ENOMEM; + } + memset(tx_desc, 0, sizeof(struct tx_desc_s)); + + /* init: Tx ring buffer */ + tx_desc->tx_ring_size = HIF_TX_DESC_NT; + + /* NOTE: must be 64bit aligned */ + bd_va = (struct buf_desc *)(pfe_addr->ddr_pfe_baseaddr + + TX_BD_BASEADDR); + bd_pa = (struct buf_desc *)(pfe_addr->ddr_pfe_phys_baseaddr + + TX_BD_BASEADDR); + + tx_desc->tx_base_pa = (unsigned long)bd_pa; + tx_desc->tx_base = bd_va; + + debug("%s: Tx desc_base: %p, base_pa: %08x, desc_count: %d\n", + __func__, tx_desc->tx_base, tx_desc->tx_base_pa, + tx_desc->tx_ring_size); + + memset(bd_va, 0, sizeof(struct buf_desc) * tx_desc->tx_ring_size); + + tx_buf_pa = pfe_addr->ddr_pfe_phys_baseaddr + HIF_TX_PKT_DDR_BASEADDR; + + for (i = 0; i < tx_desc->tx_ring_size; i++) { + writel((unsigned long)(bd_pa + 1), &bd_va->next); + writel(tx_buf_pa + (i * MAX_FRAME_SIZE), &bd_va->data); + bd_va++; + bd_pa++; + } + --bd_va; + writel((u32)tx_desc->tx_base_pa, &bd_va->next); + + writel(tx_desc->tx_base_pa, HIF_TX_BDP_ADDR); + + g_tx_desc = tx_desc; + + return 0; +} + +/* + * PFE/Class initialization. + */ +static void pfe_class_init(struct pfe_ddr_address *pfe_addr) +{ + struct class_cfg class_cfg = { + .route_table_baseaddr = pfe_addr->ddr_pfe_phys_baseaddr + + ROUTE_TABLE_BASEADDR, + .route_table_hash_bits = ROUTE_TABLE_HASH_BITS, + }; + + class_init(&class_cfg); + + debug("class init complete\n"); +} + +/* + * PFE/TMU initialization. + */ +static void pfe_tmu_init(struct pfe_ddr_address *pfe_addr) +{ + struct tmu_cfg tmu_cfg = { + .llm_base_addr = pfe_addr->ddr_pfe_phys_baseaddr + + TMU_LLM_BASEADDR, + .llm_queue_len = TMU_LLM_QUEUE_LEN, + }; + + tmu_init(&tmu_cfg); + + debug("tmu init complete\n"); +} + +/* + * PFE/BMU (both BMU1 & BMU2) initialization. + */ +static void pfe_bmu_init(struct pfe_ddr_address *pfe_addr) +{ + struct bmu_cfg bmu1_cfg = { + .baseaddr = CBUS_VIRT_TO_PFE(LMEM_BASE_ADDR + + BMU1_LMEM_BASEADDR), + .count = BMU1_BUF_COUNT, + .size = BMU1_BUF_SIZE, + }; + + struct bmu_cfg bmu2_cfg = { + .baseaddr = pfe_addr->ddr_pfe_phys_baseaddr + BMU2_DDR_BASEADDR, + .count = BMU2_BUF_COUNT, + .size = BMU2_BUF_SIZE, + }; + + bmu_init(BMU1_BASE_ADDR, &bmu1_cfg); + debug("bmu1 init: done\n"); + + bmu_init(BMU2_BASE_ADDR, &bmu2_cfg); + debug("bmu2 init: done\n"); +} + +/* + * PFE/GPI initialization function. + * - egpi1, egpi2, egpi3, hgpi + */ +static void pfe_gpi_init(struct pfe_ddr_address *pfe_addr) +{ + struct gpi_cfg egpi1_cfg = { + .lmem_rtry_cnt = EGPI1_LMEM_RTRY_CNT, + .tmlf_txthres = EGPI1_TMLF_TXTHRES, + .aseq_len = EGPI1_ASEQ_LEN, + }; + + struct gpi_cfg egpi2_cfg = { + .lmem_rtry_cnt = EGPI2_LMEM_RTRY_CNT, + .tmlf_txthres = EGPI2_TMLF_TXTHRES, + .aseq_len = EGPI2_ASEQ_LEN, + }; + + struct gpi_cfg hgpi_cfg = { + .lmem_rtry_cnt = HGPI_LMEM_RTRY_CNT, + .tmlf_txthres = HGPI_TMLF_TXTHRES, + .aseq_len = HGPI_ASEQ_LEN, + }; + + gpi_init(EGPI1_BASE_ADDR, &egpi1_cfg); + debug("GPI1 init complete\n"); + + gpi_init(EGPI2_BASE_ADDR, &egpi2_cfg); + debug("GPI2 init complete\n"); + + gpi_init(HGPI_BASE_ADDR, &hgpi_cfg); + debug("HGPI init complete\n"); +} + +/* + * PFE/HIF initialization function. + */ +static int pfe_hif_init(struct pfe_ddr_address *pfe_addr) +{ + int ret = 0; + + hif_tx_disable(); + hif_rx_disable(); + + ret = hif_tx_desc_init(pfe_addr); + if (ret) + return ret; + ret = hif_rx_desc_init(pfe_addr); + if (ret) + return ret; + + hif_init(); + + hif_tx_enable(); + hif_rx_enable(); + + hif_rx_desc_dump(); + hif_tx_desc_dump(); + + debug("HIF init complete\n"); + return ret; +} + +/* + * PFE initialization + * - Firmware loading (CLASS-PE and TMU-PE) + * - BMU1 and BMU2 init + * - GEMAC init + * - GPI init + * - CLASS-PE init + * - TMU-PE init + * - HIF tx and rx descriptors init + * + * @param[in] edev Pointer to eth device structure. + * + * @return 0, on success. + */ +static int pfe_hw_init(struct pfe_ddr_address *pfe_addr) +{ + int ret = 0; + + debug("%s: start\n", __func__); + + writel(0x3, CLASS_PE_SYS_CLK_RATIO); + writel(0x3, TMU_PE_SYS_CLK_RATIO); + writel(0x3, UTIL_PE_SYS_CLK_RATIO); + udelay(10); + + pfe_class_init(pfe_addr); + + pfe_tmu_init(pfe_addr); + + pfe_bmu_init(pfe_addr); + + pfe_gpi_init(pfe_addr); + + ret = pfe_hif_init(pfe_addr); + if (ret) + return ret; + + bmu_enable(BMU1_BASE_ADDR); + debug("bmu1 enabled\n"); + + bmu_enable(BMU2_BASE_ADDR); + debug("bmu2 enabled\n"); + + debug("%s: done\n", __func__); + + return ret; +} + +/* + * PFE driver init function. + * - Initializes pfe_lib + * - pfe hw init + * - fw loading and enables PEs + * - should be executed once. + * + * @param[in] pfe Pointer the pfe control block + */ +int pfe_drv_init(struct pfe_ddr_address *pfe_addr) +{ + int ret = 0; + + pfe_lib_init(); + + ret = pfe_hw_init(pfe_addr); + if (ret) + return ret; + + /* Load the class,TM, Util fw. + * By now pfe is: + * - out of reset + disabled + configured. + * Fw loading should be done after pfe_hw_init() + */ + /* It loads default inbuilt sbl firmware */ + pfe_firmware_init(); + + return ret; +} + +/* + * PFE remove function + * - stops PEs + * - frees tx/rx descriptor resources + * - should be called once. + * + * @param[in] pfe Pointer to pfe control block. + */ +int pfe_eth_remove(struct udevice *dev) +{ + if (g_tx_desc) + free(g_tx_desc); + + if (g_rx_desc) + free(g_rx_desc); + + pfe_firmware_exit(); + + return 0; +} diff --git a/drivers/net/pfe_eth/pfe_eth.c b/drivers/net/pfe_eth/pfe_eth.c new file mode 100644 index 0000000000..e6c6c8c9ab --- /dev/null +++ b/drivers/net/pfe_eth/pfe_eth.c @@ -0,0 +1,297 @@ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm/platform_data/pfe_dm_eth.h> +#include <net.h> +#include <net/pfe_eth/pfe_eth.h> +#include <net/pfe_eth/pfe_mdio.h> + +struct gemac_s gem_info[] = { + /* PORT_0 configuration */ + { + /* GEMAC config */ + .gemac_speed = PFE_MAC_SPEED_1000M, + .gemac_duplex = DUPLEX_FULL, + + /* phy iface */ + .phy_address = CONFIG_PFE_EMAC1_PHY_ADDR, + .phy_mode = PHY_INTERFACE_MODE_SGMII, + }, + /* PORT_1 configuration */ + { + /* GEMAC config */ + .gemac_speed = PFE_MAC_SPEED_1000M, + .gemac_duplex = DUPLEX_FULL, + + /* phy iface */ + .phy_address = CONFIG_PFE_EMAC2_PHY_ADDR, + .phy_mode = PHY_INTERFACE_MODE_RGMII_TXID, + }, +}; + +static inline void pfe_gemac_enable(void *gemac_base) +{ + writel(readl(gemac_base + EMAC_ECNTRL_REG) | + EMAC_ECNTRL_ETHER_EN, gemac_base + EMAC_ECNTRL_REG); +} + +static inline void pfe_gemac_disable(void *gemac_base) +{ + writel(readl(gemac_base + EMAC_ECNTRL_REG) & + ~EMAC_ECNTRL_ETHER_EN, gemac_base + EMAC_ECNTRL_REG); +} + +static inline void pfe_gemac_set_speed(void *gemac_base, u32 speed) +{ + struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR; + u32 ecr = readl(gemac_base + EMAC_ECNTRL_REG) & ~EMAC_ECNTRL_SPEED; + u32 rcr = readl(gemac_base + EMAC_RCNTRL_REG) & ~EMAC_RCNTRL_RMII_10T; + u32 rgmii_pcr = in_be32(&scfg->rgmiipcr) & + ~(SCFG_RGMIIPCR_SETSP_1000M | SCFG_RGMIIPCR_SETSP_10M); + + if (speed == _1000BASET) { + ecr |= EMAC_ECNTRL_SPEED; + rgmii_pcr |= SCFG_RGMIIPCR_SETSP_1000M; + } else if (speed != _100BASET) { + rcr |= EMAC_RCNTRL_RMII_10T; + rgmii_pcr |= SCFG_RGMIIPCR_SETSP_10M; + } + + writel(ecr, gemac_base + EMAC_ECNTRL_REG); + out_be32(&scfg->rgmiipcr, rgmii_pcr | SCFG_RGMIIPCR_SETFD); + + /* remove loop back */ + rcr &= ~EMAC_RCNTRL_LOOP; + /* enable flow control */ + rcr |= EMAC_RCNTRL_FCE; + + /* Enable MII mode */ + rcr |= EMAC_RCNTRL_MII_MODE; + + writel(rcr, gemac_base + EMAC_RCNTRL_REG); + + /* Enable Tx full duplex */ + writel(readl(gemac_base + EMAC_TCNTRL_REG) | EMAC_TCNTRL_FDEN, + gemac_base + EMAC_TCNTRL_REG); +} + +static int pfe_eth_write_hwaddr(struct udevice *dev) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + struct gemac_s *gem = priv->gem; + struct eth_pdata *pdata = dev_get_platdata(dev); + uchar *mac = pdata->enetaddr; + + writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], + gem->gemac_base + EMAC_PHY_ADDR_LOW); + writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, gem->gemac_base + + EMAC_PHY_ADDR_HIGH); + return 0; +} + +/** Stops or Disables GEMAC pointing to this eth iface. + * + * @param[in] edev Pointer to eth device structure. + * + * @return none + */ +static inline void pfe_eth_stop(struct udevice *dev) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + + pfe_gemac_disable(priv->gem->gemac_base); + + gpi_disable(priv->gem->egpi_base); +} + +static int pfe_eth_start(struct udevice *dev) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + struct gemac_s *gem = priv->gem; + int speed; + + /* set ethernet mac address */ + pfe_eth_write_hwaddr(dev); + + writel(EMAC_TFWR, gem->gemac_base + EMAC_TFWR_STR_FWD); + writel(EMAC_RX_SECTION_FULL_32, gem->gemac_base + EMAC_RX_SECTIOM_FULL); + writel(EMAC_TRUNC_FL_16K, gem->gemac_base + EMAC_TRUNC_FL); + writel(EMAC_TX_SECTION_EMPTY_30, gem->gemac_base + + EMAC_TX_SECTION_EMPTY); + writel(EMAC_MIBC_NO_CLR_NO_DIS, gem->gemac_base + + EMAC_MIB_CTRL_STS_REG); + +#ifdef CONFIG_PHYLIB + /* Start up the PHY */ + if (phy_startup(priv->phydev)) { + printf("Could not initialize PHY %s\n", + priv->phydev->dev->name); + return -1; + } + speed = priv->phydev->speed; + printf("Speed detected %x\n", speed); + if (priv->phydev->duplex == DUPLEX_HALF) { + printf("Half duplex not supported\n"); + return -1; + } +#endif + + pfe_gemac_set_speed(gem->gemac_base, speed); + + /* Enable GPI */ + gpi_enable(gem->egpi_base); + + /* Enable GEMAC */ + pfe_gemac_enable(gem->gemac_base); + + return 0; +} + +static int pfe_eth_send(struct udevice *dev, void *packet, int length) +{ + struct pfe_eth_dev *priv = (struct pfe_eth_dev *)dev->priv; + + int rc; + int i = 0; + + rc = pfe_send(priv->gemac_port, packet, length); + + if (rc < 0) { + printf("Tx Queue full\n"); + return rc; + } + + while (1) { + rc = pfe_tx_done(); + if (rc == 0) + break; + + udelay(100); + i++; + if (i == 30000) + printf("Tx timeout, send failed\n"); + break; + } + + return 0; +} + +static int pfe_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + uchar *pkt_buf; + int len; + int phy_port; + + len = pfe_recv(&pkt_buf, &phy_port); + + if (len == 0) + return -EAGAIN; /* no packet in rx */ + else if (len < 0) + return -EAGAIN; + + debug("Rx pkt: pkt_buf(0x%p), phy_port(%d), len(%d)\n", pkt_buf, + phy_port, len); + if (phy_port != priv->gemac_port) { + printf("Rx pkt not on expected port\n"); + return -EAGAIN; + } + + *packetp = pkt_buf; + + return len; +} + +static int pfe_eth_probe(struct udevice *dev) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + struct pfe_ddr_address *pfe_addr; + struct pfe_eth_pdata *pdata = dev_get_platdata(dev); + int ret = 0; + static int init_done; + + if (!init_done) { + pfe_addr = (struct pfe_ddr_address *)malloc(sizeof + (struct pfe_ddr_address)); + if (!pfe_addr) + return -ENOMEM; + + pfe_addr->ddr_pfe_baseaddr = + (void *)pdata->pfe_ddr_addr.ddr_pfe_baseaddr; + pfe_addr->ddr_pfe_phys_baseaddr = + (unsigned long)pdata->pfe_ddr_addr.ddr_pfe_phys_baseaddr; + + debug("ddr_pfe_baseaddr: %p, ddr_pfe_phys_baseaddr: %08x\n", + pfe_addr->ddr_pfe_baseaddr, + (u32)pfe_addr->ddr_pfe_phys_baseaddr); + + ret = pfe_drv_init(pfe_addr); + if (ret) + return ret; + + init_pfe_scfg_dcfg_regs(); + init_done = 1; + } + + priv->gemac_port = pdata->pfe_eth_pdata_mac.phy_interface; + priv->gem = &gem_info[priv->gemac_port]; + priv->dev = dev; + + switch (priv->gemac_port) { + case EMAC_PORT_0: + default: + priv->gem->gemac_base = EMAC1_BASE_ADDR; + priv->gem->egpi_base = EGPI1_BASE_ADDR; + break; + case EMAC_PORT_1: + priv->gem->gemac_base = EMAC2_BASE_ADDR; + priv->gem->egpi_base = EGPI2_BASE_ADDR; + break; + } + + ret = pfe_eth_board_init(dev); + if (ret) + return ret; + +#if defined(CONFIG_PHYLIB) + ret = pfe_phy_configure(priv, pdata->pfe_eth_pdata_mac.phy_interface, + gem_info[priv->gemac_port].phy_address); +#endif + return ret; +} + +static int pfe_eth_bind(struct udevice *dev) +{ + struct pfe_eth_pdata *pdata = dev_get_platdata(dev); + char name[20]; + + sprintf(name, "pfe_eth%u", pdata->pfe_eth_pdata_mac.phy_interface); + + return device_set_name(dev, name); +} + +static const struct eth_ops pfe_eth_ops = { + .start = pfe_eth_start, + .send = pfe_eth_send, + .recv = pfe_eth_recv, + .free_pkt = pfe_eth_free_pkt, + .stop = pfe_eth_stop, + .write_hwaddr = pfe_eth_write_hwaddr, +}; + +U_BOOT_DRIVER(pfe_eth) = { + .name = "pfe_eth", + .id = UCLASS_ETH, + .bind = pfe_eth_bind, + .probe = pfe_eth_probe, + .remove = pfe_eth_remove, + .ops = &pfe_eth_ops, + .priv_auto_alloc_size = sizeof(struct pfe_eth_dev), + .platdata_auto_alloc_size = sizeof(struct pfe_eth_pdata) +}; diff --git a/drivers/net/pfe_eth/pfe_firmware.c b/drivers/net/pfe_eth/pfe_firmware.c new file mode 100644 index 0000000000..9dc063dc75 --- /dev/null +++ b/drivers/net/pfe_eth/pfe_firmware.c @@ -0,0 +1,230 @@ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * @file + * Contains all the functions to handle parsing and loading of PE firmware + * files. + */ + +#include <net/pfe_eth/pfe_eth.h> +#include <net/pfe_eth/pfe_firmware.h> + +#define PFE_FIRMEWARE_FIT_CNF_NAME "config@1" + +static const void *pfe_fit_addr = (void *)CONFIG_SYS_LS_PFE_FW_ADDR; + +/* + * PFE elf firmware loader. + * Loads an elf firmware image into a list of PE's (specified using a bitmask) + * + * @param pe_mask Mask of PE id's to load firmware to + * @param pfe_firmware Pointer to the firmware image + * + * @return 0 on success, a negative value on error + */ +static int pfe_load_elf(int pe_mask, uint8_t *pfe_firmware) +{ + Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *)pfe_firmware; + Elf32_Half sections = be16_to_cpu(elf_hdr->e_shnum); + Elf32_Shdr *shdr = (Elf32_Shdr *)(pfe_firmware + + be32_to_cpu(elf_hdr->e_shoff)); + int id, section; + int ret; + + debug("%s: no of sections: %d\n", __func__, sections); + + /* Some sanity checks */ + if (strncmp((char *)&elf_hdr->e_ident[EI_MAG0], ELFMAG, SELFMAG)) { + printf("%s: incorrect elf magic number\n", __func__); + return -1; + } + + if (elf_hdr->e_ident[EI_CLASS] != ELFCLASS32) { + printf("%s: incorrect elf class(%x)\n", __func__, + elf_hdr->e_ident[EI_CLASS]); + return -1; + } + + if (elf_hdr->e_ident[EI_DATA] != ELFDATA2MSB) { + printf("%s: incorrect elf data(%x)\n", __func__, + elf_hdr->e_ident[EI_DATA]); + return -1; + } + + if (be16_to_cpu(elf_hdr->e_type) != ET_EXEC) { + printf("%s: incorrect elf file type(%x)\n", __func__, + be16_to_cpu(elf_hdr->e_type)); + return -1; + } + + for (section = 0; section < sections; section++, shdr++) { + if (!(be32_to_cpu(shdr->sh_flags) & (SHF_WRITE | SHF_ALLOC | + SHF_EXECINSTR))) + continue; + for (id = 0; id < MAX_PE; id++) + if (pe_mask & BIT(id)) { + ret = pe_load_elf_section(id, + pfe_firmware, shdr); + if (ret < 0) + goto err; + } + } + return 0; + +err: + return ret; +} + +/* + * Get PFE firmware from FIT image + * + * @param data pointer to PFE firmware + * @param size pointer to size of the firmware + * @param fw_name pfe firmware name, either class or tmu + * + * @return 0 on success, a negative value on error + */ +static int pfe_get_fw(const void **data, + size_t *size, char *fw_name) +{ + int conf_node_off, fw_node_off; + char *conf_node_name = NULL; + char *desc; + int ret = 0; + + conf_node_name = PFE_FIRMEWARE_FIT_CNF_NAME; + + conf_node_off = fit_conf_get_node(pfe_fit_addr, conf_node_name); + if (conf_node_off < 0) { + printf("PFE Firmware: %s: no such config\n", conf_node_name); + return -ENOENT; + } + + fw_node_off = fit_conf_get_prop_node(pfe_fit_addr, conf_node_off, + fw_name); + if (fw_node_off < 0) { + printf("PFE Firmware: No '%s' in config\n", + fw_name); + return -ENOLINK; + } + + if (!(fit_image_verify(pfe_fit_addr, fw_node_off))) { + printf("PFE Firmware: Bad firmware image (bad CRC)\n"); + return -EINVAL; + } + + if (fit_image_get_data(pfe_fit_addr, fw_node_off, data, size)) { + printf("PFE Firmware: Can't get %s subimage data/size", + fw_name); + return -ENOENT; + } + + ret = fit_get_desc(pfe_fit_addr, fw_node_off, &desc); + if (ret) + printf("PFE Firmware: Can't get description\n"); + else + printf("%s\n", desc); + + return ret; +} + +/* + * Check PFE FIT image + * + * @return 0 on success, a negative value on error + */ +static int pfe_fit_check(void) +{ + int ret = 0; + + ret = fdt_check_header(pfe_fit_addr); + if (ret) { + printf("PFE Firmware: Bad firmware image (not a FIT image)\n"); + return ret; + } + + if (!fit_check_format(pfe_fit_addr)) { + printf("PFE Firmware: Bad firmware image (bad FIT header)\n"); + ret = -1; + return ret; + } + + return ret; +} + +/* + * PFE firmware initialization. + * Loads different firmware files from FIT image. + * Initializes PE IMEM/DMEM and UTIL-PE DDR + * Initializes control path symbol addresses (by looking them up in the elf + * firmware files + * Takes PE's out of reset + * + * @return 0 on success, a negative value on error + */ +int pfe_firmware_init(void) +{ + char *pfe_firmware_name; + const void *raw_image_addr; + size_t raw_image_size = 0; + u8 *pfe_firmware; + int ret = 0; + int fw_count; + + ret = pfe_fit_check(); + if (ret) + goto err; + + for (fw_count = 0; fw_count < 2; fw_count++) { + if (fw_count == 0) + pfe_firmware_name = "class"; + else if (fw_count == 1) + pfe_firmware_name = "tmu"; + + pfe_get_fw(&raw_image_addr, &raw_image_size, pfe_firmware_name); + pfe_firmware = malloc(raw_image_size); + if (!pfe_firmware) + return -ENOMEM; + memcpy((void *)pfe_firmware, (void *)raw_image_addr, + raw_image_size); + + if (fw_count == 0) + ret = pfe_load_elf(CLASS_MASK, pfe_firmware); + else if (fw_count == 1) + ret = pfe_load_elf(TMU_MASK, pfe_firmware); + + if (ret < 0) { + printf("%s: %s firmware load failed\n", __func__, + pfe_firmware_name); + goto err; + } + debug("%s: %s firmware loaded\n", __func__, pfe_firmware_name); + free(pfe_firmware); + } + + tmu_enable(0xb); + class_enable(); + gpi_enable(HGPI_BASE_ADDR); + +err: + return ret; +} + +/* + * PFE firmware cleanup + * Puts PE's in reset + */ +void pfe_firmware_exit(void) +{ + debug("%s\n", __func__); + + class_disable(); + tmu_disable(0xf); + hif_tx_disable(); + hif_rx_disable(); +} diff --git a/drivers/net/pfe_eth/pfe_hw.c b/drivers/net/pfe_eth/pfe_hw.c new file mode 100644 index 0000000000..12bb0da9b9 --- /dev/null +++ b/drivers/net/pfe_eth/pfe_hw.c @@ -0,0 +1,999 @@ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier:GPL-2.0+ + */ +#include <net/pfe_eth/pfe_eth.h> +#include <net/pfe_eth/pfe/pfe_hw.h> + +static struct pe_info pe[MAX_PE]; + +/* + * Initializes the PFE library. + * Must be called before using any of the library functions. + */ +void pfe_lib_init(void) +{ + int pfe_pe_id; + + for (pfe_pe_id = CLASS0_ID; pfe_pe_id <= CLASS_MAX_ID; pfe_pe_id++) { + pe[pfe_pe_id].dmem_base_addr = + (u32)CLASS_DMEM_BASE_ADDR(pfe_pe_id); + pe[pfe_pe_id].pmem_base_addr = + (u32)CLASS_IMEM_BASE_ADDR(pfe_pe_id); + pe[pfe_pe_id].pmem_size = (u32)CLASS_IMEM_SIZE; + pe[pfe_pe_id].mem_access_wdata = + (void *)CLASS_MEM_ACCESS_WDATA; + pe[pfe_pe_id].mem_access_addr = (void *)CLASS_MEM_ACCESS_ADDR; + pe[pfe_pe_id].mem_access_rdata = (void *)CLASS_MEM_ACCESS_RDATA; + } + + for (pfe_pe_id = TMU0_ID; pfe_pe_id <= TMU_MAX_ID; pfe_pe_id++) { + if (pfe_pe_id == TMU2_ID) + continue; + pe[pfe_pe_id].dmem_base_addr = + (u32)TMU_DMEM_BASE_ADDR(pfe_pe_id - TMU0_ID); + pe[pfe_pe_id].pmem_base_addr = + (u32)TMU_IMEM_BASE_ADDR(pfe_pe_id - TMU0_ID); + pe[pfe_pe_id].pmem_size = (u32)TMU_IMEM_SIZE; + pe[pfe_pe_id].mem_access_wdata = (void *)TMU_MEM_ACCESS_WDATA; + pe[pfe_pe_id].mem_access_addr = (void *)TMU_MEM_ACCESS_ADDR; + pe[pfe_pe_id].mem_access_rdata = (void *)TMU_MEM_ACCESS_RDATA; + } +} + +/* + * Writes a buffer to PE internal memory from the host + * through indirect access registers. + * + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] mem_access_addr DMEM destination address (must be 32bit + * aligned) + * @param[in] src Buffer source address + * @param[in] len Number of bytes to copy + */ +static void pe_mem_memcpy_to32(int id, u32 mem_access_addr, const void *src, + unsigned int len) +{ + u32 offset = 0, val, addr; + unsigned int len32 = len >> 2; + int i; + + addr = mem_access_addr | PE_MEM_ACCESS_WRITE | + PE_MEM_ACCESS_BYTE_ENABLE(0, 4); + + for (i = 0; i < len32; i++, offset += 4, src += 4) { + val = *(u32 *)src; + writel(cpu_to_be32(val), pe[id].mem_access_wdata); + writel(addr + offset, pe[id].mem_access_addr); + } + + len = (len & 0x3); + if (len) { + val = 0; + + addr = (mem_access_addr | PE_MEM_ACCESS_WRITE | + PE_MEM_ACCESS_BYTE_ENABLE(0, len)) + offset; + + for (i = 0; i < len; i++, src++) + val |= (*(u8 *)src) << (8 * i); + + writel(cpu_to_be32(val), pe[id].mem_access_wdata); + writel(addr, pe[id].mem_access_addr); + } +} + +/* + * Writes a buffer to PE internal data memory (DMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] dst DMEM destination address (must be 32bit + * aligned) + * @param[in] src Buffer source address + * @param[in] len Number of bytes to copy + */ +static void pe_dmem_memcpy_to32(int id, u32 dst, const void *src, + unsigned int len) +{ + pe_mem_memcpy_to32(id, pe[id].dmem_base_addr | dst | PE_MEM_ACCESS_DMEM, + src, len); +} + +/* + * Writes a buffer to PE internal program memory (PMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., TMU3_ID) + * @param[in] dst PMEM destination address (must be 32bit + * aligned) + * @param[in] src Buffer source address + * @param[in] len Number of bytes to copy + */ +static void pe_pmem_memcpy_to32(int id, u32 dst, const void *src, + unsigned int len) +{ + pe_mem_memcpy_to32(id, pe[id].pmem_base_addr | (dst & (pe[id].pmem_size + - 1)) | PE_MEM_ACCESS_IMEM, src, len); +} + +/* + * Reads PE internal program memory (IMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., TMU3_ID) + * @param[in] addr PMEM read address (must be aligned on size) + * @param[in] size Number of bytes to read (maximum 4, must not + * cross 32bit boundaries) + * @return the data read (in PE endianness, i.e BE). + */ +u32 pe_pmem_read(int id, u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + u32 mask = 0xffffffff >> ((4 - size) << 3); + u32 val; + + addr = pe[id].pmem_base_addr | ((addr & ~0x3) & (pe[id].pmem_size - 1)) + | PE_MEM_ACCESS_READ | PE_MEM_ACCESS_IMEM | + PE_MEM_ACCESS_BYTE_ENABLE(offset, size); + + writel(addr, pe[id].mem_access_addr); + val = be32_to_cpu(readl(pe[id].mem_access_rdata)); + + return (val >> (offset << 3)) & mask; +} + +/* + * Writes PE internal data memory (DMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] val Value to write (in PE endianness, i.e BE) + * @param[in] addr DMEM write address (must be aligned on size) + * @param[in] size Number of bytes to write (maximum 4, must not + * cross 32bit boundaries) + */ +void pe_dmem_write(int id, u32 val, u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + + addr = pe[id].dmem_base_addr | (addr & ~0x3) | PE_MEM_ACCESS_WRITE | + PE_MEM_ACCESS_DMEM | PE_MEM_ACCESS_BYTE_ENABLE(offset, size); + + /* Indirect access interface is byte swapping data being written */ + writel(cpu_to_be32(val << (offset << 3)), pe[id].mem_access_wdata); + writel(addr, pe[id].mem_access_addr); +} + +/* + * Reads PE internal data memory (DMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] addr DMEM read address (must be aligned on size) + * @param[in] size Number of bytes to read (maximum 4, must not + * cross 32bit boundaries) + * @return the data read (in PE endianness, i.e BE). + */ +u32 pe_dmem_read(int id, u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + u32 mask = 0xffffffff >> ((4 - size) << 3); + u32 val; + + addr = pe[id].dmem_base_addr | (addr & ~0x3) | PE_MEM_ACCESS_READ | + PE_MEM_ACCESS_DMEM | PE_MEM_ACCESS_BYTE_ENABLE(offset, size); + + writel(addr, pe[id].mem_access_addr); + + /* Indirect access interface is byte swapping data being read */ + val = be32_to_cpu(readl(pe[id].mem_access_rdata)); + + return (val >> (offset << 3)) & mask; +} + +/* + * This function is used to write to CLASS internal bus peripherals (ccu, + * pe-lem) from the host + * through indirect access registers. + * @param[in] val value to write + * @param[in] addr Address to write to (must be aligned on size) + * @param[in] size Number of bytes to write (1, 2 or 4) + * + */ +static void class_bus_write(u32 val, u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + + writel((addr & CLASS_BUS_ACCESS_BASE_MASK), CLASS_BUS_ACCESS_BASE); + + addr = (addr & ~CLASS_BUS_ACCESS_BASE_MASK) | PE_MEM_ACCESS_WRITE | + (size << 24); + + writel(cpu_to_be32(val << (offset << 3)), CLASS_BUS_ACCESS_WDATA); + writel(addr, CLASS_BUS_ACCESS_ADDR); +} + +/* + * Reads from CLASS internal bus peripherals (ccu, pe-lem) from the host + * through indirect access registers. + * @param[in] addr Address to read from (must be aligned on size) + * @param[in] size Number of bytes to read (1, 2 or 4) + * @return the read data + */ +static u32 class_bus_read(u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + u32 mask = 0xffffffff >> ((4 - size) << 3); + u32 val; + + writel((addr & CLASS_BUS_ACCESS_BASE_MASK), CLASS_BUS_ACCESS_BASE); + + addr = (addr & ~CLASS_BUS_ACCESS_BASE_MASK) | (size << 24); + + writel(addr, CLASS_BUS_ACCESS_ADDR); + val = be32_to_cpu(readl(CLASS_BUS_ACCESS_RDATA)); + + return (val >> (offset << 3)) & mask; +} + +/* + * Writes data to the cluster memory (PE_LMEM) + * @param[in] dst PE LMEM destination address (must be 32bit aligned) + * @param[in] src Buffer source address + * @param[in] len Number of bytes to copy + */ +static void class_pe_lmem_memcpy_to32(u32 dst, const void *src, + unsigned int len) +{ + u32 len32 = len >> 2; + int i; + + for (i = 0; i < len32; i++, src += 4, dst += 4) + class_bus_write(*(u32 *)src, dst, 4); + + if (len & 0x2) { + class_bus_write(*(u16 *)src, dst, 2); + src += 2; + dst += 2; + } + + if (len & 0x1) { + class_bus_write(*(u8 *)src, dst, 1); + src++; + dst++; + } +} + +/* + * Writes value to the cluster memory (PE_LMEM) + * @param[in] dst PE LMEM destination address (must be 32bit aligned) + * @param[in] val Value to write + * @param[in] len Number of bytes to write + */ +static void class_pe_lmem_memset(u32 dst, int val, unsigned int len) +{ + u32 len32 = len >> 2; + int i; + + val = val | (val << 8) | (val << 16) | (val << 24); + + for (i = 0; i < len32; i++, dst += 4) + class_bus_write(val, dst, 4); + + if (len & 0x2) { + class_bus_write(val, dst, 2); + dst += 2; + } + + if (len & 0x1) { + class_bus_write(val, dst, 1); + dst++; + } +} + +/* + * Reads data from the cluster memory (PE_LMEM) + * @param[out] dst pointer to the source buffer data are copied to + * @param[in] len length in bytes of the amount of data to read + * from cluster memory + * @param[in] offset offset in bytes in the cluster memory where data are + * read from + */ +void pe_lmem_read(u32 *dst, u32 len, u32 offset) +{ + u32 len32 = len >> 2; + int i = 0; + + for (i = 0; i < len32; dst++, i++, offset += 4) + *dst = class_bus_read(PE_LMEM_BASE_ADDR + offset, 4); + + if (len & 0x03) + *dst = class_bus_read(PE_LMEM_BASE_ADDR + offset, (len & 0x03)); +} + +/* + * Writes data to the cluster memory (PE_LMEM) + * @param[in] src pointer to the source buffer data are copied from + * @param[in] len length in bytes of the amount of data to write to the + * cluster memory + * @param[in] offset offset in bytes in the cluster memory where data are + * written to + */ +void pe_lmem_write(u32 *src, u32 len, u32 offset) +{ + u32 len32 = len >> 2; + int i = 0; + + for (i = 0; i < len32; src++, i++, offset += 4) + class_bus_write(*src, PE_LMEM_BASE_ADDR + offset, 4); + + if (len & 0x03) + class_bus_write(*src, PE_LMEM_BASE_ADDR + offset, (len & + 0x03)); +} + +/* + * Loads an elf section into pmem + * Code needs to be at least 16bit aligned and only PROGBITS sections are + * supported + * + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., + * TMU3_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +static int pe_load_pmem_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 offset = be32_to_cpu(shdr->sh_offset); + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + u32 type = be32_to_cpu(shdr->sh_type); + + if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { + printf( + "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n", + __func__, addr, (unsigned long)data + offset); + + return -1; + } + + if (addr & 0x1) { + printf("%s: load address(%x) is not 16bit aligned\n", + __func__, addr); + return -1; + } + + if (size & 0x1) { + printf("%s: load size(%x) is not 16bit aligned\n", __func__, + size); + return -1; + } + + debug("pmem pe%d @%x len %d\n", id, addr, size); + switch (type) { + case SHT_PROGBITS: + pe_pmem_memcpy_to32(id, addr, data + offset, size); + break; + + default: + printf("%s: unsupported section type(%x)\n", __func__, type); + return -1; + } + + return 0; +} + +/* + * Loads an elf section into dmem + * Data needs to be at least 32bit aligned, NOBITS sections are correctly + * initialized to 0 + * + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +static int pe_load_dmem_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 offset = be32_to_cpu(shdr->sh_offset); + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + u32 type = be32_to_cpu(shdr->sh_type); + u32 size32 = size >> 2; + int i; + + if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { + printf( + "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n", + __func__, addr, (unsigned long)data + offset); + + return -1; + } + + if (addr & 0x3) { + printf("%s: load address(%x) is not 32bit aligned\n", + __func__, addr); + return -1; + } + + switch (type) { + case SHT_PROGBITS: + debug("dmem pe%d @%x len %d\n", id, addr, size); + pe_dmem_memcpy_to32(id, addr, data + offset, size); + break; + + case SHT_NOBITS: + debug("dmem zero pe%d @%x len %d\n", id, addr, size); + for (i = 0; i < size32; i++, addr += 4) + pe_dmem_write(id, 0, addr, 4); + + if (size & 0x3) + pe_dmem_write(id, 0, addr, size & 0x3); + + break; + + default: + printf("%s: unsupported section type(%x)\n", __func__, type); + return -1; + } + + return 0; +} + +/* + * Loads an elf section into DDR + * Data needs to be at least 32bit aligned, NOBITS sections are correctly + * initialized to 0 + * + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +static int pe_load_ddr_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 offset = be32_to_cpu(shdr->sh_offset); + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + u32 type = be32_to_cpu(shdr->sh_type); + u32 flags = be32_to_cpu(shdr->sh_flags); + + switch (type) { + case SHT_PROGBITS: + debug("ddr pe%d @%x len %d\n", id, addr, size); + if (flags & SHF_EXECINSTR) { + if (id <= CLASS_MAX_ID) { + /* DO the loading only once in DDR */ + if (id == CLASS0_ID) { + debug( + "%s: load address(%x) and elf file address(%lx) rcvd\n" + , __func__, addr, + (unsigned long)data + offset); + if (((unsigned long)(data + offset) + & 0x3) != (addr & 0x3)) { + printf( + "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n", + __func__, addr, + (unsigned long)data + + offset); + + return -1; + } + + if (addr & 0x1) { + printf( + "%s: load address(%x) is not 16bit aligned\n" + , __func__, addr); + return -1; + } + + if (size & 0x1) { + printf( + "%s: load length(%x) is not 16bit aligned\n" + , __func__, size); + return -1; + } + + memcpy((void *)DDR_PFE_TO_VIRT(addr), + data + offset, size); + } + } else { + printf( + "%s: unsupported ddr section type(%x) for PE(%d)\n" + , __func__, type, id); + return -1; + } + + } else { + memcpy((void *)DDR_PFE_TO_VIRT(addr), data + offset, + size); + } + + break; + + case SHT_NOBITS: + debug("ddr zero pe%d @%x len %d\n", id, addr, size); + memset((void *)DDR_PFE_TO_VIRT(addr), 0, size); + + break; + + default: + printf("%s: unsupported section type(%x)\n", __func__, type); + return -1; + } + + return 0; +} + +/* + * Loads an elf section into pe lmem + * Data needs to be at least 32bit aligned, NOBITS sections are correctly + * initialized to 0 + * + * @param[in] id PE identification (CLASS0_ID,..., CLASS5_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +static int pe_load_pe_lmem_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 offset = be32_to_cpu(shdr->sh_offset); + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + u32 type = be32_to_cpu(shdr->sh_type); + + if (id > CLASS_MAX_ID) { + printf("%s: unsupported pe-lmem section type(%x) for PE(%d)\n", + __func__, type, id); + return -1; + } + + if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { + printf( + "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n", + __func__, addr, (unsigned long)data + offset); + + return -1; + } + + if (addr & 0x3) { + printf("%s: load address(%x) is not 32bit aligned\n", + __func__, addr); + return -1; + } + + debug("lmem pe%d @%x len %d\n", id, addr, size); + + switch (type) { + case SHT_PROGBITS: + class_pe_lmem_memcpy_to32(addr, data + offset, size); + break; + + case SHT_NOBITS: + class_pe_lmem_memset(addr, 0, size); + break; + + default: + printf("%s: unsupported section type(%x)\n", __func__, type); + return -1; + } + + return 0; +} + +/* + * Loads an elf section into a PE + * For now only supports loading a section to dmem (all PE's), pmem (class and + * tmu PE's), DDDR (util PE code) + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +int pe_load_elf_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + + if (IS_DMEM(addr, size)) + return pe_load_dmem_section(id, data, shdr); + else if (IS_PMEM(addr, size)) + return pe_load_pmem_section(id, data, shdr); + else if (IS_PFE_LMEM(addr, size)) + return 0; + else if (IS_PHYS_DDR(addr, size)) + return pe_load_ddr_section(id, data, shdr); + else if (IS_PE_LMEM(addr, size)) + return pe_load_pe_lmem_section(id, data, shdr); + + printf("%s: unsupported memory range(%x)\n", __func__, addr); + + return 0; +} + +/**************************** BMU ***************************/ +/* + * Resets a BMU block. + * @param[in] base BMU block base address + */ +static inline void bmu_reset(void *base) +{ + writel(CORE_SW_RESET, base + BMU_CTRL); + + /* Wait for self clear */ + while (readl(base + BMU_CTRL) & CORE_SW_RESET) + ; +} + +/* + * Enabled a BMU block. + * @param[in] base BMU block base address + */ +void bmu_enable(void *base) +{ + writel(CORE_ENABLE, base + BMU_CTRL); +} + +/* + * Disables a BMU block. + * @param[in] base BMU block base address + */ +static inline void bmu_disable(void *base) +{ + writel(CORE_DISABLE, base + BMU_CTRL); +} + +/* + * Sets the configuration of a BMU block. + * @param[in] base BMU block base address + * @param[in] cfg BMU configuration + */ +static inline void bmu_set_config(void *base, struct bmu_cfg *cfg) +{ + writel(cfg->baseaddr, base + BMU_UCAST_BASE_ADDR); + writel(cfg->count & 0xffff, base + BMU_UCAST_CONFIG); + writel(cfg->size & 0xffff, base + BMU_BUF_SIZE); + + /* Interrupts are never used */ + writel(0x0, base + BMU_INT_ENABLE); +} + +/* + * Initializes a BMU block. + * @param[in] base BMU block base address + * @param[in] cfg BMU configuration + */ +void bmu_init(void *base, struct bmu_cfg *cfg) +{ + bmu_disable(base); + + bmu_set_config(base, cfg); + + bmu_reset(base); +} + +/**************************** GPI ***************************/ +/* + * Resets a GPI block. + * @param[in] base GPI base address + */ +static inline void gpi_reset(void *base) +{ + writel(CORE_SW_RESET, base + GPI_CTRL); +} + +/* + * Enables a GPI block. + * @param[in] base GPI base address + */ +void gpi_enable(void *base) +{ + writel(CORE_ENABLE, base + GPI_CTRL); +} + +/* + * Disables a GPI block. + * @param[in] base GPI base address + */ +void gpi_disable(void *base) +{ + writel(CORE_DISABLE, base + GPI_CTRL); +} + +/* + * Sets the configuration of a GPI block. + * @param[in] base GPI base address + * @param[in] cfg GPI configuration + */ +static inline void gpi_set_config(void *base, struct gpi_cfg *cfg) +{ + writel(CBUS_VIRT_TO_PFE(BMU1_BASE_ADDR + BMU_ALLOC_CTRL), base + + GPI_LMEM_ALLOC_ADDR); + writel(CBUS_VIRT_TO_PFE(BMU1_BASE_ADDR + BMU_FREE_CTRL), base + + GPI_LMEM_FREE_ADDR); + writel(CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_ALLOC_CTRL), base + + GPI_DDR_ALLOC_ADDR); + writel(CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_FREE_CTRL), base + + GPI_DDR_FREE_ADDR); + writel(CBUS_VIRT_TO_PFE(CLASS_INQ_PKTPTR), base + GPI_CLASS_ADDR); + writel(DDR_HDR_SIZE, base + GPI_DDR_DATA_OFFSET); + writel(LMEM_HDR_SIZE, base + GPI_LMEM_DATA_OFFSET); + writel(0, base + GPI_LMEM_SEC_BUF_DATA_OFFSET); + writel(0, base + GPI_DDR_SEC_BUF_DATA_OFFSET); + writel((DDR_HDR_SIZE << 16) | LMEM_HDR_SIZE, base + GPI_HDR_SIZE); + writel((DDR_BUF_SIZE << 16) | LMEM_BUF_SIZE, base + GPI_BUF_SIZE); + + writel(((cfg->lmem_rtry_cnt << 16) | (GPI_DDR_BUF_EN << 1) | + GPI_LMEM_BUF_EN), base + GPI_RX_CONFIG); + writel(cfg->tmlf_txthres, base + GPI_TMLF_TX); + writel(cfg->aseq_len, base + GPI_DTX_ASEQ); + + /*Make GPI AXI transactions non-bufferable */ + writel(0x1, base + GPI_AXI_CTRL); +} + +/* + * Initializes a GPI block. + * @param[in] base GPI base address + * @param[in] cfg GPI configuration + */ +void gpi_init(void *base, struct gpi_cfg *cfg) +{ + gpi_reset(base); + + gpi_disable(base); + + gpi_set_config(base, cfg); +} + +/**************************** CLASSIFIER ***************************/ +/* + * Resets CLASSIFIER block. + */ +static inline void class_reset(void) +{ + writel(CORE_SW_RESET, CLASS_TX_CTRL); +} + +/* + * Enables all CLASS-PE's cores. + */ +void class_enable(void) +{ + writel(CORE_ENABLE, CLASS_TX_CTRL); +} + +/* + * Disables all CLASS-PE's cores. + */ +void class_disable(void) +{ + writel(CORE_DISABLE, CLASS_TX_CTRL); +} + +/* + * Sets the configuration of the CLASSIFIER block. + * @param[in] cfg CLASSIFIER configuration + */ +static inline void class_set_config(struct class_cfg *cfg) +{ + if (PLL_CLK_EN == 0) { + /* Clock ratio: for 1:1 the value is 0 */ + writel(0x0, CLASS_PE_SYS_CLK_RATIO); + } else { + /* Clock ratio: for 1:2 the value is 1 */ + writel(0x1, CLASS_PE_SYS_CLK_RATIO); + } + writel((DDR_HDR_SIZE << 16) | LMEM_HDR_SIZE, CLASS_HDR_SIZE); + writel(LMEM_BUF_SIZE, CLASS_LMEM_BUF_SIZE); + writel(CLASS_ROUTE_ENTRY_SIZE(CLASS_ROUTE_SIZE) | + CLASS_ROUTE_HASH_SIZE(cfg->route_table_hash_bits), + CLASS_ROUTE_HASH_ENTRY_SIZE); + writel(HASH_CRC_PORT_IP | QB2BUS_LE, CLASS_ROUTE_MULTI); + + writel(cfg->route_table_baseaddr, CLASS_ROUTE_TABLE_BASE); + memset((void *)DDR_PFE_TO_VIRT(cfg->route_table_baseaddr), 0, + ROUTE_TABLE_SIZE); + + writel(CLASS_PE0_RO_DM_ADDR0_VAL, CLASS_PE0_RO_DM_ADDR0); + writel(CLASS_PE0_RO_DM_ADDR1_VAL, CLASS_PE0_RO_DM_ADDR1); + writel(CLASS_PE0_QB_DM_ADDR0_VAL, CLASS_PE0_QB_DM_ADDR0); + writel(CLASS_PE0_QB_DM_ADDR1_VAL, CLASS_PE0_QB_DM_ADDR1); + writel(CBUS_VIRT_TO_PFE(TMU_PHY_INQ_PKTPTR), CLASS_TM_INQ_ADDR); + + writel(23, CLASS_AFULL_THRES); + writel(23, CLASS_TSQ_FIFO_THRES); + + writel(24, CLASS_MAX_BUF_CNT); + writel(24, CLASS_TSQ_MAX_CNT); + + /*Make Class AXI transactions non-bufferable */ + writel(0x1, CLASS_AXI_CTRL); + + /*Make Util AXI transactions non-bufferable */ + /*Util is disabled in U-boot, do it from here */ + writel(0x1, UTIL_AXI_CTRL); +} + +/* + * Initializes CLASSIFIER block. + * @param[in] cfg CLASSIFIER configuration + */ +void class_init(struct class_cfg *cfg) +{ + class_reset(); + + class_disable(); + + class_set_config(cfg); +} + +/**************************** TMU ***************************/ +/* + * Enables TMU-PE cores. + * @param[in] pe_mask TMU PE mask + */ +void tmu_enable(u32 pe_mask) +{ + writel(readl(TMU_TX_CTRL) | (pe_mask & 0xF), TMU_TX_CTRL); +} + +/* + * Disables TMU cores. + * @param[in] pe_mask TMU PE mask + */ +void tmu_disable(u32 pe_mask) +{ + writel(readl(TMU_TX_CTRL) & ~(pe_mask & 0xF), TMU_TX_CTRL); +} + +/* + * Initializes TMU block. + * @param[in] cfg TMU configuration + */ +void tmu_init(struct tmu_cfg *cfg) +{ + int q, phyno; + + /* keep in soft reset */ + writel(SW_RESET, TMU_CTRL); + + /*Make Class AXI transactions non-bufferable */ + writel(0x1, TMU_AXI_CTRL); + + /* enable EMAC PHY ports */ + writel(0x3, TMU_SYS_GENERIC_CONTROL); + + writel(750, TMU_INQ_WATERMARK); + + writel(CBUS_VIRT_TO_PFE(EGPI1_BASE_ADDR + GPI_INQ_PKTPTR), + TMU_PHY0_INQ_ADDR); + writel(CBUS_VIRT_TO_PFE(EGPI2_BASE_ADDR + GPI_INQ_PKTPTR), + TMU_PHY1_INQ_ADDR); + + writel(CBUS_VIRT_TO_PFE(HGPI_BASE_ADDR + GPI_INQ_PKTPTR), + TMU_PHY3_INQ_ADDR); + writel(CBUS_VIRT_TO_PFE(HIF_NOCPY_RX_INQ0_PKTPTR), TMU_PHY4_INQ_ADDR); + writel(CBUS_VIRT_TO_PFE(UTIL_INQ_PKTPTR), TMU_PHY5_INQ_ADDR); + writel(CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_FREE_CTRL), + TMU_BMU_INQ_ADDR); + + /* enabling all 10 schedulers [9:0] of each TDQ */ + writel(0x3FF, TMU_TDQ0_SCH_CTRL); + writel(0x3FF, TMU_TDQ1_SCH_CTRL); + writel(0x3FF, TMU_TDQ3_SCH_CTRL); + + if (PLL_CLK_EN == 0) { + /* Clock ratio: for 1:1 the value is 0 */ + writel(0x0, TMU_PE_SYS_CLK_RATIO); + } else { + /* Clock ratio: for 1:2 the value is 1 */ + writel(0x1, TMU_PE_SYS_CLK_RATIO); + } + + /* Extra packet pointers will be stored from this address onwards */ + debug("TMU_LLM_BASE_ADDR %x\n", cfg->llm_base_addr); + writel(cfg->llm_base_addr, TMU_LLM_BASE_ADDR); + + debug("TMU_LLM_QUE_LEN %x\n", cfg->llm_queue_len); + writel(cfg->llm_queue_len, TMU_LLM_QUE_LEN); + + writel(5, TMU_TDQ_IIFG_CFG); + writel(DDR_BUF_SIZE, TMU_BMU_BUF_SIZE); + + writel(0x0, TMU_CTRL); + + /* MEM init */ + writel(MEM_INIT, TMU_CTRL); + + while (!(readl(TMU_CTRL) & MEM_INIT_DONE)) + ; + + /* LLM init */ + writel(LLM_INIT, TMU_CTRL); + + while (!(readl(TMU_CTRL) & LLM_INIT_DONE)) + ; + + /* set up each queue for tail drop */ + for (phyno = 0; phyno < 4; phyno++) { + if (phyno == 2) + continue; + for (q = 0; q < 16; q++) { + u32 qmax; + + writel((phyno << 8) | q, TMU_TEQ_CTRL); + writel(BIT(22), TMU_TEQ_QCFG); + + if (phyno == 3) + qmax = DEFAULT_TMU3_QDEPTH; + else + qmax = (q == 0) ? DEFAULT_Q0_QDEPTH : + DEFAULT_MAX_QDEPTH; + + writel(qmax << 18, TMU_TEQ_HW_PROB_CFG2); + writel(qmax >> 14, TMU_TEQ_HW_PROB_CFG3); + } + } + writel(0x05, TMU_TEQ_DISABLE_DROPCHK); + writel(0, TMU_CTRL); +} + +/**************************** HIF ***************************/ +/* + * Enable hif tx DMA and interrupt + */ +void hif_tx_enable(void) +{ + writel(HIF_CTRL_DMA_EN, HIF_TX_CTRL); +} + +/* + * Disable hif tx DMA and interrupt + */ +void hif_tx_disable(void) +{ + u32 hif_int; + + writel(0, HIF_TX_CTRL); + + hif_int = readl(HIF_INT_ENABLE); + hif_int &= HIF_TXPKT_INT_EN; + writel(hif_int, HIF_INT_ENABLE); +} + +/* + * Enable hif rx DMA and interrupt + */ +void hif_rx_enable(void) +{ + writel((HIF_CTRL_DMA_EN | HIF_CTRL_BDP_CH_START_WSTB), HIF_RX_CTRL); +} + +/* + * Disable hif rx DMA and interrupt + */ +void hif_rx_disable(void) +{ + u32 hif_int; + + writel(0, HIF_RX_CTRL); + + hif_int = readl(HIF_INT_ENABLE); + hif_int &= HIF_RXPKT_INT_EN; + writel(hif_int, HIF_INT_ENABLE); +} + +/* + * Initializes HIF copy block. + */ +void hif_init(void) +{ + /* Initialize HIF registers */ + writel(HIF_RX_POLL_CTRL_CYCLE << 16 | HIF_TX_POLL_CTRL_CYCLE, + HIF_POLL_CTRL); + /* Make HIF AXI transactions non-bufferable */ + writel(0x1, HIF_AXI_CTRL); +} diff --git a/drivers/net/pfe_eth/pfe_mdio.c b/drivers/net/pfe_eth/pfe_mdio.c new file mode 100644 index 0000000000..a78a4d63f3 --- /dev/null +++ b/drivers/net/pfe_eth/pfe_mdio.c @@ -0,0 +1,291 @@ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <dm.h> +#include <dm/platform_data/pfe_dm_eth.h> +#include <net.h> +#include <net/pfe_eth/pfe_eth.h> + +extern struct gemac_s gem_info[]; +#if defined(CONFIG_PHYLIB) + +#define MDIO_TIMEOUT 5000 +static int pfe_write_addr(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr) +{ + void *reg_base = bus->priv; + u32 devadr; + u32 phy; + u32 reg_data; + int timeout = MDIO_TIMEOUT; + + devadr = ((dev_addr & EMAC_MII_DATA_RA_MASK) << EMAC_MII_DATA_RA_SHIFT); + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); + + reg_data = (EMAC_MII_DATA_TA | phy | devadr | reg_addr); + + writel(reg_data, reg_base + EMAC_MII_DATA_REG); + + /* + * wait for the MII interrupt + */ + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { + if (timeout-- <= 0) { + printf("Phy MDIO read/write timeout\n"); + return -1; + } + } + + /* + * clear MII interrupt + */ + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); + + return 0; +} + +static int pfe_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr) +{ + void *reg_base = bus->priv; + u32 reg; + u32 phy; + u32 reg_data; + u16 val; + int timeout = MDIO_TIMEOUT; + + if (dev_addr == MDIO_DEVAD_NONE) { + reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } else { + pfe_write_addr(bus, phy_addr, dev_addr, reg_addr); + reg = ((dev_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } + + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); + + if (dev_addr == MDIO_DEVAD_NONE) + reg_data = (EMAC_MII_DATA_ST | EMAC_MII_DATA_OP_RD | + EMAC_MII_DATA_TA | phy | reg); + else + reg_data = (EMAC_MII_DATA_OP_CL45_RD | EMAC_MII_DATA_TA | + phy | reg); + + writel(reg_data, reg_base + EMAC_MII_DATA_REG); + + /* + * wait for the MII interrupt + */ + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { + if (timeout-- <= 0) { + printf("Phy MDIO read/write timeout\n"); + return -1; + } + } + + /* + * clear MII interrupt + */ + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); + + /* + * it's now safe to read the PHY's register + */ + val = (u16)readl(reg_base + EMAC_MII_DATA_REG); + debug("%s: %p phy: 0x%x reg:0x%08x val:%#x\n", __func__, reg_base, + phy_addr, reg_addr, val); + + return val; +} + +static int pfe_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr, u16 data) +{ + void *reg_base = bus->priv; + u32 reg; + u32 phy; + u32 reg_data; + int timeout = MDIO_TIMEOUT; + int val; + + if (dev_addr == MDIO_DEVAD_NONE) { + reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } else { + pfe_write_addr(bus, phy_addr, dev_addr, reg_addr); + reg = ((dev_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } + + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); + + if (dev_addr == MDIO_DEVAD_NONE) + reg_data = (EMAC_MII_DATA_ST | EMAC_MII_DATA_OP_WR | + EMAC_MII_DATA_TA | phy | reg | data); + else + reg_data = (EMAC_MII_DATA_OP_CL45_WR | EMAC_MII_DATA_TA | + phy | reg | data); + + writel(reg_data, reg_base + EMAC_MII_DATA_REG); + + /* + * wait for the MII interrupt + */ + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { + if (timeout-- <= 0) { + printf("Phy MDIO read/write timeout\n"); + return -1; + } + } + + /* + * clear MII interrupt + */ + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); + + debug("%s: phy: %02x reg:%02x val:%#x\n", __func__, phy_addr, + reg_addr, data); + + return val; +} + +static void pfe_configure_serdes(struct pfe_eth_dev *priv) +{ + struct mii_dev bus; + int value, sgmii_2500 = 0; + struct gemac_s *gem = priv->gem; + + if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) + sgmii_2500 = 1; + + printf("%s %d\n", __func__, priv->gemac_port); + + /* PCS configuration done with corresponding GEMAC */ + bus.priv = gem_info[priv->gemac_port].gemac_base; + + pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x0); + pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x1); + pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x2); + pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x3); + + /* Reset serdes */ + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x0, 0x8000); + + /* SGMII IF mode + AN enable only for 1G SGMII, not for 2.5G */ + value = PHY_SGMII_IF_MODE_SGMII; + if (!sgmii_2500) + value |= PHY_SGMII_IF_MODE_AN; + else + value |= PHY_SGMII_IF_MODE_SGMII_GBT; + + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value); + + /* Dev ability according to SGMII specification */ + value = PHY_SGMII_DEV_ABILITY_SGMII; + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x4, value); + + /* These values taken from validation team */ + if (!sgmii_2500) { + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x0); + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0x400); + } else { + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x7); + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0xa120); + } + + /* Restart AN */ + value = PHY_SGMII_CR_DEF_VAL; + if (!sgmii_2500) + value |= PHY_SGMII_CR_RESET_AN; + /* Disable Auto neg for 2.5G SGMII as it doesn't support auto neg*/ + if (sgmii_2500) + value &= ~PHY_SGMII_ENABLE_AN; + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0, value); +} + +int pfe_phy_configure(struct pfe_eth_dev *priv, int dev_id, int phy_id) +{ + struct phy_device *phydev = NULL; + struct udevice *dev = priv->dev; + struct gemac_s *gem = priv->gem; + struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR; + + if (!gem->bus) + return -1; + + /* Configure SGMII PCS */ + if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII || + gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) { + out_be32(&scfg->mdioselcr, 0x00000000); + pfe_configure_serdes(priv); + } + + mdelay(100); + + /* By this time on-chip SGMII initialization is done + * we can switch mdio interface to external PHYs + */ + out_be32(&scfg->mdioselcr, 0x80000000); + + phydev = phy_connect(gem->bus, phy_id, dev, gem->phy_mode); + if (!phydev) { + printf("phy_connect failed\n"); + return -ENODEV; + } + + phy_config(phydev); + + priv->phydev = phydev; + + return 0; +} +#endif + +struct mii_dev *pfe_mdio_init(struct pfe_mdio_info *mdio_info) +{ + struct mii_dev *bus; + int ret; + u32 mdio_speed; + u32 pclk = 250000000; + + bus = mdio_alloc(); + if (!bus) { + printf("mdio_alloc failed\n"); + return NULL; + } + bus->read = pfe_phy_read; + bus->write = pfe_phy_write; + + /* MAC1 MDIO used to communicate with external PHYS */ + bus->priv = mdio_info->reg_base; + sprintf(bus->name, mdio_info->name); + + /* configure mdio speed */ + mdio_speed = (DIV_ROUND_UP(pclk, 4000000) << EMAC_MII_SPEED_SHIFT); + mdio_speed |= EMAC_HOLDTIME(0x5); + writel(mdio_speed, mdio_info->reg_base + EMAC_MII_CTRL_REG); + + ret = mdio_register(bus); + if (ret) { + printf("mdio_register failed\n"); + free(bus); + return NULL; + } + return bus; +} + +void pfe_set_mdio(int dev_id, struct mii_dev *bus) +{ + gem_info[dev_id].bus = bus; +} + +void pfe_set_phy_address_mode(int dev_id, int phy_id, int phy_mode) +{ + gem_info[dev_id].phy_address = phy_id; + gem_info[dev_id].phy_mode = phy_mode; +} diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 7fd4a8d261..f5821dfed9 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -13,6 +13,21 @@ menuconfig PHYLIB if PHYLIB +config PHY_ADDR_ENABLE + bool "Limit phy address" + default y if ARCH_SUNXI + help + Select this if you want to control which phy address is used + +if PHY_ADDR_ENABLE +config PHY_ADDR + int "PHY address" + default 1 if ARCH_SUNXI + default 0 + help + The address of PHY on MII bus. Usually in range of 0 to 31. +endif + config B53_SWITCH bool "Broadcom BCM53xx (RoboSwitch) Ethernet switch PHY support." help @@ -132,6 +147,16 @@ config PHY_NATSEMI config PHY_REALTEK bool "Realtek Ethernet PHYs support" +config RTL8211E_PINE64_GIGABIT_FIX + bool "Fix gigabit throughput on some Pine64+ models" + depends on PHY_REALTEK + help + Configure the Realtek RTL8211E found on some Pine64+ models differently to + fix throughput on Gigabit links, turning off all internal delays in the + process. The settings that this touches are not documented in the CONFREG + section of the RTL8211E datasheet, but come from Realtek by way of the + Pine64 engineering team. + config RTL8211X_PHY_FORCE_MASTER bool "Ethernet PHY RTL8211x: force 1000BASE-T master mode" depends on PHY_REALTEK diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index ad12f6d61f..6678147545 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -7,6 +7,7 @@ */ #include <config.h> #include <common.h> +#include <dm.h> #include <phy.h> #ifndef CONFIG_PHYLIB_10G diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index 637d89a1e1..9cb3a52c20 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-2.0+ * * Copyright 2014 Freescale Semiconductor, Inc. + * Copyright 2018 NXP * */ @@ -27,6 +28,7 @@ #error The Cortina PHY needs 10G support #endif +#ifndef CORTINA_NO_FW_UPLOAD struct cortina_reg_config cortina_reg_cfg[] = { /* CS4315_enable_sr_mode */ {VILLA_GLOBAL_MSEQCLKCTRL, 0x8004}, @@ -215,12 +217,22 @@ void cs4340_upload_firmware(struct phy_device *phydev) phy_write(phydev, 0x00, fw_temp.reg_addr, fw_temp.reg_value); } } +#endif int cs4340_phy_init(struct phy_device *phydev) { +#ifndef CORTINA_NO_FW_UPLOAD int timeout = 100; /* 100ms */ +#endif int reg_value; + /* + * Cortina phy has provision to store + * phy firmware in attached dedicated EEPROM. + * Boards designed with EEPROM attached to Cortina + * does not require FW upload. + */ +#ifndef CORTINA_NO_FW_UPLOAD /* step1: BIST test */ phy_write(phydev, 0x00, VILLA_GLOBAL_MSEQCLKCTRL, 0x0004); phy_write(phydev, 0x00, VILLA_GLOBAL_LINE_SOFT_RESET, 0x0000); @@ -241,6 +253,7 @@ int cs4340_phy_init(struct phy_device *phydev) /* setp2: upload ucode */ cs4340_upload_firmware(phydev); +#endif reg_value = phy_read(phydev, 0x00, VILLA_GLOBAL_DWNLD_CHECKSUM_STATUS); if (reg_value) { debug("%s checksum status failed.\n", __func__); @@ -295,45 +308,33 @@ int phy_cortina_init(void) int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) { int phy_reg; - bool is_cortina_phy = false; - - switch (addr) { -#ifdef CORTINA_PHY_ADDR1 - case CORTINA_PHY_ADDR1: -#endif -#ifdef CORTINA_PHY_ADDR2 - case CORTINA_PHY_ADDR2: -#endif -#ifdef CORTINA_PHY_ADDR3 - case CORTINA_PHY_ADDR3: -#endif -#ifdef CORTINA_PHY_ADDR4 - case CORTINA_PHY_ADDR4: -#endif - is_cortina_phy = true; - break; - default: - break; - } /* Cortina PHY has non-standard offset of PHY ID registers */ - if (is_cortina_phy) - phy_reg = bus->read(bus, addr, 0, VILLA_GLOBAL_CHIP_ID_LSB); - else - phy_reg = bus->read(bus, addr, devad, MII_PHYSID1); + phy_reg = bus->read(bus, addr, 0, VILLA_GLOBAL_CHIP_ID_LSB); + if (phy_reg < 0) + return -EIO; + *phy_id = (phy_reg & 0xffff) << 16; + phy_reg = bus->read(bus, addr, 0, VILLA_GLOBAL_CHIP_ID_MSB); if (phy_reg < 0) return -EIO; + *phy_id |= (phy_reg & 0xffff); - *phy_id = (phy_reg & 0xffff) << 16; - if (is_cortina_phy) - phy_reg = bus->read(bus, addr, 0, VILLA_GLOBAL_CHIP_ID_MSB); - else - phy_reg = bus->read(bus, addr, devad, MII_PHYSID2); + if (*phy_id == PHY_UID_CS4340) + return 0; + /* + * If Cortina PHY not detected, + * try generic way to find PHY ID registers + */ + phy_reg = bus->read(bus, addr, devad, MII_PHYSID1); if (phy_reg < 0) return -EIO; + *phy_id = (phy_reg & 0xffff) << 16; + phy_reg = bus->read(bus, addr, devad, MII_PHYSID2); + if (phy_reg < 0) + return -EIO; *phy_id |= (phy_reg & 0xffff); return 0; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 6d917f86f4..d5c2a46c67 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -13,6 +13,7 @@ #include <phy.h> #define PHY_RTL8211x_FORCE_MASTER BIT(1) +#define PHY_RTL8211E_PINE64_GIGABIT_FIX BIT(2) #define PHY_AUTONEGOTIATE_TIMEOUT 5000 @@ -47,6 +48,13 @@ #define MIIM_RTL8211F_PHYSTAT_SPDDONE 0x0800 #define MIIM_RTL8211F_PHYSTAT_LINK 0x0004 +#define MIIM_RTL8211E_CONFREG 0x1c +#define MIIM_RTL8211E_CONFREG_TXD 0x0002 +#define MIIM_RTL8211E_CONFREG_RXD 0x0004 +#define MIIM_RTL8211E_CONFREG_MAGIC 0xb400 /* Undocumented */ + +#define MIIM_RTL8211E_EXT_PAGE_SELECT 0x1e + #define MIIM_RTL8211F_PAGE_SELECT 0x1f #define MIIM_RTL8211F_TX_DELAY 0x100 #define MIIM_RTL8211F_LCR 0x10 @@ -60,6 +68,15 @@ static int rtl8211b_probe(struct phy_device *phydev) return 0; } +static int rtl8211e_probe(struct phy_device *phydev) +{ +#ifdef CONFIG_RTL8211E_PINE64_GIGABIT_FIX + phydev->flags |= PHY_RTL8211E_PINE64_GIGABIT_FIX; +#endif + + return 0; +} + /* RealTek RTL8211x */ static int rtl8211x_config(struct phy_device *phydev) { @@ -81,6 +98,22 @@ static int rtl8211x_config(struct phy_device *phydev) reg |= MIIM_RTL8211x_CTRL1000T_MASTER; phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, reg); } + if (phydev->flags & PHY_RTL8211E_PINE64_GIGABIT_FIX) { + unsigned int reg; + + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, + 7); + phy_write(phydev, MDIO_DEVAD_NONE, + MIIM_RTL8211E_EXT_PAGE_SELECT, 0xa4); + reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211E_CONFREG); + /* Ensure both internal delays are turned off */ + reg &= ~(MIIM_RTL8211E_CONFREG_TXD | MIIM_RTL8211E_CONFREG_RXD); + /* Flip the magic undocumented bits */ + reg |= MIIM_RTL8211E_CONFREG_MAGIC; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211E_CONFREG, reg); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, + 0); + } /* read interrupt status just to clear it */ phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER); @@ -279,6 +312,7 @@ static struct phy_driver RTL8211E_driver = { .uid = 0x1cc915, .mask = 0xffffff, .features = PHY_GBIT_FEATURES, + .probe = &rtl8211e_probe, .config = &rtl8211x_config, .startup = &rtl8211e_startup, .shutdown = &genphy_shutdown, diff --git a/drivers/net/phy/xilinx_phy.c b/drivers/net/phy/xilinx_phy.c index 3f80f0495e..7142a99ce5 100644 --- a/drivers/net/phy/xilinx_phy.c +++ b/drivers/net/phy/xilinx_phy.c @@ -105,7 +105,7 @@ static int xilinxphy_of_init(struct phy_device *phydev) debug("%s\n", __func__); phytype = fdtdec_get_int(gd->fdt_blob, dev_of_offset(phydev->dev), - "phy-type", -1); + "xlnx,phy-type", -1); if (phytype == XAE_PHY_TYPE_1000BASE_X) phydev->flags |= XAE_PHY_TYPE_1000BASE_X; diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 6f48e93ab5..cb3970e051 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -11,6 +11,7 @@ #include <config.h> #include <common.h> +#include <environment.h> #include <malloc.h> #include <net.h> #include <netdev.h> @@ -905,7 +906,10 @@ int sh_ether_ofdata_to_platdata(struct udevice *dev) } static const struct udevice_id sh_ether_ids[] = { + { .compatible = "renesas,ether-r8a7790" }, { .compatible = "renesas,ether-r8a7791" }, + { .compatible = "renesas,ether-r8a7793" }, + { .compatible = "renesas,ether-r8a7794" }, { } }; diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 3ccc6b0bb6..b6e5dafe83 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -21,6 +21,7 @@ #include <malloc.h> #include <miiphy.h> #include <net.h> +#include <dt-bindings/pinctrl/sun4i-a10.h> #ifdef CONFIG_DM_GPIO #include <asm-generic/gpio.h> #endif @@ -278,7 +279,7 @@ static int sun8i_emac_set_syscon(struct emac_eth_dev *priv) int ret; u32 reg; - reg = readl(priv->sysctl_reg); + reg = readl(priv->sysctl_reg + 0x30); if (priv->variant == H3_EMAC) { ret = sun8i_emac_set_syscon_ephy(priv, ®); @@ -309,7 +310,7 @@ static int sun8i_emac_set_syscon(struct emac_eth_dev *priv) return -EINVAL; } - writel(reg, priv->sysctl_reg); + writel(reg, priv->sysctl_reg + 0x30); return 0; } @@ -431,7 +432,7 @@ static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr) tx_descs_init(priv); /* PHY Start Up */ - genphy_parse_link(priv->phydev); + phy_startup(priv->phydev); sun8i_adjust_link(priv, priv->phydev); @@ -455,7 +456,7 @@ static int parse_phy_pins(struct udevice *dev) { int offset; const char *pin_name; - int drive, pull, i; + int drive, pull = SUN4I_PINCTRL_NO_PULL, i; offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev), "pinctrl-0"); @@ -465,30 +466,44 @@ static int parse_phy_pins(struct udevice *dev) } drive = fdt_getprop_u32_default_node(gd->fdt_blob, offset, 0, - "allwinner,drive", 4); - pull = fdt_getprop_u32_default_node(gd->fdt_blob, offset, 0, - "allwinner,pull", 0); + "drive-strength", ~0); + if (drive != ~0) { + if (drive <= 10) + drive = SUN4I_PINCTRL_10_MA; + else if (drive <= 20) + drive = SUN4I_PINCTRL_20_MA; + else if (drive <= 30) + drive = SUN4I_PINCTRL_30_MA; + else + drive = SUN4I_PINCTRL_40_MA; + } + + if (fdt_get_property(gd->fdt_blob, offset, "bias-pull-up", NULL)) + pull = SUN4I_PINCTRL_PULL_UP; + else if (fdt_get_property(gd->fdt_blob, offset, "bias-pull-down", NULL)) + pull = SUN4I_PINCTRL_PULL_DOWN; + for (i = 0; ; i++) { int pin; pin_name = fdt_stringlist_get(gd->fdt_blob, offset, - "allwinner,pins", i, NULL); + "pins", i, NULL); if (!pin_name) break; - if (pin_name[0] != 'P') - continue; - pin = (pin_name[1] - 'A') << 5; - if (pin >= 26 << 5) + + pin = sunxi_name_to_gpio(pin_name); + if (pin < 0) continue; - pin += simple_strtol(&pin_name[2], NULL, 10); sunxi_gpio_set_cfgpin(pin, SUN8I_GPD8_GMAC); - sunxi_gpio_set_drv(pin, drive); - sunxi_gpio_set_pull(pin, pull); + if (drive != ~0) + sunxi_gpio_set_drv(pin, drive); + if (pull != ~0) + sunxi_gpio_set_pull(pin, pull); } if (!i) { - printf("WARNING: emac: cannot find allwinner,pins property\n"); + printf("WARNING: emac: cannot find pins property\n"); return -2; } @@ -772,6 +787,7 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) struct eth_pdata *pdata = &sun8i_pdata->eth_pdata; struct emac_eth_dev *priv = dev_get_priv(dev); const char *phy_mode; + const fdt32_t *reg; int node = dev_of_offset(dev); int offset = 0; #ifdef CONFIG_DM_GPIO @@ -779,18 +795,40 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) int ret = 0; #endif - pdata->iobase = devfdt_get_addr_name(dev, "emac"); - priv->sysctl_reg = devfdt_get_addr_name(dev, "syscon"); + pdata->iobase = devfdt_get_addr(dev); + if (pdata->iobase == FDT_ADDR_T_NONE) { + debug("%s: Cannot find MAC base address\n", __func__); + return -EINVAL; + } + + offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "syscon"); + if (offset < 0) { + debug("%s: cannot find syscon node\n", __func__); + return -EINVAL; + } + reg = fdt_getprop(gd->fdt_blob, offset, "reg", NULL); + if (!reg) { + debug("%s: cannot find reg property in syscon node\n", + __func__); + return -EINVAL; + } + priv->sysctl_reg = fdt_translate_address((void *)gd->fdt_blob, + offset, reg); + if (priv->sysctl_reg == FDT_ADDR_T_NONE) { + debug("%s: Cannot find syscon base address\n", __func__); + return -EINVAL; + } pdata->phy_interface = -1; priv->phyaddr = -1; priv->use_internal_phy = false; - offset = fdtdec_lookup_phandle(gd->fdt_blob, node, - "phy"); - if (offset > 0) - priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", - -1); + offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "phy-handle"); + if (offset < 0) { + debug("%s: Cannot find PHY address\n", __func__); + return -EINVAL; + } + priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1); phy_mode = fdt_getprop(gd->fdt_blob, node, "phy-mode", NULL); @@ -812,8 +850,11 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) } if (priv->variant == H3_EMAC) { - if (fdt_getprop(gd->fdt_blob, node, - "allwinner,use-internal-phy", NULL)) + int parent = fdt_parent_offset(gd->fdt_blob, offset); + + if (parent >= 0 && + !fdt_node_check_compatible(gd->fdt_blob, parent, + "allwinner,sun8i-h3-mdio-internal")) priv->use_internal_phy = true; } diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c index 70a2e95a8e..80ed06ac66 100644 --- a/drivers/net/xilinx_axi_emac.c +++ b/drivers/net/xilinx_axi_emac.c @@ -78,9 +78,10 @@ static u8 rxframe[PKTSIZE_ALIGN] __attribute((aligned(DMAALIGN))); struct axidma_reg { u32 control; /* DMACR */ u32 status; /* DMASR */ - u32 current; /* CURDESC */ - u32 reserved; - u32 tail; /* TAILDESC */ + u32 current; /* CURDESC low 32 bit */ + u32 current_hi; /* CURDESC high 32 bit */ + u32 tail; /* TAILDESC low 32 bit */ + u32 tail_hi; /* TAILDESC high 32 bit */ }; /* Private driver structures */ @@ -168,6 +169,22 @@ static inline int mdio_wait(struct axi_regs *regs) return 0; } +/** + * axienet_dma_write - Memory mapped Axi DMA register Buffer Descriptor write. + * @bd: pointer to BD descriptor structure + * @desc: Address offset of DMA descriptors + * + * This function writes the value into the corresponding Axi DMA register. + */ +static inline void axienet_dma_write(struct axidma_bd *bd, u32 *desc) +{ +#if defined(CONFIG_PHYS_64BIT) + writeq(bd, desc); +#else + writel((u32)bd, desc); +#endif +} + static u32 phyread(struct axidma_priv *priv, u32 phyaddress, u32 registernum, u16 *val) { @@ -465,7 +482,7 @@ static int axiemac_start(struct udevice *dev) writel(temp, &priv->dmarx->control); /* Start DMA RX channel. Now it's ready to receive data.*/ - writel((u32)&rx_bd, &priv->dmarx->current); + axienet_dma_write(&rx_bd, &priv->dmarx->current); /* Setup the BD. */ memset(&rx_bd, 0, sizeof(rx_bd)); @@ -485,7 +502,7 @@ static int axiemac_start(struct udevice *dev) writel(temp, &priv->dmarx->control); /* Rx BD is ready - start */ - writel((u32)&rx_bd, &priv->dmarx->tail); + axienet_dma_write(&rx_bd, &priv->dmarx->tail); /* Enable TX */ writel(XAE_TC_TX_MASK, ®s->tc); @@ -527,7 +544,7 @@ static int axiemac_send(struct udevice *dev, void *ptr, int len) if (readl(&priv->dmatx->status) & XAXIDMA_HALTED_MASK) { u32 temp; - writel((u32)&tx_bd, &priv->dmatx->current); + axienet_dma_write(&tx_bd, &priv->dmatx->current); /* Start the hardware */ temp = readl(&priv->dmatx->control); temp |= XAXIDMA_CR_RUNSTOP_MASK; @@ -535,7 +552,7 @@ static int axiemac_send(struct udevice *dev, void *ptr, int len) } /* Start transfer */ - writel((u32)&tx_bd, &priv->dmatx->tail); + axienet_dma_write(&tx_bd, &priv->dmatx->tail); /* Wait for transmission to complete */ debug("axiemac: Waiting for tx to be done\n"); @@ -626,7 +643,7 @@ static int axiemac_free_pkt(struct udevice *dev, uchar *packet, int length) flush_cache((u32)&rxframe, sizeof(rxframe)); /* Rx BD is ready - start again */ - writel((u32)&rx_bd, &priv->dmarx->tail); + axienet_dma_write(&rx_bd, &priv->dmarx->tail); debug("axiemac: RX completed, framelength = %d\n", length); diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 2cc49bca92..dd36a8c22a 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -182,6 +182,7 @@ struct zynq_gem_priv { int phy_of_handle; struct mii_dev *bus; struct clk clk; + u32 max_speed; bool int_pcs; }; @@ -325,7 +326,8 @@ static int zynq_phy_init(struct udevice *dev) /* Enable only MDIO bus */ writel(ZYNQ_GEM_NWCTRL_MDEN_MASK, ®s->nwctrl); - if (priv->interface != PHY_INTERFACE_MODE_SGMII) { + if ((priv->interface != PHY_INTERFACE_MODE_SGMII) && + (priv->interface != PHY_INTERFACE_MODE_GMII)) { ret = phy_detection(dev); if (ret) { printf("GEM PHY init failed\n"); @@ -340,6 +342,12 @@ static int zynq_phy_init(struct udevice *dev) priv->phydev->supported &= supported | ADVERTISED_Pause | ADVERTISED_Asym_Pause; + if (priv->max_speed) { + ret = phy_set_supported(priv->phydev, priv->max_speed); + if (ret) + return ret; + } + priv->phydev->advertising = priv->phydev->supported; if (priv->phy_of_handle > 0) @@ -703,6 +711,8 @@ static int zynq_gem_ofdata_to_platdata(struct udevice *dev) } priv->interface = pdata->phy_interface; + priv->max_speed = fdtdec_get_uint(gd->fdt_blob, priv->phy_of_handle, + "max-speed", SPEED_1000); priv->int_pcs = fdtdec_get_bool(gd->fdt_blob, node, "is-internal-pcspma"); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index da6421f35c..c20a0cc060 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -26,6 +26,16 @@ config DM_PCI_COMPAT measure when porting a board to use driver model for PCI. Once the board is fully supported, this option should be disabled. +config PCI_AARDVARK + bool "Enable Aardvark PCIe driver" + default n + depends on DM_PCI + depends on ARMADA_3700 + help + Say Y here if you want to enable PCIe controller support on + Armada37x0 SoCs. The PCIe controller on Armada37x0 is based on + Aardvark hardware. + config PCI_PNP bool "Enable Plug & Play support for PCI" depends on PCI || DM_PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 8fbab462a4..40ebc06f6d 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_SH4_PCI) += pci_sh4.o obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o +obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape_fixup.o diff --git a/drivers/pci/pci-aardvark.c b/drivers/pci/pci-aardvark.c new file mode 100644 index 0000000000..69a4d81c2e --- /dev/null +++ b/drivers/pci/pci-aardvark.c @@ -0,0 +1,690 @@ +/* + * *************************************************************************** + * Copyright (C) 2015 Marvell International Ltd. + * *************************************************************************** + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 2 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * *************************************************************************** + */ +/* pcie_advk.c + * + * Ported from Linux driver - driver/pci/host/pci-aardvark.c + * + * Author: Victor Gu <xigu@marvell.com> + * Hezi Shahmoon <hezi.shahmoon@marvell.com> + * + */ + +#include <common.h> +#include <dm.h> +#include <pci.h> +#include <asm/io.h> +#include <asm-generic/gpio.h> +#include <linux/ioport.h> + +/* PCIe core registers */ +#define PCIE_CORE_CMD_STATUS_REG 0x4 +#define PCIE_CORE_CMD_IO_ACCESS_EN BIT(0) +#define PCIE_CORE_CMD_MEM_ACCESS_EN BIT(1) +#define PCIE_CORE_CMD_MEM_IO_REQ_EN BIT(2) +#define PCIE_CORE_DEV_CTRL_STATS_REG 0xc8 +#define PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE (0 << 4) +#define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11) +#define PCIE_CORE_LINK_CTRL_STAT_REG 0xd0 +#define PCIE_CORE_LINK_TRAINING BIT(5) +#define PCIE_CORE_ERR_CAPCTL_REG 0x118 +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHECK BIT(7) +#define PCIE_CORE_ERR_CAPCTL_ECRC_CHECK_RCV BIT(8) + +/* PIO registers base address and register offsets */ +#define PIO_BASE_ADDR 0x4000 +#define PIO_CTRL (PIO_BASE_ADDR + 0x0) +#define PIO_CTRL_TYPE_MASK GENMASK(3, 0) +#define PIO_CTRL_ADDR_WIN_DISABLE BIT(24) +#define PIO_STAT (PIO_BASE_ADDR + 0x4) +#define PIO_COMPLETION_STATUS_SHIFT 7 +#define PIO_COMPLETION_STATUS_MASK GENMASK(9, 7) +#define PIO_COMPLETION_STATUS_OK 0 +#define PIO_COMPLETION_STATUS_UR 1 +#define PIO_COMPLETION_STATUS_CRS 2 +#define PIO_COMPLETION_STATUS_CA 4 +#define PIO_NON_POSTED_REQ BIT(10) +#define PIO_ERR_STATUS BIT(11) +#define PIO_ADDR_LS (PIO_BASE_ADDR + 0x8) +#define PIO_ADDR_MS (PIO_BASE_ADDR + 0xc) +#define PIO_WR_DATA (PIO_BASE_ADDR + 0x10) +#define PIO_WR_DATA_STRB (PIO_BASE_ADDR + 0x14) +#define PIO_RD_DATA (PIO_BASE_ADDR + 0x18) +#define PIO_START (PIO_BASE_ADDR + 0x1c) +#define PIO_ISR (PIO_BASE_ADDR + 0x20) + +/* Aardvark Control registers */ +#define CONTROL_BASE_ADDR 0x4800 +#define PCIE_CORE_CTRL0_REG (CONTROL_BASE_ADDR + 0x0) +#define PCIE_GEN_SEL_MSK 0x3 +#define PCIE_GEN_SEL_SHIFT 0x0 +#define SPEED_GEN_1 0 +#define SPEED_GEN_2 1 +#define SPEED_GEN_3 2 +#define IS_RC_MSK 1 +#define IS_RC_SHIFT 2 +#define LANE_CNT_MSK 0x18 +#define LANE_CNT_SHIFT 0x3 +#define LANE_COUNT_1 (0 << LANE_CNT_SHIFT) +#define LANE_COUNT_2 (1 << LANE_CNT_SHIFT) +#define LANE_COUNT_4 (2 << LANE_CNT_SHIFT) +#define LANE_COUNT_8 (3 << LANE_CNT_SHIFT) +#define LINK_TRAINING_EN BIT(6) +#define PCIE_CORE_CTRL2_REG (CONTROL_BASE_ADDR + 0x8) +#define PCIE_CORE_CTRL2_RESERVED 0x7 +#define PCIE_CORE_CTRL2_TD_ENABLE BIT(4) +#define PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE BIT(5) +#define PCIE_CORE_CTRL2_ADDRWIN_MAP_ENABLE BIT(6) + +/* LMI registers base address and register offsets */ +#define LMI_BASE_ADDR 0x6000 +#define CFG_REG (LMI_BASE_ADDR + 0x0) +#define LTSSM_SHIFT 24 +#define LTSSM_MASK 0x3f +#define LTSSM_L0 0x10 + +/* PCIe core controller registers */ +#define CTRL_CORE_BASE_ADDR 0x18000 +#define CTRL_CONFIG_REG (CTRL_CORE_BASE_ADDR + 0x0) +#define CTRL_MODE_SHIFT 0x0 +#define CTRL_MODE_MASK 0x1 +#define PCIE_CORE_MODE_DIRECT 0x0 +#define PCIE_CORE_MODE_COMMAND 0x1 + +/* Transaction types */ +#define PCIE_CONFIG_RD_TYPE0 0x8 +#define PCIE_CONFIG_RD_TYPE1 0x9 +#define PCIE_CONFIG_WR_TYPE0 0xa +#define PCIE_CONFIG_WR_TYPE1 0xb + +/* PCI_BDF shifts 8bit, so we need extra 4bit shift */ +#define PCIE_BDF(dev) (dev << 4) +#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20) +#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15) +#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12) +#define PCIE_CONF_REG(reg) ((reg) & 0xffc) +#define PCIE_CONF_ADDR(bus, devfn, where) \ + (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ + PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where)) + +/* PCIe Retries & Timeout definitions */ +#define MAX_RETRIES 10 +#define PIO_WAIT_TIMEOUT 100 +#define LINK_WAIT_TIMEOUT 100000 + +#define CFG_RD_UR_VAL 0xFFFFFFFF +#define CFG_RD_CRS_VAL 0xFFFF0001 + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct pcie_advk - Advk PCIe controller state + * + * @reg_base: The base address of the register space. + * @first_busno: This driver supports multiple PCIe controllers. + * first_busno stores the bus number of the PCIe root-port + * number which may vary depending on the PCIe setup + * (PEX switches etc). + * @device: The pointer to PCI uclass device. + */ +struct pcie_advk { + void *base; + int first_busno; + struct udevice *dev; +}; + +static inline void advk_writel(struct pcie_advk *pcie, uint val, uint reg) +{ + writel(val, pcie->base + reg); +} + +static inline uint advk_readl(struct pcie_advk *pcie, uint reg) +{ + return readl(pcie->base + reg); +} + +/** + * pcie_advk_addr_valid() - Check for valid bus address + * + * @bdf: The PCI device to access + * @first_busno: Bus number of the PCIe controller root complex + * + * Return: 1 on valid, 0 on invalid + */ +static int pcie_advk_addr_valid(pci_dev_t bdf, int first_busno) +{ + /* + * In PCIE-E only a single device (0) can exist + * on the local bus. Beyound the local bus, there might be + * a Switch and everything is possible. + */ + if ((PCI_BUS(bdf) == first_busno) && (PCI_DEV(bdf) > 0)) + return 0; + + return 1; +} + +/** + * pcie_advk_wait_pio() - Wait for PIO access to be accomplished + * + * @pcie: The PCI device to access + * + * Wait up to 1 micro second for PIO access to be accomplished. + * + * Return 1 (true) if PIO access is accomplished. + * Return 0 (false) if PIO access is timed out. + */ +static int pcie_advk_wait_pio(struct pcie_advk *pcie) +{ + uint start, isr; + uint count; + + for (count = 0; count < MAX_RETRIES; count++) { + start = advk_readl(pcie, PIO_START); + isr = advk_readl(pcie, PIO_ISR); + if (!start && isr) + return 1; + /* + * Do not check the PIO state too frequently, + * 100us delay is appropriate. + */ + udelay(PIO_WAIT_TIMEOUT); + } + + dev_err(pcie->dev, "config read/write timed out\n"); + return 0; +} + +/** + * pcie_advk_check_pio_status() - Validate PIO status and get the read result + * + * @pcie: Pointer to the PCI bus + * @read: Read from or write to configuration space - true(read) false(write) + * @read_val: Pointer to the read result, only valid when read is true + * + */ +static int pcie_advk_check_pio_status(struct pcie_advk *pcie, + bool read, + uint *read_val) +{ + uint reg; + unsigned int status; + char *strcomp_status, *str_posted; + + reg = advk_readl(pcie, PIO_STAT); + status = (reg & PIO_COMPLETION_STATUS_MASK) >> + PIO_COMPLETION_STATUS_SHIFT; + + switch (status) { + case PIO_COMPLETION_STATUS_OK: + if (reg & PIO_ERR_STATUS) { + strcomp_status = "COMP_ERR"; + break; + } + /* Get the read result */ + if (read) + *read_val = advk_readl(pcie, PIO_RD_DATA); + /* No error */ + strcomp_status = NULL; + break; + case PIO_COMPLETION_STATUS_UR: + if (read) { + /* For reading, UR is not an error status. */ + *read_val = CFG_RD_UR_VAL; + strcomp_status = NULL; + } else { + strcomp_status = "UR"; + } + break; + case PIO_COMPLETION_STATUS_CRS: + if (read) { + /* For reading, CRS is not an error status. */ + *read_val = CFG_RD_CRS_VAL; + strcomp_status = NULL; + } else { + strcomp_status = "CRS"; + } + break; + case PIO_COMPLETION_STATUS_CA: + strcomp_status = "CA"; + break; + default: + strcomp_status = "Unknown"; + break; + } + + if (!strcomp_status) + return 0; + + if (reg & PIO_NON_POSTED_REQ) + str_posted = "Non-posted"; + else + str_posted = "Posted"; + + dev_err(pcie->dev, "%s PIO Response Status: %s, %#x @ %#x\n", + str_posted, strcomp_status, reg, + advk_readl(pcie, PIO_ADDR_LS)); + + return -EFAULT; +} + +/** + * pcie_advk_read_config() - Read from configuration space + * + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @valuep: A pointer at which to store the read value + * @size: Indicates the size of access to perform + * + * Read a value of size @size from offset @offset within the configuration + * space of the device identified by the bus, device & function numbers in @bdf + * on the PCI bus @bus. + * + * Return: 0 on success + */ +static int pcie_advk_read_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + struct pcie_advk *pcie = dev_get_priv(bus); + uint reg; + int ret; + + dev_dbg(pcie->dev, "PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + + if (!pcie_advk_addr_valid(bdf, pcie->first_busno)) { + dev_dbg(pcie->dev, "- out of range\n"); + *valuep = pci_get_ff(size); + return 0; + } + + /* Start PIO */ + advk_writel(pcie, 0, PIO_START); + advk_writel(pcie, 1, PIO_ISR); + + /* Program the control register */ + reg = advk_readl(pcie, PIO_CTRL); + reg &= ~PIO_CTRL_TYPE_MASK; + if (PCI_BUS(bdf) == pcie->first_busno) + reg |= PCIE_CONFIG_RD_TYPE0; + else + reg |= PCIE_CONFIG_RD_TYPE1; + advk_writel(pcie, reg, PIO_CTRL); + + /* Program the address registers */ + reg = PCIE_BDF(bdf) | PCIE_CONF_REG(offset); + advk_writel(pcie, reg, PIO_ADDR_LS); + advk_writel(pcie, 0, PIO_ADDR_MS); + + /* Start the transfer */ + advk_writel(pcie, 1, PIO_START); + + if (!pcie_advk_wait_pio(pcie)) + return -EINVAL; + + /* Check PIO status and get the read result */ + ret = pcie_advk_check_pio_status(pcie, true, ®); + if (ret) + return ret; + + dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08x)\n", + offset, size, reg); + *valuep = pci_conv_32_to_size(reg, offset, size); + + return 0; +} + +/** + * pcie_calc_datastrobe() - Calculate data strobe + * + * @offset: The offset into the device's configuration space + * @size: Indicates the size of access to perform + * + * Calculate data strobe according to offset and size + * + */ +static uint pcie_calc_datastrobe(uint offset, enum pci_size_t size) +{ + uint bytes, data_strobe; + + switch (size) { + case PCI_SIZE_8: + bytes = 1; + break; + case PCI_SIZE_16: + bytes = 2; + break; + default: + bytes = 4; + } + + data_strobe = GENMASK(bytes - 1, 0) << (offset & 0x3); + + return data_strobe; +} + +/** + * pcie_advk_write_config() - Write to configuration space + * + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @value: The value to write + * @size: Indicates the size of access to perform + * + * Write the value @value of size @size from offset @offset within the + * configuration space of the device identified by the bus, device & function + * numbers in @bdf on the PCI bus @bus. + * + * Return: 0 on success + */ +static int pcie_advk_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + struct pcie_advk *pcie = dev_get_priv(bus); + uint reg; + + dev_dbg(pcie->dev, "PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08lx)\n", + offset, size, value); + + if (!pcie_advk_addr_valid(bdf, pcie->first_busno)) { + dev_dbg(pcie->dev, "- out of range\n"); + return 0; + } + + /* Start PIO */ + advk_writel(pcie, 0, PIO_START); + advk_writel(pcie, 1, PIO_ISR); + + /* Program the control register */ + reg = advk_readl(pcie, PIO_CTRL); + reg &= ~PIO_CTRL_TYPE_MASK; + if (PCI_BUS(bdf) == pcie->first_busno) + reg |= PCIE_CONFIG_WR_TYPE0; + else + reg |= PCIE_CONFIG_WR_TYPE1; + advk_writel(pcie, reg, PIO_CTRL); + + /* Program the address registers */ + reg = PCIE_BDF(bdf) | PCIE_CONF_REG(offset); + advk_writel(pcie, reg, PIO_ADDR_LS); + advk_writel(pcie, 0, PIO_ADDR_MS); + dev_dbg(pcie->dev, "\tPIO req. - addr = 0x%08x\n", reg); + + /* Program the data register */ + reg = pci_conv_size_to_32(0, value, offset, size); + advk_writel(pcie, reg, PIO_WR_DATA); + dev_dbg(pcie->dev, "\tPIO req. - val = 0x%08x\n", reg); + + /* Program the data strobe */ + reg = pcie_calc_datastrobe(offset, size); + advk_writel(pcie, reg, PIO_WR_DATA_STRB); + dev_dbg(pcie->dev, "\tPIO req. - strb = 0x%02x\n", reg); + + /* Start the transfer */ + advk_writel(pcie, 1, PIO_START); + + if (!pcie_advk_wait_pio(pcie)) { + dev_dbg(pcie->dev, "- wait pio timeout\n"); + return -EINVAL; + } + + /* Check PIO status */ + pcie_advk_check_pio_status(pcie, false, ®); + + return 0; +} + +/** + * pcie_advk_link_up() - Check if PCIe link is up or not + * + * @pcie: The PCI device to access + * + * Return 1 (true) on link up. + * Return 0 (false) on link down. + */ +static int pcie_advk_link_up(struct pcie_advk *pcie) +{ + u32 val, ltssm_state; + + val = advk_readl(pcie, CFG_REG); + ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; + return ltssm_state >= LTSSM_L0; +} + +/** + * pcie_advk_wait_for_link() - Wait for link training to be accomplished + * + * @pcie: The PCI device to access + * + * Wait up to 1 second for link training to be accomplished. + * + * Return 1 (true) if link training ends up with link up success. + * Return 0 (false) if link training ends up with link up failure. + */ +static int pcie_advk_wait_for_link(struct pcie_advk *pcie) +{ + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < MAX_RETRIES; retries++) { + if (pcie_advk_link_up(pcie)) { + printf("PCIE-%d: Link up\n", pcie->first_busno); + return 0; + } + + udelay(LINK_WAIT_TIMEOUT); + } + + printf("PCIE-%d: Link down\n", pcie->first_busno); + + return -ETIMEDOUT; +} + +/** + * pcie_advk_setup_hw() - PCIe initailzation + * + * @pcie: The PCI device to access + * + * Return: 0 on success + */ +static int pcie_advk_setup_hw(struct pcie_advk *pcie) +{ + u32 reg; + + /* Set to Direct mode */ + reg = advk_readl(pcie, CTRL_CONFIG_REG); + reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT); + reg |= ((PCIE_CORE_MODE_DIRECT & CTRL_MODE_MASK) << CTRL_MODE_SHIFT); + advk_writel(pcie, reg, CTRL_CONFIG_REG); + + /* Set PCI global control register to RC mode */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg |= (IS_RC_MSK << IS_RC_SHIFT); + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Set Advanced Error Capabilities and Control PF0 register */ + reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX | + PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN | + PCIE_CORE_ERR_CAPCTL_ECRC_CHECK | + PCIE_CORE_ERR_CAPCTL_ECRC_CHECK_RCV; + advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG); + + /* Set PCIe Device Control and Status 1 PF0 register */ + reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE | + PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE; + advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG); + + /* Program PCIe Control 2 to disable strict ordering */ + reg = PCIE_CORE_CTRL2_RESERVED | + PCIE_CORE_CTRL2_TD_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Set GEN2 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~PCIE_GEN_SEL_MSK; + reg |= SPEED_GEN_2; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Set lane X1 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~LANE_CNT_MSK; + reg |= LANE_COUNT_1; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* Enable link training */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg |= LINK_TRAINING_EN; + advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + + /* + * Enable AXI address window location generation: + * When it is enabled, the default outbound window + * configurations (Default User Field: 0xD0074CFC) + * are used to transparent address translation for + * the outbound transactions. Thus, PCIe address + * windows are not required. + */ + reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); + reg |= PCIE_CORE_CTRL2_ADDRWIN_MAP_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* + * Bypass the address window mapping for PIO: + * Since PIO access already contains all required + * info over AXI interface by PIO registers, the + * address window is not required. + */ + reg = advk_readl(pcie, PIO_CTRL); + reg |= PIO_CTRL_ADDR_WIN_DISABLE; + advk_writel(pcie, reg, PIO_CTRL); + + /* Start link training */ + reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); + reg |= PCIE_CORE_LINK_TRAINING; + advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); + + /* Wait for PCIe link up */ + if (pcie_advk_wait_for_link(pcie)) + return -ENXIO; + + reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG); + reg |= PCIE_CORE_CMD_MEM_ACCESS_EN | + PCIE_CORE_CMD_IO_ACCESS_EN | + PCIE_CORE_CMD_MEM_IO_REQ_EN; + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); + + return 0; +} + +/** + * pcie_advk_probe() - Probe the PCIe bus for active link + * + * @dev: A pointer to the device being operated on + * + * Probe for an active link on the PCIe bus and configure the controller + * to enable this port. + * + * Return: 0 on success, else -ENODEV + */ +static int pcie_advk_probe(struct udevice *dev) +{ + struct pcie_advk *pcie = dev_get_priv(dev); + +#ifdef CONFIG_DM_GPIO + struct gpio_desc reset_gpio; + + gpio_request_by_name(dev, "reset-gpio", 0, &reset_gpio, + GPIOD_IS_OUT); + /* + * Issue reset to add-in card through the dedicated GPIO. + * Some boards are connecting the card reset pin to common system + * reset wire and others are using separate GPIO port. + * In the last case we have to release a reset of the addon card + * using this GPIO. + * + * FIX-ME: + * The PCIe RESET signal is not supposed to be released along + * with the SOC RESET signal. It should be lowered as early as + * possible before PCIe PHY initialization. Moreover, the PCIe + * clock should be gated as well. + */ + if (dm_gpio_is_valid(&reset_gpio)) { + dev_dbg(pcie->dev, "Toggle PCIE Reset GPIO ...\n"); + dm_gpio_set_value(&reset_gpio, 0); + mdelay(200); + dm_gpio_set_value(&reset_gpio, 1); + } +#else + dev_dbg(pcie->dev, "PCIE Reset on GPIO support is missing\n"); +#endif /* CONFIG_DM_GPIO */ + + pcie->first_busno = dev->seq; + pcie->dev = pci_get_controller(dev); + + return pcie_advk_setup_hw(pcie); +} + +/** + * pcie_advk_ofdata_to_platdata() - Translate from DT to device state + * + * @dev: A pointer to the device being operated on + * + * Translate relevant data from the device tree pertaining to device @dev into + * state that the driver will later make use of. This state is stored in the + * device's private data structure. + * + * Return: 0 on success, else -EINVAL + */ +static int pcie_advk_ofdata_to_platdata(struct udevice *dev) +{ + struct pcie_advk *pcie = dev_get_priv(dev); + + /* Get the register base address */ + pcie->base = (void *)dev_read_addr_index(dev, 0); + if ((fdt_addr_t)pcie->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static const struct dm_pci_ops pcie_advk_ops = { + .read_config = pcie_advk_read_config, + .write_config = pcie_advk_write_config, +}; + +static const struct udevice_id pcie_advk_ids[] = { + { .compatible = "marvell,armada-37xx-pcie" }, + { } +}; + +U_BOOT_DRIVER(pcie_advk) = { + .name = "pcie_advk", + .id = UCLASS_PCI, + .of_match = pcie_advk_ids, + .ops = &pcie_advk_ops, + .ofdata_to_platdata = pcie_advk_ofdata_to_platdata, + .probe = pcie_advk_probe, + .priv_auto_alloc_size = sizeof(struct pcie_advk), +}; diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index ad43e8a27c..a2e829608a 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -876,6 +876,9 @@ static int decode_regions(struct pci_controller *hose, ofnode parent_node, #ifdef CONFIG_NR_DRAM_BANKS bd_t *bd = gd->bd; + if (!bd) + return 0; + for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { if (bd->bi_dram[i].size) { pci_set_region(hose->regions + hose->region_count++, @@ -894,8 +897,9 @@ static int decode_regions(struct pci_controller *hose, ofnode parent_node, #endif if (gd->pci_ram_top && gd->pci_ram_top < base + size) size = gd->pci_ram_top - base; - pci_set_region(hose->regions + hose->region_count++, base, base, - size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + if (size) + pci_set_region(hose->regions + hose->region_count++, base, + base, size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); #endif return 0; diff --git a/drivers/pci/pcie_dw_mvebu.c b/drivers/pci/pcie_dw_mvebu.c index a19885501c..a0032b7b03 100644 --- a/drivers/pci/pcie_dw_mvebu.c +++ b/drivers/pci/pcie_dw_mvebu.c @@ -111,6 +111,10 @@ struct pcie_dw_mvebu { void *cfg_base; fdt_size_t cfg_size; int first_busno; + + /* IO and MEM PCI regions */ + struct pci_region io; + struct pci_region mem; }; static int pcie_dw_get_link_speed(const void *regs_base) @@ -126,6 +130,34 @@ static int pcie_dw_get_link_width(const void *regs_base) } /** + * pcie_dw_prog_outbound_atu() - Configure ATU for outbound accesses + * + * @pcie: Pointer to the PCI controller state + * @index: ATU region index + * @type: ATU accsess type + * @cpu_addr: the physical address for the translation entry + * @pci_addr: the pcie bus address for the translation entry + * @size: the size of the translation entry + */ +static void pcie_dw_prog_outbound_atu(struct pcie_dw_mvebu *pcie, int index, + int type, u64 cpu_addr, u64 pci_addr, + u32 size) +{ + writel(PCIE_ATU_REGION_OUTBOUND | index, + pcie->ctrl_base + PCIE_ATU_VIEWPORT); + writel(lower_32_bits(cpu_addr), pcie->ctrl_base + PCIE_ATU_LOWER_BASE); + writel(upper_32_bits(cpu_addr), pcie->ctrl_base + PCIE_ATU_UPPER_BASE); + writel(lower_32_bits(cpu_addr + size - 1), + pcie->ctrl_base + PCIE_ATU_LIMIT); + writel(lower_32_bits(pci_addr), + pcie->ctrl_base + PCIE_ATU_LOWER_TARGET); + writel(upper_32_bits(pci_addr), + pcie->ctrl_base + PCIE_ATU_UPPER_TARGET); + writel(type, pcie->ctrl_base + PCIE_ATU_CR1); + writel(PCIE_ATU_ENABLE, pcie->ctrl_base + PCIE_ATU_CR2); +} + +/** * set_cfg_address() - Configure the PCIe controller config space access * * @pcie: Pointer to the PCI controller state @@ -143,27 +175,29 @@ static uintptr_t set_cfg_address(struct pcie_dw_mvebu *pcie, pci_dev_t d, uint where) { uintptr_t va_address; + u32 atu_type; /* * Region #0 is used for Outbound CFG space access. * Direction = Outbound * Region Index = 0 */ - writel(0, pcie->ctrl_base + PCIE_ATU_VIEWPORT); if (PCI_BUS(d) == (pcie->first_busno + 1)) /* For local bus, change TLP Type field to 4. */ - writel(PCIE_ATU_TYPE_CFG0, pcie->ctrl_base + PCIE_ATU_CR1); + atu_type = PCIE_ATU_TYPE_CFG0; else /* Otherwise, change TLP Type field to 5. */ - writel(PCIE_ATU_TYPE_CFG1, pcie->ctrl_base + PCIE_ATU_CR1); + atu_type = PCIE_ATU_TYPE_CFG1; if (PCI_BUS(d) == pcie->first_busno) { /* Accessing root port configuration space. */ va_address = (uintptr_t)pcie->ctrl_base; } else { d = PCI_MASK_BUS(d) | (PCI_BUS(d) - pcie->first_busno); - writel(d << 8, pcie->ctrl_base + PCIE_ATU_LOWER_TARGET); + pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, + atu_type, (u64)pcie->cfg_base, + d << 8, pcie->cfg_size); va_address = (uintptr_t)pcie->cfg_base; } @@ -231,6 +265,10 @@ static int pcie_dw_mvebu_read_config(struct udevice *bus, pci_dev_t bdf, debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); *valuep = pci_conv_32_to_size(value, offset, size); + pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_IO, pcie->io.phys_start, + pcie->io.bus_start, pcie->io.size); + return 0; } @@ -272,6 +310,10 @@ static int pcie_dw_mvebu_write_config(struct udevice *bus, pci_dev_t bdf, value = pci_conv_size_to_32(old, value, offset, size); writel(value, va_address); + pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_IO, pcie->io.phys_start, + pcie->io.bus_start, pcie->io.size); + return 0; } @@ -388,34 +430,6 @@ static int pcie_dw_mvebu_pcie_link_up(const void *regs_base, u32 cap_speed) } /** - * pcie_dw_regions_setup() - iATU region setup - * - * @pcie: Pointer to the PCI controller state - * - * Configure the iATU regions in the PCIe controller for outbound access. - */ -static void pcie_dw_regions_setup(struct pcie_dw_mvebu *pcie) -{ - /* - * Region #0 is used for Outbound CFG space access. - * Direction = Outbound - * Region Index = 0 - */ - writel(0, pcie->ctrl_base + PCIE_ATU_VIEWPORT); - - writel((u32)(uintptr_t)pcie->cfg_base, pcie->ctrl_base - + PCIE_ATU_LOWER_BASE); - writel(0, pcie->ctrl_base + PCIE_ATU_UPPER_BASE); - writel((u32)(uintptr_t)pcie->cfg_base + pcie->cfg_size, - pcie->ctrl_base + PCIE_ATU_LIMIT); - - writel(0, pcie->ctrl_base + PCIE_ATU_LOWER_TARGET); - writel(0, pcie->ctrl_base + PCIE_ATU_UPPER_TARGET); - writel(PCIE_ATU_TYPE_CFG0, pcie->ctrl_base + PCIE_ATU_CR1); - writel(PCIE_ATU_ENABLE, pcie->ctrl_base + PCIE_ATU_CR2); -} - -/** * pcie_dw_set_host_bars() - Configure the host BARs * * @regs_base: A pointer to the PCIe controller registers @@ -495,7 +509,18 @@ static int pcie_dw_mvebu_probe(struct udevice *dev) hose->first_busno); } - pcie_dw_regions_setup(pcie); + /* Store the IO and MEM windows settings for future use by the ATU */ + pcie->io.phys_start = hose->regions[0].phys_start; /* IO base */ + pcie->io.bus_start = hose->regions[0].bus_start; /* IO_bus_addr */ + pcie->io.size = hose->regions[0].size; /* IO size */ + + pcie->mem.phys_start = hose->regions[1].phys_start; /* MEM base */ + pcie->mem.bus_start = hose->regions[1].bus_start; /* MEM_bus_addr */ + pcie->mem.size = hose->regions[1].size; /* MEM size */ + + pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_MEM, pcie->mem.phys_start, + pcie->mem.bus_start, pcie->mem.size); /* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */ clrsetbits_le32(pcie->ctrl_base + PCI_CLASS_REVISION, diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 3b9a09ce18..119edec204 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -59,6 +59,31 @@ config SPL_NOP_PHY This is useful when a driver uses the PHY framework but no real PHY hardware exists. +config BCM6318_USBH_PHY + bool "BCM6318 USBH PHY support" + depends on PHY && ARCH_BMIPS + select POWER_DOMAIN + help + Support for the Broadcom MIPS BCM6318 USBH PHY. + +config BCM6348_USBH_PHY + bool "BCM6348 USBH PHY support" + depends on PHY && ARCH_BMIPS + help + Support for the Broadcom MIPS BCM6348 USBH PHY. + +config BCM6358_USBH_PHY + bool "BCM6358 USBH PHY support" + depends on PHY && ARCH_BMIPS + help + Support for the Broadcom MIPS BCM6358 USBH PHY. + +config BCM6368_USBH_PHY + bool "BCM6368 USBH PHY support" + depends on PHY && ARCH_BMIPS + help + Support for the Broadcom MIPS BCM6368 USBH PHY. + config PIPE3_PHY bool "Support omap's PIPE3 PHY" depends on PHY && ARCH_OMAP2PLUS @@ -85,4 +110,12 @@ config STI_USB_PHY used by USB2 and USB3 Host controllers available on STiH407 SoC families. +config MESON_GXL_USB_PHY + bool "Amlogic Meson GXL USB PHYs" + depends on PHY && ARCH_MESON && MESON_GXL + imply REGMAP + help + This is the generic phy driver for the Amlogic Meson GXL + USB2 and USB3 PHYS. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 668040b0bb..72c14921b0 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -7,6 +7,11 @@ obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o obj-$(CONFIG_$(SPL_)NOP_PHY) += nop-phy.o +obj-$(CONFIG_BCM6318_USBH_PHY) += bcm6318-usbh-phy.o +obj-$(CONFIG_BCM6348_USBH_PHY) += bcm6348-usbh-phy.o +obj-$(CONFIG_BCM6358_USBH_PHY) += bcm6358-usbh-phy.o +obj-$(CONFIG_BCM6368_USBH_PHY) += bcm6368-usbh-phy.o obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o obj-$(CONFIG_$(SPL_)PIPE3_PHY) += ti-pipe3-phy.o obj-$(CONFIG_STI_USB_PHY) += sti_usb_phy.o +obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o meson-gxl-usb3.o diff --git a/drivers/phy/bcm6318-usbh-phy.c b/drivers/phy/bcm6318-usbh-phy.c new file mode 100644 index 0000000000..6d54214581 --- /dev/null +++ b/drivers/phy/bcm6318-usbh-phy.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018 Ãlvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/usb-common.c: + * Copyright 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright 2013 Florian Fainelli <florian@openwrt.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <power-domain.h> +#include <reset.h> +#include <asm/io.h> +#include <dm/device.h> + +/* USBH Setup register */ +#define USBH_SETUP_REG 0x00 +#define USBH_SETUP_IOC BIT(4) + +/* USBH PLL Control register */ +#define USBH_PLL_REG 0x04 +#define USBH_PLL_SUSP_EN BIT(27) +#define USBH_PLL_IDDQ_PWRDN BIT(31) + +/* USBH Swap Control register */ +#define USBH_SWAP_REG 0x0c +#define USBH_SWAP_OHCI_DATA BIT(0) +#define USBH_SWAP_OHCI_ENDIAN BIT(1) +#define USBH_SWAP_EHCI_DATA BIT(3) +#define USBH_SWAP_EHCI_ENDIAN BIT(4) + +/* USBH Sim Control register */ +#define USBH_SIM_REG 0x20 +#define USBH_SIM_LADDR BIT(5) + +struct bcm6318_usbh_priv { + void __iomem *regs; +}; + +static int bcm6318_usbh_init(struct phy *phy) +{ + struct bcm6318_usbh_priv *priv = dev_get_priv(phy->dev); + + /* enable pll control susp */ + setbits_be32(priv->regs + USBH_PLL_REG, USBH_PLL_SUSP_EN); + + /* configure to work in native cpu endian */ + clrsetbits_be32(priv->regs + USBH_SWAP_REG, + USBH_SWAP_EHCI_ENDIAN | USBH_SWAP_OHCI_ENDIAN, + USBH_SWAP_EHCI_DATA | USBH_SWAP_OHCI_DATA); + + /* setup config */ + setbits_be32(priv->regs + USBH_SETUP_REG, USBH_SETUP_IOC); + + /* disable pll control pwrdn */ + clrbits_be32(priv->regs + USBH_PLL_REG, USBH_PLL_IDDQ_PWRDN); + + /* sim control config */ + setbits_be32(priv->regs + USBH_SIM_REG, USBH_SIM_LADDR); + + return 0; +} + +static struct phy_ops bcm6318_usbh_ops = { + .init = bcm6318_usbh_init, +}; + +static const struct udevice_id bcm6318_usbh_ids[] = { + { .compatible = "brcm,bcm6318-usbh" }, + { /* sentinel */ } +}; + +static int bcm6318_usbh_probe(struct udevice *dev) +{ + struct bcm6318_usbh_priv *priv = dev_get_priv(dev); + struct power_domain pwr_dom; + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + + /* enable usbh clock */ + ret = clk_get_by_name(dev, "usbh", &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* enable power domain */ + ret = power_domain_get(dev, &pwr_dom); + if (ret < 0) + return ret; + + ret = power_domain_on(&pwr_dom); + if (ret < 0) + return ret; + + ret = power_domain_free(&pwr_dom); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + mdelay(100); + + return 0; +} + +U_BOOT_DRIVER(bcm6318_usbh) = { + .name = "bcm6318-usbh", + .id = UCLASS_PHY, + .of_match = bcm6318_usbh_ids, + .ops = &bcm6318_usbh_ops, + .priv_auto_alloc_size = sizeof(struct bcm6318_usbh_priv), + .probe = bcm6318_usbh_probe, +}; diff --git a/drivers/phy/bcm6348-usbh-phy.c b/drivers/phy/bcm6348-usbh-phy.c new file mode 100644 index 0000000000..169ee0ecec --- /dev/null +++ b/drivers/phy/bcm6348-usbh-phy.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 Ãlvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/usb-common.c: + * Copyright 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright 2013 Florian Fainelli <florian@openwrt.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <reset.h> +#include <asm/io.h> +#include <dm/device.h> + +#define USBH_SETUP_PORT1_EN BIT(0) + +struct bcm6348_usbh_priv { + void __iomem *regs; +}; + +static int bcm6348_usbh_init(struct phy *phy) +{ + struct bcm6348_usbh_priv *priv = dev_get_priv(phy->dev); + + writel_be(USBH_SETUP_PORT1_EN, priv->regs); + + return 0; +} + +static struct phy_ops bcm6348_usbh_ops = { + .init = bcm6348_usbh_init, +}; + +static const struct udevice_id bcm6348_usbh_ids[] = { + { .compatible = "brcm,bcm6348-usbh" }, + { /* sentinel */ } +}; + +static int bcm6348_usbh_probe(struct udevice *dev) +{ + struct bcm6348_usbh_priv *priv = dev_get_priv(dev); + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + + /* enable usbh clock */ + ret = clk_get_by_name(dev, "usbh", &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + return 0; +} + +U_BOOT_DRIVER(bcm6348_usbh) = { + .name = "bcm6348-usbh", + .id = UCLASS_PHY, + .of_match = bcm6348_usbh_ids, + .ops = &bcm6348_usbh_ops, + .priv_auto_alloc_size = sizeof(struct bcm6348_usbh_priv), + .probe = bcm6348_usbh_probe, +}; diff --git a/drivers/phy/bcm6358-usbh-phy.c b/drivers/phy/bcm6358-usbh-phy.c new file mode 100644 index 0000000000..e000316a93 --- /dev/null +++ b/drivers/phy/bcm6358-usbh-phy.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 Ãlvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/usb-common.c: + * Copyright 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright 2013 Florian Fainelli <florian@openwrt.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <generic-phy.h> +#include <reset.h> +#include <asm/io.h> +#include <dm/device.h> + +/* USBH Swap Control register */ +#define USBH_SWAP_REG 0x00 +#define USBH_SWAP_OHCI_DATA BIT(0) +#define USBH_SWAP_OHCI_ENDIAN BIT(1) +#define USBH_SWAP_EHCI_DATA BIT(3) +#define USBH_SWAP_EHCI_ENDIAN BIT(4) + +/* USBH Test register */ +#define USBH_TEST_REG 0x24 +#define USBH_TEST_PORT_CTL 0x1c0020 + +struct bcm6358_usbh_priv { + void __iomem *regs; +}; + +static int bcm6358_usbh_init(struct phy *phy) +{ + struct bcm6358_usbh_priv *priv = dev_get_priv(phy->dev); + + /* configure to work in native cpu endian */ + clrsetbits_be32(priv->regs + USBH_SWAP_REG, + USBH_SWAP_EHCI_ENDIAN | USBH_SWAP_OHCI_ENDIAN, + USBH_SWAP_EHCI_DATA | USBH_SWAP_OHCI_DATA); + + /* test port control */ + writel_be(USBH_TEST_PORT_CTL, priv->regs + USBH_TEST_REG); + + return 0; +} + +static struct phy_ops bcm6358_usbh_ops = { + .init = bcm6358_usbh_init, +}; + +static const struct udevice_id bcm6358_usbh_ids[] = { + { .compatible = "brcm,bcm6358-usbh" }, + { /* sentinel */ } +}; + +static int bcm6358_usbh_probe(struct udevice *dev) +{ + struct bcm6358_usbh_priv *priv = dev_get_priv(dev); + struct reset_ctl rst_ctl; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + return 0; +} + +U_BOOT_DRIVER(bcm6358_usbh) = { + .name = "bcm6358-usbh", + .id = UCLASS_PHY, + .of_match = bcm6358_usbh_ids, + .ops = &bcm6358_usbh_ops, + .priv_auto_alloc_size = sizeof(struct bcm6358_usbh_priv), + .probe = bcm6358_usbh_probe, +}; diff --git a/drivers/phy/bcm6368-usbh-phy.c b/drivers/phy/bcm6368-usbh-phy.c new file mode 100644 index 0000000000..71abc0fcc4 --- /dev/null +++ b/drivers/phy/bcm6368-usbh-phy.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2018 Ãlvaro Fernández Rojas <noltari@gmail.com> + * + * Derived from linux/arch/mips/bcm63xx/usb-common.c: + * Copyright 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright 2013 Florian Fainelli <florian@openwrt.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <power-domain.h> +#include <reset.h> +#include <asm/io.h> +#include <dm/device.h> + +/* USBH PLL Control register */ +#define USBH_PLL_REG 0x18 +#define USBH_PLL_IDDQ_PWRDN BIT(9) +#define USBH_PLL_PWRDN_DELAY BIT(10) + +/* USBH Swap Control register */ +#define USBH_SWAP_REG 0x1c +#define USBH_SWAP_OHCI_DATA BIT(0) +#define USBH_SWAP_OHCI_ENDIAN BIT(1) +#define USBH_SWAP_EHCI_DATA BIT(3) +#define USBH_SWAP_EHCI_ENDIAN BIT(4) + +/* USBH Setup register */ +#define USBH_SETUP_REG 0x28 +#define USBH_SETUP_IOC BIT(4) +#define USBH_SETUP_IPP BIT(5) + +struct bcm6368_usbh_hw { + uint32_t setup_clr; + uint32_t pll_clr; +}; + +struct bcm6368_usbh_priv { + const struct bcm6368_usbh_hw *hw; + void __iomem *regs; +}; + +static int bcm6368_usbh_init(struct phy *phy) +{ + struct bcm6368_usbh_priv *priv = dev_get_priv(phy->dev); + const struct bcm6368_usbh_hw *hw = priv->hw; + + /* configure to work in native cpu endian */ + clrsetbits_be32(priv->regs + USBH_SWAP_REG, + USBH_SWAP_EHCI_ENDIAN | USBH_SWAP_OHCI_ENDIAN, + USBH_SWAP_EHCI_DATA | USBH_SWAP_OHCI_DATA); + + /* setup config */ + if (hw->setup_clr) + clrbits_be32(priv->regs + USBH_SETUP_REG, hw->setup_clr); + + setbits_be32(priv->regs + USBH_SETUP_REG, USBH_SETUP_IOC); + + /* enable pll control */ + if (hw->pll_clr) + clrbits_be32(priv->regs + USBH_PLL_REG, hw->pll_clr); + + return 0; +} + +static struct phy_ops bcm6368_usbh_ops = { + .init = bcm6368_usbh_init, +}; + +static const struct bcm6368_usbh_hw bcm6328_hw = { + .pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY, + .setup_clr = 0, +}; + +static const struct bcm6368_usbh_hw bcm6362_hw = { + .pll_clr = 0, + .setup_clr = 0, +}; + +static const struct bcm6368_usbh_hw bcm6368_hw = { + .pll_clr = 0, + .setup_clr = 0, +}; + +static const struct bcm6368_usbh_hw bcm63268_hw = { + .pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY, + .setup_clr = USBH_SETUP_IPP, +}; + +static const struct udevice_id bcm6368_usbh_ids[] = { + { + .compatible = "brcm,bcm6328-usbh", + .data = (ulong)&bcm6328_hw, + }, { + .compatible = "brcm,bcm6362-usbh", + .data = (ulong)&bcm6362_hw, + }, { + .compatible = "brcm,bcm6368-usbh", + .data = (ulong)&bcm6368_hw, + }, { + .compatible = "brcm,bcm63268-usbh", + .data = (ulong)&bcm63268_hw, + }, { /* sentinel */ } +}; + +static int bcm6368_usbh_probe(struct udevice *dev) +{ + struct bcm6368_usbh_priv *priv = dev_get_priv(dev); + const struct bcm6368_usbh_hw *hw = + (const struct bcm6368_usbh_hw *)dev_get_driver_data(dev); +#if defined(CONFIG_POWER_DOMAIN) + struct power_domain pwr_dom; +#endif + struct reset_ctl rst_ctl; + struct clk clk; + fdt_addr_t addr; + fdt_size_t size; + int ret; + + addr = devfdt_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->hw = hw; + + /* enable usbh clock */ + ret = clk_get_by_name(dev, "usbh", &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + +#if defined(CONFIG_POWER_DOMAIN) + /* enable power domain */ + ret = power_domain_get(dev, &pwr_dom); + if (ret < 0) + return ret; + + ret = power_domain_on(&pwr_dom); + if (ret < 0) + return ret; + + ret = power_domain_free(&pwr_dom); + if (ret < 0) + return ret; +#endif + + /* perform reset */ + ret = reset_get_by_index(dev, 0, &rst_ctl); + if (ret < 0) + return ret; + + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + + ret = reset_free(&rst_ctl); + if (ret < 0) + return ret; + + /* enable usb_ref clock */ + ret = clk_get_by_name(dev, "usb_ref", &clk); + if (!ret) { + ret = clk_enable(&clk); + if (ret < 0) + return ret; + + ret = clk_free(&clk); + if (ret < 0) + return ret; + } + + mdelay(100); + + return 0; +} + +U_BOOT_DRIVER(bcm6368_usbh) = { + .name = "bcm6368-usbh", + .id = UCLASS_PHY, + .of_match = bcm6368_usbh_ids, + .ops = &bcm6368_usbh_ops, + .priv_auto_alloc_size = sizeof(struct bcm6368_usbh_priv), + .probe = bcm6368_usbh_probe, +}; diff --git a/drivers/phy/meson-gxl-usb2.c b/drivers/phy/meson-gxl-usb2.c new file mode 100644 index 0000000000..15c9c89fd9 --- /dev/null +++ b/drivers/phy/meson-gxl-usb2.c @@ -0,0 +1,238 @@ +/* + * Meson GXL and GXM USB2 PHY driver + * + * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstron@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <bitfield.h> +#include <dm.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <power/regulator.h> +#include <clk.h> + +#include <linux/bitops.h> +#include <linux/compat.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* bits [31:27] are read-only */ +#define U2P_R0 0x0 + #define U2P_R0_BYPASS_SEL BIT(0) + #define U2P_R0_BYPASS_DM_EN BIT(1) + #define U2P_R0_BYPASS_DP_EN BIT(2) + #define U2P_R0_TXBITSTUFF_ENH BIT(3) + #define U2P_R0_TXBITSTUFF_EN BIT(4) + #define U2P_R0_DM_PULLDOWN BIT(5) + #define U2P_R0_DP_PULLDOWN BIT(6) + #define U2P_R0_DP_VBUS_VLD_EXT_SEL BIT(7) + #define U2P_R0_DP_VBUS_VLD_EXT BIT(8) + #define U2P_R0_ADP_PRB_EN BIT(9) + #define U2P_R0_ADP_DISCHARGE BIT(10) + #define U2P_R0_ADP_CHARGE BIT(11) + #define U2P_R0_DRV_VBUS BIT(12) + #define U2P_R0_ID_PULLUP BIT(13) + #define U2P_R0_LOOPBACK_EN_B BIT(14) + #define U2P_R0_OTG_DISABLE BIT(15) + #define U2P_R0_COMMON_ONN BIT(16) + #define U2P_R0_FSEL_MASK GENMASK(19, 17) + #define U2P_R0_REF_CLK_SEL_MASK GENMASK(21, 20) + #define U2P_R0_POWER_ON_RESET BIT(22) + #define U2P_R0_V_ATE_TEST_EN_B_MASK GENMASK(24, 23) + #define U2P_R0_ID_SET_ID_DQ BIT(25) + #define U2P_R0_ATE_RESET BIT(26) + #define U2P_R0_FSV_MINUS BIT(27) + #define U2P_R0_FSV_PLUS BIT(28) + #define U2P_R0_BYPASS_DM_DATA BIT(29) + #define U2P_R0_BYPASS_DP_DATA BIT(30) + +#define U2P_R1 0x4 + #define U2P_R1_BURN_IN_TEST BIT(0) + #define U2P_R1_ACA_ENABLE BIT(1) + #define U2P_R1_DCD_ENABLE BIT(2) + #define U2P_R1_VDAT_SRC_EN_B BIT(3) + #define U2P_R1_VDAT_DET_EN_B BIT(4) + #define U2P_R1_CHARGES_SEL BIT(5) + #define U2P_R1_TX_PREEMP_PULSE_TUNE BIT(6) + #define U2P_R1_TX_PREEMP_AMP_TUNE_MASK GENMASK(8, 7) + #define U2P_R1_TX_RES_TUNE_MASK GENMASK(10, 9) + #define U2P_R1_TX_RISE_TUNE_MASK GENMASK(12, 11) + #define U2P_R1_TX_VREF_TUNE_MASK GENMASK(16, 13) + #define U2P_R1_TX_FSLS_TUNE_MASK GENMASK(20, 17) + #define U2P_R1_TX_HSXV_TUNE_MASK GENMASK(22, 21) + #define U2P_R1_OTG_TUNE_MASK GENMASK(25, 23) + #define U2P_R1_SQRX_TUNE_MASK GENMASK(28, 26) + #define U2P_R1_COMP_DIS_TUNE_MASK GENMASK(31, 29) + +/* bits [31:14] are read-only */ +#define U2P_R2 0x8 + #define U2P_R2_TESTDATA_IN_MASK GENMASK(7, 0) + #define U2P_R2_TESTADDR_MASK GENMASK(11, 8) + #define U2P_R2_TESTDATA_OUT_SEL BIT(12) + #define U2P_R2_TESTCLK BIT(13) + #define U2P_R2_TESTDATA_OUT_MASK GENMASK(17, 14) + #define U2P_R2_ACA_PIN_RANGE_C BIT(18) + #define U2P_R2_ACA_PIN_RANGE_B BIT(19) + #define U2P_R2_ACA_PIN_RANGE_A BIT(20) + #define U2P_R2_ACA_PIN_GND BIT(21) + #define U2P_R2_ACA_PIN_FLOAT BIT(22) + #define U2P_R2_CHARGE_DETECT BIT(23) + #define U2P_R2_DEVICE_SESSION_VALID BIT(24) + #define U2P_R2_ADP_PROBE BIT(25) + #define U2P_R2_ADP_SENSE BIT(26) + #define U2P_R2_SESSION_END BIT(27) + #define U2P_R2_VBUS_VALID BIT(28) + #define U2P_R2_B_VALID BIT(29) + #define U2P_R2_A_VALID BIT(30) + #define U2P_R2_ID_DIG BIT(31) + +#define U2P_R3 0xc + +#define RESET_COMPLETE_TIME 500 + +struct phy_meson_gxl_usb2_priv { + struct regmap *regmap; +#if CONFIG_IS_ENABLED(DM_REGULATOR) + struct udevice *phy_supply; +#endif +#if CONFIG_IS_ENABLED(CLK) + struct clk clk; +#endif +}; + +static void phy_meson_gxl_usb2_reset(struct phy_meson_gxl_usb2_priv *priv) +{ + uint val; + + regmap_read(priv->regmap, U2P_R0, &val); + + /* reset the PHY and wait until settings are stabilized */ + val |= U2P_R0_POWER_ON_RESET; + regmap_write(priv->regmap, U2P_R0, val); + udelay(RESET_COMPLETE_TIME); + + val &= ~U2P_R0_POWER_ON_RESET; + regmap_write(priv->regmap, U2P_R0, val); + udelay(RESET_COMPLETE_TIME); +} + +static void +phy_meson_gxl_usb2_set_host_mode(struct phy_meson_gxl_usb2_priv *priv) +{ + uint val; + + regmap_read(priv->regmap, U2P_R0, &val); + val |= U2P_R0_DM_PULLDOWN; + val |= U2P_R0_DP_PULLDOWN; + val &= ~U2P_R0_ID_PULLUP; + regmap_write(priv->regmap, U2P_R0, val); + + phy_meson_gxl_usb2_reset(priv); +} + +static int phy_meson_gxl_usb2_power_on(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_gxl_usb2_priv *priv = dev_get_priv(dev); + uint val; + + regmap_read(priv->regmap, U2P_R0, &val); + /* power on the PHY by taking it out of reset mode */ + val &= ~U2P_R0_POWER_ON_RESET; + regmap_write(priv->regmap, U2P_R0, val); + + phy_meson_gxl_usb2_set_host_mode(priv); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (priv->phy_supply) { + int ret = regulator_set_enable(priv->phy_supply, true); + if (ret) + return ret; + } +#endif + + return 0; +} + +static int phy_meson_gxl_usb2_power_off(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_gxl_usb2_priv *priv = dev_get_priv(dev); + uint val; + + regmap_read(priv->regmap, U2P_R0, &val); + /* power off the PHY by putting it into reset mode */ + val |= U2P_R0_POWER_ON_RESET; + regmap_write(priv->regmap, U2P_R0, val); + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (priv->phy_supply) { + int ret = regulator_set_enable(priv->phy_supply, false); + if (ret) { + pr_err("Error disabling PHY supply\n"); + return ret; + } + } +#endif + + return 0; +} + +struct phy_ops meson_gxl_usb2_phy_ops = { + .power_on = phy_meson_gxl_usb2_power_on, + .power_off = phy_meson_gxl_usb2_power_off, +}; + +int meson_gxl_usb2_phy_probe(struct udevice *dev) +{ + struct phy_meson_gxl_usb2_priv *priv = dev_get_priv(dev); + int ret; + + ret = regmap_init_mem(dev, &priv->regmap); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret < 0) + return ret; + + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { + pr_err("failed to enable PHY clock\n"); + clk_free(&priv->clk); + return ret; + } +#endif + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + ret = device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply); + if (ret && ret != -ENOENT) { + pr_err("Failed to get PHY regulator\n"); + return ret; + } +#endif + + return 0; +} + +static const struct udevice_id meson_gxl_usb2_phy_ids[] = { + { .compatible = "amlogic,meson-gxl-usb2-phy" }, + { } +}; + +U_BOOT_DRIVER(meson_gxl_usb2_phy) = { + .name = "meson_gxl_usb2_phy", + .id = UCLASS_PHY, + .of_match = meson_gxl_usb2_phy_ids, + .probe = meson_gxl_usb2_phy_probe, + .ops = &meson_gxl_usb2_phy_ops, + .priv_auto_alloc_size = sizeof(struct phy_meson_gxl_usb2_priv), +}; diff --git a/drivers/phy/meson-gxl-usb3.c b/drivers/phy/meson-gxl-usb3.c new file mode 100644 index 0000000000..a385fbdf12 --- /dev/null +++ b/drivers/phy/meson-gxl-usb3.c @@ -0,0 +1,201 @@ +/* + * Meson GXL USB3 PHY driver + * + * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstron@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <bitfield.h> +#include <dm.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <clk.h> + +#include <linux/bitops.h> +#include <linux/compat.h> +#include <linux/bitfield.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define USB_R0 0x00 + #define USB_R0_P30_FSEL_MASK GENMASK(5, 0) + #define USB_R0_P30_PHY_RESET BIT(6) + #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7) + #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8) + #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9) + #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14) + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x04 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x08 + #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0) + #define USB_R2_P30_CR_READ BIT(16) + #define USB_R2_P30_CR_WRITE BIT(17) + #define USB_R2_P30_CR_CAP_ADDR BIT(18) + #define USB_R2_P30_CR_CAP_DATA BIT(19) + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x0c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16) + #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19) + #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24) + +#define USB_R4 0x10 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x14 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +/* read-only register */ +#define USB_R6 0x18 + #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0) + #define USB_R6_P30_CR_ACK BIT(16) + +struct phy_meson_gxl_usb3_priv { + struct regmap *regmap; +#if CONFIG_IS_ENABLED(CLK) + struct clk clk; +#endif +}; + +static int +phy_meson_gxl_usb3_set_host_mode(struct phy_meson_gxl_usb3_priv *priv) +{ + uint val; + + regmap_read(priv->regmap, USB_R0, &val); + val &= ~USB_R0_U2D_ACT; + regmap_write(priv->regmap, USB_R0, val); + + regmap_read(priv->regmap, USB_R4, &val); + val &= ~USB_R4_P21_SLEEP_M0; + regmap_write(priv->regmap, USB_R4, val); + + return 0; +} + +static int phy_meson_gxl_usb3_power_on(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); + uint val; + + regmap_read(priv->regmap, USB_R5, &val); + val |= USB_R5_ID_DIG_EN_0; + val |= USB_R5_ID_DIG_EN_1; + val &= ~USB_R5_ID_DIG_TH_MASK; + val |= FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff); + regmap_write(priv->regmap, USB_R5, val); + + return phy_meson_gxl_usb3_set_host_mode(priv); +} + +static int phy_meson_gxl_usb3_power_off(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); + uint val; + + regmap_read(priv->regmap, USB_R5, &val); + val &= ~USB_R5_ID_DIG_EN_0; + val &= ~USB_R5_ID_DIG_EN_1; + regmap_write(priv->regmap, USB_R5, val); + + return 0; +} + +static int phy_meson_gxl_usb3_init(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); + uint val; + + regmap_read(priv->regmap, USB_R1, &val); + val &= ~USB_R1_U3H_FLADJ_30MHZ_REG_MASK; + val |= FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20); + regmap_write(priv->regmap, USB_R1, val); + + return 0; +} + +struct phy_ops meson_gxl_usb3_phy_ops = { + .init = phy_meson_gxl_usb3_init, + .power_on = phy_meson_gxl_usb3_power_on, + .power_off = phy_meson_gxl_usb3_power_off, +}; + +int meson_gxl_usb3_phy_probe(struct udevice *dev) +{ + struct phy_meson_gxl_usb3_priv *priv = dev_get_priv(dev); + int ret; + + ret = regmap_init_mem(dev, &priv->regmap); + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret < 0) + return ret; + + ret = clk_enable(&priv->clk); + if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { + pr_err("failed to enable PHY clock\n"); + clk_free(&priv->clk); + return ret; + } +#endif + + return 0; +} + +static const struct udevice_id meson_gxl_usb3_phy_ids[] = { + { .compatible = "amlogic,meson-gxl-usb3-phy" }, + { } +}; + +U_BOOT_DRIVER(meson_gxl_usb3_phy) = { + .name = "meson_gxl_usb3_phy", + .id = UCLASS_PHY, + .of_match = meson_gxl_usb3_phy_ids, + .probe = meson_gxl_usb3_phy_probe, + .ops = &meson_gxl_usb3_phy_ops, + .priv_auto_alloc_size = sizeof(struct phy_meson_gxl_usb3_priv), +}; diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index 2bf853eba1..010eb203b7 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -44,7 +44,7 @@ DECLARE_GLOBAL_DATA_PTR; #define IRQ_STATUS 0x10 #define IRQ_WKUP 0x18 -#define NB_FUNCS 2 +#define NB_FUNCS 3 #define GPIO_PER_REG 32 /** @@ -128,6 +128,16 @@ struct armada_37xx_pinctrl { .funcs = {_func1, "gpio"} \ } +#define PIN_GRP_GPIO_3(_name, _start, _nr, _mask, _v1, _v2, _v3, _f1, _f2) \ + { \ + .name = _name, \ + .start_pin = _start, \ + .npins = _nr, \ + .reg_mask = _mask, \ + .val = {_v1, _v2, _v3}, \ + .funcs = {_f1, _f2, "gpio"} \ + } + #define PIN_GRP_EXTRA(_name, _start, _nr, _mask, _v1, _v2, _start2, _nr2, \ _f1, _f2) \ { \ @@ -149,8 +159,8 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = { PIN_GRP_GPIO("pwm1", 12, 1, BIT(4), "pwm"), PIN_GRP_GPIO("pwm2", 13, 1, BIT(5), "pwm"), PIN_GRP_GPIO("pwm3", 14, 1, BIT(6), "pwm"), - PIN_GRP_GPIO("pmic1", 17, 1, BIT(7), "pmic"), - PIN_GRP_GPIO("pmic0", 16, 1, BIT(8), "pmic"), + PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"), + PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"), PIN_GRP_GPIO("i2c2", 2, 2, BIT(9), "i2c"), PIN_GRP_GPIO("i2c1", 0, 2, BIT(10), "i2c"), PIN_GRP_GPIO("spi_cs1", 17, 1, BIT(12), "spi"), @@ -172,13 +182,15 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = { static struct armada_37xx_pin_group armada_37xx_sb_groups[] = { PIN_GRP_GPIO("usb32_drvvbus0", 0, 1, BIT(0), "drvbus"), PIN_GRP_GPIO("usb2_drvvbus1", 1, 1, BIT(1), "drvbus"), - PIN_GRP_GPIO("sdio_sb", 24, 5, BIT(2), "sdio"), - PIN_GRP_EXTRA("rgmii", 6, 14, BIT(3), 0, BIT(3), 23, 1, "mii", "gpio"), - PIN_GRP_GPIO("pcie1", 3, 2, BIT(4), "pcie"), - PIN_GRP_GPIO("ptp", 20, 3, BIT(5), "ptp"), + PIN_GRP_GPIO("sdio_sb", 24, 6, BIT(2), "sdio"), + PIN_GRP_GPIO("rgmii", 6, 12, BIT(3), "mii"), + PIN_GRP_GPIO("smi", 18, 2, BIT(4), "smi"), + PIN_GRP_GPIO("pcie1", 3, 3, BIT(5) | BIT(9) | BIT(10), "pcie"), + PIN_GRP_GPIO("ptp", 20, 3, BIT(11) | BIT(12) | BIT(13), "ptp"), PIN_GRP("ptp_clk", 21, 1, BIT(6), "ptp", "mii"), PIN_GRP("ptp_trig", 22, 1, BIT(7), "ptp", "mii"), - PIN_GRP("mii_col", 23, 1, BIT(8), "mii", "mii_err"), + PIN_GRP_GPIO_3("mii_col", 23, 1, BIT(8) | BIT(14), 0, BIT(8), BIT(14), + "mii", "mii_err"), }; const struct armada_37xx_pin_data armada_37xx_pin_nb = { @@ -189,18 +201,18 @@ const struct armada_37xx_pin_data armada_37xx_pin_nb = { }; const struct armada_37xx_pin_data armada_37xx_pin_sb = { - .nr_pins = 29, + .nr_pins = 30, .name = "GPIO2", .groups = armada_37xx_sb_groups, .ngroups = ARRAY_SIZE(armada_37xx_sb_groups), }; static inline void armada_37xx_update_reg(unsigned int *reg, - unsigned int offset) + unsigned int *offset) { /* We never have more than 2 registers */ - if (offset >= GPIO_PER_REG) { - offset -= GPIO_PER_REG; + if (*offset >= GPIO_PER_REG) { + *offset -= GPIO_PER_REG; *reg += sizeof(u32); } } @@ -210,7 +222,7 @@ static int armada_37xx_get_func_reg(struct armada_37xx_pin_group *grp, { int f; - for (f = 0; f < NB_FUNCS; f++) + for (f = 0; (f < NB_FUNCS) && grp->funcs[f]; f++) if (!strcmp(grp->funcs[f], func)) return f; @@ -352,7 +364,7 @@ static int armada_37xx_fill_group(struct armada_37xx_pinctrl *info) for (j = 0; j < grp->extra_npins; j++) grp->pins[i+j] = grp->extra_pin + j; - for (f = 0; f < NB_FUNCS; f++) { + for (f = 0; (f < NB_FUNCS) && grp->funcs[f]; f++) { int ret; /* check for unique functions and count groups */ ret = armada_37xx_add_function(info->funcs, &funcsize, @@ -404,7 +416,7 @@ static int armada_37xx_fill_func(struct armada_37xx_pinctrl *info) struct armada_37xx_pin_group *gp = &info->groups[g]; int f; - for (f = 0; f < NB_FUNCS; f++) { + for (f = 0; (f < NB_FUNCS) && gp->funcs[f]; f++) { if (strcmp(gp->funcs[f], name) == 0) { *groups = gp->name; groups++; @@ -421,7 +433,7 @@ static int armada_37xx_gpio_get(struct udevice *dev, unsigned int offset) unsigned int reg = INPUT_VAL; unsigned int val, mask; - armada_37xx_update_reg(®, offset); + armada_37xx_update_reg(®, &offset); mask = BIT(offset); val = readl(info->base + reg); @@ -436,7 +448,7 @@ static int armada_37xx_gpio_set(struct udevice *dev, unsigned int offset, unsigned int reg = OUTPUT_VAL; unsigned int mask, val; - armada_37xx_update_reg(®, offset); + armada_37xx_update_reg(®, &offset); mask = BIT(offset); val = value ? mask : 0; @@ -452,7 +464,7 @@ static int armada_37xx_gpio_get_direction(struct udevice *dev, unsigned int reg = OUTPUT_EN; unsigned int val, mask; - armada_37xx_update_reg(®, offset); + armada_37xx_update_reg(®, &offset); mask = BIT(offset); val = readl(info->base + reg); @@ -469,7 +481,7 @@ static int armada_37xx_gpio_direction_input(struct udevice *dev, unsigned int reg = OUTPUT_EN; unsigned int mask; - armada_37xx_update_reg(®, offset); + armada_37xx_update_reg(®, &offset); mask = BIT(offset); clrbits_le32(info->base + reg, mask); @@ -484,7 +496,7 @@ static int armada_37xx_gpio_direction_output(struct udevice *dev, unsigned int reg = OUTPUT_EN; unsigned int mask; - armada_37xx_update_reg(®, offset); + armada_37xx_update_reg(®, &offset); mask = BIT(offset); setbits_le32(info->base + reg, mask); diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index 6a73a0679b..a0a326a142 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -12,6 +12,7 @@ #include <dm/lists.h> #include <dm/pinctrl.h> #include <dm/util.h> +#include <dm/of_access.h> DECLARE_GLOBAL_DATA_PTR; @@ -63,16 +64,13 @@ static int pinctrl_config_one(struct udevice *config) */ static int pinctrl_select_state_full(struct udevice *dev, const char *statename) { - const void *fdt = gd->fdt_blob; - int node = dev_of_offset(dev); char propname[32]; /* long enough */ const fdt32_t *list; uint32_t phandle; - int config_node; struct udevice *config; int state, size, i, ret; - state = fdt_stringlist_search(fdt, node, "pinctrl-names", statename); + state = dev_read_stringlist_search(dev, "pinctrl-names", statename); if (state < 0) { char *end; /* @@ -85,22 +83,15 @@ static int pinctrl_select_state_full(struct udevice *dev, const char *statename) } snprintf(propname, sizeof(propname), "pinctrl-%d", state); - list = fdt_getprop(fdt, node, propname, &size); + list = dev_read_prop(dev, propname, &size); if (!list) return -EINVAL; size /= sizeof(*list); for (i = 0; i < size; i++) { phandle = fdt32_to_cpu(*list++); - - config_node = fdt_node_offset_by_phandle(fdt, phandle); - if (config_node < 0) { - dev_err(dev, "prop %s index %d invalid phandle\n", - propname, i); - return -EINVAL; - } - ret = uclass_get_device_by_of_offset(UCLASS_PINCONFIG, - config_node, &config); + ret = uclass_get_device_by_phandle_id(UCLASS_PINCONFIG, phandle, + &config); if (ret) return ret; diff --git a/drivers/pinctrl/pinctrl_stm32.c b/drivers/pinctrl/pinctrl_stm32.c index 2066e11cf1..31285cdd57 100644 --- a/drivers/pinctrl/pinctrl_stm32.c +++ b/drivers/pinctrl/pinctrl_stm32.c @@ -41,9 +41,10 @@ static int stm32_gpio_config(struct gpio_desc *desc, return 0; } + static int prep_gpio_dsc(struct stm32_gpio_dsc *gpio_dsc, u32 port_pin) { - gpio_dsc->port = (port_pin & 0xF000) >> 12; + gpio_dsc->port = (port_pin & 0x1F000) >> 12; gpio_dsc->pin = (port_pin & 0x0F00) >> 8; debug("%s: GPIO:port= %d, pin= %d\n", __func__, gpio_dsc->port, gpio_dsc->pin); @@ -115,11 +116,13 @@ static int stm32_pinctrl_config(int offset) return -EINVAL; for (i = 0; i < len; i++) { struct gpio_desc desc; + debug("%s: pinmux = %x\n", __func__, *(pin_mux + i)); prep_gpio_dsc(&gpio_dsc, *(pin_mux + i)); prep_gpio_ctl(&gpio_ctl, *(pin_mux + i), offset); rv = uclass_get_device_by_seq(UCLASS_GPIO, - gpio_dsc.port, &desc.dev); + gpio_dsc.port, + &desc.dev); if (rv) return rv; desc.offset = gpio_dsc.pin; @@ -186,6 +189,8 @@ static const struct udevice_id stm32_pinctrl_ids[] = { { .compatible = "st,stm32f469-pinctrl" }, { .compatible = "st,stm32f746-pinctrl" }, { .compatible = "st,stm32h743-pinctrl" }, + { .compatible = "st,stm32mp157-pinctrl" }, + { .compatible = "st,stm32mp157-z-pinctrl" }, { } }; diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3036.c b/drivers/pinctrl/rockchip/pinctrl_rk3036.c index 94f6d7ad40..7e93d85dbb 100644 --- a/drivers/pinctrl/rockchip/pinctrl_rk3036.c +++ b/drivers/pinctrl/rockchip/pinctrl_rk3036.c @@ -18,6 +18,416 @@ DECLARE_GLOBAL_DATA_PTR; +/* GRF_GPIO0A_IOMUX */ +enum { + GPIO0A3_SHIFT = 6, + GPIO0A3_MASK = 1 << GPIO0A3_SHIFT, + GPIO0A3_GPIO = 0, + GPIO0A3_I2C1_SDA, + + GPIO0A2_SHIFT = 4, + GPIO0A2_MASK = 1 << GPIO0A2_SHIFT, + GPIO0A2_GPIO = 0, + GPIO0A2_I2C1_SCL, + + GPIO0A1_SHIFT = 2, + GPIO0A1_MASK = 3 << GPIO0A1_SHIFT, + GPIO0A1_GPIO = 0, + GPIO0A1_I2C0_SDA, + GPIO0A1_PWM2, + + GPIO0A0_SHIFT = 0, + GPIO0A0_MASK = 3 << GPIO0A0_SHIFT, + GPIO0A0_GPIO = 0, + GPIO0A0_I2C0_SCL, + GPIO0A0_PWM1, +}; + +/* GRF_GPIO0B_IOMUX */ +enum { + GPIO0B6_SHIFT = 12, + GPIO0B6_MASK = 3 << GPIO0B6_SHIFT, + GPIO0B6_GPIO = 0, + GPIO0B6_MMC1_D3, + GPIO0B6_I2S1_SCLK, + + GPIO0B5_SHIFT = 10, + GPIO0B5_MASK = 3 << GPIO0B5_SHIFT, + GPIO0B5_GPIO = 0, + GPIO0B5_MMC1_D2, + GPIO0B5_I2S1_SDI, + + GPIO0B4_SHIFT = 8, + GPIO0B4_MASK = 3 << GPIO0B4_SHIFT, + GPIO0B4_GPIO = 0, + GPIO0B4_MMC1_D1, + GPIO0B4_I2S1_LRCKTX, + + GPIO0B3_SHIFT = 6, + GPIO0B3_MASK = 3 << GPIO0B3_SHIFT, + GPIO0B3_GPIO = 0, + GPIO0B3_MMC1_D0, + GPIO0B3_I2S1_LRCKRX, + + GPIO0B1_SHIFT = 2, + GPIO0B1_MASK = 3 << GPIO0B1_SHIFT, + GPIO0B1_GPIO = 0, + GPIO0B1_MMC1_CLKOUT, + GPIO0B1_I2S1_MCLK, + + GPIO0B0_SHIFT = 0, + GPIO0B0_MASK = 3, + GPIO0B0_GPIO = 0, + GPIO0B0_MMC1_CMD, + GPIO0B0_I2S1_SDO, +}; + +/* GRF_GPIO0C_IOMUX */ +enum { + GPIO0C4_SHIFT = 8, + GPIO0C4_MASK = 1 << GPIO0C4_SHIFT, + GPIO0C4_GPIO = 0, + GPIO0C4_DRIVE_VBUS, + + GPIO0C3_SHIFT = 6, + GPIO0C3_MASK = 1 << GPIO0C3_SHIFT, + GPIO0C3_GPIO = 0, + GPIO0C3_UART0_CTSN, + + GPIO0C2_SHIFT = 4, + GPIO0C2_MASK = 1 << GPIO0C2_SHIFT, + GPIO0C2_GPIO = 0, + GPIO0C2_UART0_RTSN, + + GPIO0C1_SHIFT = 2, + GPIO0C1_MASK = 1 << GPIO0C1_SHIFT, + GPIO0C1_GPIO = 0, + GPIO0C1_UART0_SIN, + + + GPIO0C0_SHIFT = 0, + GPIO0C0_MASK = 1 << GPIO0C0_SHIFT, + GPIO0C0_GPIO = 0, + GPIO0C0_UART0_SOUT, +}; + +/* GRF_GPIO0D_IOMUX */ +enum { + GPIO0D4_SHIFT = 8, + GPIO0D4_MASK = 1 << GPIO0D4_SHIFT, + GPIO0D4_GPIO = 0, + GPIO0D4_SPDIF, + + GPIO0D3_SHIFT = 6, + GPIO0D3_MASK = 1 << GPIO0D3_SHIFT, + GPIO0D3_GPIO = 0, + GPIO0D3_PWM3, + + GPIO0D2_SHIFT = 4, + GPIO0D2_MASK = 1 << GPIO0D2_SHIFT, + GPIO0D2_GPIO = 0, + GPIO0D2_PWM0, +}; + +/* GRF_GPIO1A_IOMUX */ +enum { + GPIO1A5_SHIFT = 10, + GPIO1A5_MASK = 1 << GPIO1A5_SHIFT, + GPIO1A5_GPIO = 0, + GPIO1A5_I2S_SDI, + + GPIO1A4_SHIFT = 8, + GPIO1A4_MASK = 1 << GPIO1A4_SHIFT, + GPIO1A4_GPIO = 0, + GPIO1A4_I2S_SD0, + + GPIO1A3_SHIFT = 6, + GPIO1A3_MASK = 1 << GPIO1A3_SHIFT, + GPIO1A3_GPIO = 0, + GPIO1A3_I2S_LRCKTX, + + GPIO1A2_SHIFT = 4, + GPIO1A2_MASK = 3 << GPIO1A2_SHIFT, + GPIO1A2_GPIO = 0, + GPIO1A2_I2S_LRCKRX, + GPIO1A2_PWM1_0, + + GPIO1A1_SHIFT = 2, + GPIO1A1_MASK = 1 << GPIO1A1_SHIFT, + GPIO1A1_GPIO = 0, + GPIO1A1_I2S_SCLK, + + GPIO1A0_SHIFT = 0, + GPIO1A0_MASK = 1 << GPIO1A0_SHIFT, + GPIO1A0_GPIO = 0, + GPIO1A0_I2S_MCLK, + +}; + +/* GRF_GPIO1B_IOMUX */ +enum { + GPIO1B7_SHIFT = 14, + GPIO1B7_MASK = 1 << GPIO1B7_SHIFT, + GPIO1B7_GPIO = 0, + GPIO1B7_MMC0_CMD, + + GPIO1B3_SHIFT = 6, + GPIO1B3_MASK = 1 << GPIO1B3_SHIFT, + GPIO1B3_GPIO = 0, + GPIO1B3_HDMI_HPD, + + GPIO1B2_SHIFT = 4, + GPIO1B2_MASK = 1 << GPIO1B2_SHIFT, + GPIO1B2_GPIO = 0, + GPIO1B2_HDMI_SCL, + + GPIO1B1_SHIFT = 2, + GPIO1B1_MASK = 1 << GPIO1B1_SHIFT, + GPIO1B1_GPIO = 0, + GPIO1B1_HDMI_SDA, + + GPIO1B0_SHIFT = 0, + GPIO1B0_MASK = 1 << GPIO1B0_SHIFT, + GPIO1B0_GPIO = 0, + GPIO1B0_HDMI_CEC, +}; + +/* GRF_GPIO1C_IOMUX */ +enum { + GPIO1C5_SHIFT = 10, + GPIO1C5_MASK = 3 << GPIO1C5_SHIFT, + GPIO1C5_GPIO = 0, + GPIO1C5_MMC0_D3, + GPIO1C5_JTAG_TMS, + + GPIO1C4_SHIFT = 8, + GPIO1C4_MASK = 3 << GPIO1C4_SHIFT, + GPIO1C4_GPIO = 0, + GPIO1C4_MMC0_D2, + GPIO1C4_JTAG_TCK, + + GPIO1C3_SHIFT = 6, + GPIO1C3_MASK = 3 << GPIO1C3_SHIFT, + GPIO1C3_GPIO = 0, + GPIO1C3_MMC0_D1, + GPIO1C3_UART2_SOUT, + + GPIO1C2_SHIFT = 4, + GPIO1C2_MASK = 3 << GPIO1C2_SHIFT , + GPIO1C2_GPIO = 0, + GPIO1C2_MMC0_D0, + GPIO1C2_UART2_SIN, + + GPIO1C1_SHIFT = 2, + GPIO1C1_MASK = 1 << GPIO1C1_SHIFT, + GPIO1C1_GPIO = 0, + GPIO1C1_MMC0_DETN, + + GPIO1C0_SHIFT = 0, + GPIO1C0_MASK = 1 << GPIO1C0_SHIFT, + GPIO1C0_GPIO = 0, + GPIO1C0_MMC0_CLKOUT, +}; + +/* GRF_GPIO1D_IOMUX */ +enum { + GPIO1D7_SHIFT = 14, + GPIO1D7_MASK = 3 << GPIO1D7_SHIFT, + GPIO1D7_GPIO = 0, + GPIO1D7_NAND_D7, + GPIO1D7_EMMC_D7, + GPIO1D7_SPI_CSN1, + + GPIO1D6_SHIFT = 12, + GPIO1D6_MASK = 3 << GPIO1D6_SHIFT, + GPIO1D6_GPIO = 0, + GPIO1D6_NAND_D6, + GPIO1D6_EMMC_D6, + GPIO1D6_SPI_CSN0, + + GPIO1D5_SHIFT = 10, + GPIO1D5_MASK = 3 << GPIO1D5_SHIFT, + GPIO1D5_GPIO = 0, + GPIO1D5_NAND_D5, + GPIO1D5_EMMC_D5, + GPIO1D5_SPI_TXD, + + GPIO1D4_SHIFT = 8, + GPIO1D4_MASK = 3 << GPIO1D4_SHIFT, + GPIO1D4_GPIO = 0, + GPIO1D4_NAND_D4, + GPIO1D4_EMMC_D4, + GPIO1D4_SPI_RXD, + + GPIO1D3_SHIFT = 6, + GPIO1D3_MASK = 3 << GPIO1D3_SHIFT, + GPIO1D3_GPIO = 0, + GPIO1D3_NAND_D3, + GPIO1D3_EMMC_D3, + GPIO1D3_SFC_SIO3, + + GPIO1D2_SHIFT = 4, + GPIO1D2_MASK = 3 << GPIO1D2_SHIFT, + GPIO1D2_GPIO = 0, + GPIO1D2_NAND_D2, + GPIO1D2_EMMC_D2, + GPIO1D2_SFC_SIO2, + + GPIO1D1_SHIFT = 2, + GPIO1D1_MASK = 3 << GPIO1D1_SHIFT, + GPIO1D1_GPIO = 0, + GPIO1D1_NAND_D1, + GPIO1D1_EMMC_D1, + GPIO1D1_SFC_SIO1, + + GPIO1D0_SHIFT = 0, + GPIO1D0_MASK = 3 << GPIO1D0_SHIFT, + GPIO1D0_GPIO = 0, + GPIO1D0_NAND_D0, + GPIO1D0_EMMC_D0, + GPIO1D0_SFC_SIO0, +}; + +/* GRF_GPIO2A_IOMUX */ +enum { + GPIO2A7_SHIFT = 14, + GPIO2A7_MASK = 1 << GPIO2A7_SHIFT, + GPIO2A7_GPIO = 0, + GPIO2A7_TESTCLK_OUT, + + GPIO2A6_SHIFT = 12, + GPIO2A6_MASK = 1 << GPIO2A6_SHIFT, + GPIO2A6_GPIO = 0, + GPIO2A6_NAND_CS0, + + GPIO2A4_SHIFT = 8, + GPIO2A4_MASK = 3 << GPIO2A4_SHIFT, + GPIO2A4_GPIO = 0, + GPIO2A4_NAND_RDY, + GPIO2A4_EMMC_CMD, + GPIO2A3_SFC_CLK, + + GPIO2A3_SHIFT = 6, + GPIO2A3_MASK = 3 << GPIO2A3_SHIFT, + GPIO2A3_GPIO = 0, + GPIO2A3_NAND_RDN, + GPIO2A4_SFC_CSN1, + + GPIO2A2_SHIFT = 4, + GPIO2A2_MASK = 3 << GPIO2A2_SHIFT, + GPIO2A2_GPIO = 0, + GPIO2A2_NAND_WRN, + GPIO2A4_SFC_CSN0, + + GPIO2A1_SHIFT = 2, + GPIO2A1_MASK = 3 << GPIO2A1_SHIFT, + GPIO2A1_GPIO = 0, + GPIO2A1_NAND_CLE, + GPIO2A1_EMMC_CLKOUT, + + GPIO2A0_SHIFT = 0, + GPIO2A0_MASK = 3 << GPIO2A0_SHIFT, + GPIO2A0_GPIO = 0, + GPIO2A0_NAND_ALE, + GPIO2A0_SPI_CLK, +}; + +/* GRF_GPIO2B_IOMUX */ +enum { + GPIO2B7_SHIFT = 14, + GPIO2B7_MASK = 1 << GPIO2B7_SHIFT, + GPIO2B7_GPIO = 0, + GPIO2B7_MAC_RXER, + + GPIO2B6_SHIFT = 12, + GPIO2B6_MASK = 3 << GPIO2B6_SHIFT, + GPIO2B6_GPIO = 0, + GPIO2B6_MAC_CLKOUT, + GPIO2B6_MAC_CLKIN, + + GPIO2B5_SHIFT = 10, + GPIO2B5_MASK = 1 << GPIO2B5_SHIFT, + GPIO2B5_GPIO = 0, + GPIO2B5_MAC_TXEN, + + GPIO2B4_SHIFT = 8, + GPIO2B4_MASK = 1 << GPIO2B4_SHIFT, + GPIO2B4_GPIO = 0, + GPIO2B4_MAC_MDIO, + + GPIO2B2_SHIFT = 4, + GPIO2B2_MASK = 1 << GPIO2B2_SHIFT, + GPIO2B2_GPIO = 0, + GPIO2B2_MAC_CRS, +}; + +/* GRF_GPIO2C_IOMUX */ +enum { + GPIO2C7_SHIFT = 14, + GPIO2C7_MASK = 3 << GPIO2C7_SHIFT, + GPIO2C7_GPIO = 0, + GPIO2C7_UART1_SOUT, + GPIO2C7_TESTCLK_OUT1, + + GPIO2C6_SHIFT = 12, + GPIO2C6_MASK = 1 << GPIO2C6_SHIFT, + GPIO2C6_GPIO = 0, + GPIO2C6_UART1_SIN, + + GPIO2C5_SHIFT = 10, + GPIO2C5_MASK = 1 << GPIO2C5_SHIFT, + GPIO2C5_GPIO = 0, + GPIO2C5_I2C2_SCL, + + GPIO2C4_SHIFT = 8, + GPIO2C4_MASK = 1 << GPIO2C4_SHIFT, + GPIO2C4_GPIO = 0, + GPIO2C4_I2C2_SDA, + + GPIO2C3_SHIFT = 6, + GPIO2C3_MASK = 1 << GPIO2C3_SHIFT, + GPIO2C3_GPIO = 0, + GPIO2C3_MAC_TXD0, + + GPIO2C2_SHIFT = 4, + GPIO2C2_MASK = 1 << GPIO2C2_SHIFT, + GPIO2C2_GPIO = 0, + GPIO2C2_MAC_TXD1, + + GPIO2C1_SHIFT = 2, + GPIO2C1_MASK = 1 << GPIO2C1_SHIFT, + GPIO2C1_GPIO = 0, + GPIO2C1_MAC_RXD0, + + GPIO2C0_SHIFT = 0, + GPIO2C0_MASK = 1 << GPIO2C0_SHIFT, + GPIO2C0_GPIO = 0, + GPIO2C0_MAC_RXD1, +}; + +/* GRF_GPIO2D_IOMUX */ +enum { + GPIO2D6_SHIFT = 12, + GPIO2D6_MASK = 1 << GPIO2D6_SHIFT, + GPIO2D6_GPIO = 0, + GPIO2D6_I2S_SDO1, + + GPIO2D5_SHIFT = 10, + GPIO2D5_MASK = 1 << GPIO2D5_SHIFT, + GPIO2D5_GPIO = 0, + GPIO2D5_I2S_SDO2, + + GPIO2D4_SHIFT = 8, + GPIO2D4_MASK = 1 << GPIO2D4_SHIFT, + GPIO2D4_GPIO = 0, + GPIO2D4_I2S_SDO3, + + GPIO2D1_SHIFT = 2, + GPIO2D1_MASK = 1 << GPIO2D1_SHIFT, + GPIO2D1_GPIO = 0, + GPIO2D1_MAC_MDC, +}; + struct rk3036_pinctrl_priv { struct rk3036_grf *grf; }; diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3188.c b/drivers/pinctrl/rockchip/pinctrl_rk3188.c index 692d8e298d..fdab836e5a 100644 --- a/drivers/pinctrl/rockchip/pinctrl_rk3188.c +++ b/drivers/pinctrl/rockchip/pinctrl_rk3188.c @@ -20,6 +20,386 @@ DECLARE_GLOBAL_DATA_PTR; +/* GRF_GPIO0D_IOMUX */ +enum { + GPIO0D7_SHIFT = 14, + GPIO0D7_MASK = 1, + GPIO0D7_GPIO = 0, + GPIO0D7_SPI1_CSN0, + + GPIO0D6_SHIFT = 12, + GPIO0D6_MASK = 1, + GPIO0D6_GPIO = 0, + GPIO0D6_SPI1_CLK, + + GPIO0D5_SHIFT = 10, + GPIO0D5_MASK = 1, + GPIO0D5_GPIO = 0, + GPIO0D5_SPI1_TXD, + + GPIO0D4_SHIFT = 8, + GPIO0D4_MASK = 1, + GPIO0D4_GPIO = 0, + GPIO0D4_SPI0_RXD, + + GPIO0D3_SHIFT = 6, + GPIO0D3_MASK = 3, + GPIO0D3_GPIO = 0, + GPIO0D3_FLASH_CSN3, + GPIO0D3_EMMC_RSTN_OUT, + + GPIO0D2_SHIFT = 4, + GPIO0D2_MASK = 3, + GPIO0D2_GPIO = 0, + GPIO0D2_FLASH_CSN2, + GPIO0D2_EMMC_CMD, + + GPIO0D1_SHIFT = 2, + GPIO0D1_MASK = 1, + GPIO0D1_GPIO = 0, + GPIO0D1_FLASH_CSN1, + + GPIO0D0_SHIFT = 0, + GPIO0D0_MASK = 3, + GPIO0D0_GPIO = 0, + GPIO0D0_FLASH_DQS, + GPIO0D0_EMMC_CLKOUT +}; + +/* GRF_GPIO1A_IOMUX */ +enum { + GPIO1A7_SHIFT = 14, + GPIO1A7_MASK = 3, + GPIO1A7_GPIO = 0, + GPIO1A7_UART1_RTS_N, + GPIO1A7_SPI0_CSN0, + + GPIO1A6_SHIFT = 12, + GPIO1A6_MASK = 3, + GPIO1A6_GPIO = 0, + GPIO1A6_UART1_CTS_N, + GPIO1A6_SPI0_CLK, + + GPIO1A5_SHIFT = 10, + GPIO1A5_MASK = 3, + GPIO1A5_GPIO = 0, + GPIO1A5_UART1_SOUT, + GPIO1A5_SPI0_TXD, + + GPIO1A4_SHIFT = 8, + GPIO1A4_MASK = 3, + GPIO1A4_GPIO = 0, + GPIO1A4_UART1_SIN, + GPIO1A4_SPI0_RXD, + + GPIO1A3_SHIFT = 6, + GPIO1A3_MASK = 1, + GPIO1A3_GPIO = 0, + GPIO1A3_UART0_RTS_N, + + GPIO1A2_SHIFT = 4, + GPIO1A2_MASK = 1, + GPIO1A2_GPIO = 0, + GPIO1A2_UART0_CTS_N, + + GPIO1A1_SHIFT = 2, + GPIO1A1_MASK = 1, + GPIO1A1_GPIO = 0, + GPIO1A1_UART0_SOUT, + + GPIO1A0_SHIFT = 0, + GPIO1A0_MASK = 1, + GPIO1A0_GPIO = 0, + GPIO1A0_UART0_SIN, +}; + +/* GRF_GPIO1B_IOMUX */ +enum { + GPIO1B7_SHIFT = 14, + GPIO1B7_MASK = 1, + GPIO1B7_GPIO = 0, + GPIO1B7_SPI0_CSN1, + + GPIO1B6_SHIFT = 12, + GPIO1B6_MASK = 3, + GPIO1B6_GPIO = 0, + GPIO1B6_SPDIF_TX, + GPIO1B6_SPI1_CSN1, + + GPIO1B5_SHIFT = 10, + GPIO1B5_MASK = 3, + GPIO1B5_GPIO = 0, + GPIO1B5_UART3_RTS_N, + GPIO1B5_RESERVED, + + GPIO1B4_SHIFT = 8, + GPIO1B4_MASK = 3, + GPIO1B4_GPIO = 0, + GPIO1B4_UART3_CTS_N, + GPIO1B4_GPS_RFCLK, + + GPIO1B3_SHIFT = 6, + GPIO1B3_MASK = 3, + GPIO1B3_GPIO = 0, + GPIO1B3_UART3_SOUT, + GPIO1B3_GPS_SIG, + + GPIO1B2_SHIFT = 4, + GPIO1B2_MASK = 3, + GPIO1B2_GPIO = 0, + GPIO1B2_UART3_SIN, + GPIO1B2_GPS_MAG, + + GPIO1B1_SHIFT = 2, + GPIO1B1_MASK = 3, + GPIO1B1_GPIO = 0, + GPIO1B1_UART2_SOUT, + GPIO1B1_JTAG_TDO, + + GPIO1B0_SHIFT = 0, + GPIO1B0_MASK = 3, + GPIO1B0_GPIO = 0, + GPIO1B0_UART2_SIN, + GPIO1B0_JTAG_TDI, +}; + +/* GRF_GPIO1D_IOMUX */ +enum { + GPIO1D7_SHIFT = 14, + GPIO1D7_MASK = 1, + GPIO1D7_GPIO = 0, + GPIO1D7_I2C4_SCL, + + GPIO1D6_SHIFT = 12, + GPIO1D6_MASK = 1, + GPIO1D6_GPIO = 0, + GPIO1D6_I2C4_SDA, + + GPIO1D5_SHIFT = 10, + GPIO1D5_MASK = 1, + GPIO1D5_GPIO = 0, + GPIO1D5_I2C2_SCL, + + GPIO1D4_SHIFT = 8, + GPIO1D4_MASK = 1, + GPIO1D4_GPIO = 0, + GPIO1D4_I2C2_SDA, + + GPIO1D3_SHIFT = 6, + GPIO1D3_MASK = 1, + GPIO1D3_GPIO = 0, + GPIO1D3_I2C1_SCL, + + GPIO1D2_SHIFT = 4, + GPIO1D2_MASK = 1, + GPIO1D2_GPIO = 0, + GPIO1D2_I2C1_SDA, + + GPIO1D1_SHIFT = 2, + GPIO1D1_MASK = 1, + GPIO1D1_GPIO = 0, + GPIO1D1_I2C0_SCL, + + GPIO1D0_SHIFT = 0, + GPIO1D0_MASK = 1, + GPIO1D0_GPIO = 0, + GPIO1D0_I2C0_SDA, +}; + +/* GRF_GPIO3A_IOMUX */ +enum { + GPIO3A7_SHIFT = 14, + GPIO3A7_MASK = 1, + GPIO3A7_GPIO = 0, + GPIO3A7_SDMMC0_DATA3, + + GPIO3A6_SHIFT = 12, + GPIO3A6_MASK = 1, + GPIO3A6_GPIO = 0, + GPIO3A6_SDMMC0_DATA2, + + GPIO3A5_SHIFT = 10, + GPIO3A5_MASK = 1, + GPIO3A5_GPIO = 0, + GPIO3A5_SDMMC0_DATA1, + + GPIO3A4_SHIFT = 8, + GPIO3A4_MASK = 1, + GPIO3A4_GPIO = 0, + GPIO3A4_SDMMC0_DATA0, + + GPIO3A3_SHIFT = 6, + GPIO3A3_MASK = 1, + GPIO3A3_GPIO = 0, + GPIO3A3_SDMMC0_CMD, + + GPIO3A2_SHIFT = 4, + GPIO3A2_MASK = 1, + GPIO3A2_GPIO = 0, + GPIO3A2_SDMMC0_CLKOUT, + + GPIO3A1_SHIFT = 2, + GPIO3A1_MASK = 1, + GPIO3A1_GPIO = 0, + GPIO3A1_SDMMC0_PWREN, + + GPIO3A0_SHIFT = 0, + GPIO3A0_MASK = 1, + GPIO3A0_GPIO = 0, + GPIO3A0_SDMMC0_RSTN, +}; + +/* GRF_GPIO3B_IOMUX */ +enum { + GPIO3B7_SHIFT = 14, + GPIO3B7_MASK = 3, + GPIO3B7_GPIO = 0, + GPIO3B7_CIF_DATA11, + GPIO3B7_I2C3_SCL, + + GPIO3B6_SHIFT = 12, + GPIO3B6_MASK = 3, + GPIO3B6_GPIO = 0, + GPIO3B6_CIF_DATA10, + GPIO3B6_I2C3_SDA, + + GPIO3B5_SHIFT = 10, + GPIO3B5_MASK = 3, + GPIO3B5_GPIO = 0, + GPIO3B5_CIF_DATA1, + GPIO3B5_HSADC_DATA9, + + GPIO3B4_SHIFT = 8, + GPIO3B4_MASK = 3, + GPIO3B4_GPIO = 0, + GPIO3B4_CIF_DATA0, + GPIO3B4_HSADC_DATA8, + + GPIO3B3_SHIFT = 6, + GPIO3B3_MASK = 1, + GPIO3B3_GPIO = 0, + GPIO3B3_CIF_CLKOUT, + + GPIO3B2_SHIFT = 4, + GPIO3B2_MASK = 1, + GPIO3B2_GPIO = 0, + /* no muxes */ + + GPIO3B1_SHIFT = 2, + GPIO3B1_MASK = 1, + GPIO3B1_GPIO = 0, + GPIO3B1_SDMMC0_WRITE_PRT, + + GPIO3B0_SHIFT = 0, + GPIO3B0_MASK = 1, + GPIO3B0_GPIO = 0, + GPIO3B0_SDMMC_DETECT_N, +}; + +/* GRF_GPIO3C_IOMUX */ +enum { + GPIO3C7_SHIFT = 14, + GPIO3C7_MASK = 3, + GPIO3C7_GPIO = 0, + GPIO3C7_SDMMC1_WRITE_PRT, + GPIO3C7_RMII_CRS_DVALID, + GPIO3C7_RESERVED, + + GPIO3C6_SHIFT = 12, + GPIO3C6_MASK = 3, + GPIO3C6_GPIO = 0, + GPIO3C6_SDMMC1_DECTN, + GPIO3C6_RMII_RX_ERR, + GPIO3C6_RESERVED, + + GPIO3C5_SHIFT = 10, + GPIO3C5_MASK = 3, + GPIO3C5_GPIO = 0, + GPIO3C5_SDMMC1_CLKOUT, + GPIO3C5_RMII_CLKOUT, + GPIO3C5_RMII_CLKIN, + + GPIO3C4_SHIFT = 8, + GPIO3C4_MASK = 3, + GPIO3C4_GPIO = 0, + GPIO3C4_SDMMC1_DATA3, + GPIO3C4_RMII_RXD1, + GPIO3C4_RESERVED, + + GPIO3C3_SHIFT = 6, + GPIO3C3_MASK = 3, + GPIO3C3_GPIO = 0, + GPIO3C3_SDMMC1_DATA2, + GPIO3C3_RMII_RXD0, + GPIO3C3_RESERVED, + + GPIO3C2_SHIFT = 4, + GPIO3C2_MASK = 3, + GPIO3C2_GPIO = 0, + GPIO3C2_SDMMC1_DATA1, + GPIO3C2_RMII_TXD0, + GPIO3C2_RESERVED, + + GPIO3C1_SHIFT = 2, + GPIO3C1_MASK = 3, + GPIO3C1_GPIO = 0, + GPIO3C1_SDMMC1_DATA0, + GPIO3C1_RMII_TXD1, + GPIO3C1_RESERVED, + + GPIO3C0_SHIFT = 0, + GPIO3C0_MASK = 3, + GPIO3C0_GPIO = 0, + GPIO3C0_SDMMC1_CMD, + GPIO3C0_RMII_TX_EN, + GPIO3C0_RESERVED, +}; + +/* GRF_GPIO3D_IOMUX */ +enum { + GPIO3D6_SHIFT = 12, + GPIO3D6_MASK = 3, + GPIO3D6_GPIO = 0, + GPIO3D6_PWM_3, + GPIO3D6_JTAG_TMS, + GPIO3D6_HOST_DRV_VBUS, + + GPIO3D5_SHIFT = 10, + GPIO3D5_MASK = 3, + GPIO3D5_GPIO = 0, + GPIO3D5_PWM_2, + GPIO3D5_JTAG_TCK, + GPIO3D5_OTG_DRV_VBUS, + + GPIO3D4_SHIFT = 8, + GPIO3D4_MASK = 3, + GPIO3D4_GPIO = 0, + GPIO3D4_PWM_1, + GPIO3D4_JTAG_TRSTN, + + GPIO3D3_SHIFT = 6, + GPIO3D3_MASK = 3, + GPIO3D3_GPIO = 0, + GPIO3D3_PWM_0, + + GPIO3D2_SHIFT = 4, + GPIO3D2_MASK = 3, + GPIO3D2_GPIO = 0, + GPIO3D2_SDMMC1_INT_N, + + GPIO3D1_SHIFT = 2, + GPIO3D1_MASK = 3, + GPIO3D1_GPIO = 0, + GPIO3D1_SDMMC1_BACKEND_PWR, + GPIO3D1_MII_MDCLK, + + GPIO3D0_SHIFT = 0, + GPIO3D0_MASK = 3, + GPIO3D0_GPIO = 0, + GPIO3D0_SDMMC1_PWR_EN, + GPIO3D0_MII_MD, +}; + struct rk3188_pinctrl_priv { struct rk3188_grf *grf; struct rk3188_pmu *pmu; diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3399.c b/drivers/pinctrl/rockchip/pinctrl_rk3399.c index 19a7415522..c7052257aa 100644 --- a/drivers/pinctrl/rockchip/pinctrl_rk3399.c +++ b/drivers/pinctrl/rockchip/pinctrl_rk3399.c @@ -70,6 +70,60 @@ static void pinctrl_rk3399_i2c_config(struct rk3399_grf_regs *grf, PMUGRF_GPIO1C0_SEL_MASK, PMUGRF_I2C0PMU_SCL << PMUGRF_GPIO1C0_SEL_SHIFT); break; + + case PERIPH_ID_I2C1: + rk_clrsetreg(&grf->gpio4a_iomux, + GRF_GPIO4A1_SEL_MASK, + GRF_I2C1_SDA << GRF_GPIO4A1_SEL_SHIFT); + rk_clrsetreg(&grf->gpio4a_iomux, + GRF_GPIO4A2_SEL_MASK, + GRF_I2C1_SCL << GRF_GPIO4A2_SEL_SHIFT); + break; + + case PERIPH_ID_I2C2: + rk_clrsetreg(&grf->gpio2a_iomux, + GRF_GPIO2A0_SEL_MASK, + GRF_I2C2_SDA << GRF_GPIO2A0_SEL_SHIFT); + rk_clrsetreg(&grf->gpio2a_iomux, + GRF_GPIO2A1_SEL_MASK, + GRF_I2C2_SCL << GRF_GPIO2A1_SEL_SHIFT); + break; + case PERIPH_ID_I2C3: + rk_clrsetreg(&grf->gpio4c_iomux, + GRF_GPIO4C0_SEL_MASK, + GRF_HDMII2C_SCL << GRF_GPIO4C0_SEL_SHIFT); + rk_clrsetreg(&grf->gpio4c_iomux, + GRF_GPIO4C1_SEL_MASK, + GRF_HDMII2C_SDA << GRF_GPIO4C1_SEL_SHIFT); + break; + + case PERIPH_ID_I2C4: + rk_clrsetreg(&pmugrf->gpio1b_iomux, + PMUGRF_GPIO1B3_SEL_MASK, + PMUGRF_I2C4_SDA << PMUGRF_GPIO1B3_SEL_SHIFT); + rk_clrsetreg(&pmugrf->gpio1b_iomux, + PMUGRF_GPIO1B4_SEL_MASK, + PMUGRF_I2C4_SCL << PMUGRF_GPIO1B4_SEL_SHIFT); + break; + + case PERIPH_ID_I2C7: + rk_clrsetreg(&grf->gpio2a_iomux, + GRF_GPIO2A7_SEL_MASK, + GRF_I2C7_SDA << GRF_GPIO2A7_SEL_SHIFT); + rk_clrsetreg(&grf->gpio2b_iomux, + GRF_GPIO2B0_SEL_MASK, + GRF_I2C7_SCL << GRF_GPIO2B0_SEL_SHIFT); + break; + + case PERIPH_ID_I2C6: + rk_clrsetreg(&grf->gpio2b_iomux, + GRF_GPIO2B1_SEL_MASK, + GRF_I2C6_SDA << GRF_GPIO2B1_SEL_SHIFT); + rk_clrsetreg(&grf->gpio2b_iomux, + GRF_GPIO2B2_SEL_MASK, + GRF_I2C6_SDA << GRF_GPIO2B2_SEL_SHIFT); + break; + case PERIPH_ID_I2C8: rk_clrsetreg(&pmugrf->gpio1c_iomux, PMUGRF_GPIO1C4_SEL_MASK, @@ -78,13 +132,8 @@ static void pinctrl_rk3399_i2c_config(struct rk3399_grf_regs *grf, PMUGRF_GPIO1C5_SEL_MASK, PMUGRF_I2C8PMU_SCL << PMUGRF_GPIO1C5_SEL_SHIFT); break; - case PERIPH_ID_I2C1: - case PERIPH_ID_I2C2: - case PERIPH_ID_I2C3: - case PERIPH_ID_I2C4: + case PERIPH_ID_I2C5: - case PERIPH_ID_I2C6: - case PERIPH_ID_I2C7: default: debug("i2c id = %d iomux error!\n", i2c_id); break; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index d8c107e206..1a3852442a 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -22,6 +22,7 @@ config SUNXI_NO_PMIC config AXP152_POWER bool "axp152 pmic support" depends on MACH_SUN5I + select AXP_PMIC_BUS select CMD_POWEROFF ---help--- Select this to enable support for the axp152 pmic found on most @@ -30,6 +31,7 @@ config AXP152_POWER config AXP209_POWER bool "axp209 pmic support" depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I + select AXP_PMIC_BUS select CMD_POWEROFF ---help--- Select this to enable support for the axp209 pmic found on most @@ -38,6 +40,7 @@ config AXP209_POWER config AXP221_POWER bool "axp221 / axp223 pmic support" depends on MACH_SUN6I || MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_R40 + select AXP_PMIC_BUS select CMD_POWEROFF ---help--- Select this to enable support for the axp221/axp223 pmic found on most @@ -46,6 +49,7 @@ config AXP221_POWER config AXP809_POWER bool "axp809 pmic support" depends on MACH_SUN9I + select AXP_PMIC_BUS select CMD_POWEROFF ---help--- Say y here to enable support for the axp809 pmic found on A80 boards. @@ -53,6 +57,7 @@ config AXP809_POWER config AXP818_POWER bool "axp818 pmic support" depends on MACH_SUN8I_A83T + select AXP_PMIC_BUS select CMD_POWEROFF ---help--- Say y here to enable support for the axp818 pmic found on diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 5d49c93f32..40ab9f7fa5 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -209,3 +209,11 @@ config DM_PMIC_TPS65910 The TPS65910 is a PMIC containing 3 buck DC-DC converters, one boost DC-DC converter, 8 LDOs and a RTC. This driver binds the SMPS and LDO pmic children. + +config PMIC_STPMU1 + bool "Enable support for STMicroelectronics STPMU1 PMIC" + depends on DM_PMIC && DM_I2C + ---help--- + The STPMU1 PMIC provides 4 BUCKs, 6 LDOs, 1 VREF and 2 power switches. + It is accessed via an I2C interface. The device is used with STM32MP1 + SoCs. This driver implements register read/write operations. diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index fc19fdc701..ad32068b3a 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_DM_PMIC_TPS65910) += pmic_tps65910_dm.o obj-$(CONFIG_$(SPL_)PMIC_PALMAS) += palmas.o obj-$(CONFIG_$(SPL_)PMIC_LP873X) += lp873x.o obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o +obj-$(CONFIG_PMIC_STPMU1) += stpmu1.o obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o diff --git a/drivers/power/pmic/stpmu1.c b/drivers/power/pmic/stpmu1.c new file mode 100644 index 0000000000..4615365ea8 --- /dev/null +++ b/drivers/power/pmic/stpmu1.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/stpmu1.h> + +#define STMPU1_NUM_OF_REGS 0x100 + +static int stpmu1_reg_count(struct udevice *dev) +{ + return STMPU1_NUM_OF_REGS; +} + +static int stpmu1_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + int ret; + + ret = dm_i2c_write(dev, reg, buff, len); + if (ret) + dev_err(dev, "%s: failed to write register %#x :%d", + __func__, reg, ret); + + return ret; +} + +static int stpmu1_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + int ret; + + ret = dm_i2c_read(dev, reg, buff, len); + if (ret) + dev_err(dev, "%s: failed to read register %#x : %d", + __func__, reg, ret); + + return ret; +} + +static struct dm_pmic_ops stpmu1_ops = { + .reg_count = stpmu1_reg_count, + .read = stpmu1_read, + .write = stpmu1_write, +}; + +static const struct udevice_id stpmu1_ids[] = { + { .compatible = "st,stpmu1" }, + { } +}; + +U_BOOT_DRIVER(pmic_stpmu1) = { + .name = "stpmu1_pmic", + .id = UCLASS_PMIC, + .of_match = stpmu1_ids, + .ops = &stpmu1_ops, +}; diff --git a/drivers/power/regulator/pbias_regulator.c b/drivers/power/regulator/pbias_regulator.c index 116b7f480a..adf589b224 100644 --- a/drivers/power/regulator/pbias_regulator.c +++ b/drivers/power/regulator/pbias_regulator.c @@ -225,9 +225,6 @@ static int pbias_regulator_set_value(struct udevice *dev, int uV) int rc; u32 reg; - debug("Setting %s voltage to %s\n", p->name, - (reg & p->vmode) ? "3.0v" : "1.8v"); - rc = pmic_read(dev->parent, 0, (uint8_t *)®, sizeof(reg)); if (rc) return rc; @@ -239,6 +236,9 @@ static int pbias_regulator_set_value(struct udevice *dev, int uV) else return -EINVAL; + debug("Setting %s voltage to %s\n", p->name, + (reg & p->vmode) ? "3.0v" : "1.8v"); + return pmic_write(dev->parent, 0, (uint8_t *)®, sizeof(reg)); } diff --git a/drivers/pwm/rk_pwm.c b/drivers/pwm/rk_pwm.c index 2364c2dfdd..7d3e11d667 100644 --- a/drivers/pwm/rk_pwm.c +++ b/drivers/pwm/rk_pwm.c @@ -76,7 +76,7 @@ static int rk_pwm_ofdata_to_platdata(struct udevice *dev) { struct rk_pwm_priv *priv = dev_get_priv(dev); - priv->regs = (struct rk3288_pwm *)devfdt_get_addr(dev); + priv->regs = (struct rk3288_pwm *)dev_read_addr(dev); return 0; } diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index 47969f3f28..496e2b793b 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -33,3 +33,5 @@ config STM32_SDRAM STM32F7 family devices support flexible memory controller(FMC) to support external memories like sdram, psram & nand. This driver is for the sdram memory interface with the FMC. + +source "drivers/ram/stm32mp1/Kconfig" diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile index 51ae6be655..3820d03aa4 100644 --- a/drivers/ram/Makefile +++ b/drivers/ram/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_RAM) += ram-uclass.o obj-$(CONFIG_SANDBOX) += sandbox_ram.o +obj-$(CONFIG_STM32MP1_DDR) += stm32mp1/ obj-$(CONFIG_STM32_SDRAM) += stm32_sdram.o obj-$(CONFIG_ARCH_BMIPS) += bmips_ram.o diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c index 76c1fe80a7..5cb470c209 100644 --- a/drivers/ram/rockchip/sdram_rk3399.c +++ b/drivers/ram/rockchip/sdram_rk3399.c @@ -1015,6 +1015,7 @@ static int switch_to_phy_index1(struct dram_info *dram, writel(RK_CLRSETBITS(1 << 1, 1 << 1), &dram->cic->cic_ctrl0); while (!(readl(&dram->cic->cic_status0) & (1 << 0))) { mdelay(10); + i++; if (i > 10) { debug("index1 frequency done overtime\n"); return -ETIME; diff --git a/drivers/ram/stm32mp1/Kconfig b/drivers/ram/stm32mp1/Kconfig new file mode 100644 index 0000000000..b9c816662c --- /dev/null +++ b/drivers/ram/stm32mp1/Kconfig @@ -0,0 +1,12 @@ + +config STM32MP1_DDR + bool "STM32MP1 DDR driver" + depends on DM && OF_CONTROL && ARCH_STM32MP + select RAM + select SPL_RAM if SPL + default y + help + activate STM32MP1 DDR controller driver for STM32MP1 soc + family: support for LPDDR2, LPDDR3 and DDR3 + the SDRAM parameters for controleur and phy need to be provided + in device tree (computed by DDR tuning tools) diff --git a/drivers/ram/stm32mp1/Makefile b/drivers/ram/stm32mp1/Makefile new file mode 100644 index 0000000000..9f05cd4b21 --- /dev/null +++ b/drivers/ram/stm32mp1/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (C) 2018, STMicroelectronics - All Rights Reserved +# +# SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause +# + +obj-y += stm32mp1_ram.o +obj-y += stm32mp1_ddr.o diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c new file mode 100644 index 0000000000..ffe50d9cc2 --- /dev/null +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +#include <common.h> +#include <clk.h> +#include <ram.h> +#include <reset.h> +#include <timer.h> +#include <asm/io.h> +#include <asm/arch/ddr.h> +#include <linux/iopoll.h> +#include "stm32mp1_ddr.h" +#include "stm32mp1_ddr_regs.h" + +#define RCC_DDRITFCR 0xD8 + +#define RCC_DDRITFCR_DDRCAPBRST (BIT(14)) +#define RCC_DDRITFCR_DDRCAXIRST (BIT(15)) +#define RCC_DDRITFCR_DDRCORERST (BIT(16)) +#define RCC_DDRITFCR_DPHYAPBRST (BIT(17)) +#define RCC_DDRITFCR_DPHYRST (BIT(18)) +#define RCC_DDRITFCR_DPHYCTLRST (BIT(19)) + +struct reg_desc { + const char *name; + u16 offset; /* offset for base address */ + u8 par_offset; /* offset for parameter array */ +}; + +#define INVALID_OFFSET 0xFF + +#define DDRCTL_REG(x, y) \ + {#x,\ + offsetof(struct stm32mp1_ddrctl, x),\ + offsetof(struct y, x)} + +#define DDRPHY_REG(x, y) \ + {#x,\ + offsetof(struct stm32mp1_ddrphy, x),\ + offsetof(struct y, x)} + +#define DDRCTL_REG_REG(x) DDRCTL_REG(x, stm32mp1_ddrctrl_reg) +static const struct reg_desc ddr_reg[] = { + DDRCTL_REG_REG(mstr), + DDRCTL_REG_REG(mrctrl0), + DDRCTL_REG_REG(mrctrl1), + DDRCTL_REG_REG(derateen), + DDRCTL_REG_REG(derateint), + DDRCTL_REG_REG(pwrctl), + DDRCTL_REG_REG(pwrtmg), + DDRCTL_REG_REG(hwlpctl), + DDRCTL_REG_REG(rfshctl0), + DDRCTL_REG_REG(rfshctl3), + DDRCTL_REG_REG(crcparctl0), + DDRCTL_REG_REG(zqctl0), + DDRCTL_REG_REG(dfitmg0), + DDRCTL_REG_REG(dfitmg1), + DDRCTL_REG_REG(dfilpcfg0), + DDRCTL_REG_REG(dfiupd0), + DDRCTL_REG_REG(dfiupd1), + DDRCTL_REG_REG(dfiupd2), + DDRCTL_REG_REG(dfiphymstr), + DDRCTL_REG_REG(odtmap), + DDRCTL_REG_REG(dbg0), + DDRCTL_REG_REG(dbg1), + DDRCTL_REG_REG(dbgcmd), + DDRCTL_REG_REG(poisoncfg), + DDRCTL_REG_REG(pccfg), +}; + +#define DDRCTL_REG_TIMING(x) DDRCTL_REG(x, stm32mp1_ddrctrl_timing) +static const struct reg_desc ddr_timing[] = { + DDRCTL_REG_TIMING(rfshtmg), + DDRCTL_REG_TIMING(dramtmg0), + DDRCTL_REG_TIMING(dramtmg1), + DDRCTL_REG_TIMING(dramtmg2), + DDRCTL_REG_TIMING(dramtmg3), + DDRCTL_REG_TIMING(dramtmg4), + DDRCTL_REG_TIMING(dramtmg5), + DDRCTL_REG_TIMING(dramtmg6), + DDRCTL_REG_TIMING(dramtmg7), + DDRCTL_REG_TIMING(dramtmg8), + DDRCTL_REG_TIMING(dramtmg14), + DDRCTL_REG_TIMING(odtcfg), +}; + +#define DDRCTL_REG_MAP(x) DDRCTL_REG(x, stm32mp1_ddrctrl_map) +static const struct reg_desc ddr_map[] = { + DDRCTL_REG_MAP(addrmap1), + DDRCTL_REG_MAP(addrmap2), + DDRCTL_REG_MAP(addrmap3), + DDRCTL_REG_MAP(addrmap4), + DDRCTL_REG_MAP(addrmap5), + DDRCTL_REG_MAP(addrmap6), + DDRCTL_REG_MAP(addrmap9), + DDRCTL_REG_MAP(addrmap10), + DDRCTL_REG_MAP(addrmap11), +}; + +#define DDRCTL_REG_PERF(x) DDRCTL_REG(x, stm32mp1_ddrctrl_perf) +static const struct reg_desc ddr_perf[] = { + DDRCTL_REG_PERF(sched), + DDRCTL_REG_PERF(sched1), + DDRCTL_REG_PERF(perfhpr1), + DDRCTL_REG_PERF(perflpr1), + DDRCTL_REG_PERF(perfwr1), + DDRCTL_REG_PERF(pcfgr_0), + DDRCTL_REG_PERF(pcfgw_0), + DDRCTL_REG_PERF(pcfgqos0_0), + DDRCTL_REG_PERF(pcfgqos1_0), + DDRCTL_REG_PERF(pcfgwqos0_0), + DDRCTL_REG_PERF(pcfgwqos1_0), + DDRCTL_REG_PERF(pcfgr_1), + DDRCTL_REG_PERF(pcfgw_1), + DDRCTL_REG_PERF(pcfgqos0_1), + DDRCTL_REG_PERF(pcfgqos1_1), + DDRCTL_REG_PERF(pcfgwqos0_1), + DDRCTL_REG_PERF(pcfgwqos1_1), +}; + +#define DDRPHY_REG_REG(x) DDRPHY_REG(x, stm32mp1_ddrphy_reg) +static const struct reg_desc ddrphy_reg[] = { + DDRPHY_REG_REG(pgcr), + DDRPHY_REG_REG(aciocr), + DDRPHY_REG_REG(dxccr), + DDRPHY_REG_REG(dsgcr), + DDRPHY_REG_REG(dcr), + DDRPHY_REG_REG(odtcr), + DDRPHY_REG_REG(zq0cr1), + DDRPHY_REG_REG(dx0gcr), + DDRPHY_REG_REG(dx1gcr), + DDRPHY_REG_REG(dx2gcr), + DDRPHY_REG_REG(dx3gcr), +}; + +#define DDRPHY_REG_TIMING(x) DDRPHY_REG(x, stm32mp1_ddrphy_timing) +static const struct reg_desc ddrphy_timing[] = { + DDRPHY_REG_TIMING(ptr0), + DDRPHY_REG_TIMING(ptr1), + DDRPHY_REG_TIMING(ptr2), + DDRPHY_REG_TIMING(dtpr0), + DDRPHY_REG_TIMING(dtpr1), + DDRPHY_REG_TIMING(dtpr2), + DDRPHY_REG_TIMING(mr0), + DDRPHY_REG_TIMING(mr1), + DDRPHY_REG_TIMING(mr2), + DDRPHY_REG_TIMING(mr3), +}; + +#define DDRPHY_REG_CAL(x) DDRPHY_REG(x, stm32mp1_ddrphy_cal) +static const struct reg_desc ddrphy_cal[] = { + DDRPHY_REG_CAL(dx0dllcr), + DDRPHY_REG_CAL(dx0dqtr), + DDRPHY_REG_CAL(dx0dqstr), + DDRPHY_REG_CAL(dx1dllcr), + DDRPHY_REG_CAL(dx1dqtr), + DDRPHY_REG_CAL(dx1dqstr), + DDRPHY_REG_CAL(dx2dllcr), + DDRPHY_REG_CAL(dx2dqtr), + DDRPHY_REG_CAL(dx2dqstr), + DDRPHY_REG_CAL(dx3dllcr), + DDRPHY_REG_CAL(dx3dqtr), + DDRPHY_REG_CAL(dx3dqstr), +}; + +enum reg_type { + REG_REG, + REG_TIMING, + REG_PERF, + REG_MAP, + REGPHY_REG, + REGPHY_TIMING, + REGPHY_CAL, + REG_TYPE_NB +}; + +enum base_type { + DDR_BASE, + DDRPHY_BASE, + NONE_BASE +}; + +struct ddr_reg_info { + const char *name; + const struct reg_desc *desc; + u8 size; + enum base_type base; +}; + +#define DDRPHY_REG_CAL(x) DDRPHY_REG(x, stm32mp1_ddrphy_cal) + +const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = { +[REG_REG] = { + "static", ddr_reg, ARRAY_SIZE(ddr_reg), DDR_BASE}, +[REG_TIMING] = { + "timing", ddr_timing, ARRAY_SIZE(ddr_timing), DDR_BASE}, +[REG_PERF] = { + "perf", ddr_perf, ARRAY_SIZE(ddr_perf), DDR_BASE}, +[REG_MAP] = { + "map", ddr_map, ARRAY_SIZE(ddr_map), DDR_BASE}, +[REGPHY_REG] = { + "static", ddrphy_reg, ARRAY_SIZE(ddrphy_reg), DDRPHY_BASE}, +[REGPHY_TIMING] = { + "timing", ddrphy_timing, ARRAY_SIZE(ddrphy_timing), DDRPHY_BASE}, +[REGPHY_CAL] = { + "cal", ddrphy_cal, ARRAY_SIZE(ddrphy_cal), DDRPHY_BASE}, +}; + +const char *base_name[] = { + [DDR_BASE] = "ctl", + [DDRPHY_BASE] = "phy", +}; + +static u32 get_base_addr(const struct ddr_info *priv, enum base_type base) +{ + if (base == DDRPHY_BASE) + return (u32)priv->phy; + else + return (u32)priv->ctl; +} + +static void set_reg(const struct ddr_info *priv, + enum reg_type type, + const void *param) +{ + unsigned int i; + unsigned int *ptr, value; + enum base_type base = ddr_registers[type].base; + u32 base_addr = get_base_addr(priv, base); + const struct reg_desc *desc = ddr_registers[type].desc; + + debug("init %s\n", ddr_registers[type].name); + for (i = 0; i < ddr_registers[type].size; i++) { + ptr = (unsigned int *)(base_addr + desc[i].offset); + if (desc[i].par_offset == INVALID_OFFSET) { + pr_err("invalid parameter offset for %s", desc[i].name); + } else { + value = *((u32 *)((u32)param + + desc[i].par_offset)); + writel(value, ptr); + debug("[0x%x] %s= 0x%08x\n", + (u32)ptr, desc[i].name, value); + } + } +} + +static void ddrphy_idone_wait(struct stm32mp1_ddrphy *phy) +{ + u32 pgsr; + int ret; + + ret = readl_poll_timeout(&phy->pgsr, pgsr, + pgsr & (DDRPHYC_PGSR_IDONE | + DDRPHYC_PGSR_DTERR | + DDRPHYC_PGSR_DTIERR | + DDRPHYC_PGSR_DFTERR | + DDRPHYC_PGSR_RVERR | + DDRPHYC_PGSR_RVEIRR), + 1000000); + debug("\n[0x%08x] pgsr = 0x%08x ret=%d\n", + (u32)&phy->pgsr, pgsr, ret); +} + +void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, u32 pir) +{ + pir |= DDRPHYC_PIR_INIT; + writel(pir, &phy->pir); + debug("[0x%08x] pir = 0x%08x -> 0x%08x\n", + (u32)&phy->pir, pir, readl(&phy->pir)); + + /* need to wait 10 configuration clock before start polling */ + udelay(10); + + /* Wait DRAM initialization and Gate Training Evaluation complete */ + ddrphy_idone_wait(phy); +} + +/* start quasi dynamic register update */ +static void start_sw_done(struct stm32mp1_ddrctl *ctl) +{ + clrbits_le32(&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); +} + +/* wait quasi dynamic register update */ +static void wait_sw_done_ack(struct stm32mp1_ddrctl *ctl) +{ + int ret; + u32 swstat; + + setbits_le32(&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); + + ret = readl_poll_timeout(&ctl->swstat, swstat, + swstat & DDRCTRL_SWSTAT_SW_DONE_ACK, + 1000000); + if (ret) + panic("Timeout initialising DRAM : DDR->swstat = %x\n", + swstat); + + debug("[0x%08x] swstat = 0x%08x\n", (u32)&ctl->swstat, swstat); +} + +/* wait quasi dynamic register update */ +static void wait_operating_mode(struct ddr_info *priv, int mode) +{ + u32 stat, val, mask, val2 = 0, mask2 = 0; + int ret; + + mask = DDRCTRL_STAT_OPERATING_MODE_MASK; + val = mode; + /* self-refresh due to software => check also STAT.selfref_type */ + if (mode == DDRCTRL_STAT_OPERATING_MODE_SR) { + mask |= DDRCTRL_STAT_SELFREF_TYPE_MASK; + stat |= DDRCTRL_STAT_SELFREF_TYPE_SR; + } else if (mode == DDRCTRL_STAT_OPERATING_MODE_NORMAL) { + /* normal mode: handle also automatic self refresh */ + mask2 = DDRCTRL_STAT_OPERATING_MODE_MASK | + DDRCTRL_STAT_SELFREF_TYPE_MASK; + val2 = DDRCTRL_STAT_OPERATING_MODE_SR | + DDRCTRL_STAT_SELFREF_TYPE_ASR; + } + + ret = readl_poll_timeout(&priv->ctl->stat, stat, + ((stat & mask) == val) || + (mask2 && ((stat & mask2) == val2)), + 1000000); + + if (ret) + panic("Timeout DRAM : DDR->stat = %x\n", stat); + + debug("[0x%08x] stat = 0x%08x\n", (u32)&priv->ctl->stat, stat); +} + +void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl) +{ + start_sw_done(ctl); + /* quasi-dynamic register update*/ + setbits_le32(&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); + clrbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); + clrbits_le32(&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + wait_sw_done_ack(ctl); +} + +void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, + u32 rfshctl3, u32 pwrctl) +{ + start_sw_done(ctl); + if (!(rfshctl3 & DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH)) + clrbits_le32(&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); + if (pwrctl & DDRCTRL_PWRCTL_POWERDOWN_EN) + setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); + setbits_le32(&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + wait_sw_done_ack(ctl); +} + +/* board-specific DDR power initializations. */ +__weak int board_ddr_power_init(void) +{ + return 0; +} + +__maybe_unused +void stm32mp1_ddr_init(struct ddr_info *priv, + const struct stm32mp1_ddr_config *config) +{ + u32 pir; + int ret; + + ret = board_ddr_power_init(); + + if (ret) + panic("ddr power init failed\n"); + + debug("name = %s\n", config->info.name); + debug("speed = %d MHz\n", config->info.speed); + debug("size = 0x%x\n", config->info.size); +/* + * 1. Program the DWC_ddr_umctl2 registers + * 1.1 RESETS: presetn, core_ddrc_rstn, aresetn + */ + /* Assert All DDR part */ + setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST); + setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST); + setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST); + setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYAPBRST); + setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYRST); + setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYCTLRST); + +/* 1.2. start CLOCK */ + if (stm32mp1_ddr_clk_enable(priv, config->info.speed)) + panic("invalid DRAM clock : %d MHz\n", + config->info.speed); + +/* 1.3. deassert reset */ + /* de-assert PHY rstn and ctl_rstn via DPHYRST and DPHYCTLRST */ + clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYRST); + clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYCTLRST); + /* De-assert presetn once the clocks are active + * and stable via DDRCAPBRST bit + */ + clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST); + +/* 1.4. wait 4 cycles for synchronization */ + asm(" nop"); + asm(" nop"); + asm(" nop"); + asm(" nop"); + +/* 1.5. initialize registers ddr_umctl2 */ + /* Stop uMCTL2 before PHY is ready */ + clrbits_le32(&priv->ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + debug("[0x%08x] dfimisc = 0x%08x\n", + (u32)&priv->ctl->dfimisc, readl(&priv->ctl->dfimisc)); + + set_reg(priv, REG_REG, &config->c_reg); + set_reg(priv, REG_TIMING, &config->c_timing); + set_reg(priv, REG_MAP, &config->c_map); + + /* skip CTRL init, SDRAM init is done by PHY PUBL */ + clrsetbits_le32(&priv->ctl->init0, + DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK, + DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL); + + set_reg(priv, REG_PERF, &config->c_perf); + +/* 2. deassert reset signal core_ddrc_rstn, aresetn and presetn */ + clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST); + clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST); + clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYAPBRST); + +/* 3. start PHY init by accessing relevant PUBL registers + * (DXGCR, DCR, PTR*, MR*, DTPR*) + */ + set_reg(priv, REGPHY_REG, &config->p_reg); + set_reg(priv, REGPHY_TIMING, &config->p_timing); + set_reg(priv, REGPHY_CAL, &config->p_cal); + +/* 4. Monitor PHY init status by polling PUBL register PGSR.IDONE + * Perform DDR PHY DRAM initialization and Gate Training Evaluation + */ + ddrphy_idone_wait(priv->phy); + +/* 5. Indicate to PUBL that controller performs SDRAM initialization + * by setting PIR.INIT and PIR CTLDINIT and pool PGSR.IDONE + * DRAM init is done by PHY, init0.skip_dram.init = 1 + */ + pir = DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK | DDRPHYC_PIR_ZCAL | + DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_DRAMINIT | DDRPHYC_PIR_ICPC; + + if (config->c_reg.mstr & DDRCTRL_MSTR_DDR3) + pir |= DDRPHYC_PIR_DRAMRST; /* only for DDR3 */ + + stm32mp1_ddrphy_init(priv->phy, pir); + +/* 6. SET DFIMISC.dfi_init_complete_en to 1 */ + /* Enable quasi-dynamic register programming*/ + start_sw_done(priv->ctl); + setbits_le32(&priv->ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); + wait_sw_done_ack(priv->ctl); + +/* 7. Wait for DWC_ddr_umctl2 to move to normal operation mode + * by monitoring STAT.operating_mode signal + */ + /* wait uMCTL2 ready */ + + wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); + + debug("DDR DQS training : "); +/* 8. Disable Auto refresh and power down by setting + * - RFSHCTL3.dis_au_refresh = 1 + * - PWRCTL.powerdown_en = 0 + * - DFIMISC.dfiinit_complete_en = 0 + */ + stm32mp1_refresh_disable(priv->ctl); + +/* 9. Program PUBL PGCR to enable refresh during training and rank to train + * not done => keep the programed value in PGCR + */ + +/* 10. configure PUBL PIR register to specify which training step to run */ + /* warning : RVTRN is not supported by this PUBL */ + stm32mp1_ddrphy_init(priv->phy, DDRPHYC_PIR_QSTRN); + +/* 11. monitor PUB PGSR.IDONE to poll cpmpletion of training sequence */ + ddrphy_idone_wait(priv->phy); + +/* 12. set back registers in step 8 to the orginal values if desidered */ + stm32mp1_refresh_restore(priv->ctl, config->c_reg.rfshctl3, + config->c_reg.pwrctl); + + /* enable uMCTL2 AXI port 0 and 1 */ + setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); + setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); +} diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.h b/drivers/ram/stm32mp1/stm32mp1_ddr.h new file mode 100644 index 0000000000..b77d823868 --- /dev/null +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +#ifndef _RAM_STM32MP1_DDR_H +#define _RAM_STM32MP1_DDR_H + +enum stm32mp1_ddr_interact_step { + STEP_DDR_RESET, + STEP_CTL_INIT, + STEP_PHY_INIT, + STEP_DDR_READY, + STEP_RUN, +}; + +/* DDR CTL and DDR PHY REGISTERS */ +struct stm32mp1_ddrctl; +struct stm32mp1_ddrphy; + +/** + * struct ddr_info + * + * @dev: pointer for the device + * @info: UCLASS RAM information + * @ctl: DDR controleur base address + * @clk: DDR clock + * @phy: DDR PHY base address + * @rcc: rcc base address + */ +struct ddr_info { + struct udevice *dev; + struct ram_info info; + struct clk clk; + struct stm32mp1_ddrctl *ctl; + struct stm32mp1_ddrphy *phy; + u32 rcc; +}; + +struct stm32mp1_ddrctrl_reg { + u32 mstr; + u32 mrctrl0; + u32 mrctrl1; + u32 derateen; + u32 derateint; + u32 pwrctl; + u32 pwrtmg; + u32 hwlpctl; + u32 rfshctl0; + u32 rfshctl3; + u32 crcparctl0; + u32 zqctl0; + u32 dfitmg0; + u32 dfitmg1; + u32 dfilpcfg0; + u32 dfiupd0; + u32 dfiupd1; + u32 dfiupd2; + u32 dfiphymstr; + u32 odtmap; + u32 dbg0; + u32 dbg1; + u32 dbgcmd; + u32 poisoncfg; + u32 pccfg; + +}; + +struct stm32mp1_ddrctrl_timing { + u32 rfshtmg; + u32 dramtmg0; + u32 dramtmg1; + u32 dramtmg2; + u32 dramtmg3; + u32 dramtmg4; + u32 dramtmg5; + u32 dramtmg6; + u32 dramtmg7; + u32 dramtmg8; + u32 dramtmg14; + u32 odtcfg; +}; + +struct stm32mp1_ddrctrl_map { + u32 addrmap1; + u32 addrmap2; + u32 addrmap3; + u32 addrmap4; + u32 addrmap5; + u32 addrmap6; + u32 addrmap9; + u32 addrmap10; + u32 addrmap11; +}; + +struct stm32mp1_ddrctrl_perf { + u32 sched; + u32 sched1; + u32 perfhpr1; + u32 perflpr1; + u32 perfwr1; + u32 pcfgr_0; + u32 pcfgw_0; + u32 pcfgqos0_0; + u32 pcfgqos1_0; + u32 pcfgwqos0_0; + u32 pcfgwqos1_0; + u32 pcfgr_1; + u32 pcfgw_1; + u32 pcfgqos0_1; + u32 pcfgqos1_1; + u32 pcfgwqos0_1; + u32 pcfgwqos1_1; +}; + +struct stm32mp1_ddrphy_reg { + u32 pgcr; + u32 aciocr; + u32 dxccr; + u32 dsgcr; + u32 dcr; + u32 odtcr; + u32 zq0cr1; + u32 dx0gcr; + u32 dx1gcr; + u32 dx2gcr; + u32 dx3gcr; +}; + +struct stm32mp1_ddrphy_timing { + u32 ptr0; + u32 ptr1; + u32 ptr2; + u32 dtpr0; + u32 dtpr1; + u32 dtpr2; + u32 mr0; + u32 mr1; + u32 mr2; + u32 mr3; +}; + +struct stm32mp1_ddrphy_cal { + u32 dx0dllcr; + u32 dx0dqtr; + u32 dx0dqstr; + u32 dx1dllcr; + u32 dx1dqtr; + u32 dx1dqstr; + u32 dx2dllcr; + u32 dx2dqtr; + u32 dx2dqstr; + u32 dx3dllcr; + u32 dx3dqtr; + u32 dx3dqstr; +}; + +struct stm32mp1_ddr_info { + const char *name; + u16 speed; /* in MHZ */ + u32 size; /* memory size in byte = col * row * width */ +}; + +struct stm32mp1_ddr_config { + struct stm32mp1_ddr_info info; + struct stm32mp1_ddrctrl_reg c_reg; + struct stm32mp1_ddrctrl_timing c_timing; + struct stm32mp1_ddrctrl_map c_map; + struct stm32mp1_ddrctrl_perf c_perf; + struct stm32mp1_ddrphy_reg p_reg; + struct stm32mp1_ddrphy_timing p_timing; + struct stm32mp1_ddrphy_cal p_cal; +}; + +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, u16 mem_speed); +void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, u32 pir); +void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl); +void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, + u32 rfshctl3, + u32 pwrctl); + +void stm32mp1_ddr_init( + struct ddr_info *priv, + const struct stm32mp1_ddr_config *config); + +int stm32mp1_dump_reg(const struct ddr_info *priv, + const char *name); + +void stm32mp1_edit_reg(const struct ddr_info *priv, + char *name, + char *string); + +int stm32mp1_dump_param(const struct stm32mp1_ddr_config *config, + const char *name); + +void stm32mp1_edit_param(const struct stm32mp1_ddr_config *config, + char *name, + char *string); + +void stm32mp1_dump_info( + const struct ddr_info *priv, + const struct stm32mp1_ddr_config *config); + +bool stm32mp1_ddr_interactive( + void *priv, + enum stm32mp1_ddr_interact_step step, + const struct stm32mp1_ddr_config *config); + +#endif diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h new file mode 100644 index 0000000000..82c254b50d --- /dev/null +++ b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +#ifndef _RAM_STM32MP1_DDR_REGS_H +#define _RAM_STM32MP1_DDR_REGS_H + +/* DDR3/LPDDR2/LPDDR3 Controller (DDRCTRL) registers */ +struct stm32mp1_ddrctl { + u32 mstr ; /* 0x0 Master*/ + u32 stat; /* 0x4 Operating Mode Status*/ + u8 reserved008[0x10 - 0x8]; + u32 mrctrl0; /* 0x10 Control 0.*/ + u32 mrctrl1; /* 0x14 Control 1*/ + u32 mrstat; /* 0x18 Status*/ + u32 reserved01c; /* 0x1c */ + u32 derateen; /* 0x20 Temperature Derate Enable*/ + u32 derateint; /* 0x24 Temperature Derate Interval*/ + u8 reserved028[0x30 - 0x28]; + u32 pwrctl; /* 0x30 Low Power Control*/ + u32 pwrtmg; /* 0x34 Low Power Timing*/ + u32 hwlpctl; /* 0x38 Hardware Low Power Control*/ + u8 reserved03c[0x50 - 0x3C]; + u32 rfshctl0; /* 0x50 Refresh Control 0*/ + u32 reserved054; /* 0x54 Refresh Control 1*/ + u32 reserved058; /* 0x58 Refresh Control 2*/ + u32 reserved05C; + u32 rfshctl3; /* 0x60 Refresh Control 0*/ + u32 rfshtmg; /* 0x64 Refresh Timing*/ + u8 reserved068[0xc0 - 0x68]; + u32 crcparctl0; /* 0xc0 CRC Parity Control0*/ + u32 reserved0c4; /* 0xc4 CRC Parity Control1*/ + u32 reserved0c8; /* 0xc8 CRC Parity Control2*/ + u32 crcparstat; /* 0xcc CRC Parity Status*/ + u32 init0; /* 0xd0 SDRAM Initialization 0*/ + u32 init1; /* 0xd4 SDRAM Initialization 1*/ + u32 init2; /* 0xd8 SDRAM Initialization 2*/ + u32 init3; /* 0xdc SDRAM Initialization 3*/ + u32 init4; /* 0xe0 SDRAM Initialization 4*/ + u32 init5; /* 0xe4 SDRAM Initialization 5*/ + u32 reserved0e8; + u32 reserved0ec; + u32 dimmctl; /* 0xf0 DIMM Control*/ + u8 reserved0f4[0x100 - 0xf4]; + u32 dramtmg0; /* 0x100 SDRAM Timing 0*/ + u32 dramtmg1; /* 0x104 SDRAM Timing 1*/ + u32 dramtmg2; /* 0x108 SDRAM Timing 2*/ + u32 dramtmg3; /* 0x10c SDRAM Timing 3*/ + u32 dramtmg4; /* 0x110 SDRAM Timing 4*/ + u32 dramtmg5; /* 0x114 SDRAM Timing 5*/ + u32 dramtmg6; /* 0x118 SDRAM Timing 6*/ + u32 dramtmg7; /* 0x11c SDRAM Timing 7*/ + u32 dramtmg8; /* 0x120 SDRAM Timing 8*/ + u8 reserved124[0x138 - 0x124]; + u32 dramtmg14; /* 0x138 SDRAM Timing 14*/ + u32 dramtmg15; /* 0x13C SDRAM Timing 15*/ + u8 reserved140[0x180 - 0x140]; + u32 zqctl0; /* 0x180 ZQ Control 0*/ + u32 zqctl1; /* 0x184 ZQ Control 1*/ + u32 zqctl2; /* 0x188 ZQ Control 2*/ + u32 zqstat; /* 0x18c ZQ Status*/ + u32 dfitmg0; /* 0x190 DFI Timing 0*/ + u32 dfitmg1; /* 0x194 DFI Timing 1*/ + u32 dfilpcfg0; /* 0x198 DFI Low Power Configuration 0*/ + u32 reserved19c; + u32 dfiupd0; /* 0x1a0 DFI Update 0*/ + u32 dfiupd1; /* 0x1a4 DFI Update 1*/ + u32 dfiupd2; /* 0x1a8 DFI Update 2*/ + u32 reserved1ac; + u32 dfimisc; /* 0x1b0 DFI Miscellaneous Control*/ + u8 reserved1b4[0x1bc - 0x1b4]; + u32 dfistat; /* 0x1bc DFI Miscellaneous Control*/ + u8 reserved1c0[0x1c4 - 0x1c0]; + u32 dfiphymstr; /* 0x1c4 DFI PHY Master interface*/ + u8 reserved1c8[0x204 - 0x1c8]; + u32 addrmap1; /* 0x204 Address Map 1*/ + u32 addrmap2; /* 0x208 Address Map 2*/ + u32 addrmap3; /* 0x20c Address Map 3*/ + u32 addrmap4; /* 0x210 Address Map 4*/ + u32 addrmap5; /* 0x214 Address Map 5*/ + u32 addrmap6; /* 0x218 Address Map 6*/ + u8 reserved21c[0x224 - 0x21c]; + u32 addrmap9; /* 0x224 Address Map 9*/ + u32 addrmap10; /* 0x228 Address Map 10*/ + u32 addrmap11; /* 0x22C Address Map 11*/ + u8 reserved230[0x240 - 0x230]; + u32 odtcfg; /* 0x240 ODT Configuration*/ + u32 odtmap; /* 0x244 ODT/Rank Map*/ + u8 reserved248[0x250 - 0x248]; + u32 sched; /* 0x250 Scheduler Control*/ + u32 sched1; /* 0x254 Scheduler Control 1*/ + u32 reserved258; + u32 perfhpr1; /* 0x25c High Priority Read CAM 1*/ + u32 reserved260; + u32 perflpr1; /* 0x264 Low Priority Read CAM 1*/ + u32 reserved268; + u32 perfwr1; /* 0x26c Write CAM 1*/ + u8 reserved27c[0x300 - 0x270]; + u32 dbg0; /* 0x300 Debug 0*/ + u32 dbg1; /* 0x304 Debug 1*/ + u32 dbgcam; /* 0x308 CAM Debug*/ + u32 dbgcmd; /* 0x30c Command Debug*/ + u32 dbgstat; /* 0x310 Status Debug*/ + u8 reserved314[0x320 - 0x314]; + u32 swctl; /* 0x320 Software Programming Control Enable*/ + u32 swstat; /* 0x324 Software Programming Control Status*/ + u8 reserved328[0x36c - 0x328]; + u32 poisoncfg; /* 0x36c AXI Poison Configuration Register*/ + u32 poisonstat; /* 0x370 AXI Poison Status Register*/ + u8 reserved374[0x3fc - 0x374]; + + /* Multi Port registers */ + u32 pstat; /* 0x3fc Port Status*/ + u32 pccfg; /* 0x400 Port Common Configuration*/ + + /* PORT 0 */ + u32 pcfgr_0; /* 0x404 Configuration Read*/ + u32 pcfgw_0; /* 0x408 Configuration Write*/ + u8 reserved40c[0x490 - 0x40c]; + u32 pctrl_0; /* 0x490 Port Control Register */ + u32 pcfgqos0_0; /* 0x494 Read QoS Configuration 0*/ + u32 pcfgqos1_0; /* 0x498 Read QoS Configuration 1*/ + u32 pcfgwqos0_0; /* 0x49c Write QoS Configuration 0*/ + u32 pcfgwqos1_0; /* 0x4a0 Write QoS Configuration 1*/ + u8 reserved4a4[0x4b4 - 0x4a4]; + + /* PORT 1 */ + u32 pcfgr_1; /* 0x4b4 Configuration Read*/ + u32 pcfgw_1; /* 0x4b8 Configuration Write*/ + u8 reserved4bc[0x540 - 0x4bc]; + u32 pctrl_1; /* 0x540 Port 2 Control Register */ + u32 pcfgqos0_1; /* 0x544 Read QoS Configuration 0*/ + u32 pcfgqos1_1; /* 0x548 Read QoS Configuration 1*/ + u32 pcfgwqos0_1; /* 0x54c Write QoS Configuration 0*/ + u32 pcfgwqos1_1; /* 0x550 Write QoS Configuration 1*/ +}; + +/* DDR Physical Interface Control (DDRPHYC) registers*/ +struct stm32mp1_ddrphy { + u32 ridr; /* 0x00 R Revision Identification*/ + u32 pir; /* 0x04 R/W PHY Initialization*/ + u32 pgcr; /* 0x08 R/W PHY General Configuration*/ + u32 pgsr; /* 0x0C PHY General Status*/ + u32 dllgcr; /* 0x10 R/W DLL General Control*/ + u32 acdllcr; /* 0x14 R/W AC DLL Control*/ + u32 ptr0; /* 0x18 R/W PHY Timing 0*/ + u32 ptr1; /* 0x1C R/W PHY Timing 1*/ + u32 ptr2; /* 0x20 R/W PHY Timing 2*/ + u32 aciocr; /* 0x24 AC I/O Configuration*/ + u32 dxccr; /* 0x28 DATX8 Common Configuration*/ + u32 dsgcr; /* 0x2C DDR System General Configuration*/ + u32 dcr; /* 0x30 DRAM Configuration*/ + u32 dtpr0; /* 0x34 DRAM Timing Parameters0*/ + u32 dtpr1; /* 0x38 DRAM Timing Parameters1*/ + u32 dtpr2; /* 0x3C DRAM Timing Parameters2*/ + u32 mr0; /* 0x40 Mode 0*/ + u32 mr1; /* 0x44 Mode 1*/ + u32 mr2; /* 0x48 Mode 2*/ + u32 mr3; /* 0x4C Mode 3*/ + u32 odtcr; /* 0x50 ODT Configuration*/ + u32 dtar; /* 0x54 data training address*/ + u32 dtdr0; /* 0x58 */ + u32 dtdr1; /* 0x5c */ + u8 res1[0x0c0 - 0x060]; /* 0x60 */ + u32 dcuar; /* 0xc0 Address*/ + u32 dcudr; /* 0xc4 DCU Data*/ + u32 dcurr; /* 0xc8 DCU Run*/ + u32 dculr; /* 0xcc DCU Loop*/ + u32 dcugcr; /* 0xd0 DCU General Configuration*/ + u32 dcutpr; /* 0xd4 DCU Timing Parameters */ + u32 dcusr0; /* 0xd8 DCU Status 0*/ + u32 dcusr1; /* 0xdc DCU Status 1*/ + u8 res2[0x100 - 0xe0]; /* 0xe0 */ + u32 bistrr; /* 0x100 BIST Run*/ + u32 bistmskr0; /* 0x104 BIST Mask 0*/ + u32 bistmskr1; /* 0x108 BIST Mask 0*/ + u32 bistwcr; /* 0x10c BIST Word Count*/ + u32 bistlsr; /* 0x110 BIST LFSR Seed*/ + u32 bistar0; /* 0x114 BIST Address 0*/ + u32 bistar1; /* 0x118 BIST Address 1*/ + u32 bistar2; /* 0x11c BIST Address 2*/ + u32 bistupdr; /* 0x120 BIST User Data Pattern*/ + u32 bistgsr; /* 0x124 BIST General Status*/ + u32 bistwer; /* 0x128 BIST Word Error*/ + u32 bistber0; /* 0x12c BIST Bit Error 0*/ + u32 bistber1; /* 0x130 BIST Bit Error 1*/ + u32 bistber2; /* 0x134 BIST Bit Error 2*/ + u32 bistwcsr; /* 0x138 BIST Word Count Status*/ + u32 bistfwr0; /* 0x13c BIST Fail Word 0*/ + u32 bistfwr1; /* 0x140 BIST Fail Word 1*/ + u8 res3[0x178 - 0x144]; /* 0x144 */ + u32 gpr0; /* 0x178 General Purpose 0 (GPR0)*/ + u32 gpr1; /* 0x17C General Purpose 1 (GPR1)*/ + u32 zq0cr0; /* 0x180 zq 0 control 0 */ + u32 zq0cr1; /* 0x184 zq 0 control 1 */ + u32 zq0sr0; /* 0x188 zq 0 status 0 */ + u32 zq0sr1; /* 0x18C zq 0 status 1 */ + u8 res4[0x1C0 - 0x190]; /* 0x190 */ + u32 dx0gcr; /* 0x1c0 Byte lane 0 General Configuration*/ + u32 dx0gsr0; /* 0x1c4 Byte lane 0 General Status 0*/ + u32 dx0gsr1; /* 0x1c8 Byte lane 0 General Status 1*/ + u32 dx0dllcr; /* 0x1cc Byte lane 0 DLL Control*/ + u32 dx0dqtr; /* 0x1d0 Byte lane 0 DQ Timing*/ + u32 dx0dqstr; /* 0x1d4 Byte lane 0 DQS Timing*/ + u8 res5[0x200 - 0x1d8]; /* 0x1d8 */ + u32 dx1gcr; /* 0x200 Byte lane 1 General Configuration*/ + u32 dx1gsr0; /* 0x204 Byte lane 1 General Status 0*/ + u32 dx1gsr1; /* 0x208 Byte lane 1 General Status 1*/ + u32 dx1dllcr; /* 0x20c Byte lane 1 DLL Control*/ + u32 dx1dqtr; /* 0x210 Byte lane 1 DQ Timing*/ + u32 dx1dqstr; /* 0x214 Byte lane 1 QS Timing*/ + u8 res6[0x240 - 0x218]; /* 0x218 */ + u32 dx2gcr; /* 0x240 Byte lane 2 General Configuration*/ + u32 dx2gsr0; /* 0x244 Byte lane 2 General Status 0*/ + u32 dx2gsr1; /* 0x248 Byte lane 2 General Status 1*/ + u32 dx2dllcr; /* 0x24c Byte lane 2 DLL Control*/ + u32 dx2dqtr; /* 0x250 Byte lane 2 DQ Timing*/ + u32 dx2dqstr; /* 0x254 Byte lane 2 QS Timing*/ + u8 res7[0x280 - 0x258]; /* 0x258 */ + u32 dx3gcr; /* 0x280 Byte lane 3 General Configuration*/ + u32 dx3gsr0; /* 0x284 Byte lane 3 General Status 0*/ + u32 dx3gsr1; /* 0x288 Byte lane 3 General Status 1*/ + u32 dx3dllcr; /* 0x28c Byte lane 3 DLL Control*/ + u32 dx3dqtr; /* 0x290 Byte lane 3 DQ Timing*/ + u32 dx3dqstr; /* 0x294 Byte lane 3 QS Timing*/ +}; + +#define DXN(phy, offset, byte) ((u32)(phy) + (offset) + ((u32)(byte) * 0x40)) +#define DXNGCR(phy, byte) DXN(phy, 0x1c0, byte) +#define DXNDLLCR(phy, byte) DXN(phy, 0x1cc, byte) +#define DXNDQTR(phy, byte) DXN(phy, 0x1d0, byte) +#define DXNDQSTR(phy, byte) DXN(phy, 0x1d4, byte) + +/* DDRCTRL REGISTERS */ +#define DDRCTRL_MSTR_DDR3 BIT(0) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK GENMASK(13, 12) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_FULL (0 << 12) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF (1 << 12) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER (2 << 12) +#define DDRCTRL_MSTR_DLL_OFF_MODE BIT(15) + +#define DDRCTRL_STAT_OPERATING_MODE_MASK GENMASK(2, 0) +#define DDRCTRL_STAT_OPERATING_MODE_NORMAL 1 +#define DDRCTRL_STAT_OPERATING_MODE_SR 3 +#define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK(5, 4) +#define DDRCTRL_STAT_SELFREF_TYPE_ASR (3 << 4) +#define DDRCTRL_STAT_SELFREF_TYPE_SR (2 << 4) + +#define DDRCTRL_MRCTRL0_MR_TYPE_WRITE 0 +/* only one rank supported */ +#define DDRCTRL_MRCTRL0_MR_RANK_SHIFT 4 +#define DDRCTRL_MRCTRL0_MR_RANK_ALL \ + (0x1 << DDRCTRL_MRCTRL0_MR_RANK_SHIFT) +#define DDRCTRL_MRCTRL0_MR_ADDR_SHIFT 12 +#define DDRCTRL_MRCTRL0_MR_ADDR_MASK GENMASK(15, 12) +#define DDRCTRL_MRCTRL0_MR_WR BIT(31) + +#define DDRCTRL_MRSTAT_MR_WR_BUSY BIT(0) + +#define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1) +#define DDRCTRL_PWRCTL_SELFREF_SW BIT(5) + +#define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0) + +#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK(27, 16) +#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_SHIFT 16 + +#define DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK (0xC0000000) +#define DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL (BIT(30)) + +#define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0) + +#define DDRCTRL_DBG1_DIS_HIF BIT(1) + +#define DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY BIT(29) +#define DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY BIT(28) +#define DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY BIT(26) +#define DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH GENMASK(12, 8) +#define DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH GENMASK(4, 0) +#define DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY \ + (DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY | \ + DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY) +#define DDRCTRL_DBGCAM_DBG_Q_DEPTH \ + (DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY | \ + DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH | \ + DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH) + +#define DDRCTRL_DBGCMD_RANK0_REFRESH BIT(0) + +#define DDRCTRL_DBGSTAT_RANK0_REFRESH_BUSY BIT(0) + +#define DDRCTRL_SWCTL_SW_DONE BIT(0) + +#define DDRCTRL_SWSTAT_SW_DONE_ACK BIT(0) + +#define DDRCTRL_PCTRL_N_PORT_EN BIT(0) + +/* DDRPHYC registers */ +#define DDRPHYC_PIR_INIT BIT(0) +#define DDRPHYC_PIR_DLLSRST BIT(1) +#define DDRPHYC_PIR_DLLLOCK BIT(2) +#define DDRPHYC_PIR_ZCAL BIT(3) +#define DDRPHYC_PIR_ITMSRST BIT(4) +#define DDRPHYC_PIR_DRAMRST BIT(5) +#define DDRPHYC_PIR_DRAMINIT BIT(6) +#define DDRPHYC_PIR_QSTRN BIT(7) +#define DDRPHYC_PIR_ICPC BIT(16) +#define DDRPHYC_PIR_ZCALBYP BIT(30) +#define DDRPHYC_PIR_INITSTEPS_MASK GENMASK(31, 7) + +#define DDRPHYC_PGCR_DFTCMP BIT(2) +#define DDRPHYC_PGCR_PDDISDX BIT(24) +#define DDRPHYC_PGCR_RFSHDT_MASK GENMASK(28, 25) + +#define DDRPHYC_PGSR_IDONE BIT(0) +#define DDRPHYC_PGSR_DTERR BIT(5) +#define DDRPHYC_PGSR_DTIERR BIT(6) +#define DDRPHYC_PGSR_DFTERR BIT(7) +#define DDRPHYC_PGSR_RVERR BIT(8) +#define DDRPHYC_PGSR_RVEIRR BIT(9) + +#define DDRPHYC_DLLGCR_BPS200 BIT(23) + +#define DDRPHYC_ACDLLCR_DLLDIS BIT(31) + +#define DDRPHYC_ZQ0CRN_ZDATA_MASK GENMASK(27, 0) +#define DDRPHYC_ZQ0CRN_ZDATA_SHIFT 0 +#define DDRPHYC_ZQ0CRN_ZDEN BIT(28) + +#define DDRPHYC_DXNGCR_DXEN BIT(0) + +#define DDRPHYC_DXNDLLCR_DLLDIS BIT(31) +#define DDRPHYC_DXNDLLCR_SDPHASE_MASK GENMASK(17, 14) +#define DDRPHYC_DXNDLLCR_SDPHASE_SHIFT 14 + +#define DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit) (4 * (bit)) +#define DDRPHYC_DXNDQTR_DQDLY_MASK GENMASK(3, 0) +#define DDRPHYC_DXNDQTR_DQDLY_LOW_MASK GENMASK(1, 0) +#define DDRPHYC_DXNDQTR_DQDLY_HIGH_MASK GENMASK(3, 2) + +#define DDRPHYC_DXNDQSTR_DQSDLY_MASK GENMASK(22, 20) +#define DDRPHYC_DXNDQSTR_DQSDLY_SHIFT 20 +#define DDRPHYC_DXNDQSTR_DQSNDLY_MASK GENMASK(25, 23) +#define DDRPHYC_DXNDQSTR_DQSNDLY_SHIFT 23 +#define DDRPHYC_DXNDQSTR_R0DGSL_MASK GENMASK(2, 0) +#define DDRPHYC_DXNDQSTR_R0DGSL_SHIFT 0 +#define DDRPHYC_DXNDQSTR_R0DGPS_MASK GENMASK(13, 12) +#define DDRPHYC_DXNDQSTR_R0DGPS_SHIFT 12 + +#define DDRPHYC_BISTRR_BDXSEL_MASK GENMASK(22, 19) +#define DDRPHYC_BISTRR_BDXSEL_SHIFT 19 + +#define DDRPHYC_BISTGSR_BDDONE BIT(0) +#define DDRPHYC_BISTGSR_BDXERR BIT(2) + +#define DDRPHYC_BISTWCSR_DXWCNT_SHIFT 16 + +/* PWR registers */ +#define PWR_CR3 0x00C +#define PWR_CR3_DDRSRDIS BIT(11) +#define PWR_CR3_DDRRETEN BIT(12) + +#endif diff --git a/drivers/ram/stm32mp1/stm32mp1_ram.c b/drivers/ram/stm32mp1/stm32mp1_ram.c new file mode 100644 index 0000000000..9599444650 --- /dev/null +++ b/drivers/ram/stm32mp1/stm32mp1_ram.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <ram.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/io.h> +#include "stm32mp1_ddr.h" + +DECLARE_GLOBAL_DATA_PTR; + +static const char *const clkname[] = { + "ddrc1", + "ddrc2", + "ddrcapb", + "ddrphycapb", + "ddrphyc" /* LAST clock => used for get_rate() */ +}; + +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed) +{ + unsigned long ddrphy_clk; + unsigned long ddr_clk; + struct clk clk; + int ret; + int idx; + + for (idx = 0; idx < ARRAY_SIZE(clkname); idx++) { + ret = clk_get_by_name(priv->dev, clkname[idx], &clk); + + if (!ret) + ret = clk_enable(&clk); + + if (ret) { + printf("error for %s : %d\n", clkname[idx], ret); + return ret; + } + } + + priv->clk = clk; + ddrphy_clk = clk_get_rate(&priv->clk); + + debug("DDR: mem_speed (%d MHz), RCC %d MHz\n", + mem_speed, (u32)(ddrphy_clk / 1000 / 1000)); + /* max 10% frequency delta */ + ddr_clk = abs(ddrphy_clk - mem_speed * 1000 * 1000); + if (ddr_clk > (mem_speed * 1000 * 100)) { + pr_err("DDR expected freq %d MHz, current is %d MHz\n", + mem_speed, (u32)(ddrphy_clk / 1000 / 1000)); + return -EINVAL; + } + + return 0; +} + +static __maybe_unused int stm32mp1_ddr_setup(struct udevice *dev) +{ + struct ddr_info *priv = dev_get_priv(dev); + int ret, idx; + struct clk axidcg; + struct stm32mp1_ddr_config config; + +#define PARAM(x, y) \ + { x,\ + offsetof(struct stm32mp1_ddr_config, y),\ + sizeof(config.y) / sizeof(u32)} + +#define CTL_PARAM(x) PARAM("st,ctl-"#x, c_##x) +#define PHY_PARAM(x) PARAM("st,phy-"#x, p_##x) + + const struct { + const char *name; /* name in DT */ + const u32 offset; /* offset in config struct */ + const u32 size; /* size of parameters */ + } param[] = { + CTL_PARAM(reg), + CTL_PARAM(timing), + CTL_PARAM(map), + CTL_PARAM(perf), + PHY_PARAM(reg), + PHY_PARAM(timing), + PHY_PARAM(cal) + }; + + config.info.speed = dev_read_u32_default(dev, "st,mem-speed", 0); + config.info.size = dev_read_u32_default(dev, "st,mem-size", 0); + config.info.name = dev_read_string(dev, "st,mem-name"); + if (!config.info.name) { + debug("%s: no st,mem-name\n", __func__); + return -EINVAL; + } + printf("RAM: %s\n", config.info.name); + + for (idx = 0; idx < ARRAY_SIZE(param); idx++) { + ret = dev_read_u32_array(dev, param[idx].name, + (void *)((u32)&config + + param[idx].offset), + param[idx].size); + debug("%s: %s[0x%x] = %d\n", __func__, + param[idx].name, param[idx].size, ret); + if (ret) { + pr_err("%s: Cannot read %s\n", + __func__, param[idx].name); + return -EINVAL; + } + } + + ret = clk_get_by_name(dev, "axidcg", &axidcg); + if (ret) { + debug("%s: Cannot found axidcg\n", __func__); + return -EINVAL; + } + clk_disable(&axidcg); /* disable clock gating during init */ + + stm32mp1_ddr_init(priv, &config); + + clk_enable(&axidcg); /* enable clock gating */ + + /* check size */ + debug("%s : get_ram_size(%x, %x)\n", __func__, + (u32)priv->info.base, (u32)STM32_DDR_SIZE); + + priv->info.size = get_ram_size((long *)priv->info.base, + STM32_DDR_SIZE); + + debug("%s : %x\n", __func__, (u32)priv->info.size); + + /* check memory access for all memory */ + if (config.info.size != priv->info.size) { + printf("DDR invalid size : 0x%x, expected 0x%x\n", + priv->info.size, config.info.size); + return -EINVAL; + } + return 0; +} + +static int stm32mp1_ddr_probe(struct udevice *dev) +{ + struct ddr_info *priv = dev_get_priv(dev); + struct regmap *map; + int ret; + + debug("STM32MP1 DDR probe\n"); + priv->dev = dev; + + ret = regmap_init_mem(dev, &map); + if (ret) + return ret; + + priv->ctl = regmap_get_range(map, 0); + priv->phy = regmap_get_range(map, 1); + + priv->rcc = STM32_RCC_BASE; + + priv->info.base = STM32_DDR_BASE; + +#if !defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD) + priv->info.size = 0; + return stm32mp1_ddr_setup(dev); +#else + priv->info.size = dev_read_u32_default(dev, "st,mem-size", 0); + return 0; +#endif +} + +static int stm32mp1_ddr_get_info(struct udevice *dev, struct ram_info *info) +{ + struct ddr_info *priv = dev_get_priv(dev); + + *info = priv->info; + + return 0; +} + +static struct ram_ops stm32mp1_ddr_ops = { + .get_info = stm32mp1_ddr_get_info, +}; + +static const struct udevice_id stm32mp1_ddr_ids[] = { + { .compatible = "st,stm32mp1-ddr" }, + { } +}; + +U_BOOT_DRIVER(ddr_stm32mp1) = { + .name = "stm32mp1_ddr", + .id = UCLASS_RAM, + .of_match = stm32mp1_ddr_ids, + .ops = &stm32mp1_ddr_ops, + .probe = stm32mp1_ddr_probe, + .priv_auto_alloc_size = sizeof(struct ddr_info), +}; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 3964b9eb6e..ccfdac7823 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -30,7 +30,7 @@ config STI_RESET config STM32_RESET bool "Enable the STM32 reset" - depends on STM32 + depends on STM32 || ARCH_STM32MP help Support for reset controllers on STMicroelectronics STM32 family SoCs. This resset driver is compatible with STM32 F4/F7 and H7 SoCs. @@ -83,4 +83,12 @@ config RESET_ROCKCHIP though is that some reset signals, like I2C or MISC reset multiple devices. +config RESET_MESON + bool "Reset controller driver for Amlogic Meson SoCs" + depends on DM_RESET && ARCH_MESON + imply REGMAP + default y + help + Support for reset controller on Amlogic Meson SoC. + endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 7d7e080c78..d1d5146825 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o obj-$(CONFIG_AST2500_RESET) += ast2500-reset.o obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o +obj-$(CONFIG_RESET_MESON) += reset-meson.o diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c new file mode 100644 index 0000000000..5324f86f5f --- /dev/null +++ b/drivers/reset/reset-meson.c @@ -0,0 +1,90 @@ +/* + * Amlogic Meson Reset Controller driver + * + * Copyright (c) 2018 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <reset-uclass.h> +#include <regmap.h> + +#define REG_COUNT 8 +#define BITS_PER_REG 32 +#define LEVEL_OFFSET 0x7c + +struct meson_reset_priv { + struct regmap *regmap; +}; + +static int meson_reset_request(struct reset_ctl *reset_ctl) +{ + if (reset_ctl->id > (REG_COUNT * BITS_PER_REG)) + return -EINVAL; + + return 0; +} + +static int meson_reset_free(struct reset_ctl *reset_ctl) +{ + return 0; +} + +static int meson_reset_level(struct reset_ctl *reset_ctl, bool assert) +{ + struct meson_reset_priv *priv = dev_get_priv(reset_ctl->dev); + uint bank = reset_ctl->id / BITS_PER_REG; + uint offset = reset_ctl->id % BITS_PER_REG; + uint reg_offset = LEVEL_OFFSET + (bank << 2); + uint val; + + regmap_read(priv->regmap, reg_offset, &val); + if (assert) + val &= ~BIT(offset); + else + val |= BIT(offset); + regmap_write(priv->regmap, reg_offset, val); + + return 0; +} + +static int meson_reset_assert(struct reset_ctl *reset_ctl) +{ + return meson_reset_level(reset_ctl, true); +} + +static int meson_reset_deassert(struct reset_ctl *reset_ctl) +{ + return meson_reset_level(reset_ctl, false); +} + +struct reset_ops meson_reset_ops = { + .request = meson_reset_request, + .free = meson_reset_free, + .rst_assert = meson_reset_assert, + .rst_deassert = meson_reset_deassert, +}; + +static const struct udevice_id meson_reset_ids[] = { + { .compatible = "amlogic,meson-gxbb-reset" }, + { } +}; + +static int meson_reset_probe(struct udevice *dev) +{ + struct meson_reset_priv *priv = dev_get_priv(dev); + + return regmap_init_mem(dev, &priv->regmap); +} + +U_BOOT_DRIVER(meson_reset) = { + .name = "meson_reset", + .id = UCLASS_RESET, + .of_match = meson_reset_ids, + .probe = meson_reset_probe, + .ops = &meson_reset_ops, + .priv_auto_alloc_size = sizeof(struct meson_reset_priv), +}; diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c index 307a29705f..9a5c9c91b9 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -81,6 +81,40 @@ int reset_get_by_index(struct udevice *dev, int index, return 0; } +int reset_get_bulk(struct udevice *dev, struct reset_ctl_bulk *bulk) +{ + int i, ret, err, count; + + bulk->count = 0; + + count = dev_count_phandle_with_args(dev, "resets", "#reset-cells"); + if (!count) + return 0; + + bulk->resets = devm_kcalloc(dev, count, sizeof(struct reset_ctl), + GFP_KERNEL); + if (!bulk->resets) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = reset_get_by_index(dev, i, &bulk->resets[i]); + if (ret < 0) + goto bulk_get_err; + + ++bulk->count; + } + + return 0; + +bulk_get_err: + err = reset_release_all(bulk->resets, bulk->count); + if (err) + debug("%s: could release all resets for %p\n", + __func__, dev); + + return ret; +} + int reset_get_by_name(struct udevice *dev, const char *name, struct reset_ctl *reset_ctl) { @@ -126,6 +160,19 @@ int reset_assert(struct reset_ctl *reset_ctl) return ops->rst_assert(reset_ctl); } +int reset_assert_bulk(struct reset_ctl_bulk *bulk) +{ + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = reset_assert(&bulk->resets[i]); + if (ret < 0) + return ret; + } + + return 0; +} + int reset_deassert(struct reset_ctl *reset_ctl) { struct reset_ops *ops = reset_dev_ops(reset_ctl->dev); @@ -135,6 +182,19 @@ int reset_deassert(struct reset_ctl *reset_ctl) return ops->rst_deassert(reset_ctl); } +int reset_deassert_bulk(struct reset_ctl_bulk *bulk) +{ + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = reset_deassert(&bulk->resets[i]); + if (ret < 0) + return ret; + } + + return 0; +} + int reset_release_all(struct reset_ctl *reset_ctl, int count) { int i, ret; diff --git a/drivers/reset/sandbox-reset-test.c b/drivers/reset/sandbox-reset-test.c index e37d6c91ef..f0ceaa0483 100644 --- a/drivers/reset/sandbox-reset-test.c +++ b/drivers/reset/sandbox-reset-test.c @@ -12,6 +12,7 @@ struct sandbox_reset_test { struct reset_ctl ctl; + struct reset_ctl_bulk bulk; }; int sandbox_reset_test_get(struct udevice *dev) @@ -21,6 +22,13 @@ int sandbox_reset_test_get(struct udevice *dev) return reset_get_by_name(dev, "test", &sbrt->ctl); } +int sandbox_reset_test_get_bulk(struct udevice *dev) +{ + struct sandbox_reset_test *sbrt = dev_get_priv(dev); + + return reset_get_bulk(dev, &sbrt->bulk); +} + int sandbox_reset_test_assert(struct udevice *dev) { struct sandbox_reset_test *sbrt = dev_get_priv(dev); @@ -28,6 +36,13 @@ int sandbox_reset_test_assert(struct udevice *dev) return reset_assert(&sbrt->ctl); } +int sandbox_reset_test_assert_bulk(struct udevice *dev) +{ + struct sandbox_reset_test *sbrt = dev_get_priv(dev); + + return reset_assert_bulk(&sbrt->bulk); +} + int sandbox_reset_test_deassert(struct udevice *dev) { struct sandbox_reset_test *sbrt = dev_get_priv(dev); @@ -35,6 +50,13 @@ int sandbox_reset_test_deassert(struct udevice *dev) return reset_deassert(&sbrt->ctl); } +int sandbox_reset_test_deassert_bulk(struct udevice *dev) +{ + struct sandbox_reset_test *sbrt = dev_get_priv(dev); + + return reset_deassert_bulk(&sbrt->bulk); +} + int sandbox_reset_test_free(struct udevice *dev) { struct sandbox_reset_test *sbrt = dev_get_priv(dev); @@ -42,6 +64,13 @@ int sandbox_reset_test_free(struct udevice *dev) return reset_free(&sbrt->ctl); } +int sandbox_reset_test_release_bulk(struct udevice *dev) +{ + struct sandbox_reset_test *sbrt = dev_get_priv(dev); + + return reset_release_bulk(&sbrt->bulk); +} + static const struct udevice_id sandbox_reset_test_ids[] = { { .compatible = "sandbox,reset-ctl-test" }, { } diff --git a/drivers/reset/sandbox-reset.c b/drivers/reset/sandbox-reset.c index 4258af521b..c310749dc8 100644 --- a/drivers/reset/sandbox-reset.c +++ b/drivers/reset/sandbox-reset.c @@ -10,7 +10,7 @@ #include <asm/io.h> #include <asm/reset.h> -#define SANDBOX_RESET_SIGNALS 3 +#define SANDBOX_RESET_SIGNALS 101 struct sandbox_reset_signal { bool asserted; diff --git a/drivers/reset/stm32-reset.c b/drivers/reset/stm32-reset.c index b266f46263..e98f34b037 100644 --- a/drivers/reset/stm32-reset.c +++ b/drivers/reset/stm32-reset.c @@ -11,7 +11,13 @@ #include <reset-uclass.h> #include <asm/io.h> -DECLARE_GLOBAL_DATA_PTR; +/* reset clear offset for STM32MP RCC */ +#define RCC_CL 0x4 + +enum rcc_type { + RCC_STM32 = 0, + RCC_STM32MP, +}; struct stm32_reset_priv { fdt_addr_t base; @@ -35,7 +41,11 @@ static int stm32_reset_assert(struct reset_ctl *reset_ctl) debug("%s: reset id = %ld bank = %d offset = %d)\n", __func__, reset_ctl->id, bank, offset); - setbits_le32(priv->base + bank, BIT(offset)); + if (dev_get_driver_data(reset_ctl->dev) == RCC_STM32MP) + /* reset assert is done in rcc set register */ + writel(BIT(offset), priv->base + bank); + else + setbits_le32(priv->base + bank, BIT(offset)); return 0; } @@ -48,7 +58,11 @@ static int stm32_reset_deassert(struct reset_ctl *reset_ctl) debug("%s: reset id = %ld bank = %d offset = %d)\n", __func__, reset_ctl->id, bank, offset); - clrbits_le32(priv->base + bank, BIT(offset)); + if (dev_get_driver_data(reset_ctl->dev) == RCC_STM32MP) + /* reset deassert is done in rcc clr register */ + writel(BIT(offset), priv->base + bank + RCC_CL); + else + clrbits_le32(priv->base + bank, BIT(offset)); return 0; } @@ -64,16 +78,26 @@ static int stm32_reset_probe(struct udevice *dev) { struct stm32_reset_priv *priv = dev_get_priv(dev); - priv->base = devfdt_get_addr(dev); - if (priv->base == FDT_ADDR_T_NONE) - return -EINVAL; + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) { + /* for MFD, get address of parent */ + priv->base = dev_read_addr(dev->parent); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + } return 0; } +static const struct udevice_id stm32_reset_ids[] = { + { .compatible = "st,stm32mp1-rcc-rst", .data = RCC_STM32MP }, + { } +}; + U_BOOT_DRIVER(stm32_rcc_reset) = { .name = "stm32_rcc_reset", .id = UCLASS_RESET, + .of_match = stm32_reset_ids, .probe = stm32_reset_probe, .priv_auto_alloc_size = sizeof(struct stm32_reset_priv), .ops = &stm32_reset_ops, diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 95ac031243..277dc3de73 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -30,6 +30,18 @@ config RTC_DS1307 Support for Dallas Semiconductor (now Maxim) DS1307 and DS1338/9 and compatible Real Time Clock devices. +config RTC_ISL1208 + bool "Enable ISL1208 driver" + depends on DM_RTC + help + The Renesas (formerly Intersil) ISL1208 is a I2C Real Time Clock (RTC) and + calendar with automatic leap year correction, 2-byte battery backed SRAM, + automatic power switch-over, alarm function and 15 selectable frequency + outputs. + + This driver supports reading and writing the RTC/calendar and detects + total power failures. + config RTC_RX8010SJ bool "Enable RX8010SJ driver" depends on DM_RTC diff --git a/drivers/rtc/ds1307.c b/drivers/rtc/ds1307.c index 5df15c7fd6..5e74b93b72 100644 --- a/drivers/rtc/ds1307.c +++ b/drivers/rtc/ds1307.c @@ -184,25 +184,8 @@ int rtc_set (struct rtc_time *tmp) */ void rtc_reset (void) { - struct rtc_time tmp; - rtc_write (RTC_SEC_REG_ADDR, 0x00); /* clearing Clock Halt */ rtc_write (RTC_CTL_REG_ADDR, RTC_CTL_BIT_SQWE | RTC_CTL_BIT_RS1 | RTC_CTL_BIT_RS0); - - tmp.tm_year = 1970; - tmp.tm_mon = 1; - tmp.tm_mday= 1; - tmp.tm_hour = 0; - tmp.tm_min = 0; - tmp.tm_sec = 0; - - rtc_set(&tmp); - - printf ( "RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", - tmp.tm_year, tmp.tm_mon, tmp.tm_mday, - tmp.tm_hour, tmp.tm_min, tmp.tm_sec); - - return; } @@ -321,14 +304,6 @@ read_rtc: static int ds1307_rtc_reset(struct udevice *dev) { int ret; - struct rtc_time tmp = { - .tm_year = 1970, - .tm_mon = 1, - .tm_mday = 1, - .tm_hour = 0, - .tm_min = 0, - .tm_sec = 0, - }; /* clear Clock Halt */ ret = dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR, 0x00); @@ -340,14 +315,6 @@ static int ds1307_rtc_reset(struct udevice *dev) if (ret < 0) return ret; - ret = ds1307_rtc_set(dev, &tmp); - if (ret < 0) - return ret; - - debug("RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", - tmp.tm_year, tmp.tm_mon, tmp.tm_mday, - tmp.tm_hour, tmp.tm_min, tmp.tm_sec); - return 0; } diff --git a/drivers/rtc/ds1374.c b/drivers/rtc/ds1374.c index 78473570b9..9e440d8457 100644 --- a/drivers/rtc/ds1374.c +++ b/drivers/rtc/ds1374.c @@ -172,8 +172,6 @@ int rtc_set (struct rtc_time *tmp){ */ void rtc_reset (void){ - struct rtc_time tmp; - /* clear status flags */ rtc_write(RTC_SR_ADDR, (RTC_SR_BIT_AF|RTC_SR_BIT_OSF), false); /* clearing OSF and AF */ @@ -189,19 +187,6 @@ void rtc_reset (void){ |RTC_CTL_BIT_BBSQW), true);/* disable WD/ALM, WDSTR set to INT-pin, set BBSQW and SQW to 32k - set to 1 */ - tmp.tm_year = 1970; - tmp.tm_mon = 1; - tmp.tm_mday= 1; - tmp.tm_hour = 0; - tmp.tm_min = 0; - tmp.tm_sec = 0; - - rtc_set(&tmp); - - printf("RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", - tmp.tm_year, tmp.tm_mon, tmp.tm_mday, - tmp.tm_hour, tmp.tm_min, tmp.tm_sec); - rtc_write(RTC_WD_ALM_CNT_BYTE2_ADDR, 0xAC, true); rtc_write(RTC_WD_ALM_CNT_BYTE1_ADDR, 0xDE, true); rtc_write(RTC_WD_ALM_CNT_BYTE2_ADDR, 0xAD, true); diff --git a/drivers/rtc/isl1208.c b/drivers/rtc/isl1208.c index 807e2e404e..fa1178a36e 100644 --- a/drivers/rtc/isl1208.c +++ b/drivers/rtc/isl1208.c @@ -14,6 +14,7 @@ #include <common.h> #include <command.h> +#include <dm.h> #include <rtc.h> #include <i2c.h> @@ -51,45 +52,38 @@ #define RTC_STAT_BIT_BAT 0x02 /* BATTERY BIT */ #define RTC_STAT_BIT_RTCF 0x01 /* REAL TIME CLOCK FAIL BIT */ -static uchar rtc_read (uchar reg); -static void rtc_write (uchar reg, uchar val); - /* * Get the current time from the RTC */ -int rtc_get (struct rtc_time *tmp) +static int isl1208_rtc_get(struct udevice *dev, struct rtc_time *tmp) { - int rel = 0; - uchar sec, min, hour, mday, wday, mon, year, status; - - status = rtc_read (RTC_STAT_REG_ADDR); - sec = rtc_read (RTC_SEC_REG_ADDR); - min = rtc_read (RTC_MIN_REG_ADDR); - hour = rtc_read (RTC_HR_REG_ADDR); - wday = rtc_read (RTC_DAY_REG_ADDR); - mday = rtc_read (RTC_DATE_REG_ADDR); - mon = rtc_read (RTC_MON_REG_ADDR); - year = rtc_read (RTC_YR_REG_ADDR); - - DEBUGR ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " - "hr: %02x min: %02x sec: %02x status: %02x\n", - year, mon, mday, wday, hour, min, sec, status); - - if (status & RTC_STAT_BIT_RTCF) { + int ret; + uchar buf[8], val; + + ret = dm_i2c_read(dev, 0, buf, sizeof(buf)); + if (ret < 0) + return ret; + + if (buf[RTC_STAT_REG_ADDR] & RTC_STAT_BIT_RTCF) { printf ("### Warning: RTC oscillator has stopped\n"); - rtc_write(RTC_STAT_REG_ADDR, - rtc_read(RTC_STAT_REG_ADDR) &~ (RTC_STAT_BIT_BAT|RTC_STAT_BIT_RTCF)); - rel = -1; + ret = dm_i2c_read(dev, RTC_STAT_REG_ADDR, &val, sizeof(val)); + if (ret < 0) + return ret; + + val = val & ~(RTC_STAT_BIT_BAT | RTC_STAT_BIT_RTCF); + ret = dm_i2c_write(dev, RTC_STAT_REG_ADDR, &val, sizeof(val)); + if (ret < 0) + return ret; } - tmp->tm_sec = bcd2bin (sec & 0x7F); - tmp->tm_min = bcd2bin (min & 0x7F); - tmp->tm_hour = bcd2bin (hour & 0x3F); - tmp->tm_mday = bcd2bin (mday & 0x3F); - tmp->tm_mon = bcd2bin (mon & 0x1F); - tmp->tm_year = bcd2bin (year)+2000; - tmp->tm_wday = bcd2bin (wday & 0x07); + tmp->tm_sec = bcd2bin(buf[RTC_SEC_REG_ADDR] & 0x7F); + tmp->tm_min = bcd2bin(buf[RTC_MIN_REG_ADDR] & 0x7F); + tmp->tm_hour = bcd2bin(buf[RTC_HR_REG_ADDR] & 0x3F); + tmp->tm_mday = bcd2bin(buf[RTC_DATE_REG_ADDR] & 0x3F); + tmp->tm_mon = bcd2bin(buf[RTC_MON_REG_ADDR] & 0x1F); + tmp->tm_year = bcd2bin(buf[RTC_YR_REG_ADDR]) + 2000; + tmp->tm_wday = bcd2bin(buf[RTC_DAY_REG_ADDR] & 0x07); tmp->tm_yday = 0; tmp->tm_isdst= 0; @@ -97,51 +91,88 @@ int rtc_get (struct rtc_time *tmp) tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - return rel; + return 0; } /* * Set the RTC */ -int rtc_set (struct rtc_time *tmp) +static int isl1208_rtc_set(struct udevice *dev, const struct rtc_time *tmp) { + int ret; + uchar val, buf[7]; + DEBUGR ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + if (tmp->tm_year < 2000 || tmp->tm_year > 2099) + printf("WARNING: year should be between 2000 and 2099!\n"); + /* enable write */ - rtc_write(RTC_STAT_REG_ADDR, - rtc_read(RTC_STAT_REG_ADDR) | RTC_STAT_BIT_WRTC); + ret = dm_i2c_read(dev, RTC_STAT_REG_ADDR, &val, sizeof(val)); + if (ret < 0) + return ret; + + val = val | RTC_STAT_BIT_WRTC; + + ret = dm_i2c_write(dev, RTC_STAT_REG_ADDR, &val, sizeof(val)); + if (ret < 0) + return ret; + + buf[RTC_YR_REG_ADDR] = bin2bcd(tmp->tm_year % 100); + buf[RTC_MON_REG_ADDR] = bin2bcd(tmp->tm_mon); + buf[RTC_DAY_REG_ADDR] = bin2bcd(tmp->tm_wday); + buf[RTC_DATE_REG_ADDR] = bin2bcd(tmp->tm_mday); + buf[RTC_HR_REG_ADDR] = bin2bcd(tmp->tm_hour) | 0x80; /* 24h clock */ + buf[RTC_MIN_REG_ADDR] = bin2bcd(tmp->tm_min); + buf[RTC_SEC_REG_ADDR] = bin2bcd(tmp->tm_sec); - rtc_write (RTC_YR_REG_ADDR, bin2bcd (tmp->tm_year % 100)); - rtc_write (RTC_MON_REG_ADDR, bin2bcd (tmp->tm_mon)); - rtc_write (RTC_DAY_REG_ADDR, bin2bcd (tmp->tm_wday)); - rtc_write (RTC_DATE_REG_ADDR, bin2bcd (tmp->tm_mday)); - rtc_write (RTC_HR_REG_ADDR, bin2bcd (tmp->tm_hour) | 0x80 ); /* 24h clock */ - rtc_write (RTC_MIN_REG_ADDR, bin2bcd (tmp->tm_min)); - rtc_write (RTC_SEC_REG_ADDR, bin2bcd (tmp->tm_sec)); + ret = dm_i2c_write(dev, 0, buf, sizeof(buf)); + if (ret < 0) + return ret; /* disable write */ - rtc_write(RTC_STAT_REG_ADDR, - rtc_read(RTC_STAT_REG_ADDR) & ~RTC_STAT_BIT_WRTC); + ret = dm_i2c_read(dev, RTC_STAT_REG_ADDR, &val, sizeof(val)); + if (ret < 0) + return ret; + + val = val & ~RTC_STAT_BIT_WRTC; + ret = dm_i2c_write(dev, RTC_STAT_REG_ADDR, &val, sizeof(val)); + if (ret < 0) + return ret; return 0; } -void rtc_reset (void) +static int isl1208_rtc_reset(struct udevice *dev) { + return 0; } -/* - * Helper functions - */ - -static uchar rtc_read (uchar reg) +static int isl1208_probe(struct udevice *dev) { - return (i2c_reg_read (CONFIG_SYS_I2C_RTC_ADDR, reg)); -} + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | + DM_I2C_CHIP_WR_ADDRESS); -static void rtc_write (uchar reg, uchar val) -{ - i2c_reg_write (CONFIG_SYS_I2C_RTC_ADDR, reg, val); + return 0; } + +static const struct rtc_ops isl1208_rtc_ops = { + .get = isl1208_rtc_get, + .set = isl1208_rtc_set, + .reset = isl1208_rtc_reset, +}; + +static const struct udevice_id isl1208_rtc_ids[] = { + { .compatible = "isil,isl1208" }, + { } +}; + +U_BOOT_DRIVER(rtc_isl1208) = { + .name = "rtc-isl1208", + .id = UCLASS_RTC, + .probe = isl1208_probe, + .of_match = isl1208_rtc_ids, + .ops = &isl1208_rtc_ops, +}; diff --git a/drivers/rtc/mx27rtc.c b/drivers/rtc/mx27rtc.c index 29ccdf1730..b42770e05b 100644 --- a/drivers/rtc/mx27rtc.c +++ b/drivers/rtc/mx27rtc.c @@ -61,9 +61,5 @@ int rtc_set(struct rtc_time *time) void rtc_reset(void) { - struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE; - - writel(0, &rtc_regs->dayr); - writel(0, &rtc_regs->hourmin); - writel(0, &rtc_regs->seconds); + /* nothing to do */ } diff --git a/drivers/rtc/rs5c372.c b/drivers/rtc/rs5c372.c index 65f45ea5e3..c815c915d5 100644 --- a/drivers/rtc/rs5c372.c +++ b/drivers/rtc/rs5c372.c @@ -247,35 +247,13 @@ int rtc_set (struct rtc_time *tmp) } /* - * Reset the RTC. We set the date back to 1970-01-01. + * Reset the RTC. */ void rtc_reset (void) { - struct rtc_time tmp; - if (!setup_done) rs5c372_enable(); - - if (!setup_done) - return; - - tmp.tm_year = 1970; - tmp.tm_mon = 1; - /* Jan. 1, 1970 was a Thursday */ - tmp.tm_wday= 4; - tmp.tm_mday= 1; - tmp.tm_hour = 0; - tmp.tm_min = 0; - tmp.tm_sec = 0; - - rtc_set(&tmp); - - printf ("RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", - tmp.tm_year, tmp.tm_mon, tmp.tm_mday, - tmp.tm_hour, tmp.tm_min, tmp.tm_sec); - - return; } #endif diff --git a/drivers/rtc/rx8025.c b/drivers/rtc/rx8025.c index b4a149b738..c43966a50d 100644 --- a/drivers/rtc/rx8025.c +++ b/drivers/rtc/rx8025.c @@ -163,11 +163,10 @@ int rtc_set (struct rtc_time *tmp) } /* - * Reset the RTC. We setting the date back to 1970-01-01. + * Reset the RTC */ void rtc_reset (void) { - struct rtc_time tmp; uchar buf[16]; uchar ctl2; @@ -178,21 +177,6 @@ void rtc_reset (void) ctl2 &= ~(RTC_CTL2_BIT_PON | RTC_CTL2_BIT_VDET); ctl2 |= RTC_CTL2_BIT_XST | RTC_CTL2_BIT_VDSL; rtc_write (RTC_CTL2_REG_ADDR, ctl2); - - tmp.tm_year = 1970; - tmp.tm_mon = 1; - tmp.tm_mday= 1; - tmp.tm_hour = 0; - tmp.tm_min = 0; - tmp.tm_sec = 0; - - rtc_set(&tmp); - - printf ( "RTC: %4d-%02d-%02d %2d:%02d:%02d UTC\n", - tmp.tm_year, tmp.tm_mon, tmp.tm_mday, - tmp.tm_hour, tmp.tm_min, tmp.tm_sec); - - return; } /* diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 93e602e0ee..3d5b2bf15f 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -24,6 +24,15 @@ config REQUIRE_SERIAL_CONSOLE during serial port initialization (default y). Set this to n on boards which have no debug serial port whatsoever. +config SPECIFY_CONSOLE_INDEX + bool "Specify the port number used for console" + default y if !DM_SERIAL || (SPL && !SPL_DM_SERIAL) || \ + (TPL && !TPL_DM_SERIAL) + help + In various cases, we need to specify which of the UART devices that + a board or SoC has available are to be used for the console device + in U-Boot. + config SERIAL_PRESENT bool "Provide a serial driver" depends on DM_SERIAL @@ -44,16 +53,39 @@ config SPL_SERIAL_PRESENT This option enables the full UART in SPL, so if is it disabled, the full UART driver will be omitted, thus saving space. +# Logic to allow us to use the imply keyword to set what the default port +# should be. The default is otherwise 1. +config CONS_INDEX_0 + bool + +config CONS_INDEX_2 + bool + +config CONS_INDEX_3 + bool + +config CONS_INDEX_4 + bool + +config CONS_INDEX_5 + bool + +config CONS_INDEX_6 + bool + config CONS_INDEX int "UART used for console" - depends on ARCH_SUNXI - default 2 if MACH_SUN5I - default 5 if MACH_SUN8I_A23 || MACH_SUN8I_A33 + depends on SPECIFY_CONSOLE_INDEX + range 0 6 + default 0 if CONS_INDEX_0 + default 2 if CONS_INDEX_2 + default 3 if CONS_INDEX_3 + default 4 if CONS_INDEX_4 + default 5 if CONS_INDEX_5 + default 6 if CONS_INDEX_6 default 1 help - Configures the console index. - For Allwinner SoC., default values are 2 for SUN5I and 5 for A23/A33. - Otherwise, the index equals 1. + Set this to match the UART number of the serial console. config DM_SERIAL bool "Enable Driver Model for serial drivers" @@ -93,8 +125,8 @@ config SERIAL_SEARCH_ALL config SPL_DM_SERIAL bool "Enable Driver Model for serial drivers in SPL" - depends on DM_SERIAL - default y if SPL && DM_SERIAL + depends on DM_SERIAL && SPL_DM + default y help Enable driver model for serial in SPL. This replaces drivers/serial/serial.c with the serial uclass, which @@ -577,10 +609,10 @@ config STI_ASC_SERIAL config STM32_SERIAL bool "STMicroelectronics STM32 SoCs on-chip UART" - depends on DM_SERIAL && (STM32F4 || STM32F7 || STM32H7) + depends on DM_SERIAL && (STM32F4 || STM32F7 || STM32H7 || ARCH_STM32MP) help - If you have a machine based on a STM32 F4, F7 or H7 SoC you can - enable its onboard serial ports, say Y to this option. + If you have a machine based on a STM32 F4, F7, H7 or MP1 SOC + you can enable its onboard serial ports, say Y to this option. If unsure, say N. config ZYNQ_SERIAL @@ -592,7 +624,7 @@ config ZYNQ_SERIAL config MPC8XX_CONS bool "Console driver for MPC8XX" - depends on 8xx + depends on MPC8xx default y choice diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index cac9a8b312..6937ef9628 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -5,11 +5,27 @@ # SPDX-License-Identifier: GPL-2.0+ # +ifdef CONFIG_SPL_BUILD + +ifeq ($(CONFIG_$(SPL_TPL_)BUILD)$(CONFIG_$(SPL_TPL_)DM_SERIAL),yy) +obj-y += serial-uclass.o +else +obj-y += serial.o +endif + +else + ifdef CONFIG_DM_SERIAL -obj-$(CONFIG_$(SPL_TPL_)DM_SERIAL) += serial-uclass.o -obj-$(CONFIG_PL01X_SERIAL) += serial_pl01x.o +obj-y += serial-uclass.o else obj-y += serial.o +endif + +endif + +ifdef CONFIG_DM_SERIAL +obj-$(CONFIG_PL01X_SERIAL) += serial_pl01x.o +else obj-$(CONFIG_PL010_SERIAL) += serial_pl01x.o obj-$(CONFIG_PL011_SERIAL) += serial_pl01x.o obj-$(CONFIG_SYS_NS16550_SERIAL) += serial_ns16550.o diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index cc4bdcb834..397c6f5203 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -109,48 +109,16 @@ U_BOOT_ENV_CALLBACK(baudrate, on_baudrate); void name(void) \ __attribute__((weak, alias("serial_null"))); -serial_initfunc(amirix_serial_initialize); -serial_initfunc(arc_serial_initialize); -serial_initfunc(arm_dcc_initialize); -serial_initfunc(asc_serial_initialize); serial_initfunc(atmel_serial_initialize); serial_initfunc(au1x00_serial_initialize); -serial_initfunc(bfin_jtag_initialize); -serial_initfunc(bfin_serial_initialize); -serial_initfunc(bmw_serial_initialize); -serial_initfunc(clps7111_serial_initialize); -serial_initfunc(cogent_serial_initialize); -serial_initfunc(cpci750_serial_initialize); -serial_initfunc(evb64260_serial_initialize); -serial_initfunc(imx_serial_initialize); -serial_initfunc(iop480_serial_initialize); -serial_initfunc(jz_serial_initialize); -serial_initfunc(leon2_serial_initialize); -serial_initfunc(leon3_serial_initialize); -serial_initfunc(lh7a40x_serial_initialize); -serial_initfunc(lpc32xx_serial_initialize); -serial_initfunc(marvell_serial_initialize); -serial_initfunc(max3100_serial_initialize); serial_initfunc(mcf_serial_initialize); -serial_initfunc(ml2_serial_initialize); serial_initfunc(mpc85xx_serial_initialize); serial_initfunc(mpc8xx_serial_initialize); serial_initfunc(mxc_serial_initialize); -serial_initfunc(mxs_auart_initialize); serial_initfunc(ns16550_serial_initialize); -serial_initfunc(oc_serial_initialize); -serial_initfunc(p3mx_serial_initialize); serial_initfunc(pl01x_serial_initialize); serial_initfunc(pxa_serial_initialize); -serial_initfunc(s3c24xx_serial_initialize); -serial_initfunc(s5p_serial_initialize); -serial_initfunc(sa1100_serial_initialize); -serial_initfunc(sandbox_serial_initialize); -serial_initfunc(sconsole_serial_initialize); serial_initfunc(sh_serial_initialize); -serial_initfunc(stm32_serial_initialize); -serial_initfunc(uartlite_serial_initialize); -serial_initfunc(zynq_serial_initialize); /** * serial_register() - Register serial driver with serial driver core @@ -196,48 +164,16 @@ void serial_register(struct serial_device *dev) */ void serial_initialize(void) { - amirix_serial_initialize(); - arc_serial_initialize(); - arm_dcc_initialize(); - asc_serial_initialize(); atmel_serial_initialize(); au1x00_serial_initialize(); - bfin_jtag_initialize(); - bfin_serial_initialize(); - bmw_serial_initialize(); - clps7111_serial_initialize(); - cogent_serial_initialize(); - cpci750_serial_initialize(); - evb64260_serial_initialize(); - imx_serial_initialize(); - iop480_serial_initialize(); - jz_serial_initialize(); - leon2_serial_initialize(); - leon3_serial_initialize(); - lh7a40x_serial_initialize(); - lpc32xx_serial_initialize(); - marvell_serial_initialize(); - max3100_serial_initialize(); mcf_serial_initialize(); - ml2_serial_initialize(); mpc85xx_serial_initialize(); mpc8xx_serial_initialize(); mxc_serial_initialize(); - mxs_auart_initialize(); ns16550_serial_initialize(); - oc_serial_initialize(); - p3mx_serial_initialize(); pl01x_serial_initialize(); pxa_serial_initialize(); - s3c24xx_serial_initialize(); - s5p_serial_initialize(); - sa1100_serial_initialize(); - sandbox_serial_initialize(); - sconsole_serial_initialize(); sh_serial_initialize(); - stm32_serial_initialize(); - uartlite_serial_initialize(); - zynq_serial_initialize(); serial_assign(default_serial_console()->name); } diff --git a/drivers/serial/serial_meson.c b/drivers/serial/serial_meson.c index 363affb8c5..6412ca6496 100644 --- a/drivers/serial/serial_meson.c +++ b/drivers/serial/serial_meson.c @@ -125,6 +125,7 @@ static const struct dm_serial_ops meson_serial_ops = { static const struct udevice_id meson_serial_ids[] = { { .compatible = "amlogic,meson-uart" }, + { .compatible = "amlogic,meson-gx-uart" }, { } }; diff --git a/drivers/serial/serial_mpc8xx.c b/drivers/serial/serial_mpc8xx.c index 26a8085a69..7a5908f464 100644 --- a/drivers/serial/serial_mpc8xx.c +++ b/drivers/serial/serial_mpc8xx.c @@ -6,10 +6,10 @@ */ #include <common.h> -#include <commproc.h> #include <command.h> #include <serial.h> #include <watchdog.h> +#include <asm/cpm_8xx.h> #include <linux/compiler.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/serial/serial_sh.c b/drivers/serial/serial_sh.c index a17698f90e..5f4ace7848 100644 --- a/drivers/serial/serial_sh.c +++ b/drivers/serial/serial_sh.c @@ -270,6 +270,8 @@ U_BOOT_DRIVER(serial_sh) = { # define SCIF_BASE SCIF6_BASE #elif defined(CONFIG_CONS_SCIF7) # define SCIF_BASE SCIF7_BASE +#elif defined(CONFIG_CONS_SCIFA0) +# define SCIF_BASE SCIFA0_BASE #else # error "Default SCIF doesn't set....." #endif diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 235a8c7d73..ec92b84ed2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -23,6 +23,13 @@ config ALTERA_SPI IP core. Please find details on the "Embedded Peripherals IP User Guide" of Altera. +config ATCSPI200_SPI + bool "Andestech ATCSPI200 SPI driver" + help + Enable the Andestech ATCSPI200 SPI driver. This driver can be + used to access the SPI flash on AE3XX and AE250 platforms embedding + this Andestech IP core. + config ATH79_SPI bool "Atheros SPI driver" depends on ARCH_ATH79 @@ -34,7 +41,7 @@ config ATH79_SPI config ATMEL_SPI bool "Atmel SPI driver" - depends on ARCH_AT91 + default y if ARCH_AT91 help This enables driver for the Atmel SPI Controller, present on many AT91 (ARM) chips. This driver can be used to access @@ -107,6 +114,14 @@ config PIC32_SPI to access the SPI NOR flash, MMC-over-SPI on platforms based on Microchip PIC32 family devices. +config RENESAS_RPC_SPI + bool "Renesas RPC SPI driver" + depends on RCAR_GEN3 + help + Enable the Renesas RPC SPI driver, used to access SPI NOR flash + on Renesas RCar Gen3 SoCs. This uses driver model and requires a + device tree binding to operate. + config ROCKCHIP_SPI bool "Rockchip SPI driver" help @@ -232,13 +247,6 @@ config FSL_QSPI used to access the SPI NOR flash on platforms embedding this Freescale IP core. -config ATCSPI200_SPI - bool "Andestech ATCSPI200 SPI driver" - help - Enable the Andestech ATCSPI200 SPI driver. This driver can be - used to access the SPI flash on AE3XX and AE250 platforms embedding - this Andestech IP core. - config DAVINCI_SPI bool "Davinci & Keystone SPI driver" depends on ARCH_DAVINCI || ARCH_KEYSTONE @@ -276,7 +284,7 @@ config LPC32XX_SSP config MPC8XX_SPI bool "MPC8XX SPI Driver" - depends on 8xx + depends on MPC8xx help Enable support for SPI on MPC8XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4b6000fd9a..176bfa05cf 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_MXS_SPI) += mxs_spi.o obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o obj-$(CONFIG_PIC32_SPI) += pic32_spi.o +obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o obj-$(CONFIG_SH_SPI) += sh_spi.o diff --git a/drivers/spi/atcspi200_spi.c b/drivers/spi/atcspi200_spi.c index 5b2e9d6264..bc08914b9e 100644 --- a/drivers/spi/atcspi200_spi.c +++ b/drivers/spi/atcspi200_spi.c @@ -75,9 +75,6 @@ struct atcspi200_spi_regs { }; struct nds_spi_slave { -#ifndef CONFIG_DM_SPI - struct spi_slave slave; -#endif volatile struct atcspi200_spi_regs *regs; int to; unsigned int freq; @@ -286,89 +283,6 @@ static int __atcspi200_spi_xfer(struct nds_spi_slave *ns, return ret; } -#ifndef CONFIG_DM_SPI -#define to_nds_spi_slave(s) container_of(s, struct nds_spi_slave, slave) -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) -{ - struct nds_spi_slave *ns; - - if (!spi_cs_is_valid(bus, cs)) - return NULL; - - ns = spi_alloc_slave(struct nds_spi_slave, bus, cs); - if (!ns) - return NULL; - - switch (bus) { - case SPI0_BUS: - ns->regs = (struct atcspi200_spi_regs *)SPI0_BASE; - break; - - case SPI1_BUS: - ns->regs = (struct atcspi200_spi_regs *)SPI1_BASE; - break; - - default: - return NULL; - } - - ns->freq= max_hz; - ns->mode = mode; - ns->to = SPI_TIMEOUT; - ns->max_transfer_length = MAX_TRANSFER_LEN; - ns->slave.max_write_size = MAX_TRANSFER_LEN; - - return &ns->slave; -} - -void spi_free_slave(struct spi_slave *slave) -{ - struct nds_spi_slave *ns = to_nds_spi_slave(slave); - free(ns); -} - -void spi_init(void) -{ - /* do nothing */ -} - -int spi_claim_bus(struct spi_slave *slave) -{ - struct nds_spi_slave *ns = to_nds_spi_slave(slave); - return __atcspi200_spi_claim_bus(ns); -} - -void spi_release_bus(struct spi_slave *slave) -{ - struct nds_spi_slave *ns = to_nds_spi_slave(slave); - __atcspi200_spi_release_bus(ns); -} - -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out, - void *data_in, unsigned long flags) -{ - struct nds_spi_slave *ns = to_nds_spi_slave(slave); - return __atcspi200_spi_xfer(ns, bitlen, data_out, data_in, flags); -} - -int spi_cs_is_valid(unsigned int bus, unsigned int cs) -{ - return bus == 0 && cs < NSPI_MAX_CS_NUM; -} - -void spi_cs_activate(struct spi_slave *slave) -{ - struct nds_spi_slave *ns = to_nds_spi_slave(slave); - __atcspi200_spi_start(ns); -} - -void spi_cs_deactivate(struct spi_slave *slave) -{ - struct nds_spi_slave *ns = to_nds_spi_slave(slave); - __atcspi200_spi_stop(ns); -} -#else static int atcspi200_spi_set_speed(struct udevice *bus, uint max_hz) { struct nds_spi_slave *ns = dev_get_priv(bus); @@ -496,4 +410,3 @@ U_BOOT_DRIVER(atcspi200_spi) = { .priv_auto_alloc_size = sizeof(struct nds_spi_slave), .probe = atcspi200_spi_probe, }; -#endif diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 8010ab434c..3cdfd366ab 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -236,7 +236,9 @@ struct atmel_spi_priv { unsigned int freq; /* Default frequency */ unsigned int mode; ulong bus_clk_rate; +#ifdef CONFIG_DM_GPIO struct gpio_desc cs_gpios[MAX_CS_COUNT]; +#endif }; static int atmel_spi_claim_bus(struct udevice *dev) @@ -291,6 +293,7 @@ static int atmel_spi_release_bus(struct udevice *dev) static void atmel_spi_cs_activate(struct udevice *dev) { +#ifdef CONFIG_DM_GPIO struct udevice *bus = dev_get_parent(dev); struct atmel_spi_priv *priv = dev_get_priv(bus); struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); @@ -300,10 +303,12 @@ static void atmel_spi_cs_activate(struct udevice *dev) return; dm_gpio_set_value(&priv->cs_gpios[cs], 0); +#endif } static void atmel_spi_cs_deactivate(struct udevice *dev) { +#ifdef CONFIG_DM_GPIO struct udevice *bus = dev_get_parent(dev); struct atmel_spi_priv *priv = dev_get_priv(bus); struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); @@ -313,6 +318,7 @@ static void atmel_spi_cs_deactivate(struct udevice *dev) return; dm_gpio_set_value(&priv->cs_gpios[cs], 1); +#endif } static int atmel_spi_xfer(struct udevice *dev, unsigned int bitlen, @@ -462,8 +468,7 @@ static int atmel_spi_enable_clk(struct udevice *bus) static int atmel_spi_probe(struct udevice *bus) { struct atmel_spi_platdata *bus_plat = dev_get_platdata(bus); - struct atmel_spi_priv *priv = dev_get_priv(bus); - int i, ret; + int ret; ret = atmel_spi_enable_clk(bus); if (ret) @@ -471,6 +476,10 @@ static int atmel_spi_probe(struct udevice *bus) bus_plat->regs = (struct at91_spi *)devfdt_get_addr(bus); +#ifdef CONFIG_DM_GPIO + struct atmel_spi_priv *priv = dev_get_priv(bus); + int i; + ret = gpio_request_list_by_name(bus, "cs-gpios", priv->cs_gpios, ARRAY_SIZE(priv->cs_gpios), 0); if (ret < 0) { @@ -485,6 +494,7 @@ static int atmel_spi_probe(struct udevice *bus) dm_gpio_set_dir_flags(&priv->cs_gpios[i], GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); } +#endif writel(ATMEL_SPI_CR_SWRST, &bus_plat->regs->cr); diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index c501aeea16..0e93b62eee 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -10,6 +10,7 @@ * SPDX-License-Identifier: GPL-2.0 */ +#include <asm-generic/gpio.h> #include <common.h> #include <clk.h> #include <dm.h> @@ -18,6 +19,7 @@ #include <spi.h> #include <fdtdec.h> #include <linux/compat.h> +#include <linux/iopoll.h> #include <asm/io.h> DECLARE_GLOBAL_DATA_PTR; @@ -97,6 +99,8 @@ struct dw_spi_priv { struct clk clk; unsigned long bus_clk_rate; + struct gpio_desc cs_gpio; /* External chip-select gpio */ + int bits_per_word; u8 cs; /* chip select pin */ u8 tmode; /* TR/TO/RO/EEPROM */ @@ -110,24 +114,40 @@ struct dw_spi_priv { void *rx_end; }; -static inline u32 dw_readl(struct dw_spi_priv *priv, u32 offset) +static inline u32 dw_read(struct dw_spi_priv *priv, u32 offset) { return __raw_readl(priv->regs + offset); } -static inline void dw_writel(struct dw_spi_priv *priv, u32 offset, u32 val) +static inline void dw_write(struct dw_spi_priv *priv, u32 offset, u32 val) { __raw_writel(val, priv->regs + offset); } -static inline u16 dw_readw(struct dw_spi_priv *priv, u32 offset) +static int request_gpio_cs(struct udevice *bus) { - return __raw_readw(priv->regs + offset); -} +#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_SPL_BUILD) + struct dw_spi_priv *priv = dev_get_priv(bus); + int ret; -static inline void dw_writew(struct dw_spi_priv *priv, u32 offset, u16 val) -{ - __raw_writew(val, priv->regs + offset); + /* External chip select gpio line is optional */ + ret = gpio_request_by_name(bus, "cs-gpio", 0, &priv->cs_gpio, 0); + if (ret == -ENOENT) + return 0; + + if (ret < 0) { + printf("Error: %d: Can't get %s gpio!\n", ret, bus->name); + return ret; + } + + if (dm_gpio_is_valid(&priv->cs_gpio)) { + dm_gpio_set_dir_flags(&priv->cs_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + } + + debug("%s: used external gpio for CS management\n", __func__); +#endif + return 0; } static int dw_spi_ofdata_to_platdata(struct udevice *bus) @@ -144,19 +164,19 @@ static int dw_spi_ofdata_to_platdata(struct udevice *bus) debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs, plat->frequency); - return 0; + return request_gpio_cs(bus); } static inline void spi_enable_chip(struct dw_spi_priv *priv, int enable) { - dw_writel(priv, DW_SPI_SSIENR, (enable ? 1 : 0)); + dw_write(priv, DW_SPI_SSIENR, (enable ? 1 : 0)); } /* Restart the controller, disable all interrupts, clean rx fifo */ static void spi_hw_init(struct dw_spi_priv *priv) { spi_enable_chip(priv, 0); - dw_writel(priv, DW_SPI_IMR, 0xff); + dw_write(priv, DW_SPI_IMR, 0xff); spi_enable_chip(priv, 1); /* @@ -167,13 +187,13 @@ static void spi_hw_init(struct dw_spi_priv *priv) u32 fifo; for (fifo = 1; fifo < 256; fifo++) { - dw_writew(priv, DW_SPI_TXFLTR, fifo); - if (fifo != dw_readw(priv, DW_SPI_TXFLTR)) + dw_write(priv, DW_SPI_TXFLTR, fifo); + if (fifo != dw_read(priv, DW_SPI_TXFLTR)) break; } priv->fifo_len = (fifo == 1) ? 0 : fifo; - dw_writew(priv, DW_SPI_TXFLTR, 0); + dw_write(priv, DW_SPI_TXFLTR, 0); } debug("%s: fifo_len=%d\n", __func__, priv->fifo_len); } @@ -242,7 +262,7 @@ static inline u32 tx_max(struct dw_spi_priv *priv) u32 tx_left, tx_room, rxtx_gap; tx_left = (priv->tx_end - priv->tx) / (priv->bits_per_word >> 3); - tx_room = priv->fifo_len - dw_readw(priv, DW_SPI_TXFLR); + tx_room = priv->fifo_len - dw_read(priv, DW_SPI_TXFLR); /* * Another concern is about the tx/rx mismatch, we @@ -263,7 +283,7 @@ static inline u32 rx_max(struct dw_spi_priv *priv) { u32 rx_left = (priv->rx_end - priv->rx) / (priv->bits_per_word >> 3); - return min_t(u32, rx_left, dw_readw(priv, DW_SPI_RXFLR)); + return min_t(u32, rx_left, dw_read(priv, DW_SPI_RXFLR)); } static void dw_writer(struct dw_spi_priv *priv) @@ -279,34 +299,22 @@ static void dw_writer(struct dw_spi_priv *priv) else txw = *(u16 *)(priv->tx); } - dw_writew(priv, DW_SPI_DR, txw); + dw_write(priv, DW_SPI_DR, txw); debug("%s: tx=0x%02x\n", __func__, txw); priv->tx += priv->bits_per_word >> 3; } } -static int dw_reader(struct dw_spi_priv *priv) +static void dw_reader(struct dw_spi_priv *priv) { - unsigned start = get_timer(0); - u32 max; + u32 max = rx_max(priv); u16 rxw; - /* Wait for rx data to be ready */ - while (rx_max(priv) == 0) { - if (get_timer(start) > RX_TIMEOUT) - return -ETIMEDOUT; - } - - max = rx_max(priv); - while (max--) { - rxw = dw_readw(priv, DW_SPI_DR); + rxw = dw_read(priv, DW_SPI_DR); debug("%s: rx=0x%02x\n", __func__, rxw); - /* - * Care about rx only if the transfer's original "rx" is - * not null - */ + /* Care about rx if the transfer's original "rx" is not null */ if (priv->rx_end - priv->len) { if (priv->bits_per_word == 8) *(u8 *)(priv->rx) = rxw; @@ -315,24 +323,30 @@ static int dw_reader(struct dw_spi_priv *priv) } priv->rx += priv->bits_per_word >> 3; } - - return 0; } static int poll_transfer(struct dw_spi_priv *priv) { - int ret; - do { dw_writer(priv); - ret = dw_reader(priv); - if (ret < 0) - return ret; + dw_reader(priv); } while (priv->rx_end > priv->rx); return 0; } +static void external_cs_manage(struct udevice *dev, bool on) +{ +#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_SPL_BUILD) + struct dw_spi_priv *priv = dev_get_priv(dev->parent); + + if (!dm_gpio_is_valid(&priv->cs_gpio)) + return; + + dm_gpio_set_value(&priv->cs_gpio, on ? 1 : 0); +#endif +} + static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { @@ -342,6 +356,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, u8 *rx = din; int ret = 0; u32 cr0 = 0; + u32 val; u32 cs; /* spi core configured to do 8 bit transfers */ @@ -350,6 +365,10 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, return -1; } + /* Start the transaction if necessary. */ + if (flags & SPI_XFER_BEGIN) + external_cs_manage(dev, false); + cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) | (priv->mode << SPI_MODE_OFFSET) | (priv->tmode << SPI_TMOD_OFFSET); @@ -359,7 +378,11 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, else if (rx) priv->tmode = SPI_TMOD_RO; else - priv->tmode = SPI_TMOD_TO; + /* + * In transmit only mode (SPI_TMOD_TO) input FIFO never gets + * any data which breaks our logic in poll_transfer() above. + */ + priv->tmode = SPI_TMOD_TR; cr0 &= ~SPI_TMOD_MASK; cr0 |= (priv->tmode << SPI_TMOD_OFFSET); @@ -377,8 +400,8 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, debug("%s: cr0=%08x\n", __func__, cr0); /* Reprogram cr0 only if changed */ - if (dw_readw(priv, DW_SPI_CTRL0) != cr0) - dw_writew(priv, DW_SPI_CTRL0, cr0); + if (dw_read(priv, DW_SPI_CTRL0) != cr0) + dw_write(priv, DW_SPI_CTRL0, cr0); /* * Configure the desired SS (slave select 0...3) in the controller @@ -386,7 +409,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, * automatically. So no cs_activate() etc is needed in this driver. */ cs = spi_chip_select(dev); - dw_writel(priv, DW_SPI_SER, 1 << cs); + dw_write(priv, DW_SPI_SER, 1 << cs); /* Enable controller after writing control registers */ spi_enable_chip(priv, 1); @@ -394,6 +417,23 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, /* Start transfer in a polling loop */ ret = poll_transfer(priv); + /* + * Wait for current transmit operation to complete. + * Otherwise if some data still exists in Tx FIFO it can be + * silently flushed, i.e. dropped on disabling of the controller, + * which happens when writing 0 to DW_SPI_SSIENR which happens + * in the beginning of new transfer. + */ + if (readl_poll_timeout(priv->regs + DW_SPI_SR, val, + !(val & SR_TF_EMPT) || (val & SR_BUSY), + RX_TIMEOUT * 1000)) { + ret = -ETIMEDOUT; + } + + /* Stop the transaction if necessary */ + if (flags & SPI_XFER_END) + external_cs_manage(dev, true); + return ret; } @@ -412,7 +452,7 @@ static int dw_spi_set_speed(struct udevice *bus, uint speed) /* clk_div doesn't support odd number */ clk_div = priv->bus_clk_rate / speed; clk_div = (clk_div + 1) & 0xfffe; - dw_writel(priv, DW_SPI_BAUDR, clk_div); + dw_write(priv, DW_SPI_BAUDR, clk_div); /* Enable controller after writing control registers */ spi_enable_chip(priv, 1); diff --git a/drivers/spi/mpc8xx_spi.c b/drivers/spi/mpc8xx_spi.c index b5bd558526..eb035e9510 100644 --- a/drivers/spi/mpc8xx_spi.c +++ b/drivers/spi/mpc8xx_spi.c @@ -19,7 +19,7 @@ #include <common.h> #include <mpc8xx.h> -#include <commproc.h> +#include <asm/cpm_8xx.h> #include <linux/ctype.h> #include <malloc.h> #include <post.h> diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c index 1da4542af0..1ac691a68e 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -456,9 +456,6 @@ static void _omap3_spi_claim_bus(struct omap3_spi_priv *priv) conf |= OMAP3_MCSPI_MODULCTRL_SINGLE; writel(conf, &priv->regs->modulctrl); - - _omap3_spi_set_mode(priv); - _omap3_spi_set_speed(priv); } #ifndef CONFIG_DM_SPI @@ -594,8 +591,6 @@ static int omap3_spi_claim_bus(struct udevice *dev) struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); priv->cs = slave_plat->cs; - priv->mode = slave_plat->mode; - priv->freq = slave_plat->max_hz; _omap3_spi_claim_bus(priv); return 0; @@ -635,8 +630,10 @@ static int omap3_spi_probe(struct udevice *dev) (struct omap2_mcspi_platform_config*)dev_get_driver_data(dev); priv->regs = (struct mcspi *)(devfdt_get_addr(dev) + data->regs_offset); - priv->pin_dir = fdtdec_get_uint(blob, node, "ti,pindir-d0-out-d1-in", - MCSPI_PINDIR_D0_IN_D1_OUT); + if (fdtdec_get_bool(blob, node, "ti,pindir-d0-out-d1-in")) + priv->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; + else + priv->pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT; priv->wordlen = SPI_DEFAULT_WORDLEN; return 0; } @@ -650,13 +647,29 @@ static int omap3_spi_xfer(struct udevice *dev, unsigned int bitlen, return _spi_xfer(priv, bitlen, dout, din, flags); } -static int omap3_spi_set_speed(struct udevice *bus, unsigned int speed) +static int omap3_spi_set_speed(struct udevice *dev, unsigned int speed) { + struct udevice *bus = dev->parent; + struct omap3_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + + priv->cs = slave_plat->cs; + priv->freq = slave_plat->max_hz; + _omap3_spi_set_speed(priv); + return 0; } -static int omap3_spi_set_mode(struct udevice *bus, uint mode) +static int omap3_spi_set_mode(struct udevice *dev, uint mode) { + struct udevice *bus = dev->parent; + struct omap3_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + + priv->cs = slave_plat->cs; + priv->mode = slave_plat->mode; + _omap3_spi_set_mode(priv); + return 0; } diff --git a/drivers/spi/renesas_rpc_spi.c b/drivers/spi/renesas_rpc_spi.c new file mode 100644 index 0000000000..e54f24c5d8 --- /dev/null +++ b/drivers/spi/renesas_rpc_spi.c @@ -0,0 +1,465 @@ +/* + * Renesas RCar Gen3 RPC QSPI driver + * + * Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/of_access.h> +#include <dt-structs.h> +#include <errno.h> +#include <linux/errno.h> +#include <spi.h> +#include <wait_bit.h> + +#define RPC_CMNCR 0x0000 /* R/W */ +#define RPC_CMNCR_MD BIT(31) +#define RPC_CMNCR_SFDE BIT(24) +#define RPC_CMNCR_MOIIO3(val) (((val) & 0x3) << 22) +#define RPC_CMNCR_MOIIO2(val) (((val) & 0x3) << 20) +#define RPC_CMNCR_MOIIO1(val) (((val) & 0x3) << 18) +#define RPC_CMNCR_MOIIO0(val) (((val) & 0x3) << 16) +#define RPC_CMNCR_MOIIO_HIZ (RPC_CMNCR_MOIIO0(3) | RPC_CMNCR_MOIIO1(3) | \ + RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3)) +#define RPC_CMNCR_IO3FV(val) (((val) & 0x3) << 14) +#define RPC_CMNCR_IO2FV(val) (((val) & 0x3) << 12) +#define RPC_CMNCR_IO0FV(val) (((val) & 0x3) << 8) +#define RPC_CMNCR_IOFV_HIZ (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | \ + RPC_CMNCR_IO3FV(3)) +#define RPC_CMNCR_CPHAT BIT(6) +#define RPC_CMNCR_CPHAR BIT(5) +#define RPC_CMNCR_SSLP BIT(4) +#define RPC_CMNCR_CPOL BIT(3) +#define RPC_CMNCR_BSZ(val) (((val) & 0x3) << 0) + +#define RPC_SSLDR 0x0004 /* R/W */ +#define RPC_SSLDR_SPNDL(d) (((d) & 0x7) << 16) +#define RPC_SSLDR_SLNDL(d) (((d) & 0x7) << 8) +#define RPC_SSLDR_SCKDL(d) (((d) & 0x7) << 0) + +#define RPC_DRCR 0x000C /* R/W */ +#define RPC_DRCR_SSLN BIT(24) +#define RPC_DRCR_RBURST(v) (((v) & 0x1F) << 16) +#define RPC_DRCR_RCF BIT(9) +#define RPC_DRCR_RBE BIT(8) +#define RPC_DRCR_SSLE BIT(0) + +#define RPC_DRCMR 0x0010 /* R/W */ +#define RPC_DRCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPC_DRCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPC_DREAR 0x0014 /* R/W */ +#define RPC_DREAR_EAV(v) (((v) & 0xFF) << 16) +#define RPC_DREAR_EAC(v) (((v) & 0x7) << 0) + +#define RPC_DROPR 0x0018 /* R/W */ +#define RPC_DROPR_OPD3(o) (((o) & 0xFF) << 24) +#define RPC_DROPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPC_DROPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPC_DROPR_OPD0(o) (((o) & 0xFF) << 0) + +#define RPC_DRENR 0x001C /* R/W */ +#define RPC_DRENR_CDB(o) (u32)((((o) & 0x3) << 30)) +#define RPC_DRENR_OCDB(o) (((o) & 0x3) << 28) +#define RPC_DRENR_ADB(o) (((o) & 0x3) << 24) +#define RPC_DRENR_OPDB(o) (((o) & 0x3) << 20) +#define RPC_DRENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPC_DRENR_DME BIT(15) +#define RPC_DRENR_CDE BIT(14) +#define RPC_DRENR_OCDE BIT(12) +#define RPC_DRENR_ADE(v) (((v) & 0xF) << 8) +#define RPC_DRENR_OPDE(v) (((v) & 0xF) << 4) + +#define RPC_SMCR 0x0020 /* R/W */ +#define RPC_SMCR_SSLKP BIT(8) +#define RPC_SMCR_SPIRE BIT(2) +#define RPC_SMCR_SPIWE BIT(1) +#define RPC_SMCR_SPIE BIT(0) + +#define RPC_SMCMR 0x0024 /* R/W */ +#define RPC_SMCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPC_SMCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPC_SMADR 0x0028 /* R/W */ +#define RPC_SMOPR 0x002C /* R/W */ +#define RPC_SMOPR_OPD0(o) (((o) & 0xFF) << 0) +#define RPC_SMOPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPC_SMOPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPC_SMOPR_OPD3(o) (((o) & 0xFF) << 24) + +#define RPC_SMENR 0x0030 /* R/W */ +#define RPC_SMENR_CDB(o) (((o) & 0x3) << 30) +#define RPC_SMENR_OCDB(o) (((o) & 0x3) << 28) +#define RPC_SMENR_ADB(o) (((o) & 0x3) << 24) +#define RPC_SMENR_OPDB(o) (((o) & 0x3) << 20) +#define RPC_SMENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPC_SMENR_DME BIT(15) +#define RPC_SMENR_CDE BIT(14) +#define RPC_SMENR_OCDE BIT(12) +#define RPC_SMENR_ADE(v) (((v) & 0xF) << 8) +#define RPC_SMENR_OPDE(v) (((v) & 0xF) << 4) +#define RPC_SMENR_SPIDE(v) (((v) & 0xF) << 0) + +#define RPC_SMRDR0 0x0038 /* R */ +#define RPC_SMRDR1 0x003C /* R */ +#define RPC_SMWDR0 0x0040 /* R/W */ +#define RPC_SMWDR1 0x0044 /* R/W */ +#define RPC_CMNSR 0x0048 /* R */ +#define RPC_CMNSR_SSLF BIT(1) +#define RPC_CMNSR_TEND BIT(0) + +#define RPC_DRDMCR 0x0058 /* R/W */ +#define RPC_DRDMCR_DMCYC(v) (((v) & 0xF) << 0) + +#define RPC_DRDRENR 0x005C /* R/W */ +#define RPC_DRDRENR_HYPE (0x5 << 12) +#define RPC_DRDRENR_ADDRE BIT(8) +#define RPC_DRDRENR_OPDRE BIT(4) +#define RPC_DRDRENR_DRDRE BIT(0) + +#define RPC_SMDMCR 0x0060 /* R/W */ +#define RPC_SMDMCR_DMCYC(v) (((v) & 0xF) << 0) + +#define RPC_SMDRENR 0x0064 /* R/W */ +#define RPC_SMDRENR_HYPE (0x5 << 12) +#define RPC_SMDRENR_ADDRE BIT(8) +#define RPC_SMDRENR_OPDRE BIT(4) +#define RPC_SMDRENR_SPIDRE BIT(0) + +#define RPC_PHYCNT 0x007C /* R/W */ +#define RPC_PHYCNT_CAL BIT(31) +#define PRC_PHYCNT_OCTA_AA BIT(22) +#define PRC_PHYCNT_OCTA_SA BIT(23) +#define PRC_PHYCNT_EXDS BIT(21) +#define RPC_PHYCNT_OCT BIT(20) +#define RPC_PHYCNT_STRTIM(v) (((v) & 0x7) << 15) +#define RPC_PHYCNT_WBUF2 BIT(4) +#define RPC_PHYCNT_WBUF BIT(2) +#define RPC_PHYCNT_MEM(v) (((v) & 0x3) << 0) + +#define RPC_PHYINT 0x0088 /* R/W */ +#define RPC_PHYINT_RSTEN BIT(18) +#define RPC_PHYINT_WPEN BIT(17) +#define RPC_PHYINT_INTEN BIT(16) +#define RPC_PHYINT_RST BIT(2) +#define RPC_PHYINT_WP BIT(1) +#define RPC_PHYINT_INT BIT(0) + +#define RPC_WBUF 0x8000 /* R/W size=4/8/16/32/64Bytes */ +#define RPC_WBUF_SIZE 0x100 + +DECLARE_GLOBAL_DATA_PTR; + +struct rpc_spi_platdata { + fdt_addr_t regs; + fdt_addr_t extr; + s32 freq; /* Default clock freq, -1 for none */ +}; + +struct rpc_spi_priv { + fdt_addr_t regs; + fdt_addr_t extr; + struct clk clk; + + u8 cmdcopy[8]; + u32 cmdlen; + bool cmdstarted; +}; + +static int rpc_spi_wait_sslf(struct udevice *dev) +{ + struct rpc_spi_priv *priv = dev_get_priv(dev->parent); + + return wait_for_bit_le32((void *)priv->regs + RPC_CMNSR, RPC_CMNSR_SSLF, + false, 1000, false); +} + +static int rpc_spi_wait_tend(struct udevice *dev) +{ + struct rpc_spi_priv *priv = dev_get_priv(dev->parent); + + return wait_for_bit_le32((void *)priv->regs + RPC_CMNSR, RPC_CMNSR_TEND, + true, 1000, false); +} + +static void rpc_spi_flush_read_cache(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct rpc_spi_priv *priv = dev_get_priv(bus); + + /* Flush read cache */ + writel(RPC_DRCR_SSLN | RPC_DRCR_RBURST(0x1f) | + RPC_DRCR_RCF | RPC_DRCR_RBE | RPC_DRCR_SSLE, + priv->regs + RPC_DRCR); + readl(priv->regs + RPC_DRCR); + +} + +static int rpc_spi_claim_bus(struct udevice *dev, bool manual) +{ + struct udevice *bus = dev->parent; + struct rpc_spi_priv *priv = dev_get_priv(bus); + + /* + * NOTE: The 0x260 are undocumented bits, but they must be set. + * NOTE: On H3 ES1.x (not supported in mainline U-Boot), the + * RPC_PHYCNT_STRTIM shall be 0, while on newer parts, the + * RPC_PHYCNT_STRTIM shall be 6. + */ + writel(RPC_PHYCNT_CAL | RPC_PHYCNT_STRTIM(6) | 0x260, + priv->regs + RPC_PHYCNT); + writel((manual ? RPC_CMNCR_MD : 0) | RPC_CMNCR_SFDE | + RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ | RPC_CMNCR_BSZ(0), + priv->regs + RPC_CMNCR); + + writel(RPC_SSLDR_SPNDL(7) | RPC_SSLDR_SLNDL(7) | + RPC_SSLDR_SCKDL(7), priv->regs + RPC_SSLDR); + + rpc_spi_flush_read_cache(dev); + + return 0; +} + +static int rpc_spi_release_bus(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct rpc_spi_priv *priv = dev_get_priv(bus); + + /* NOTE: The 0x260 are undocumented bits, but they must be set. */ + writel(RPC_PHYCNT_STRTIM(6) | 0x260, priv->regs + RPC_PHYCNT); + + rpc_spi_flush_read_cache(dev); + + return 0; +} + +static int rpc_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct rpc_spi_priv *priv = dev_get_priv(bus); + u32 wlen = dout ? (bitlen / 8) : 0; + u32 rlen = din ? (bitlen / 8) : 0; + u32 wloop = DIV_ROUND_UP(wlen, 4); + u32 smenr, smcr, offset; + int ret = 0; + + if (!priv->cmdstarted) { + if (!wlen || rlen) + BUG(); + + memcpy(priv->cmdcopy, dout, wlen); + priv->cmdlen = wlen; + + /* Command transfer start */ + priv->cmdstarted = true; + if (!(flags & SPI_XFER_END)) + return 0; + } + + offset = (priv->cmdcopy[1] << 16) | (priv->cmdcopy[2] << 8) | + (priv->cmdcopy[3] << 0); + + smenr = 0; + + if (wlen || (!rlen && !wlen) || flags == SPI_XFER_ONCE) { + if (wlen && flags == SPI_XFER_END) + smenr = RPC_SMENR_SPIDE(0xf); + + rpc_spi_claim_bus(dev, true); + + writel(0, priv->regs + RPC_SMCR); + + if (priv->cmdlen >= 1) { /* Command(1) */ + writel(RPC_SMCMR_CMD(priv->cmdcopy[0]), + priv->regs + RPC_SMCMR); + smenr |= RPC_SMENR_CDE; + } else { + writel(0, priv->regs + RPC_SMCMR); + } + + if (priv->cmdlen >= 4) { /* Address(3) */ + writel(offset, priv->regs + RPC_SMADR); + smenr |= RPC_SMENR_ADE(7); + } else { + writel(0, priv->regs + RPC_SMADR); + } + + if (priv->cmdlen >= 5) { /* Dummy(n) */ + writel(8 * (priv->cmdlen - 4) - 1, + priv->regs + RPC_SMDMCR); + smenr |= RPC_SMENR_DME; + } else { + writel(0, priv->regs + RPC_SMDMCR); + } + + writel(0, priv->regs + RPC_SMOPR); + + writel(0, priv->regs + RPC_SMDRENR); + + if (wlen && flags == SPI_XFER_END) { + u32 *datout = (u32 *)dout; + + while (wloop--) { + smcr = RPC_SMCR_SPIWE | RPC_SMCR_SPIE; + if (wloop >= 1) + smcr |= RPC_SMCR_SSLKP; + writel(smenr, priv->regs + RPC_SMENR); + writel(*datout, priv->regs + RPC_SMWDR0); + writel(smcr, priv->regs + RPC_SMCR); + ret = rpc_spi_wait_tend(dev); + if (ret) + goto err; + datout++; + smenr = RPC_SMENR_SPIDE(0xf); + } + + ret = rpc_spi_wait_sslf(dev); + + } else { + writel(smenr, priv->regs + RPC_SMENR); + writel(RPC_SMCR_SPIE, priv->regs + RPC_SMCR); + ret = rpc_spi_wait_tend(dev); + } + } else { /* Read data only, using DRx ext access */ + rpc_spi_claim_bus(dev, false); + + if (priv->cmdlen >= 1) { /* Command(1) */ + writel(RPC_DRCMR_CMD(priv->cmdcopy[0]), + priv->regs + RPC_DRCMR); + smenr |= RPC_DRENR_CDE; + } else { + writel(0, priv->regs + RPC_DRCMR); + } + + if (priv->cmdlen >= 4) /* Address(3) */ + smenr |= RPC_DRENR_ADE(7); + + if (priv->cmdlen >= 5) { /* Dummy(n) */ + writel(8 * (priv->cmdlen - 4) - 1, + priv->regs + RPC_DRDMCR); + smenr |= RPC_DRENR_DME; + } else { + writel(0, priv->regs + RPC_DRDMCR); + } + + writel(0, priv->regs + RPC_DROPR); + + writel(smenr, priv->regs + RPC_DRENR); + + if (rlen) + memcpy_fromio(din, (void *)(priv->extr + offset), rlen); + else + readl(priv->extr); /* Dummy read */ + } + +err: + priv->cmdstarted = false; + + rpc_spi_release_bus(dev); + + return ret; +} + +static int rpc_spi_set_speed(struct udevice *bus, uint speed) +{ + /* This is a SPI NOR controller, do nothing. */ + return 0; +} + +static int rpc_spi_set_mode(struct udevice *bus, uint mode) +{ + /* This is a SPI NOR controller, do nothing. */ + return 0; +} + +static int rpc_spi_bind(struct udevice *parent) +{ + const void *fdt = gd->fdt_blob; + ofnode node; + int ret, off; + + /* + * Check if there are any SPI NOR child nodes, if so, bind as + * this controller will be operated in SPI mode. + */ + dev_for_each_subnode(node, parent) { + off = ofnode_to_offset(node); + + ret = fdt_node_check_compatible(fdt, off, "spi-flash"); + if (!ret) + return 0; + + ret = fdt_node_check_compatible(fdt, off, "jedec,spi-nor"); + if (!ret) + return 0; + } + + return -ENODEV; +} + +static int rpc_spi_probe(struct udevice *dev) +{ + struct rpc_spi_platdata *plat = dev_get_platdata(dev); + struct rpc_spi_priv *priv = dev_get_priv(dev); + + priv->regs = plat->regs; + priv->extr = plat->extr; + + clk_enable(&priv->clk); + + return 0; +} + +static int rpc_spi_ofdata_to_platdata(struct udevice *bus) +{ + struct rpc_spi_platdata *plat = dev_get_platdata(bus); + struct rpc_spi_priv *priv = dev_get_priv(bus); + int ret; + + plat->regs = dev_read_addr_index(bus, 0); + plat->extr = dev_read_addr_index(bus, 1); + + ret = clk_get_by_index(bus, 0, &priv->clk); + if (ret < 0) { + printf("%s: Could not get clock for %s: %d\n", + __func__, bus->name, ret); + return ret; + } + + plat->freq = dev_read_u32_default(bus, "spi-max-freq", 50000000); + + return 0; +} + +static const struct dm_spi_ops rpc_spi_ops = { + .xfer = rpc_spi_xfer, + .set_speed = rpc_spi_set_speed, + .set_mode = rpc_spi_set_mode, +}; + +static const struct udevice_id rpc_spi_ids[] = { + { .compatible = "renesas,rpc-r8a7795" }, + { .compatible = "renesas,rpc-r8a7796" }, + { .compatible = "renesas,rpc-r8a77965" }, + { .compatible = "renesas,rpc-r8a77970" }, + { .compatible = "renesas,rpc-r8a77995" }, + { } +}; + +U_BOOT_DRIVER(rpc_spi) = { + .name = "rpc_spi", + .id = UCLASS_SPI, + .of_match = rpc_spi_ids, + .ops = &rpc_spi_ops, + .ofdata_to_platdata = rpc_spi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct rpc_spi_platdata), + .priv_auto_alloc_size = sizeof(struct rpc_spi_priv), + .bind = rpc_spi_bind, + .probe = rpc_spi_probe, +}; diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c index 75999c812d..5075be3cd1 100644 --- a/drivers/spi/sh_qspi.c +++ b/drivers/spi/sh_qspi.c @@ -11,6 +11,7 @@ #include <console.h> #include <malloc.h> #include <spi.h> +#include <wait_bit.h> #include <asm/arch/rmobile.h> #include <asm/io.h> @@ -35,33 +36,35 @@ SPCMD_BRDV0 #define SPBFCR_TXRST BIT(7) #define SPBFCR_RXRST BIT(6) +#define SPBFCR_TXTRG 0x30 +#define SPBFCR_RXTRG 0x07 /* SH QSPI register set */ struct sh_qspi_regs { - unsigned char spcr; - unsigned char sslp; - unsigned char sppcr; - unsigned char spsr; - unsigned long spdr; - unsigned char spscr; - unsigned char spssr; - unsigned char spbr; - unsigned char spdcr; - unsigned char spckd; - unsigned char sslnd; - unsigned char spnd; - unsigned char dummy0; - unsigned short spcmd0; - unsigned short spcmd1; - unsigned short spcmd2; - unsigned short spcmd3; - unsigned char spbfcr; - unsigned char dummy1; - unsigned short spbdcr; - unsigned long spbmul0; - unsigned long spbmul1; - unsigned long spbmul2; - unsigned long spbmul3; + u8 spcr; + u8 sslp; + u8 sppcr; + u8 spsr; + u32 spdr; + u8 spscr; + u8 spssr; + u8 spbr; + u8 spdcr; + u8 spckd; + u8 sslnd; + u8 spnd; + u8 dummy0; + u16 spcmd0; + u16 spcmd1; + u16 spcmd2; + u16 spcmd3; + u8 spbfcr; + u8 dummy1; + u16 spbdcr; + u32 spbmul0; + u32 spbmul1; + u32 spbmul2; + u32 spbmul3; }; struct sh_qspi_slave { @@ -200,11 +203,11 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { struct sh_qspi_slave *ss = to_sh_qspi(slave); - unsigned long nbyte; - int ret = 0; - unsigned char dtdata = 0, drdata; - unsigned char *tdata = &dtdata, *rdata = &drdata; - unsigned long *spbmul0 = &ss->regs->spbmul0; + u32 nbyte, chunk; + int i, ret = 0; + u8 dtdata = 0, drdata; + u8 *tdata = &dtdata, *rdata = &drdata; + u32 *spbmul0 = &ss->regs->spbmul0; if (dout == NULL && din == NULL) { if (flags & SPI_XFER_END) @@ -230,46 +233,44 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, writel(nbyte, spbmul0); if (dout != NULL) - tdata = (unsigned char *)dout; + tdata = (u8 *)dout; if (din != NULL) rdata = din; while (nbyte > 0) { - while (!(readb(&ss->regs->spsr) & SPSR_SPTEF)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(10); + /* + * Check if there is 32 Byte chunk and if there is, transfer + * it in one burst, otherwise transfer on byte-by-byte basis. + */ + chunk = (nbyte >= 32) ? 32 : 1; + + clrsetbits_8(&ss->regs->spbfcr, SPBFCR_TXTRG | SPBFCR_RXTRG, + chunk == 32 ? SPBFCR_TXTRG | SPBFCR_RXTRG : 0); + + ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPTEF, + true, 1000, true); + if (ret) + return ret; + + for (i = 0; i < chunk; i++) { + writeb(*tdata, &ss->regs->spdr); + if (dout != NULL) + tdata++; } - writeb(*tdata, (unsigned char *)(&ss->regs->spdr)); + ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPRFF, + true, 1000, true); + if (ret) + return ret; - while ((readw(&ss->regs->spbdcr) != SPBDCR_RXBC0)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(1); + for (i = 0; i < chunk; i++) { + *rdata = readb(&ss->regs->spdr); + if (din != NULL) + rdata++; } - while (!(readb(&ss->regs->spsr) & SPSR_SPRFF)) { - if (ctrlc()) { - puts("abort\n"); - return 1; - } - udelay(10); - } - - *rdata = readb((unsigned char *)(&ss->regs->spdr)); - - if (dout != NULL) - tdata++; - if (din != NULL) - rdata++; - - nbyte--; + nbyte -= chunk; } if (flags & SPI_XFER_END) diff --git a/drivers/spi/stm32_qspi.c b/drivers/spi/stm32_qspi.c index ef2b64ec5f..558708a4a7 100644 --- a/drivers/spi/stm32_qspi.c +++ b/drivers/spi/stm32_qspi.c @@ -16,7 +16,6 @@ #include <dm.h> #include <errno.h> #include <asm/arch/stm32.h> -#include <asm/arch/stm32_defs.h> #include <clk.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/sysreset/sysreset_syscon.c b/drivers/sysreset/sysreset_syscon.c index 3abce7f678..22c602a4d2 100644 --- a/drivers/sysreset/sysreset_syscon.c +++ b/drivers/sysreset/sysreset_syscon.c @@ -15,8 +15,6 @@ #include <sysreset.h> #include <syscon.h> -DECLARE_GLOBAL_DATA_PTR; - struct syscon_reboot_priv { struct regmap *regmap; unsigned int offset; @@ -55,10 +53,8 @@ int syscon_reboot_probe(struct udevice *dev) return -ENODEV; } - priv->offset = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), - "offset", 0); - priv->mask = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), - "mask", 0); + priv->offset = dev_read_u32_default(dev, "offset", 0); + priv->mask = dev_read_u32_default(dev, "mask", 0); return 0; } diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 3a1f8311c1..2c96896726 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -119,4 +119,11 @@ config ROCKCHIP_TIMER Select this to enable support for the timer found on Rockchip devices. +config STM32_TIMER + bool "STM32 timer support" + depends on TIMER + help + Select this to enable support for the timer found on + STM32 devices. + endmenu diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index 15e515407e..a6e7832154 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_AG101P_TIMER) += ag101p_timer.o obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o obj-$(CONFIG_ATMEL_PIT_TIMER) += atmel_pit_timer.o +obj-$(CONFIG_STM32_TIMER) += stm32_timer.o diff --git a/drivers/timer/stm32_timer.c b/drivers/timer/stm32_timer.c new file mode 100644 index 0000000000..344e6fba1e --- /dev/null +++ b/drivers/timer/stm32_timer.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Author(s): Patrice Chotard, <patrice.chotard@st.com> for STMicroelectronics. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <fdtdec.h> +#include <timer.h> + +#include <asm/io.h> + +/* Timer control1 register */ +#define CR1_CEN BIT(0) +#define CR1_ARPE BIT(7) + +/* Event Generation Register register */ +#define EGR_UG BIT(0) + +/* Auto reload register for free running config */ +#define GPT_FREE_RUNNING 0xFFFFFFFF + +struct stm32_timer_regs { + u32 cr1; + u32 cr2; + u32 smcr; + u32 dier; + u32 sr; + u32 egr; + u32 ccmr1; + u32 ccmr2; + u32 ccer; + u32 cnt; + u32 psc; + u32 arr; + u32 reserved; + u32 ccr1; + u32 ccr2; + u32 ccr3; + u32 ccr4; + u32 reserved1; + u32 dcr; + u32 dmar; + u32 tim2_5_or; +}; + +struct stm32_timer_priv { + struct stm32_timer_regs *base; +}; + +static int stm32_timer_get_count(struct udevice *dev, u64 *count) +{ + struct stm32_timer_priv *priv = dev_get_priv(dev); + struct stm32_timer_regs *regs = priv->base; + + *count = readl(®s->cnt); + + return 0; +} + +static int stm32_timer_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct stm32_timer_priv *priv = dev_get_priv(dev); + struct stm32_timer_regs *regs; + struct clk clk; + fdt_addr_t addr; + int ret; + u32 rate, psc; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->base = (struct stm32_timer_regs *)addr; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret < 0) + return ret; + + ret = clk_enable(&clk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + + regs = priv->base; + + /* Stop the timer */ + clrbits_le32(®s->cr1, CR1_CEN); + + /* get timer clock */ + rate = clk_get_rate(&clk); + + /* we set timer prescaler to obtain a 1MHz timer counter frequency */ + psc = (rate / CONFIG_SYS_HZ_CLOCK) - 1; + writel(psc, ®s->psc); + + /* Set timer frequency to 1MHz */ + uc_priv->clock_rate = CONFIG_SYS_HZ_CLOCK; + + /* Configure timer for auto-reload */ + setbits_le32(®s->cr1, CR1_ARPE); + + /* load value for auto reload */ + writel(GPT_FREE_RUNNING, ®s->arr); + + /* start timer */ + setbits_le32(®s->cr1, CR1_CEN); + + /* Update generation */ + setbits_le32(®s->egr, EGR_UG); + + return 0; +} + +static const struct timer_ops stm32_timer_ops = { + .get_count = stm32_timer_get_count, +}; + +static const struct udevice_id stm32_timer_ids[] = { + { .compatible = "st,stm32-timer" }, + {} +}; + +U_BOOT_DRIVER(stm32_timer) = { + .name = "stm32_timer", + .id = UCLASS_TIMER, + .of_match = stm32_timer_ids, + .priv_auto_alloc_size = sizeof(struct stm32_timer_priv), + .probe = stm32_timer_probe, + .ops = &stm32_timer_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + diff --git a/drivers/usb/eth/Kconfig b/drivers/usb/eth/Kconfig index 496a6d1933..2f6bfa8e71 100644 --- a/drivers/usb/eth/Kconfig +++ b/drivers/usb/eth/Kconfig @@ -23,6 +23,7 @@ config USB_ETHER_ASIX88179 config USB_ETHER_LAN75XX bool "Microchip LAN75XX support" depends on USB_HOST_ETHER + depends on PHYLIB ---help--- Say Y here if you would like to support Microchip LAN75XX Hi-Speed USB 2.0 to 10/100/1000 Gigabit Ethernet controller. @@ -32,6 +33,7 @@ config USB_ETHER_LAN75XX config USB_ETHER_LAN78XX bool "Microchip LAN78XX support" depends on USB_HOST_ETHER + depends on PHYLIB ---help--- Say Y here if you would like to support Microchip LAN78XX USB 3.1 Gen 1 to 10/100/1000 Gigabit Ethernet controller. diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 6825e6b543..26b4d12a09 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -158,6 +158,7 @@ endif # USB_GADGET_DOWNLOAD config USB_ETHER bool "USB Ethernet Gadget" + depends on NET default y if ARCH_SUNXI && USB_MUSB_GADGET help Creates an Ethernet network device through a USB peripheral diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index a80486e91f..386505d42d 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -10,6 +10,7 @@ #include <common.h> #include <console.h> +#include <environment.h> #include <linux/errno.h> #include <linux/netdevice.h> #include <linux/usb/ch9.h> diff --git a/drivers/usb/gadget/f_rockusb.c b/drivers/usb/gadget/f_rockusb.c index d5a10f1904..ad3ae91e6d 100644 --- a/drivers/usb/gadget/f_rockusb.c +++ b/drivers/usb/gadget/f_rockusb.c @@ -552,7 +552,6 @@ static void cb_reboot(struct usb_ep *ep, struct usb_request *req) sizeof(struct fsg_bulk_cb_wrap)); struct f_rockusb *f_rkusb = get_rkusb(); - f_rkusb->reboot_flag = 0; memcpy((char *)cbw, req->buf, USB_BULK_CB_WRAP_LEN); f_rkusb->reboot_flag = cbw->CDB[1]; rockusb_func->in_req->complete = compl_do_reset; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 90b2f78ec7..6caa61552a 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -21,6 +21,13 @@ config USB_XHCI_DWC3 Say Y or if your system has a Dual Role SuperSpeed USB controller based on the DesignWare USB3 IP Core. +config USB_XHCI_DWC3_OF_SIMPLE + bool "DesignWare USB3 DRD Generic OF Simple Glue Layer" + select MISC + help + Support USB2/3 functionality in simple SoC integrations with + USB controller based on the DesignWare USB3 IP Core. + config USB_XHCI_MVEBU bool "MVEBU USB 3.0 support" default y @@ -245,3 +252,15 @@ config USB_DWC2 Hi-Speed (480 Mbps), Full-Speed (12 Mbps), and Low-Speed (1.5 Mbps) operation is compliant to the controller Supplement. If you want to enable this controller in host mode, say Y. + +if USB_DWC2 +config USB_DWC2_BUFFER_SIZE + int "Data buffer size in kB" + default 64 + ---help--- + By default 64 kB buffer is used but if amount of RAM avaialble on + the target is not enough to accommodate allocation of buffer of + that size it is possible to shrink it. Smaller sizes should be fine + because larger transactions could be split in smaller ones. + +endif # USB_DWC2 diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 7f9ba24cfe..abe4f9087f 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -35,7 +35,6 @@ obj-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o obj-$(CONFIG_USB_EHCI_MX6) += ehci-mx6.o obj-$(CONFIG_USB_EHCI_MX7) += ehci-mx6.o obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o -obj-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o obj-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o @@ -50,6 +49,7 @@ obj-$(CONFIG_USB_EHCI_ZYNQ) += ehci-zynq.o # xhci obj-$(CONFIG_USB_XHCI_HCD) += xhci.o xhci-mem.o xhci-ring.o obj-$(CONFIG_USB_XHCI_DWC3) += xhci-dwc3.o +obj-$(CONFIG_USB_XHCI_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_XHCI_ROCKCHIP) += xhci-rockchip.o obj-$(CONFIG_USB_XHCI_ZYNQMP) += xhci-zynqmp.o obj-$(CONFIG_USB_XHCI_KEYSTONE) += xhci-keystone.o diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 0efe645044..4862ab0e7d 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -25,7 +25,7 @@ DECLARE_GLOBAL_DATA_PTR; #define DWC2_HC_CHANNEL 0 #define DWC2_STATUS_BUF_SIZE 64 -#define DWC2_DATA_BUF_SIZE (64 * 1024) +#define DWC2_DATA_BUF_SIZE (CONFIG_USB_DWC2_BUFFER_SIZE * 1024) #define MAX_DEVICE 16 #define MAX_ENDPOINT 16 @@ -34,6 +34,9 @@ struct dwc2_priv { #ifdef CONFIG_DM_USB uint8_t aligned_buffer[DWC2_DATA_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); uint8_t status_buffer[DWC2_STATUS_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); +#ifdef CONFIG_DM_REGULATOR + struct udevice *vbus_supply; +#endif #else uint8_t *aligned_buffer; uint8_t *status_buffer; @@ -111,7 +114,7 @@ static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num) ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, false, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info(dev, "%s: Timeout!\n", __func__); /* Wait for 3 PHY Clocks */ udelay(1); @@ -130,7 +133,7 @@ static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, false, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info(dev, "%s: Timeout!\n", __func__); /* Wait for 3 PHY Clocks */ udelay(1); @@ -148,14 +151,14 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, true, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info(dev, "%s: Timeout!\n", __func__); /* Core Soft Reset */ writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_CSFTRST, false, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info(dev, "%s: Timeout!\n", __func__); /* * Wait for core to come out of reset. @@ -168,28 +171,52 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) #if defined(CONFIG_DM_USB) && defined(CONFIG_DM_REGULATOR) static int dwc_vbus_supply_init(struct udevice *dev) { - struct udevice *vbus_supply; + struct dwc2_priv *priv = dev_get_priv(dev); int ret; - ret = device_get_supply_regulator(dev, "vbus-supply", &vbus_supply); + ret = device_get_supply_regulator(dev, "vbus-supply", + &priv->vbus_supply); if (ret) { debug("%s: No vbus supply\n", dev->name); return 0; } - ret = regulator_set_enable(vbus_supply, true); + ret = regulator_set_enable(priv->vbus_supply, true); if (ret) { - pr_err("Error enabling vbus supply\n"); + dev_err(dev, "Error enabling vbus supply\n"); return ret; } return 0; } + +static int dwc_vbus_supply_exit(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->vbus_supply) { + ret = regulator_set_enable(priv->vbus_supply, false); + if (ret) { + dev_err(dev, "Error disabling vbus supply\n"); + return ret; + } + } + + return 0; +} #else static int dwc_vbus_supply_init(struct udevice *dev) { return 0; } + +#if defined(CONFIG_DM_USB) +static int dwc_vbus_supply_exit(struct udevice *dev) +{ + return 0; +} +#endif #endif /* @@ -270,7 +297,7 @@ static void dwc_otg_core_host_init(struct udevice *dev, ret = wait_for_bit_le32(®s->hc_regs[i].hcchar, DWC2_HCCHAR_CHEN, false, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info("%s: Timeout!\n", __func__); } /* Turn on the vbus power. */ @@ -784,7 +811,7 @@ int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle) uint32_t hcint, hctsiz; ret = wait_for_bit_le32(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true, - 1000, false); + 2000, false); if (ret) return ret; @@ -1091,7 +1118,7 @@ int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); for (;;) { if (get_timer(0) > timeout) { - printf("Timeout poll on interrupt endpoint\n"); + dev_err(dev, "Timeout poll on interrupt endpoint\n"); return -ETIMEDOUT; } ret = _submit_bulk_msg(priv, dev, pipe, buffer, len); @@ -1107,11 +1134,13 @@ static int dwc2_init_common(struct udevice *dev, struct dwc2_priv *priv) int i, j; snpsid = readl(®s->gsnpsid); - printf("Core Release: %x.%03x\n", snpsid >> 12 & 0xf, snpsid & 0xfff); + dev_info(dev, "Core Release: %x.%03x\n", + snpsid >> 12 & 0xf, snpsid & 0xfff); if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx && (snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_3xx) { - printf("SNPSID invalid (not DWC2 OTG device): %08x\n", snpsid); + dev_info(dev, "SNPSID invalid (not DWC2 OTG device): %08x\n", + snpsid); return -ENODEV; } @@ -1269,6 +1298,11 @@ static int dwc2_usb_probe(struct udevice *dev) static int dwc2_usb_remove(struct udevice *dev) { struct dwc2_priv *priv = dev_get_priv(dev); + int ret; + + ret = dwc_vbus_supply_exit(dev); + if (ret) + return ret; dwc2_uninit_common(priv->regs); diff --git a/drivers/usb/host/dwc3-of-simple.c b/drivers/usb/host/dwc3-of-simple.c new file mode 100644 index 0000000000..54a5f60b52 --- /dev/null +++ b/drivers/usb/host/dwc3-of-simple.c @@ -0,0 +1,109 @@ +/* + * dwc3-of-simple.c - OF glue layer for simple integrations + * + * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi <balbi@ti.com> + * + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstron@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <reset.h> +#include <clk.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dwc3_of_simple { + struct clk_bulk clks; + struct reset_ctl_bulk resets; +}; + +static int dwc3_of_simple_reset_init(struct udevice *dev, + struct dwc3_of_simple *simple) +{ + int ret; + + ret = reset_get_bulk(dev, &simple->resets); + if (ret == -ENOTSUPP) + return 0; + else if (ret) + return ret; + + ret = reset_deassert_bulk(&simple->resets); + if (ret) { + reset_release_bulk(&simple->resets); + return ret; + } + + return 0; +} + +static int dwc3_of_simple_clk_init(struct udevice *dev, + struct dwc3_of_simple *simple) +{ + int ret; + + ret = clk_get_bulk(dev, &simple->clks); + if (ret == -ENOTSUPP) + return 0; + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable_bulk(&simple->clks); + if (ret) { + clk_release_bulk(&simple->clks); + return ret; + } +#endif + + return 0; +} + +static int dwc3_of_simple_probe(struct udevice *dev) +{ + struct dwc3_of_simple *simple = dev_get_platdata(dev); + int ret; + + ret = dwc3_of_simple_clk_init(dev, simple); + if (ret) + return ret; + + ret = dwc3_of_simple_reset_init(dev, simple); + if (ret) + return ret; + + return 0; +} + +static int dwc3_of_simple_remove(struct udevice *dev) +{ + struct dwc3_of_simple *simple = dev_get_platdata(dev); + + reset_release_bulk(&simple->resets); + + clk_release_bulk(&simple->clks); + + return dm_scan_fdt_dev(dev); +} + +static const struct udevice_id dwc3_of_simple_ids[] = { + { .compatible = "amlogic,meson-gxl-dwc3" }, + { } +}; + +U_BOOT_DRIVER(dwc3_of_simple) = { + .name = "dwc3-of-simple", + .id = UCLASS_SIMPLE_BUS, + .of_match = dwc3_of_simple_ids, + .probe = dwc3_of_simple_probe, + .remove = dwc3_of_simple_remove, + .platdata_auto_alloc_size = sizeof(struct dwc3_of_simple), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c index 1cb92c0338..b012d8651f 100644 --- a/drivers/usb/host/ehci-generic.c +++ b/drivers/usb/host/ehci-generic.c @@ -27,6 +27,56 @@ struct generic_ehci { int reset_count; }; +static int ehci_setup_phy(struct udevice *dev, int index) +{ + struct generic_ehci *priv = dev_get_priv(dev); + int ret; + + ret = generic_phy_get_by_index(dev, index, &priv->phy); + if (ret) { + if (ret != -ENOENT) { + dev_err(dev, "failed to get usb phy\n"); + return ret; + } + } else { + ret = generic_phy_init(&priv->phy); + if (ret) { + dev_err(dev, "failed to init usb phy\n"); + return ret; + } + + ret = generic_phy_power_on(&priv->phy); + if (ret) { + dev_err(dev, "failed to power on usb phy\n"); + return generic_phy_exit(&priv->phy); + } + } + + return 0; +} + +static int ehci_shutdown_phy(struct udevice *dev) +{ + struct generic_ehci *priv = dev_get_priv(dev); + int ret = 0; + + if (generic_phy_valid(&priv->phy)) { + ret = generic_phy_power_off(&priv->phy); + if (ret) { + dev_err(dev, "failed to power off usb phy\n"); + return ret; + } + + ret = generic_phy_exit(&priv->phy); + if (ret) { + dev_err(dev, "failed to power off usb phy\n"); + return ret; + } + } + + return 0; +} + static int ehci_usb_probe(struct udevice *dev) { struct generic_ehci *priv = dev_get_priv(dev); @@ -51,7 +101,7 @@ static int ehci_usb_probe(struct udevice *dev) break; err = clk_enable(&priv->clocks[i]); if (err) { - pr_err("failed to enable clock %d\n", i); + dev_err(dev, "failed to enable clock %d\n", i); clk_free(&priv->clocks[i]); goto clk_err; } @@ -59,7 +109,8 @@ static int ehci_usb_probe(struct udevice *dev) } } else { if (clock_nb != -ENOENT) { - pr_err("failed to get clock phandle(%d)\n", clock_nb); + dev_err(dev, "failed to get clock phandle(%d)\n", + clock_nb); return clock_nb; } } @@ -80,7 +131,8 @@ static int ehci_usb_probe(struct udevice *dev) break; if (reset_deassert(&priv->resets[i])) { - pr_err("failed to deassert reset %d\n", i); + dev_err(dev, "failed to deassert reset %d\n", + i); reset_free(&priv->resets[i]); goto reset_err; } @@ -88,25 +140,15 @@ static int ehci_usb_probe(struct udevice *dev) } } else { if (reset_nb != -ENOENT) { - pr_err("failed to get reset phandle(%d)\n", reset_nb); + dev_err(dev, "failed to get reset phandle(%d)\n", + reset_nb); goto clk_err; } } - err = generic_phy_get_by_index(dev, 0, &priv->phy); - if (err) { - if (err != -ENOENT) { - pr_err("failed to get usb phy\n"); - goto reset_err; - } - } else { - - err = generic_phy_init(&priv->phy); - if (err) { - pr_err("failed to init usb phy\n"); - goto reset_err; - } - } + err = ehci_setup_phy(dev, 0); + if (err) + goto reset_err; hccr = map_physmem(dev_read_addr(dev), 0x100, MAP_NOCACHE); hcor = (struct ehci_hcor *)((uintptr_t)hccr + @@ -119,20 +161,18 @@ static int ehci_usb_probe(struct udevice *dev) return 0; phy_err: - if (generic_phy_valid(&priv->phy)) { - ret = generic_phy_exit(&priv->phy); - if (ret) - pr_err("failed to release phy\n"); - } + ret = ehci_shutdown_phy(dev); + if (ret) + dev_err(dev, "failed to shutdown usb phy\n"); reset_err: ret = reset_release_all(priv->resets, priv->reset_count); if (ret) - pr_err("failed to assert all resets\n"); + dev_err(dev, "failed to assert all resets\n"); clk_err: ret = clk_release_all(priv->clocks, priv->clock_count); if (ret) - pr_err("failed to disable all clocks\n"); + dev_err(dev, "failed to disable all clocks\n"); return err; } @@ -146,11 +186,9 @@ static int ehci_usb_remove(struct udevice *dev) if (ret) return ret; - if (generic_phy_valid(&priv->phy)) { - ret = generic_phy_exit(&priv->phy); - if (ret) - return ret; - } + ret = ehci_shutdown_phy(dev); + if (ret) + return ret; ret = reset_release_all(priv->resets, priv->reset_count); if (ret) diff --git a/drivers/usb/host/ehci-ppc4xx.c b/drivers/usb/host/ehci-ppc4xx.c deleted file mode 100644 index 9d23577642..0000000000 --- a/drivers/usb/host/ehci-ppc4xx.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * (C) Copyright 2010, Chris Zhang <chris@seamicro.com> - * - * Author: Chris Zhang <chris@seamicro.com> - * This code is based on ehci freescale driver - * - * SPDX-License-Identifier: GPL-2.0+ - */ -#include <common.h> -#include <usb.h> -#include <asm/io.h> - -#include "ehci.h" - -/* - * Create the appropriate control structures to manage - * a new EHCI host controller. - */ -int ehci_hcd_init(int index, enum usb_init_type init, - struct ehci_hccr **hccr, struct ehci_hcor **hcor) -{ - *hccr = (struct ehci_hccr *)(CONFIG_SYS_PPC4XX_USB_ADDR); - *hcor = (struct ehci_hcor *)((uint32_t) *hccr + - HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); - return 0; -} - -/* - * Destroy the appropriate control structures corresponding - * the the EHCI host controller. - */ -int ehci_hcd_stop(int index) -{ - return 0; -} diff --git a/drivers/usb/host/ohci-generic.c b/drivers/usb/host/ohci-generic.c index bf55a71d66..5bdd7995b9 100644 --- a/drivers/usb/host/ohci-generic.c +++ b/drivers/usb/host/ohci-generic.c @@ -25,6 +25,56 @@ struct generic_ohci { int reset_count; /* number of reset in reset list */ }; +static int ohci_setup_phy(struct udevice *dev, int index) +{ + struct generic_ohci *priv = dev_get_priv(dev); + int ret; + + ret = generic_phy_get_by_index(dev, index, &priv->phy); + if (ret) { + if (ret != -ENOENT) { + dev_err(dev, "failed to get usb phy\n"); + return ret; + } + } else { + ret = generic_phy_init(&priv->phy); + if (ret) { + dev_err(dev, "failed to init usb phy\n"); + return ret; + } + + ret = generic_phy_power_on(&priv->phy); + if (ret) { + dev_err(dev, "failed to power on usb phy\n"); + return generic_phy_exit(&priv->phy); + } + } + + return 0; +} + +static int ohci_shutdown_phy(struct udevice *dev) +{ + struct generic_ohci *priv = dev_get_priv(dev); + int ret = 0; + + if (generic_phy_valid(&priv->phy)) { + ret = generic_phy_power_off(&priv->phy); + if (ret) { + dev_err(dev, "failed to power off usb phy\n"); + return ret; + } + + ret = generic_phy_exit(&priv->phy); + if (ret) { + dev_err(dev, "failed to power off usb phy\n"); + return ret; + } + } + + return 0; +} + static int ohci_usb_probe(struct udevice *dev) { struct ohci_regs *regs = (struct ohci_regs *)devfdt_get_addr(dev); @@ -47,14 +97,14 @@ static int ohci_usb_probe(struct udevice *dev) err = clk_enable(&priv->clocks[i]); if (err) { - pr_err("failed to enable clock %d\n", i); + dev_err(dev, "failed to enable clock %d\n", i); clk_free(&priv->clocks[i]); goto clk_err; } priv->clock_count++; } } else if (clock_nb != -ENOENT) { - pr_err("failed to get clock phandle(%d)\n", clock_nb); + dev_err(dev, "failed to get clock phandle(%d)\n", clock_nb); return clock_nb; } @@ -74,31 +124,20 @@ static int ohci_usb_probe(struct udevice *dev) err = reset_deassert(&priv->resets[i]); if (err) { - pr_err("failed to deassert reset %d\n", i); + dev_err(dev, "failed to deassert reset %d\n", i); reset_free(&priv->resets[i]); goto reset_err; } priv->reset_count++; } } else if (reset_nb != -ENOENT) { - pr_err("failed to get reset phandle(%d)\n", reset_nb); + dev_err(dev, "failed to get reset phandle(%d)\n", reset_nb); goto clk_err; } - err = generic_phy_get_by_index(dev, 0, &priv->phy); - if (err) { - if (err != -ENOENT) { - pr_err("failed to get usb phy\n"); - goto reset_err; - } - } else { - - err = generic_phy_init(&priv->phy); - if (err) { - pr_err("failed to init usb phy\n"); - goto reset_err; - } - } + err = ohci_setup_phy(dev, 0); + if (err) + goto reset_err; err = ohci_register(dev, regs); if (err) @@ -107,20 +146,18 @@ static int ohci_usb_probe(struct udevice *dev) return 0; phy_err: - if (generic_phy_valid(&priv->phy)) { - ret = generic_phy_exit(&priv->phy); - if (ret) - pr_err("failed to release phy\n"); - } + ret = ohci_shutdown_phy(dev); + if (ret) + dev_err(dev, "failed to shutdown usb phy\n"); reset_err: ret = reset_release_all(priv->resets, priv->reset_count); if (ret) - pr_err("failed to assert all resets\n"); + dev_err(dev, "failed to assert all resets\n"); clk_err: ret = clk_release_all(priv->clocks, priv->clock_count); if (ret) - pr_err("failed to disable all clocks\n"); + dev_err(dev, "failed to disable all clocks\n"); return err; } @@ -134,11 +171,9 @@ static int ohci_usb_remove(struct udevice *dev) if (ret) return ret; - if (generic_phy_valid(&priv->phy)) { - ret = generic_phy_exit(&priv->phy); - if (ret) - return ret; - } + ret = ohci_shutdown_phy(dev); + if (ret) + return ret; ret = reset_release_all(priv->resets, priv->reset_count); if (ret) diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c index 258d1cd00a..c1007350b7 100644 --- a/drivers/usb/host/xhci-dwc3.c +++ b/drivers/usb/host/xhci-dwc3.c @@ -22,7 +22,8 @@ DECLARE_GLOBAL_DATA_PTR; struct xhci_dwc3_platdata { - struct phy usb_phy; + struct phy *usb_phys; + int num_phys; }; void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) @@ -112,9 +113,89 @@ void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val) } #ifdef CONFIG_DM_USB -static int xhci_dwc3_probe(struct udevice *dev) +static int xhci_dwc3_setup_phy(struct udevice *dev, int count) +{ + struct xhci_dwc3_platdata *plat = dev_get_platdata(dev); + int i, ret; + + if (!count) + return 0; + + plat->usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), + GFP_KERNEL); + if (!plat->usb_phys) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = generic_phy_get_by_index(dev, i, &plat->usb_phys[i]); + if (ret && ret != -ENOENT) { + pr_err("Failed to get USB PHY%d for %s\n", + i, dev->name); + return ret; + } + + ++plat->num_phys; + } + + for (i = 0; i < plat->num_phys; i++) { + ret = generic_phy_init(&plat->usb_phys[i]); + if (ret) { + pr_err("Can't init USB PHY%d for %s\n", + i, dev->name); + goto phys_init_err; + } + } + + for (i = 0; i < plat->num_phys; i++) { + ret = generic_phy_power_on(&plat->usb_phys[i]); + if (ret) { + pr_err("Can't power USB PHY%d for %s\n", + i, dev->name); + goto phys_poweron_err; + } + } + + return 0; + + +phys_poweron_err: + for (; i >= 0; i--) + generic_phy_power_off(&plat->usb_phys[i]); + + for (i = 0; i < plat->num_phys; i++) + generic_phy_exit(&plat->usb_phys[i]); + + return ret; + +phys_init_err: + for (; i >= 0; i--) + generic_phy_exit(&plat->usb_phys[i]); + + return ret; +} + +static int xhci_dwc3_shutdown_phy(struct udevice *dev) { struct xhci_dwc3_platdata *plat = dev_get_platdata(dev); + int i, ret; + + for (i = 0; i < plat->num_phys; i++) { + if (!generic_phy_valid(&plat->usb_phys[i])) + continue; + + ret = generic_phy_power_off(&plat->usb_phys[i]); + ret |= generic_phy_exit(&plat->usb_phys[i]); + if (ret) { + pr_err("Can't shutdown USB PHY%d for %s\n", + i, dev->name); + } + } + + return 0; +} + +static int xhci_dwc3_probe(struct udevice *dev) +{ struct xhci_hcor *hcor; struct xhci_hccr *hccr; struct dwc3 *dwc3_reg; @@ -125,19 +206,10 @@ static int xhci_dwc3_probe(struct udevice *dev) hcor = (struct xhci_hcor *)((uintptr_t)hccr + HC_LENGTH(xhci_readl(&(hccr)->cr_capbase))); - ret = generic_phy_get_by_index(dev, 0, &plat->usb_phy); - if (ret) { - if (ret != -ENOENT) { - pr_err("Failed to get USB PHY for %s\n", dev->name); - return ret; - } - } else { - ret = generic_phy_init(&plat->usb_phy); - if (ret) { - pr_err("Can't init USB PHY for %s\n", dev->name); - return ret; - } - } + ret = xhci_dwc3_setup_phy(dev, dev_count_phandle_with_args( + dev, "phys", "#phy-cells")); + if (ret) + return ret; dwc3_reg = (struct dwc3 *)((char *)(hccr) + DWC3_REG_OFFSET); @@ -155,16 +227,7 @@ static int xhci_dwc3_probe(struct udevice *dev) static int xhci_dwc3_remove(struct udevice *dev) { - struct xhci_dwc3_platdata *plat = dev_get_platdata(dev); - int ret; - - if (generic_phy_valid(&plat->usb_phy)) { - ret = generic_phy_exit(&plat->usb_phy); - if (ret) { - pr_err("Can't deinit USB PHY for %s\n", dev->name); - return ret; - } - } + xhci_dwc3_shutdown_phy(dev); return xhci_deregister(dev); } diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2fc0defcd0..45a105db06 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -38,7 +38,6 @@ config BACKLIGHT_GPIO config VIDEO_BPP8 bool "Support 8-bit-per-pixel displays" depends on DM_VIDEO - default n if ARCH_SUNXI default y if DM_VIDEO help Support drawing text and bitmaps onto a 8-bit-per-pixel display. @@ -49,7 +48,6 @@ config VIDEO_BPP8 config VIDEO_BPP16 bool "Support 16-bit-per-pixel displays" depends on DM_VIDEO - default n if ARCH_SUNXI default y if DM_VIDEO help Support drawing text and bitmaps onto a 16-bit-per-pixel display. diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 0b25897062..5b7795dd44 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -768,7 +768,7 @@ static void parse_putc(const char c) break; case '\n': /* next line */ - if (console_col || (!console_col && nl)) + if (console_col || nl) console_newline(1); nl = 1; break; diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 6ec4f89e34..26db73b138 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -853,9 +853,10 @@ static u32 wait_for_event(u32 event) do { ret = lcdc_irq_handler(); udelay(1000); - } while (!(ret & event)); + --timeout; + } while (!(ret & event) && timeout); - if (timeout <= 0) { + if (!(ret & event)) { printf("%s: event %d not hit\n", __func__, event); return -1; } diff --git a/drivers/video/exynos/exynos_dp.c b/drivers/video/exynos/exynos_dp.c index 30e4020686..3a6ef62890 100644 --- a/drivers/video/exynos/exynos_dp.c +++ b/drivers/video/exynos/exynos_dp.c @@ -321,7 +321,7 @@ static unsigned int exynos_dp_link_start(struct exynos_dp *regs, static unsigned int exynos_dp_training_pattern_dis(struct exynos_dp *regs) { - unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned int ret; exynos_dp_set_training_pattern(regs, DP_NONE); @@ -339,7 +339,7 @@ static unsigned int exynos_dp_enable_rx_to_enhanced_mode( struct exynos_dp *regs, unsigned char enable) { unsigned char data; - unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned int ret; ret = exynos_dp_read_byte_from_dpcd(regs, DPCD_LANE_COUNT_SET, &data); @@ -366,7 +366,7 @@ static unsigned int exynos_dp_enable_rx_to_enhanced_mode( static unsigned int exynos_dp_set_enhanced_mode(struct exynos_dp *regs, unsigned char enhance_mode) { - unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned int ret; ret = exynos_dp_enable_rx_to_enhanced_mode(regs, enhance_mode); if (ret != EXYNOS_DP_SUCCESS) { @@ -416,7 +416,7 @@ static int exynos_dp_read_dpcd_lane_stat(struct exynos_dp *regs, static unsigned int exynos_dp_read_dpcd_adj_req(struct exynos_dp *regs, unsigned char lane_num, unsigned char *sw, unsigned char *em) { - unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned int ret; unsigned char buf; unsigned int dpcd_addr; unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4}; @@ -484,7 +484,7 @@ static int exynos_dp_reduce_link_rate(struct exynos_dp *regs, static unsigned int exynos_dp_process_clock_recovery(struct exynos_dp *regs, struct exynos_dp_priv *priv) { - unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned int ret; unsigned char lane_stat; unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, }; unsigned int i; @@ -594,7 +594,7 @@ static unsigned int exynos_dp_process_clock_recovery(struct exynos_dp *regs, static unsigned int exynos_dp_process_equalizer_training( struct exynos_dp *regs, struct exynos_dp_priv *priv) { - unsigned int ret = EXYNOS_DP_SUCCESS; + unsigned int ret; unsigned char lane_stat, adj_req_sw, adj_req_em, i; unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,}; unsigned char interlane_aligned = 0; diff --git a/drivers/video/pwm_backlight.c b/drivers/video/pwm_backlight.c index fbd7bf7838..f40e57bb8e 100644 --- a/drivers/video/pwm_backlight.c +++ b/drivers/video/pwm_backlight.c @@ -32,16 +32,18 @@ static int pwm_backlight_enable(struct udevice *dev) uint duty_cycle; int ret; - plat = dev_get_uclass_platdata(priv->reg); - debug("%s: Enable '%s', regulator '%s'/'%s'\n", __func__, dev->name, - priv->reg->name, plat->name); - ret = regulator_set_enable(priv->reg, true); - if (ret) { - debug("%s: Cannot enable regulator for PWM '%s'\n", __func__, - dev->name); - return ret; + if (priv->reg) { + plat = dev_get_uclass_platdata(priv->reg); + debug("%s: Enable '%s', regulator '%s'/'%s'\n", __func__, + dev->name, priv->reg->name, plat->name); + ret = regulator_set_enable(priv->reg, true); + if (ret) { + debug("%s: Cannot enable regulator for PWM '%s'\n", + __func__, dev->name); + return ret; + } + mdelay(120); } - mdelay(120); duty_cycle = priv->period_ns * (priv->default_level - priv->min_level) / (priv->max_level - priv->min_level + 1); @@ -68,10 +70,8 @@ static int pwm_backlight_ofdata_to_platdata(struct udevice *dev) debug("%s: start\n", __func__); ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, "power-supply", &priv->reg); - if (ret) { + if (ret) debug("%s: Cannot get power supply: ret=%d\n", __func__, ret); - return ret; - } ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable, GPIOD_IS_OUT); if (ret) { diff --git a/drivers/video/rockchip/rk3288_mipi.c b/drivers/video/rockchip/rk3288_mipi.c index 953b47fb8c..a7fa9c5110 100644 --- a/drivers/video/rockchip/rk3288_mipi.c +++ b/drivers/video/rockchip/rk3288_mipi.c @@ -136,7 +136,7 @@ static int rk_mipi_ofdata_to_platdata(struct udevice *dev) struct rk_mipi_priv *priv = dev_get_priv(dev); priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); - if (IS_ERR(priv->grf)) { + if (IS_ERR_OR_NULL(priv->grf)) { debug("%s: Get syscon grf failed (ret=%p)\n", __func__, priv->grf); return -ENXIO; diff --git a/drivers/video/rockchip/rk3399_mipi.c b/drivers/video/rockchip/rk3399_mipi.c index 9ef202bf09..b936fcec9b 100644 --- a/drivers/video/rockchip/rk3399_mipi.c +++ b/drivers/video/rockchip/rk3399_mipi.c @@ -128,7 +128,7 @@ static int rk_mipi_ofdata_to_platdata(struct udevice *dev) struct rk_mipi_priv *priv = dev_get_priv(dev); priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); - if (priv->grf <= 0) { + if (IS_ERR_OR_NULL(priv->grf)) { debug("%s: Get syscon grf failed (ret=%p)\n", __func__, priv->grf); return -ENXIO; diff --git a/drivers/video/stb_truetype.h b/drivers/video/stb_truetype.h index 26e483cf56..5d00bff9fd 100644 --- a/drivers/video/stb_truetype.h +++ b/drivers/video/stb_truetype.h @@ -1993,7 +1993,7 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, STBTT_assert(fabs(area) <= 1.01f); - scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + scanline[x2] += area + sign * (1-(x_bottom-x2)/2) * (sy1-y_crossing); scanline_fill[x2] += sign * (sy1-sy0); } diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c index b417ac260a..e160c77e07 100644 --- a/drivers/video/stm32/stm32_ltdc.c +++ b/drivers/video/stm32/stm32_ltdc.c @@ -1,8 +1,7 @@ /* - * Copyright (C) STMicroelectronics SA 2017 - * - * Authors: Philippe Cornu <philippe.cornu@st.com> - * Yannick Fertre <yannick.fertre@st.com> + * Copyright (C) 2017-2018 STMicroelectronics - All Rights Reserved + * Author(s): Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics. + * Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics. * * SPDX-License-Identifier: GPL-2.0+ */ @@ -11,6 +10,7 @@ #include <clk.h> #include <dm.h> #include <panel.h> +#include <reset.h> #include <video.h> #include <asm/io.h> #include <asm/arch/gpio.h> @@ -138,7 +138,9 @@ struct stm32_ltdc_priv { #define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */ #define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */ +#define BF1_CA 0x400 /* Constant Alpha */ #define BF2_1PAXCA 0x007 /* 1 - (Pixel Alpha x Constant Alpha) */ +#define BF2_1CA 0x005 /* 1 - Constant Alpha */ enum stm32_ltdc_pix_fmt { PF_ARGB8888 = 0, @@ -161,11 +163,17 @@ static u32 stm32_ltdc_get_pixel_format(enum video_log2_bpp l2bpp) pf = PF_RGB565; break; + case VIDEO_BPP32: + pf = PF_ARGB8888; + break; + + case VIDEO_BPP8: + pf = PF_L8; + break; + case VIDEO_BPP1: case VIDEO_BPP2: case VIDEO_BPP4: - case VIDEO_BPP8: - case VIDEO_BPP32: default: debug("%s: warning %dbpp not supported yet, %dbpp instead\n", __func__, VNBITS(l2bpp), VNBITS(VIDEO_BPP16)); @@ -178,6 +186,23 @@ static u32 stm32_ltdc_get_pixel_format(enum video_log2_bpp l2bpp) return (u32)pf; } +static bool has_alpha(u32 fmt) +{ + switch (fmt) { + case PF_ARGB8888: + case PF_ARGB1555: + case PF_ARGB4444: + case PF_AL44: + case PF_AL88: + return true; + case PF_RGB888: + case PF_RGB565: + case PF_L8: + default: + return false; + } +} + static void stm32_ltdc_enable(struct stm32_ltdc_priv *priv) { /* Reload configuration immediately & enable LTDC */ @@ -219,6 +244,8 @@ static void stm32_ltdc_set_mode(struct stm32_ltdc_priv *priv) val = (total_w << 16) | total_h; clrsetbits_le32(regs + LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val); + setbits_le32(regs + LTDC_LIPCR, acc_act_h + 1); + /* Signal polarities */ val = 0; debug("%s: timing->flags 0x%08x\n", __func__, timing->flags); @@ -245,6 +272,7 @@ static void stm32_ltdc_set_layer1(struct stm32_ltdc_priv *priv, ulong fb_addr) u32 line_length; u32 bus_width; u32 val, tmp, bpp; + u32 format; x0 = priv->crop_x; x1 = priv->crop_x + priv->crop_w - 1; @@ -275,15 +303,18 @@ static void stm32_ltdc_set_layer1(struct stm32_ltdc_priv *priv, ulong fb_addr) clrsetbits_le32(regs + LTDC_L1CFBLR, LXCFBLR_CFBLL | LXCFBLR_CFBP, val); /* Pixel format */ - val = stm32_ltdc_get_pixel_format(priv->l2bpp); - clrsetbits_le32(regs + LTDC_L1PFCR, LXPFCR_PF, val); + format = stm32_ltdc_get_pixel_format(priv->l2bpp); + clrsetbits_le32(regs + LTDC_L1PFCR, LXPFCR_PF, format); /* Constant alpha value */ clrsetbits_le32(regs + LTDC_L1CACR, LXCACR_CONSTA, priv->alpha); + /* Specifies the blending factors : with or without pixel alpha */ + /* Manage hw-specific capabilities */ + val = has_alpha(format) ? BF1_PAXCA | BF2_1PAXCA : BF1_CA | BF2_1CA; + /* Blending factors */ - clrsetbits_le32(regs + LTDC_L1BFCR, LXBFCR_BF2 | LXBFCR_BF1, - BF1_PAXCA | BF2_1PAXCA); + clrsetbits_le32(regs + LTDC_L1BFCR, LXBFCR_BF2 | LXBFCR_BF1, val); /* Frame buffer line number */ clrsetbits_le32(regs + LTDC_L1CFBLNR, LXCFBLNR_CFBLN, priv->crop_h); @@ -301,8 +332,9 @@ static int stm32_ltdc_probe(struct udevice *dev) struct video_priv *uc_priv = dev_get_uclass_priv(dev); struct stm32_ltdc_priv *priv = dev_get_priv(dev); struct udevice *panel; - struct clk pclk, pxclk; - int ret; + struct clk pclk; + struct reset_ctl rst; + int rate, ret; priv->regs = (void *)dev_read_addr(dev); if ((fdt_addr_t)priv->regs == FDT_ADDR_T_NONE) { @@ -310,45 +342,60 @@ static int stm32_ltdc_probe(struct udevice *dev) return -EINVAL; } - ret = uclass_first_device(UCLASS_PANEL, &panel); + ret = clk_get_by_index(dev, 0, &pclk); if (ret) { - debug("%s: panel device error %d\n", __func__, ret); + debug("%s: peripheral clock get error %d\n", __func__, ret); return ret; } - ret = panel_enable_backlight(panel); + ret = clk_enable(&pclk); if (ret) { - debug("%s: panel %s enable backlight error %d\n", - __func__, panel->name, ret); + debug("%s: peripheral clock enable error %d\n", + __func__, ret); return ret; } - ret = fdtdec_decode_display_timing(gd->fdt_blob, dev_of_offset(dev), - 0, &priv->timing); + ret = reset_get_by_index(dev, 0, &rst); if (ret) { - debug("%s: decode display timing error %d\n", __func__, ret); - return -EINVAL; + debug("%s: missing ltdc hardware reset\n", __func__); + return -ENODEV; } - ret = clk_get_by_name(dev, "pclk", &pclk); + /* Reset */ + reset_deassert(&rst); + + ret = uclass_first_device(UCLASS_PANEL, &panel); if (ret) { - debug("%s: peripheral clock get error %d\n", __func__, ret); + debug("%s: panel device error %d\n", __func__, ret); return ret; } - ret = clk_enable(&pclk); + ret = panel_enable_backlight(panel); if (ret) { - debug("%s: peripheral clock enable error %d\n", __func__, ret); + debug("%s: panel %s enable backlight error %d\n", + __func__, panel->name, ret); return ret; } - /* Verify pixel clock value if any & inform user accordingly */ - ret = clk_get_by_name(dev, "pxclk", &pxclk); - if (!ret) { - if (clk_get_rate(&pxclk) != priv->timing.pixelclock.typ) - printf("Warning: please adjust ltdc pixel clock\n"); + ret = fdtdec_decode_display_timing(gd->fdt_blob, + dev_of_offset(dev), 0, + &priv->timing); + if (ret) { + debug("%s: decode display timing error %d\n", + __func__, ret); + return -EINVAL; + } + + rate = clk_set_rate(&pclk, priv->timing.pixelclock.typ); + if (rate < 0) { + debug("%s: fail to set pixel clock %d hz %d hz\n", + __func__, priv->timing.pixelclock.typ, rate); + return rate; } + debug("%s: Set pixel clock req %d hz get %d hz\n", __func__, + priv->timing.pixelclock.typ, rate); + /* TODO Below parameters are hard-coded for the moment... */ priv->l2bpp = VIDEO_BPP16; priv->bg_col_argb = 0xFFFFFFFF; /* white no transparency */ @@ -397,10 +444,10 @@ static const struct udevice_id stm32_ltdc_ids[] = { }; U_BOOT_DRIVER(stm32_ltdc) = { - .name = "stm32_ltdc", - .id = UCLASS_VIDEO, - .of_match = stm32_ltdc_ids, - .probe = stm32_ltdc_probe, - .bind = stm32_ltdc_bind, + .name = "stm32_display", + .id = UCLASS_VIDEO, + .of_match = stm32_ltdc_ids, + .probe = stm32_ltdc_probe, + .bind = stm32_ltdc_bind, .priv_auto_alloc_size = sizeof(struct stm32_ltdc_priv), }; diff --git a/drivers/video/sunxi/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c index f191ef16c6..4da169fffd 100644 --- a/drivers/video/sunxi/sunxi_display.c +++ b/drivers/video/sunxi/sunxi_display.c @@ -8,6 +8,7 @@ */ #include <common.h> +#include <efi_loader.h> #include <asm/arch/clock.h> #include <asm/arch/display.h> @@ -1207,6 +1208,13 @@ void *video_hw_init(void) gd->bd->bi_dram[0].size - sunxi_display.fb_size; sunxi_engines_init(); +#ifdef CONFIG_EFI_LOADER + efi_add_memory_map(gd->fb_base, + ALIGN(sunxi_display.fb_size, EFI_PAGE_SIZE) >> + EFI_PAGE_SHIFT, + EFI_RESERVED_MEMORY_TYPE, false); +#endif + fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE; sunxi_display.fb_addr = gd->fb_base; if (overscan_offset) { diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c index 5f63c12d6c..5553d629b9 100644 --- a/drivers/video/vidconsole-uclass.c +++ b/drivers/video/vidconsole-uclass.c @@ -13,7 +13,16 @@ #include <dm.h> #include <video.h> #include <video_console.h> -#include <video_font.h> /* Get font data, width and height */ +#include <video_font.h> /* Bitmap font for code page 437 */ + +/* + * Structure to describe a console color + */ +struct vid_rgb { + u32 r; + u32 g; + u32 b; +}; /* By default we scroll by a single line */ #ifndef CONFIG_CONSOLE_SCROLL_LINES @@ -108,38 +117,45 @@ static void vidconsole_newline(struct udevice *dev) video_sync(dev->parent); } -static const struct { - unsigned r; - unsigned g; - unsigned b; -} colors[] = { +static const struct vid_rgb colors[VID_COLOR_COUNT] = { { 0x00, 0x00, 0x00 }, /* black */ - { 0xff, 0x00, 0x00 }, /* red */ - { 0x00, 0xff, 0x00 }, /* green */ + { 0xc0, 0x00, 0x00 }, /* red */ + { 0x00, 0xc0, 0x00 }, /* green */ + { 0xc0, 0x60, 0x00 }, /* brown */ + { 0x00, 0x00, 0xc0 }, /* blue */ + { 0xc0, 0x00, 0xc0 }, /* magenta */ + { 0x00, 0xc0, 0xc0 }, /* cyan */ + { 0xc0, 0xc0, 0xc0 }, /* light gray */ + { 0x80, 0x80, 0x80 }, /* gray */ + { 0xff, 0x00, 0x00 }, /* bright red */ + { 0x00, 0xff, 0x00 }, /* bright green */ { 0xff, 0xff, 0x00 }, /* yellow */ - { 0x00, 0x00, 0xff }, /* blue */ - { 0xff, 0x00, 0xff }, /* magenta */ - { 0x00, 0xff, 0xff }, /* cyan */ + { 0x00, 0x00, 0xff }, /* bright blue */ + { 0xff, 0x00, 0xff }, /* bright magenta */ + { 0x00, 0xff, 0xff }, /* bright cyan */ { 0xff, 0xff, 0xff }, /* white */ }; -static void set_color(struct video_priv *priv, unsigned idx, unsigned *c) +u32 vid_console_color(struct video_priv *priv, unsigned int idx) { switch (priv->bpix) { case VIDEO_BPP16: - *c = ((colors[idx].r >> 3) << 0) | - ((colors[idx].g >> 2) << 5) | - ((colors[idx].b >> 3) << 11); - break; + return ((colors[idx].r >> 3) << 11) | + ((colors[idx].g >> 2) << 5) | + ((colors[idx].b >> 3) << 0); case VIDEO_BPP32: - *c = 0xff000000 | - (colors[idx].r << 0) | - (colors[idx].g << 8) | - (colors[idx].b << 16); - break; + return (colors[idx].r << 16) | + (colors[idx].g << 8) | + (colors[idx].b << 0); default: - /* unsupported, leave current color in place */ - break; + /* + * For unknown bit arrangements just support + * black and white. + */ + if (idx) + return 0xffffff; /* white */ + else + return 0x000000; /* black */ } } @@ -270,18 +286,30 @@ static void vidconsole_escape_char(struct udevice *dev, char ch) s++; switch (val) { + case 0: + /* all attributes off */ + video_set_default_colors(vid_priv); + break; + case 1: + /* bold */ + vid_priv->fg_col_idx |= 8; + vid_priv->colour_fg = vid_console_color( + vid_priv, vid_priv->fg_col_idx); + break; case 30 ... 37: - /* fg color */ - set_color(vid_priv, val - 30, - (unsigned *)&vid_priv->colour_fg); + /* foreground color */ + vid_priv->fg_col_idx &= ~7; + vid_priv->fg_col_idx |= val - 30; + vid_priv->colour_fg = vid_console_color( + vid_priv, vid_priv->fg_col_idx); break; case 40 ... 47: - /* bg color */ - set_color(vid_priv, val - 40, - (unsigned *)&vid_priv->colour_bg); + /* background color */ + vid_priv->colour_bg = vid_console_color( + vid_priv, val - 40); break; default: - /* unknown/unsupported */ + /* ignore unsupported SGR parameter */ break; } } diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index dcaceed42c..b5bb8e0efd 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -91,17 +91,43 @@ void video_clear(struct udevice *dev) { struct video_priv *priv = dev_get_uclass_priv(dev); - if (priv->bpix == VIDEO_BPP32) { + switch (priv->bpix) { + case VIDEO_BPP16: { + u16 *ppix = priv->fb; + u16 *end = priv->fb + priv->fb_size; + + while (ppix < end) + *ppix++ = priv->colour_bg; + break; + } + case VIDEO_BPP32: { u32 *ppix = priv->fb; u32 *end = priv->fb + priv->fb_size; while (ppix < end) *ppix++ = priv->colour_bg; - } else { + break; + } + default: memset(priv->fb, priv->colour_bg, priv->fb_size); + break; } } +void video_set_default_colors(struct video_priv *priv) +{ +#ifdef CONFIG_SYS_WHITE_ON_BLACK + /* White is used when switching to bold, use light gray here */ + priv->fg_col_idx = VID_LIGHT_GRAY; + priv->colour_fg = vid_console_color(priv, VID_LIGHT_GRAY); + priv->colour_bg = vid_console_color(priv, VID_BLACK); +#else + priv->fg_col_idx = VID_BLACK; + priv->colour_fg = vid_console_color(priv, VID_BLACK); + priv->colour_bg = vid_console_color(priv, VID_WHITE); +#endif +} + /* Flush video activity to the caches */ void video_sync(struct udevice *vid) { @@ -191,12 +217,8 @@ static int video_post_probe(struct udevice *dev) priv->line_length = priv->xsize * VNBYTES(priv->bpix); priv->fb_size = priv->line_length * priv->ysize; - /* Set up colours - we could in future support other colours */ -#ifdef CONFIG_SYS_WHITE_ON_BLACK - priv->colour_fg = 0xffffff; -#else - priv->colour_bg = 0xffffff; -#endif + /* Set up colors */ + video_set_default_colors(priv); if (!CONFIG_IS_ENABLED(NO_FB_CLEAR)) video_clear(dev); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index fc46b6774d..dca2c901ac 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1,5 +1,13 @@ menu "Watchdog Timer Support" +config WATCHDOG + bool "Enable U-Boot watchdog reset" + help + This option enables U-Boot watchdog support where U-Boot is using + watchdog_reset function to service watchdog device in U-Boot. Enable + this option if you want to service enabled watchdog by U-Boot. Disable + this option if you want U-Boot to start watchdog but never service it. + config HW_WATCHDOG bool @@ -48,9 +56,9 @@ config WDT_SANDBOX bool "Enable Watchdog Timer support for Sandbox" depends on SANDBOX && WDT help - Enable Watchdog Timer support in Sandbox. This is a dummy device that - can be probed and supports all of the methods of WDT, but does not - really do anything. + Enable Watchdog Timer support in Sandbox. This is a dummy device that + can be probed and supports all of the methods of WDT, but does not + really do anything. config WDT_ASPEED bool "Aspeed ast2400/ast2500 watchdog timer support" @@ -78,4 +86,12 @@ config WDT_ORION Select this to enable Orion watchdog timer, which can be found on some Marvell Armada chips. +config WDT_CDNS + bool "Cadence watchdog timer support" + depends on WDT + imply WATCHDOG + help + Select this to enable Cadence watchdog timer, which can be found on some + Xilinx Microzed Platform. + endmenu diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index ab6a6b79e1..4fee6dbd1f 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -22,3 +22,5 @@ obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o obj-$(CONFIG_WDT_BCM6345) += bcm6345_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o obj-$(CONFIG_WDT_ORION) += orion_wdt.o +obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o +obj-$(CONFIG_MPC8xx_WATCHDOG) += mpc8xx_wdt.o diff --git a/drivers/watchdog/cdns_wdt.c b/drivers/watchdog/cdns_wdt.c new file mode 100644 index 0000000000..71733cf8ba --- /dev/null +++ b/drivers/watchdog/cdns_wdt.c @@ -0,0 +1,276 @@ +/* + * Cadence WDT driver - Used by Xilinx Zynq + * Reference: Linux kernel Cadence watchdog driver. + * + * Author(s): Shreenidhi Shedi <yesshedi@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <wdt.h> +#include <clk.h> +#include <linux/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct cdns_regs { + u32 zmr; /* WD Zero mode register, offset - 0x0 */ + u32 ccr; /* Counter Control Register offset - 0x4 */ + u32 restart; /* Restart key register, offset - 0x8 */ + u32 status; /* Status Register, offset - 0xC */ +}; + +struct cdns_wdt_priv { + bool rst; + u32 timeout; + void __iomem *reg; + struct cdns_regs *regs; +}; + +#define CDNS_WDT_DEFAULT_TIMEOUT 10 + +/* Supports 1 - 516 sec */ +#define CDNS_WDT_MIN_TIMEOUT 1 +#define CDNS_WDT_MAX_TIMEOUT 516 + +/* Restart key */ +#define CDNS_WDT_RESTART_KEY 0x00001999 + +/* Counter register access key */ +#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 + +/* Counter value divisor */ +#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 + +/* Clock prescaler value and selection */ +#define CDNS_WDT_PRESCALE_64 64 +#define CDNS_WDT_PRESCALE_512 512 +#define CDNS_WDT_PRESCALE_4096 4096 +#define CDNS_WDT_PRESCALE_SELECT_64 1 +#define CDNS_WDT_PRESCALE_SELECT_512 2 +#define CDNS_WDT_PRESCALE_SELECT_4096 3 + +/* Input clock frequency */ +#define CDNS_WDT_CLK_75MHZ 75000000 + +/* Counter maximum value */ +#define CDNS_WDT_COUNTER_MAX 0xFFF + +/********************* Register Map **********************************/ + +/* + * Zero Mode Register - This register controls how the time out is indicated + * and also contains the access code to allow writes to the register (0xABC). + */ +#define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ +#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ +#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ +#define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ +#define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ + +/* + * Counter Control register - This register controls how fast the timer runs + * and the reset value and also contains the access code to allow writes to + * the register. + */ +#define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ + +/* Write access to Registers */ +static inline void cdns_wdt_writereg(u32 *addr, u32 val) +{ + writel(val, addr); +} + +/** + * cdns_wdt_reset - Reload the watchdog timer (i.e. pat the watchdog). + * + * @dev: Watchdog device + * + * Write the restart key value (0x00001999) to the restart register. + * + * Return: Always 0 + */ +static int cdns_wdt_reset(struct udevice *dev) +{ + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + debug("%s\n", __func__); + + cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY); + + return 0; +} + +/** + * cdns_wdt_start - Enable and start the watchdog. + * + * @dev: Watchdog device + * @timeout: Timeout value + * @flags: Driver flags + * + * The counter value is calculated according to the formula: + * count = (timeout * clock) / prescaler + 1. + * + * The calculated count is divided by 0x1000 to obtain the field value + * to write to counter control register. + * + * Clears the contents of prescaler and counter reset value. Sets the + * prescaler to 4096 and the calculated count and access key + * to write to CCR Register. + * + * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) + * or Interrupt signal(IRQEN) with a specified cycles and the access + * key to write to ZMR Register. + * + * Return: Upon success 0, failure -1. + */ +static int cdns_wdt_start(struct udevice *dev, u64 timeout, ulong flags) +{ + ulong clk_f; + u32 count, prescaler, ctrl_clksel, data = 0; + struct clk clock; + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + if (clk_get_by_index(dev, 0, &clock) < 0) { + dev_err(dev, "failed to get clock\n"); + return -1; + } + + clk_f = clk_get_rate(&clock); + if (IS_ERR_VALUE(clk_f)) { + dev_err(dev, "failed to get rate\n"); + return -1; + } + + debug("%s: CLK_FREQ %ld, timeout %lld\n", __func__, clk_f, timeout); + + if ((timeout < CDNS_WDT_MIN_TIMEOUT) || + (timeout > CDNS_WDT_MAX_TIMEOUT)) { + timeout = priv->timeout; + } + + if (clk_f <= CDNS_WDT_CLK_75MHZ) { + prescaler = CDNS_WDT_PRESCALE_512; + ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; + } else { + prescaler = CDNS_WDT_PRESCALE_4096; + ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; + } + + /* + * Counter value divisor to obtain the value of + * counter reset to be written to control register. + */ + count = (timeout * (clk_f / prescaler)) / + CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; + + if (count > CDNS_WDT_COUNTER_MAX) + count = CDNS_WDT_COUNTER_MAX; + + cdns_wdt_writereg(&priv->regs->zmr, CDNS_WDT_ZMR_ZKEY_VAL); + + count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; + + /* Write counter access key first to be able write to register */ + data = count | CDNS_WDT_REGISTER_ACCESS_KEY | ctrl_clksel; + cdns_wdt_writereg(&priv->regs->ccr, data); + + data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | + CDNS_WDT_ZMR_ZKEY_VAL; + + /* Reset on timeout if specified in device tree. */ + if (priv->rst) { + data |= CDNS_WDT_ZMR_RSTEN_MASK; + data &= ~CDNS_WDT_ZMR_IRQEN_MASK; + } else { + data &= ~CDNS_WDT_ZMR_RSTEN_MASK; + data |= CDNS_WDT_ZMR_IRQEN_MASK; + } + + cdns_wdt_writereg(&priv->regs->zmr, data); + cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY); + + return 0; +} + +/** + * cdns_wdt_stop - Stop the watchdog. + * + * @dev: Watchdog device + * + * Read the contents of the ZMR register, clear the WDEN bit in the register + * and set the access key for successful write. + * + * Return: Always 0 + */ +static int cdns_wdt_stop(struct udevice *dev) +{ + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + cdns_wdt_writereg(&priv->regs->zmr, + CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); + + return 0; +} + +/** + * cdns_wdt_probe - Probe call for the device. + * + * @dev: Handle to the udevice structure. + * + * Return: Always 0. + */ +static int cdns_wdt_probe(struct udevice *dev) +{ + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + debug("%s: Probing wdt%u\n", __func__, dev->seq); + + priv->reg = ioremap((u32)priv->regs, sizeof(struct cdns_regs)); + + cdns_wdt_stop(dev); + + return 0; +} + +static int cdns_wdt_ofdata_to_platdata(struct udevice *dev) +{ + int node = dev_of_offset(dev); + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + priv->regs = devfdt_get_addr_ptr(dev); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->timeout = fdtdec_get_int(gd->fdt_blob, node, "timeout-sec", + CDNS_WDT_DEFAULT_TIMEOUT); + + priv->rst = fdtdec_get_bool(gd->fdt_blob, node, "reset-on-timeout"); + + debug("%s: timeout %d, reset %d\n", __func__, priv->timeout, priv->rst); + + return 0; +} + +static const struct wdt_ops cdns_wdt_ops = { + .start = cdns_wdt_start, + .reset = cdns_wdt_reset, + .stop = cdns_wdt_stop, +}; + +static const struct udevice_id cdns_wdt_ids[] = { + { .compatible = "cdns,wdt-r1p2" }, + {} +}; + +U_BOOT_DRIVER(cdns_wdt) = { + .name = "cdns_wdt", + .id = UCLASS_WDT, + .of_match = cdns_wdt_ids, + .probe = cdns_wdt_probe, + .priv_auto_alloc_size = sizeof(struct cdns_wdt_priv), + .ofdata_to_platdata = cdns_wdt_ofdata_to_platdata, + .ops = &cdns_wdt_ops, +}; diff --git a/drivers/watchdog/mpc8xx_wdt.c b/drivers/watchdog/mpc8xx_wdt.c new file mode 100644 index 0000000000..ded80c4d6a --- /dev/null +++ b/drivers/watchdog/mpc8xx_wdt.c @@ -0,0 +1,21 @@ +/* + * Copyright 2017 CS Systemes d'Information + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <mpc8xx.h> +#include <asm/cpm_8xx.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +void hw_watchdog_reset(void) +{ + immap_t __iomem *immap = (immap_t __iomem *)CONFIG_SYS_IMMR; + + out_be16(&immap->im_siu_conf.sc_swsr, 0x556c); /* write magic1 */ + out_be16(&immap->im_siu_conf.sc_swsr, 0xaa39); /* write magic2 */ +} + diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 7b1f429432..d724c964ed 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -53,16 +53,25 @@ void hw_watchdog_reset(void) { struct wd_timer *wdt = (struct wd_timer *)WDT_BASE; - /* wait for posted write to complete */ - while ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WTGR) - ; + /* + * Somebody just triggered watchdog reset and write to WTGR register + * is in progress. It is resetting right now, no need to trigger it + * again + */ + if ((readl(&wdt->wdtwwps)) & WDT_WWPS_PEND_WTGR) + return; wdt_trgr_pattern = ~wdt_trgr_pattern; writel(wdt_trgr_pattern, &wdt->wdtwtgr); - /* wait for posted write to complete */ - while ((readl(&wdt->wdtwwps) & WDT_WWPS_PEND_WTGR)) - ; + /* + * Don't wait for posted write to complete, i.e. don't check + * WDT_WWPS_PEND_WTGR bit in WWPS register. There is no writes to + * WTGR register outside of this func, and if entering it + * we see WDT_WWPS_PEND_WTGR bit set, it means watchdog reset + * was just triggered. This prevents us from wasting time in busy + * polling of WDT_WWPS_PEND_WTGR bit. + */ } static int omap_wdt_set_timeout(unsigned int timeout) |