diff options
Diffstat (limited to 'drivers')
114 files changed, 10778 insertions, 3513 deletions
diff --git a/drivers/block/ide.c b/drivers/block/ide.c index c99076c6f45..085aa356fee 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -45,10 +45,6 @@ struct blk_desc ide_dev_desc[CONFIG_SYS_IDE_MAXDEVICE]; #define IDE_SPIN_UP_TIME_OUT 5000 /* 5 sec spin-up timeout */ -#ifndef CONFIG_SYS_ATA_PORT_ADDR -#define CONFIG_SYS_ATA_PORT_ADDR(port) (port) -#endif - #ifdef CONFIG_IDE_RESET extern void ide_set_reset(int idereset); @@ -678,8 +674,7 @@ static void ide_ident(struct blk_desc *dev_desc) __weak void ide_outb(int dev, int port, unsigned char val) { debug("ide_outb (dev= %d, port= 0x%x, val= 0x%02x) : @ 0x%08lx\n", - dev, port, val, - (ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port))); + dev, port, val, ATA_CURR_BASE(dev) + port); #if defined(CONFIG_IDE_AHB) if (port) { @@ -690,7 +685,7 @@ __weak void ide_outb(int dev, int port, unsigned char val) outb(val, (ATA_CURR_BASE(dev))); } #else - outb(val, (ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port))); + outb(val, ATA_CURR_BASE(dev) + port); #endif } @@ -701,12 +696,11 @@ __weak unsigned char ide_inb(int dev, int port) #if defined(CONFIG_IDE_AHB) val = ide_read_register(dev, port); #else - val = inb((ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port))); + val = inb(ATA_CURR_BASE(dev) + port); #endif debug("ide_inb (dev= %d, port= 0x%x) : @ 0x%08lx -> 0x%02x\n", - dev, port, - (ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port)), val); + dev, port, ATA_CURR_BASE(dev) + port, val); return val; } diff --git a/drivers/block/sandbox.c b/drivers/block/sandbox.c index 1c2c3b4f886..53925ce9b69 100644 --- a/drivers/block/sandbox.c +++ b/drivers/block/sandbox.c @@ -19,11 +19,11 @@ DECLARE_GLOBAL_DATA_PTR; #ifndef CONFIG_BLK -static struct host_block_dev host_devices[CONFIG_HOST_MAX_DEVICES]; +static struct host_block_dev host_devices[SANDBOX_HOST_MAX_DEVICES]; static struct host_block_dev *find_host_device(int dev) { - if (dev >= 0 && dev < CONFIG_HOST_MAX_DEVICES) + if (dev >= 0 && dev < SANDBOX_HOST_MAX_DEVICES) return &host_devices[dev]; return NULL; @@ -259,7 +259,7 @@ U_BOOT_DRIVER(sandbox_host_blk) = { U_BOOT_LEGACY_BLK(sandbox_host) = { .if_typename = "host", .if_type = IF_TYPE_HOST, - .max_devs = CONFIG_HOST_MAX_DEVICES, + .max_devs = SANDBOX_HOST_MAX_DEVICES, .get_dev = host_get_dev_err, }; #endif diff --git a/drivers/clk/aspeed/clk_ast2600.c b/drivers/clk/aspeed/clk_ast2600.c index 3a92739f5cf..42ca39421cf 100644 --- a/drivers/clk/aspeed/clk_ast2600.c +++ b/drivers/clk/aspeed/clk_ast2600.c @@ -1013,6 +1013,46 @@ static ulong ast2600_enable_usbbhclk(struct ast2600_scu *scu) return 0; } +static ulong ast2600_enable_haceclk(struct ast2600_scu *scu) +{ + uint32_t reset_bit; + uint32_t clkgate_bit; + + /* share the same reset control bit with ACRY */ + reset_bit = BIT(ASPEED_RESET_HACE); + clkgate_bit = SCU_CLKGATE1_HACE; + + /* + * we don't do reset assertion here as HACE + * shares the same reset control with ACRY + */ + writel(clkgate_bit, &scu->clkgate_clr1); + mdelay(20); + writel(reset_bit, &scu->modrst_clr1); + + return 0; +} + +static ulong ast2600_enable_rsaclk(struct ast2600_scu *scu) +{ + uint32_t reset_bit; + uint32_t clkgate_bit; + + /* same reset control bit with HACE */ + reset_bit = BIT(ASPEED_RESET_HACE); + clkgate_bit = SCU_CLKGATE1_ACRY; + + /* + * we don't do reset assertion here as HACE + * shares the same reset control with ACRY + */ + writel(clkgate_bit, &scu->clkgate_clr1); + mdelay(20); + writel(reset_bit, &scu->modrst_clr1); + + return 0; +} + static int ast2600_clk_enable(struct clk *clk) { struct ast2600_clk_priv *priv = dev_get_priv(clk->dev); @@ -1051,6 +1091,12 @@ static int ast2600_clk_enable(struct clk *clk) case ASPEED_CLK_GATE_USBPORT2CLK: ast2600_enable_usbbhclk(priv->scu); break; + case ASPEED_CLK_GATE_YCLK: + ast2600_enable_haceclk(priv->scu); + break; + case ASPEED_CLK_GATE_RSACLK: + ast2600_enable_rsaclk(priv->scu); + break; default: pr_err("can't enable clk\n"); return -ENOENT; diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c index 93a4819501c..9a0a6f66434 100644 --- a/drivers/clk/clk_scmi.c +++ b/drivers/clk/clk_scmi.c @@ -2,6 +2,9 @@ /* * Copyright (C) 2019-2020 Linaro Limited */ + +#define LOG_CATEGORY UCLASS_CLK + #include <common.h> #include <clk-uclass.h> #include <dm.h> diff --git a/drivers/clk/mpc83xx_clk.h b/drivers/clk/mpc83xx_clk.h index 8a31a4c868f..c06a51ecd43 100644 --- a/drivers/clk/mpc83xx_clk.h +++ b/drivers/clk/mpc83xx_clk.h @@ -317,7 +317,7 @@ static inline u32 get_pci_sync_in(immap_t *im) u8 clkin_div; clkin_div = (get_spmr(im) & SPMR_CKID) >> SPMR_CKID_SHIFT; - return CONFIG_SYS_CLK_FREQ / (1 + clkin_div); + return get_board_sys_clk() / (1 + clkin_div); } /** @@ -331,7 +331,7 @@ static inline u32 get_csb_clk(immap_t *im) u8 spmf; spmf = (get_spmr(im) & SPMR_SPMF) >> SPMR_SPMF_SHIFT; - return CONFIG_SYS_CLK_FREQ * spmf; + return get_board_sys_clk() * spmf; } /** diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 11d3959d20f..69c50da44a9 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -95,6 +95,9 @@ int device_unbind(struct udevice *dev) if (ret) return log_msg_ret("child unbind", ret); + ret = uclass_pre_unbind_device(dev); + if (ret) + return log_msg_ret("uc", ret); if (dev_get_flags(dev) & DM_FLAG_ALLOC_PDATA) { free(dev_get_plat(dev)); dev_set_plat(dev, NULL); @@ -142,10 +145,8 @@ void device_free(struct udevice *dev) } if (dev->parent) { size = dev->parent->driver->per_child_auto; - if (!size) { - size = dev->parent->uclass->uc_drv-> - per_child_auto; - } + if (!size) + size = dev->parent->uclass->uc_drv->per_child_auto; if (size) { free(dev_get_parent_priv(dev)); dev_set_parent_priv(dev, NULL); diff --git a/drivers/core/device.c b/drivers/core/device.c index efd07176e37..4873c47d10b 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -533,8 +533,12 @@ int device_probe(struct udevice *dev) * is set just above. However, the PCI bus' probe() method and * associated uclass methods have not yet been called. */ - if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL) - pinctrl_select_state(dev, "default"); + if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL) { + ret = pinctrl_select_state(dev, "default"); + if (ret && ret != -ENOSYS) + log_debug("Device '%s' failed to configure default pinctrl: %d (%s)\n", + dev->name, ret, errno_str(ret)); + } if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent && (device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) && @@ -586,8 +590,12 @@ int device_probe(struct udevice *dev) if (ret) goto fail_uclass; - if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL) - pinctrl_select_state(dev, "default"); + if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL) { + ret = pinctrl_select_state(dev, "default"); + if (ret && ret != -ENOSYS) + log_debug("Device '%s' failed to configure default pinctrl: %d (%s)\n", + dev->name, ret, errno_str(ret)); + } return 0; fail_uclass: @@ -731,6 +739,17 @@ int device_get_child_count(const struct udevice *parent) return count; } +int device_get_decendent_count(const struct udevice *parent) +{ + const struct udevice *dev; + int count = 1; + + list_for_each_entry(dev, &parent->child_head, sibling_node) + count += device_get_decendent_count(dev); + + return count; +} + int device_find_child_by_seq(const struct udevice *parent, int seq, struct udevice **devp) { @@ -902,15 +921,16 @@ int device_find_first_child_by_uclass(const struct udevice *parent, return -ENODEV; } -int device_find_child_by_name(const struct udevice *parent, const char *name, - struct udevice **devp) +int device_find_child_by_namelen(const struct udevice *parent, const char *name, + int len, struct udevice **devp) { struct udevice *dev; *devp = NULL; list_for_each_entry(dev, &parent->child_head, sibling_node) { - if (!strcmp(dev->name, name)) { + if (!strncmp(dev->name, name, len) && + strlen(dev->name) == len) { *devp = dev; return 0; } @@ -919,6 +939,12 @@ int device_find_child_by_name(const struct udevice *parent, const char *name, return -ENODEV; } +int device_find_child_by_name(const struct udevice *parent, const char *name, + struct udevice **devp) +{ + return device_find_child_by_namelen(parent, name, strlen(name), devp); +} + int device_first_child_err(struct udevice *parent, struct udevice **devp) { struct udevice *dev; diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 5d4f2ea0e3a..d2e9dc5719b 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -58,7 +58,7 @@ static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) const int n_ents = ll_entry_count(struct driver_info, driver_info); bool missing_parent = false; int result = 0; - uint idx; + int idx; /* * Do one iteration through the driver_info records. For of-platdata, diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c index 9960e6b310b..3707143ae66 100644 --- a/drivers/core/of_access.c +++ b/drivers/core/of_access.c @@ -581,7 +581,8 @@ int of_property_match_string(const struct device_node *np, const char *propname, * @propname: name of the property to be searched. * @out_strs: output array of string pointers. * @sz: number of array elements to read. - * @skip: Number of strings to skip over at beginning of list. + * @skip: Number of strings to skip over at beginning of list (cannot be + * negative) * * Don't call this function directly. It is a utility helper for the * of_property_read_string*() family of functions. diff --git a/drivers/core/of_extra.c b/drivers/core/of_extra.c index 632a1c2210e..59ce9174ad0 100644 --- a/drivers/core/of_extra.c +++ b/drivers/core/of_extra.c @@ -155,3 +155,15 @@ bool ofnode_phy_is_fixed_link(ofnode eth_node, ofnode *phy_node) return true; } + +bool ofnode_eth_uses_inband_aneg(ofnode eth_node) +{ + bool inband_aneg = false; + const char *managed; + + managed = ofnode_read_string(eth_node, "managed"); + if (managed && !strcmp(managed, "in-band-status")) + inband_aneg = true; + + return inband_aneg; +} diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 08705ef8d99..709bea272a6 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -456,6 +456,32 @@ int ofnode_read_string_count(ofnode node, const char *property) } } +int ofnode_read_string_list(ofnode node, const char *property, + const char ***listp) +{ + const char **prop; + int count; + int i; + + *listp = NULL; + count = ofnode_read_string_count(node, property); + if (count < 0) + return count; + if (!count) + return 0; + + prop = calloc(count + 1, sizeof(char *)); + if (!prop) + return -ENOMEM; + + for (i = 0; i < count; i++) + ofnode_read_string_index(node, property, i, &prop[i]); + prop[count] = NULL; + *listp = prop; + + return count; +} + static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in, struct ofnode_phandle_args *out) { diff --git a/drivers/core/read.c b/drivers/core/read.c index 4307ca45799..31f9e78a062 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -205,6 +205,12 @@ int dev_read_string_count(const struct udevice *dev, const char *propname) return ofnode_read_string_count(dev_ofnode(dev), propname); } +int dev_read_string_list(const struct udevice *dev, const char *propname, + const char ***listp) +{ + return ofnode_read_string_list(dev_ofnode(dev), propname, listp); +} + int dev_read_phandle_with_args(const struct udevice *dev, const char *list_name, const char *cells_name, int cell_count, int index, struct ofnode_phandle_args *out_args) diff --git a/drivers/core/root.c b/drivers/core/root.c index 26b8195faa3..815173f86eb 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -26,6 +26,7 @@ #include <dm/read.h> #include <dm/root.h> #include <dm/uclass.h> +#include <dm/uclass-internal.h> #include <dm/util.h> #include <linux/list.h> @@ -407,6 +408,12 @@ int dm_init_and_scan(bool pre_reloc_only) return 0; } +void dm_get_stats(int *device_countp, int *uclass_countp) +{ + *device_countp = device_get_decendent_count(gd->dm_root); + *uclass_countp = uclass_get_count(); +} + #ifdef CONFIG_ACPIGEN static int root_acpi_get_name(const struct udevice *dev, char *out_name) { diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index c5a50952fd0..336ea8d243d 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -180,20 +180,25 @@ void uclass_set_priv(struct uclass *uc, void *priv) uc->priv_ = priv; } -enum uclass_id uclass_get_by_name(const char *name) +enum uclass_id uclass_get_by_name_len(const char *name, int len) { int i; for (i = 0; i < UCLASS_COUNT; i++) { struct uclass_driver *uc_drv = lists_uclass_lookup(i); - if (uc_drv && !strcmp(uc_drv->name, name)) + if (uc_drv && !strncmp(uc_drv->name, name, len)) return i; } return UCLASS_INVALID; } +enum uclass_id uclass_get_by_name(const char *name) +{ + return uclass_get_by_name_len(name, strlen(name)); +} + int dev_get_uclass_index(struct udevice *dev, struct uclass **ucp) { struct udevice *iter; @@ -638,6 +643,19 @@ int uclass_next_device_check(struct udevice **devp) return device_probe(*devp); } +int uclass_get_count(void) +{ + const struct uclass *uc; + int count = 0; + + if (gd->dm_root) { + list_for_each_entry(uc, gd->uclass_root, sibling_node) + count++; + } + + return count; +} + int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data, struct udevice **devp) { @@ -682,7 +700,7 @@ err: } #if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE) -int uclass_unbind_device(struct udevice *dev) +int uclass_pre_unbind_device(struct udevice *dev) { struct uclass *uc; int ret; @@ -694,7 +712,13 @@ int uclass_unbind_device(struct udevice *dev) return ret; } + return 0; +} + +int uclass_unbind_device(struct udevice *dev) +{ list_del(&dev->uclass_node); + return 0; } #endif @@ -783,6 +807,18 @@ int uclass_probe_all(enum uclass_id id) return 0; } +int uclass_id_count(enum uclass_id id) +{ + struct udevice *dev; + struct uclass *uc; + int count = 0; + + uclass_id_foreach_dev(id, dev, uc) + count++; + + return count; +} + UCLASS_DRIVER(nop) = { .id = UCLASS_NOP, .name = "nop", diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 0082177c21f..675081ecd37 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -4,4 +4,6 @@ source drivers/crypto/hash/Kconfig source drivers/crypto/fsl/Kconfig +source drivers/crypto/aspeed/Kconfig + endmenu diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index e8bae43e3f0..6b762565a1f 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.o obj-y += rsa_mod_exp/ obj-y += fsl/ obj-y += hash/ +obj-y += aspeed/ diff --git a/drivers/crypto/aspeed/Kconfig b/drivers/crypto/aspeed/Kconfig new file mode 100644 index 00000000000..9bf317177aa --- /dev/null +++ b/drivers/crypto/aspeed/Kconfig @@ -0,0 +1,20 @@ +config ASPEED_HACE + bool "ASPEED Hash and Crypto Engine" + depends on DM_HASH + help + Select this option to enable a driver for using the SHA engine in + the ASPEED BMC SoCs. + + Enabling this allows the use of SHA operations in hardware without + requiring the SHA software implementations. It also improves performance + and saves code size. + +config ASPEED_ACRY + bool "ASPEED RSA and ECC Engine" + depends on ASPEED_AST2600 + help + Select this option to enable a driver for using the RSA/ECC engine in + the ASPEED BMC SoCs. + + Enabling this allows the use of RSA/ECC operations in hardware without requiring the + software implementations. It also improves performance and saves code size. diff --git a/drivers/crypto/aspeed/Makefile b/drivers/crypto/aspeed/Makefile new file mode 100644 index 00000000000..58b55fc46e4 --- /dev/null +++ b/drivers/crypto/aspeed/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ASPEED_HACE) += aspeed_hace.o +obj-$(CONFIG_ASPEED_ACRY) += aspeed_acry.o diff --git a/drivers/crypto/aspeed/aspeed_acry.c b/drivers/crypto/aspeed/aspeed_acry.c new file mode 100644 index 00000000000..c28cdf374b6 --- /dev/null +++ b/drivers/crypto/aspeed/aspeed_acry.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2021 ASPEED Technology Inc. + */ +#include <config.h> +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <asm/types.h> +#include <asm/io.h> +#include <dm/device.h> +#include <dm/fdtaddr.h> +#include <linux/delay.h> +#include <u-boot/rsa-mod-exp.h> + +/* ACRY register offsets */ +#define ACRY_CTRL1 0x00 +#define ACRY_CTRL1_RSA_DMA BIT(1) +#define ACRY_CTRL1_RSA_START BIT(0) +#define ACRY_CTRL2 0x44 +#define ACRY_CTRL3 0x48 +#define ACRY_CTRL3_SRAM_AHB_ACCESS BIT(8) +#define ACRY_CTRL3_ECC_RSA_MODE_MASK GENMASK(5, 4) +#define ACRY_CTRL3_ECC_RSA_MODE_SHIFT 4 +#define ACRY_DMA_DRAM_SADDR 0x4c +#define ACRY_DMA_DMEM_TADDR 0x50 +#define ACRY_DMA_DMEM_TADDR_LEN_MASK GENMASK(15, 0) +#define ACRY_DMA_DMEM_TADDR_LEN_SHIFT 0 +#define ACRY_RSA_PARAM 0x58 +#define ACRY_RSA_PARAM_EXP_MASK GENMASK(31, 16) +#define ACRY_RSA_PARAM_EXP_SHIFT 16 +#define ACRY_RSA_PARAM_MOD_MASK GENMASK(15, 0) +#define ACRY_RSA_PARAM_MOD_SHIFT 0 +#define ACRY_RSA_INT_EN 0x3f8 +#define ACRY_RSA_INT_EN_RSA_READY BIT(2) +#define ACRY_RSA_INT_EN_RSA_CMPLT BIT(1) +#define ACRY_RSA_INT_STS 0x3fc +#define ACRY_RSA_INT_STS_RSA_READY BIT(2) +#define ACRY_RSA_INT_STS_RSA_CMPLT BIT(1) + +/* misc. constant */ +#define ACRY_ECC_MODE 2 +#define ACRY_RSA_MODE 3 +#define ACRY_CTX_BUFSZ 0x600 + +struct aspeed_acry { + phys_addr_t base; + phys_addr_t sram_base; /* internal sram */ + struct clk clk; +}; + +static int aspeed_acry_mod_exp(struct udevice *dev, const uint8_t *sig, uint32_t sig_len, + struct key_prop *prop, uint8_t *out) +{ + int i, j; + u8 *ctx; + u8 *ptr; + u32 reg; + struct aspeed_acry *acry = dev_get_priv(dev); + + ctx = memalign(16, ACRY_CTX_BUFSZ); + if (!ctx) + return -ENOMEM; + + memset(ctx, 0, ACRY_CTX_BUFSZ); + + ptr = (u8 *)prop->public_exponent; + for (i = prop->exp_len - 1, j = 0; i >= 0; --i) { + ctx[j] = ptr[i]; + j++; + j = (j % 16) ? j : j + 32; + } + + ptr = (u8 *)prop->modulus; + for (i = (prop->num_bits >> 3) - 1, j = 0; i >= 0; --i) { + ctx[j + 16] = ptr[i]; + j++; + j = (j % 16) ? j : j + 32; + } + + ptr = (u8 *)sig; + for (i = sig_len - 1, j = 0; i >= 0; --i) { + ctx[j + 32] = ptr[i]; + j++; + j = (j % 16) ? j : j + 32; + } + + writel((u32)ctx, acry->base + ACRY_DMA_DRAM_SADDR); + + reg = (((prop->exp_len << 3) << ACRY_RSA_PARAM_EXP_SHIFT) & ACRY_RSA_PARAM_EXP_MASK) | + ((prop->num_bits << ACRY_RSA_PARAM_MOD_SHIFT) & ACRY_RSA_PARAM_MOD_MASK); + writel(reg, acry->base + ACRY_RSA_PARAM); + + reg = (ACRY_CTX_BUFSZ << ACRY_DMA_DMEM_TADDR_LEN_SHIFT) & ACRY_DMA_DMEM_TADDR_LEN_MASK; + writel(reg, acry->base + ACRY_DMA_DMEM_TADDR); + + reg = (ACRY_RSA_MODE << ACRY_CTRL3_ECC_RSA_MODE_SHIFT) & ACRY_CTRL3_ECC_RSA_MODE_MASK; + writel(reg, acry->base + ACRY_CTRL3); + + writel(ACRY_CTRL1_RSA_DMA | ACRY_CTRL1_RSA_START, acry->base + ACRY_CTRL1); + + /* polling RSA status */ + while (1) { + reg = readl(acry->base + ACRY_RSA_INT_STS); + if ((reg & ACRY_RSA_INT_STS_RSA_READY) && (reg & ACRY_RSA_INT_STS_RSA_CMPLT)) { + writel(reg, ACRY_RSA_INT_STS); + break; + } + udelay(20); + } + + /* grant SRAM access permission to CPU */ + writel(0x0, acry->base + ACRY_CTRL1); + writel(ACRY_CTRL3_SRAM_AHB_ACCESS, acry->base + ACRY_CTRL3); + udelay(20); + + for (i = (prop->num_bits / 8) - 1, j = 0; i >= 0; --i) { + out[i] = readb(acry->sram_base + (j + 32)); + j++; + j = (j % 16) ? j : j + 32; + } + + /* return SRAM access permission to ACRY */ + writel(0, acry->base + ACRY_CTRL3); + + free(ctx); + + return 0; +} + +static int aspeed_acry_probe(struct udevice *dev) +{ + struct aspeed_acry *acry = dev_get_priv(dev); + int ret; + + ret = clk_get_by_index(dev, 0, &acry->clk); + if (ret < 0) { + debug("Can't get clock for %s: %d\n", dev->name, ret); + return ret; + } + + ret = clk_enable(&acry->clk); + if (ret) { + debug("Failed to enable acry clock (%d)\n", ret); + return ret; + } + + acry->base = devfdt_get_addr_index(dev, 0); + if (acry->base == FDT_ADDR_T_NONE) { + debug("Failed to get acry base\n"); + return acry->base; + } + + acry->sram_base = devfdt_get_addr_index(dev, 1); + if (acry->sram_base == FDT_ADDR_T_NONE) { + debug("Failed to get acry SRAM base\n"); + return acry->sram_base; + } + + return ret; +} + +static int aspeed_acry_remove(struct udevice *dev) +{ + struct aspeed_acry *acry = dev_get_priv(dev); + + clk_disable(&acry->clk); + + return 0; +} + +static const struct mod_exp_ops aspeed_acry_ops = { + .mod_exp = aspeed_acry_mod_exp, +}; + +static const struct udevice_id aspeed_acry_ids[] = { + { .compatible = "aspeed,ast2600-acry" }, + { } +}; + +U_BOOT_DRIVER(aspeed_acry) = { + .name = "aspeed_acry", + .id = UCLASS_MOD_EXP, + .of_match = aspeed_acry_ids, + .probe = aspeed_acry_probe, + .remove = aspeed_acry_remove, + .priv_auto = sizeof(struct aspeed_acry), + .ops = &aspeed_acry_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/crypto/aspeed/aspeed_hace.c b/drivers/crypto/aspeed/aspeed_hace.c new file mode 100644 index 00000000000..1178cc6a769 --- /dev/null +++ b/drivers/crypto/aspeed/aspeed_hace.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2021 ASPEED Technology Inc. + */ +#include <config.h> +#include <common.h> +#include <dm.h> +#include <clk.h> +#include <log.h> +#include <asm/io.h> +#include <malloc.h> +#include <watchdog.h> +#include <u-boot/hash.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/iopoll.h> + +/* register offsets*/ +#define HACE_STS 0x1C +#define HACE_HASH_DATA_OVF BIT(23) +#define HACE_HASH_INT BIT(9) +#define HACE_HASH_BUSY BIT(0) +#define HACE_HASH_DATA 0x20 +#define HACE_HASH_DIGEST 0x24 +#define HACE_HASH_HMAC_KEY 0x28 +#define HACE_HASH_DATA_LEN 0x2C +#define HACE_HASH_CMD 0x30 +#define HACE_HASH_MODE_ACCUM BIT(8) +#define HACE_HASH_ALGO_SHA1 BIT(5) +#define HACE_HASH_ALGO_SHA256 (BIT(6) | BIT(4)) +#define HACE_HASH_ALGO_SHA384 (BIT(10) | BIT(6) | BIT(5)) +#define HACE_HASH_ALGO_SHA512 (BIT(6) | BIT(5)) +#define HACE_HASH_SHA_BE_EN BIT(3) + +/* buffer size based on SHA-512 need*/ +#define HASH_BLOCK_BUFSZ 128 +#define HASH_DIGEST_BUFSZ 64 + +struct aspeed_hace_ctx { + uint8_t digest[HASH_DIGEST_BUFSZ]; + + uint32_t cmd; + enum HASH_ALGO algo; + + uint32_t blk_size; + uint32_t pad_size; + uint64_t total[2]; + + uint8_t buf[HASH_BLOCK_BUFSZ]; + uint32_t buf_cnt; +} __aligned((8)); + +struct aspeed_hace { + phys_addr_t base; + struct clk clk; +}; + +static const uint32_t iv_sha1[8] = { + 0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210, + 0xf0e1d2c3, 0, 0, 0 +}; + +static const uint32_t iv_sha256[8] = { + 0x67e6096a, 0x85ae67bb, 0x72f36e3c, 0x3af54fa5, + 0x7f520e51, 0x8c68059b, 0xabd9831f, 0x19cde05bUL +}; + +static const uint32_t iv_sha384[16] = { + 0x5d9dbbcb, 0xd89e05c1, 0x2a299a62, 0x07d57c36, + 0x5a015991, 0x17dd7030, 0xd8ec2f15, 0x39590ef7, + 0x67263367, 0x310bc0ff, 0x874ab48e, 0x11155868, + 0x0d2e0cdb, 0xa78ff964, 0x1d48b547, 0xa44ffabeUL +}; + +static const uint32_t iv_sha512[16] = { + 0x67e6096a, 0x08c9bcf3, 0x85ae67bb, 0x3ba7ca84, + 0x72f36e3c, 0x2bf894fe, 0x3af54fa5, 0xf1361d5f, + 0x7f520e51, 0xd182e6ad, 0x8c68059b, 0x1f6c3e2b, + 0xabd9831f, 0x6bbd41fb, 0x19cde05b, 0x79217e13UL +}; + +static int aspeed_hace_wait_completion(uint32_t reg, uint32_t flag, int timeout_us) +{ + uint32_t val; + + return readl_poll_timeout(reg, val, (val & flag) == flag, timeout_us); +} + +static int aspeed_hace_process(struct udevice *dev, void *ctx, const void *ibuf, uint32_t ilen) +{ + struct aspeed_hace *hace = dev_get_priv(dev); + struct aspeed_hace_ctx *hace_ctx = (struct aspeed_hace_ctx *)ctx; + uint32_t sts = readl(hace->base + HACE_STS); + + if (sts & HACE_HASH_BUSY) { + debug("HACE engine busy\n"); + return -EBUSY; + } + + writel(HACE_HASH_INT, hace->base + HACE_STS); + + writel((uint32_t)ibuf, hace->base + HACE_HASH_DATA); + writel((uint32_t)hace_ctx->digest, hace->base + HACE_HASH_DIGEST); + writel((uint32_t)hace_ctx->digest, hace->base + HACE_HASH_HMAC_KEY); + writel(ilen, hace->base + HACE_HASH_DATA_LEN); + writel(hace_ctx->cmd, hace->base + HACE_HASH_CMD); + + return aspeed_hace_wait_completion(hace->base + HACE_STS, + HACE_HASH_INT, + 1000 + (ilen >> 3)); +} + +static int aspeed_hace_init(struct udevice *dev, enum HASH_ALGO algo, void **ctxp) +{ + struct aspeed_hace_ctx *hace_ctx; + + hace_ctx = memalign(8, sizeof(struct aspeed_hace_ctx)); + if (!hace_ctx) + return -ENOMEM; + + memset(hace_ctx, 0, sizeof(struct aspeed_hace_ctx)); + + hace_ctx->algo = algo; + hace_ctx->cmd = HACE_HASH_MODE_ACCUM | HACE_HASH_SHA_BE_EN; + + switch (algo) { + case HASH_ALGO_SHA1: + hace_ctx->blk_size = 64; + hace_ctx->pad_size = 8; + hace_ctx->cmd |= HACE_HASH_ALGO_SHA1; + memcpy(hace_ctx->digest, iv_sha1, sizeof(iv_sha1)); + break; + case HASH_ALGO_SHA256: + hace_ctx->blk_size = 64; + hace_ctx->pad_size = 8; + hace_ctx->cmd |= HACE_HASH_ALGO_SHA256; + memcpy(hace_ctx->digest, iv_sha256, sizeof(iv_sha256)); + break; + case HASH_ALGO_SHA384: + hace_ctx->blk_size = 128; + hace_ctx->pad_size = 16; + hace_ctx->cmd |= HACE_HASH_ALGO_SHA384; + memcpy(hace_ctx->digest, iv_sha384, sizeof(iv_sha384)); + break; + case HASH_ALGO_SHA512: + hace_ctx->blk_size = 128; + hace_ctx->pad_size = 16; + hace_ctx->cmd |= HACE_HASH_ALGO_SHA512; + memcpy(hace_ctx->digest, iv_sha512, sizeof(iv_sha512)); + break; + default: + debug("Unsupported hash algorithm '%s'\n", hash_algo_name(algo)); + goto free_n_out; + }; + + *ctxp = hace_ctx; + + return 0; + +free_n_out: + free(hace_ctx); + + return -EINVAL; +} + +static int aspeed_hace_update(struct udevice *dev, void *ctx, const void *ibuf, uint32_t ilen) +{ + int rc; + uint32_t left, fill; + struct aspeed_hace_ctx *hace_ctx = ctx; + + left = hace_ctx->total[0] & (hace_ctx->blk_size - 1); + fill = hace_ctx->blk_size - left; + + hace_ctx->total[0] += ilen; + if (hace_ctx->total[0] < ilen) + hace_ctx->total[1]++; + + if (left && ilen >= fill) { + memcpy(hace_ctx->buf + left, ibuf, fill); + rc = aspeed_hace_process(dev, ctx, hace_ctx->buf, hace_ctx->blk_size); + if (rc) { + debug("failed to process hash, rc=%d\n", rc); + return rc; + } + ilen -= fill; + ibuf += fill; + left = 0; + } + + while (ilen >= hace_ctx->blk_size) { + rc = aspeed_hace_process(dev, ctx, ibuf, hace_ctx->blk_size); + if (rc) { + debug("failed to process hash, rc=%d\n", rc); + return rc; + } + + ibuf += hace_ctx->blk_size; + ilen -= hace_ctx->blk_size; + } + + if (ilen) + memcpy(hace_ctx->buf + left, ibuf, ilen); + + return 0; +} + +static int aspeed_hace_finish(struct udevice *dev, void *ctx, void *obuf) +{ + int rc = 0; + uint8_t pad[HASH_BLOCK_BUFSZ * 2]; + uint32_t last, padn; + uint64_t ibits_h, ibits_l; + uint64_t ibits_be_h, ibits_be_l; + struct aspeed_hace_ctx *hace_ctx = ctx; + + memset(pad, 0, sizeof(pad)); + pad[0] = 0x80; + + ibits_h = (hace_ctx->total[0] >> 61) | (hace_ctx->total[1] << 3); + ibits_be_h = cpu_to_be64(ibits_h); + + ibits_l = (hace_ctx->total[0] << 3); + ibits_be_l = cpu_to_be64(ibits_l); + + last = hace_ctx->total[0] & (hace_ctx->blk_size - 1); + + switch (hace_ctx->algo) { + case HASH_ALGO_SHA1: + case HASH_ALGO_SHA256: + padn = (last < 56) ? (56 - last) : (120 - last); + + rc = aspeed_hace_update(dev, ctx, pad, padn); + if (rc) { + debug("failed to append padding, rc=%d\n", rc); + goto free_n_out; + } + + rc = aspeed_hace_update(dev, ctx, &ibits_be_l, sizeof(ibits_be_l)); + if (rc) { + debug("failed to append message bits length, rc=%d\n", rc); + goto free_n_out; + } + + break; + case HASH_ALGO_SHA384: + case HASH_ALGO_SHA512: + padn = (last < 112) ? (112 - last) : (240 - last); + + rc = aspeed_hace_update(dev, ctx, pad, padn); + if (rc) { + debug("failed to append padding, rc=%d\n", rc); + goto free_n_out; + } + + rc = aspeed_hace_update(dev, ctx, &ibits_be_h, sizeof(ibits_be_h)) | + aspeed_hace_update(dev, ctx, &ibits_be_l, sizeof(ibits_be_l)); + if (rc) { + debug("failed to append message bits length, rc=%d\n", rc); + goto free_n_out; + } + + break; + default: + rc = -EINVAL; + break; + } + + memcpy(obuf, hace_ctx->digest, hash_algo_digest_size(hace_ctx->algo)); + +free_n_out: + free(ctx); + + return rc; +} + +static int aspeed_hace_digest_wd(struct udevice *dev, enum HASH_ALGO algo, + const void *ibuf, const uint32_t ilen, + void *obuf, uint32_t chunk_sz) +{ + int rc; + void *ctx; + const void *cur, *end; + uint32_t chunk; + + rc = aspeed_hace_init(dev, algo, &ctx); + if (rc) + return rc; + + if (CONFIG_IS_ENABLED(HW_WATCHDOG) || CONFIG_IS_ENABLED(WATCHDOG)) { + cur = ibuf; + end = ibuf + ilen; + + while (cur < end) { + chunk = end - cur; + if (chunk > chunk_sz) + chunk = chunk_sz; + + rc = aspeed_hace_update(dev, ctx, cur, chunk); + if (rc) + return rc; + + cur += chunk; + WATCHDOG_RESET(); + } + } else { + rc = aspeed_hace_update(dev, ctx, ibuf, ilen); + if (rc) + return rc; + } + + rc = aspeed_hace_finish(dev, ctx, obuf); + if (rc) + return rc; + + return 0; +} + +static int aspeed_hace_digest(struct udevice *dev, enum HASH_ALGO algo, + const void *ibuf, const uint32_t ilen, + void *obuf) +{ + /* re-use the watchdog version with input length as the chunk_sz */ + return aspeed_hace_digest_wd(dev, algo, ibuf, ilen, obuf, ilen); +} + +static int aspeed_hace_probe(struct udevice *dev) +{ + int rc; + struct aspeed_hace *hace = dev_get_priv(dev); + + rc = clk_get_by_index(dev, 0, &hace->clk); + if (rc < 0) { + debug("cannot get clock for %s: %d\n", dev->name, rc); + return rc; + } + + rc = clk_enable(&hace->clk); + if (rc) { + debug("cannot enable clock for %s: %d\n", dev->name, rc); + return rc; + } + + hace->base = devfdt_get_addr(dev); + + return rc; +} + +static int aspeed_hace_remove(struct udevice *dev) +{ + struct aspeed_hace *hace = dev_get_priv(dev); + + clk_disable(&hace->clk); + + return 0; +} + +static const struct hash_ops aspeed_hace_ops = { + .hash_init = aspeed_hace_init, + .hash_update = aspeed_hace_update, + .hash_finish = aspeed_hace_finish, + .hash_digest_wd = aspeed_hace_digest_wd, + .hash_digest = aspeed_hace_digest, +}; + +static const struct udevice_id aspeed_hace_ids[] = { + { .compatible = "aspeed,ast2600-hace" }, + { } +}; + +U_BOOT_DRIVER(aspeed_hace) = { + .name = "aspeed_hace", + .id = UCLASS_HASH, + .of_match = aspeed_hace_ids, + .ops = &aspeed_hace_ops, + .probe = aspeed_hace_probe, + .remove = aspeed_hace_remove, + .priv_auto = sizeof(struct aspeed_hace), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/crypto/hash/Kconfig b/drivers/crypto/hash/Kconfig index cd29a5c6a4f..bf9540eca67 100644 --- a/drivers/crypto/hash/Kconfig +++ b/drivers/crypto/hash/Kconfig @@ -14,3 +14,11 @@ config HASH_SOFTWARE help Enable driver for hashing operations in software. Currently it support multiple hash algorithm including CRC/MD5/SHA. + +config HASH_ASPEED + bool "Enable Hash with ASPEED hash accelerator" + depends on DM_HASH + select ASPEED_HACE + help + Enable this to support HW-assisted hashing operations using ASPEED Hash + and Crypto engine - HACE diff --git a/drivers/ddr/fsl/Kconfig b/drivers/ddr/fsl/Kconfig index fe3d6fc9700..b0e6df8be41 100644 --- a/drivers/ddr/fsl/Kconfig +++ b/drivers/ddr/fsl/Kconfig @@ -163,6 +163,98 @@ config ECC_INIT_VIA_DDRCONTROLLER endif +menu "PowerPC / M68K initial memory controller definitions (FLASH, SDRAM, etc)" + depends on MCF52x2 || MPC8xx || MPC83xx || MPC85xx + +config SYS_BR0_PRELIM_BOOL + bool "Define Bank 0" + +config SYS_BR0_PRELIM + hex "Preliminary value for BR0" + depends on SYS_BR0_PRELIM_BOOL + +config SYS_OR0_PRELIM + hex "Preliminary value for OR0" + depends on SYS_BR0_PRELIM_BOOL + +config SYS_BR1_PRELIM_BOOL + bool "Define Bank 1" + +config SYS_BR1_PRELIM + hex "Preliminary value for BR1" + depends on SYS_BR1_PRELIM_BOOL + +config SYS_OR1_PRELIM + hex "Preliminary value for OR1" + depends on SYS_BR1_PRELIM_BOOL + +config SYS_BR2_PRELIM_BOOL + bool "Define Bank 2" + +config SYS_BR2_PRELIM + hex "Preliminary value for BR2" + depends on SYS_BR2_PRELIM_BOOL + +config SYS_OR2_PRELIM + hex "Preliminary value for OR2" + depends on SYS_BR2_PRELIM_BOOL + +config SYS_BR3_PRELIM_BOOL + bool "Define Bank 3" + +config SYS_BR3_PRELIM + hex "Preliminary value for BR3" + depends on SYS_BR3_PRELIM_BOOL + +config SYS_OR3_PRELIM + hex "Preliminary value for OR3" + depends on SYS_BR3_PRELIM_BOOL + +config SYS_BR4_PRELIM_BOOL + bool "Define Bank 4" + +config SYS_BR4_PRELIM + hex "Preliminary value for BR4" + depends on SYS_BR4_PRELIM_BOOL + +config SYS_OR4_PRELIM + hex "Preliminary value for OR4" + depends on SYS_BR4_PRELIM_BOOL + +config SYS_BR5_PRELIM_BOOL + bool "Define Bank 5" + +config SYS_BR5_PRELIM + hex "Preliminary value for BR5" + depends on SYS_BR5_PRELIM_BOOL + +config SYS_OR5_PRELIM + hex "Preliminary value for OR5" + depends on SYS_BR5_PRELIM_BOOL + +config SYS_BR6_PRELIM_BOOL + bool "Define Bank 6" + +config SYS_BR6_PRELIM + hex "Preliminary value for BR6" + depends on SYS_BR6_PRELIM_BOOL + +config SYS_OR6_PRELIM + hex "Preliminary value for OR6" + depends on SYS_BR6_PRELIM_BOOL + +config SYS_BR7_PRELIM_BOOL + bool "Define Bank 7" + +config SYS_BR7_PRELIM + hex "Preliminary value for BR7" + depends on SYS_BR7_PRELIM_BOOL + +config SYS_OR7_PRELIM + hex "Preliminary value for OR7" + depends on SYS_BR7_PRELIM_BOOL +endmenu + config SYS_FSL_ERRATUM_A008378 bool diff --git a/drivers/ddr/marvell/axp/ddr3_hw_training.h b/drivers/ddr/marvell/axp/ddr3_hw_training.h index 30daaa9831b..fcdef792c76 100644 --- a/drivers/ddr/marvell/axp/ddr3_hw_training.h +++ b/drivers/ddr/marvell/axp/ddr3_hw_training.h @@ -373,7 +373,6 @@ int ddr3_load_dqs_patterns(MV_DRAM_INFO *dram_info); void ddr3_static_training_init(void); -u8 ddr3_get_eprom_fabric(void); void ddr3_set_performance_params(MV_DRAM_INFO *dram_info); int ddr3_dram_sram_burst(u32 src, u32 dst, u32 len); void ddr3_save_training(MV_DRAM_INFO *dram_info); diff --git a/drivers/ddr/marvell/axp/ddr3_init.c b/drivers/ddr/marvell/axp/ddr3_init.c index 607f3e12c3a..a9dcb74cecb 100644 --- a/drivers/ddr/marvell/axp/ddr3_init.c +++ b/drivers/ddr/marvell/axp/ddr3_init.c @@ -361,12 +361,18 @@ static u32 ddr3_init_main(void) __maybe_unused u32 ddr_width = BUS_WIDTH; __maybe_unused int status; __maybe_unused u32 win_backup[16]; + __maybe_unused struct udevice *udev; + __maybe_unused int ret; /* SoC/Board special Initializtions */ fab_opt = ddr3_get_fab_opt(); #ifdef CONFIG_SPD_EEPROM - i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + ret = i2c_get_chip_for_busnum(0, BUS_WIDTH_ECC_TWSI_ADDR, 1, &udev); + if (ret) { + printf("Cannot find SPD EEPROM\n"); + return MV_DDR3_TRAINING_ERR_BAD_DIMM_SETUP; + } #endif ddr3_print_version(); @@ -438,7 +444,7 @@ static u32 ddr3_init_main(void) #if defined(ECC_SUPPORT) && defined(AUTO_DETECTION_SUPPORT) ecc = 0; - if (ddr3_check_config(BUS_WIDTH_ECC_TWSI_ADDR, CONFIG_ECC)) + if (ddr3_check_config(udev, CONFIG_ECC)) ecc = 1; #endif @@ -483,7 +489,7 @@ static u32 ddr3_init_main(void) * Dynamically Set 32Bit and ECC for AXP (Relevant only for * Marvell DB boards) */ - if (ddr3_check_config(BUS_WIDTH_ECC_TWSI_ADDR, CONFIG_BUS_WIDTH)) { + if (ddr3_check_config(udev, CONFIG_BUS_WIDTH)) { ddr_width = 32; DEBUG_INIT_S("DDR3 Training Sequence - DRAM bus width 32Bit\n"); } @@ -904,7 +910,7 @@ void ddr3_static_mc_init(void) * Notes: Only Available for ArmadaXP/Armada 370 DB boards * Returns: None. */ -int ddr3_check_config(u32 twsi_addr, MV_CONFIG_TYPE config_type) +int ddr3_check_config(struct udevice *udev, MV_CONFIG_TYPE config_type) { #ifdef AUTO_DETECTION_SUPPORT u8 data = 0; @@ -916,7 +922,7 @@ int ddr3_check_config(u32 twsi_addr, MV_CONFIG_TYPE config_type) else offset = 0; - ret = i2c_read(twsi_addr, offset, 1, (u8 *)&data, 1); + ret = dm_i2c_read(udev, offset, &data, 1); if (!ret) { switch (config_type) { case CONFIG_ECC: @@ -943,30 +949,6 @@ int ddr3_check_config(u32 twsi_addr, MV_CONFIG_TYPE config_type) return 0; } -#if defined(DB_88F78X60_REV2) -/* - * Name: ddr3_get_eprom_fabric - Get Fabric configuration from EPROM - * Desc: - * Args: twsi Address - * Notes: Only Available for ArmadaXP DB Rev2 boards - * Returns: None. - */ -u8 ddr3_get_eprom_fabric(void) -{ -#ifdef AUTO_DETECTION_SUPPORT - u8 data = 0; - int ret; - - ret = i2c_read(NEW_FABRIC_TWSI_ADDR, 1, 1, (u8 *)&data, 1); - if (!ret) - return data & 0x1F; -#endif - - return 0; -} - -#endif - /* * Name: ddr3_cl_to_valid_cl - this return register matching CL value * Desc: diff --git a/drivers/ddr/marvell/axp/ddr3_init.h b/drivers/ddr/marvell/axp/ddr3_init.h index 569a14b7184..a26bd2a120e 100644 --- a/drivers/ddr/marvell/axp/ddr3_init.h +++ b/drivers/ddr/marvell/axp/ddr3_init.h @@ -98,11 +98,10 @@ int ddr3_hw_training(u32 target_freq, u32 ddr_width, void ddr3_print_version(void); void fix_pll_val(u8 target_fab); -u8 ddr3_get_eprom_fabric(void); u32 ddr3_get_fab_opt(void); u32 ddr3_get_cpu_freq(void); u32 ddr3_get_vco_freq(void); -int ddr3_check_config(u32 addr, MV_CONFIG_TYPE config_type); +int ddr3_check_config(struct udevice *udev, MV_CONFIG_TYPE config_type); u32 ddr3_get_static_mc_value(u32 reg_addr, u32 offset1, u32 mask1, u32 offset2, u32 mask2); u32 ddr3_cl_to_valid_cl(u32 cl); diff --git a/drivers/ddr/marvell/axp/ddr3_spd.c b/drivers/ddr/marvell/axp/ddr3_spd.c index dd772e63ab1..4763403c127 100644 --- a/drivers/ddr/marvell/axp/ddr3_spd.c +++ b/drivers/ddr/marvell/axp/ddr3_spd.c @@ -209,13 +209,19 @@ static u32 ddr3_get_dimm_num(u32 *dimm_addr) /* Read the dimm eeprom */ for (dimm_cur_addr = MAX_DIMM_ADDR; dimm_cur_addr > MIN_DIMM_ADDR; dimm_cur_addr--) { + struct udevice *udev; + data[SPD_DEV_TYPE_BYTE] = 0; /* Far-End DIMM must be connected */ if ((dimm_num == 0) && (dimm_cur_addr < FAR_END_DIMM_ADDR)) return 0; - ret = i2c_read(dimm_cur_addr, 0, 1, (uchar *)data, 3); + ret = i2c_get_chip_for_busnum(0, dimm_cur_addr, 1, &udev); + if (ret) + continue; + + ret = dm_i2c_read(udev, 0, data, 3); if (!ret) { if (data[SPD_DEV_TYPE_BYTE] == SPD_MEM_TYPE_DDR3) { dimm_addr[dimm_num] = dimm_cur_addr; @@ -245,9 +251,15 @@ int ddr3_spd_init(MV_DIMM_INFO *info, u32 dimm_addr, u32 dimm_width) __maybe_unused u8 vendor_high, vendor_low; if (dimm_addr != 0) { + struct udevice *udev; + memset(spd_data, 0, SPD_SIZE * sizeof(u8)); - ret = i2c_read(dimm_addr, 0, 1, (uchar *)spd_data, SPD_SIZE); + ret = i2c_get_chip_for_busnum(0, dimm_addr, 1, &udev); + if (ret) + return MV_DDR3_TRAINING_ERR_TWSI_FAIL; + + ret = dm_i2c_read(udev, 0, spd_data, SPD_SIZE); if (ret) return MV_DDR3_TRAINING_ERR_TWSI_FAIL; } diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index c3a109beac7..8cf85f0d7a1 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -2,7 +2,7 @@ config SCMI_FIRMWARE bool "Enable SCMI support" select FIRMWARE select OF_TRANSLATE - depends on SANDBOX || DM_MAILBOX || ARM_SMCCC + depends on SANDBOX || DM_MAILBOX || ARM_SMCCC || OPTEE help System Control and Management Interface (SCMI) is a communication protocol that defines standard interfaces for power, performance @@ -14,6 +14,30 @@ config SCMI_FIRMWARE or a companion host in the CPU system. Communications between agent (client) and the SCMI server are - based on message exchange. Messages can be exchange over tranport + based on message exchange. Messages can be exchanged over transport channels as a mailbox device or an Arm SMCCC service with some piece of identified shared memory. + +config SCMI_AGENT_MAILBOX + bool "Enable SCMI agent mailbox" + depends on SCMI_FIRMWARE && DM_MAILBOX + default y + help + Enable the SCMI communication channel based on mailbox + for compatible "arm,scmi". + +config SCMI_AGENT_SMCCC + bool "Enable SCMI agent SMCCC" + depends on SCMI_FIRMWARE && ARM_SMCCC + default y + help + Enable the SCMI communication channel based on Arm SMCCC service for + compatible "arm,scmi-smc". + +config SCMI_AGENT_OPTEE + bool "Enable SCMI agent OP-TEE" + depends on SCMI_FIRMWARE && OPTEE + default y + help + Enable the SCMI communication channel based on OP-TEE transport + for compatible "linaro,scmi-optee". diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile index 966475ec10a..b2ff483c75a 100644 --- a/drivers/firmware/scmi/Makefile +++ b/drivers/firmware/scmi/Makefile @@ -1,5 +1,6 @@ obj-y += scmi_agent-uclass.o obj-y += smt.o -obj-$(CONFIG_ARM_SMCCC) += smccc_agent.o -obj-$(CONFIG_DM_MAILBOX) += mailbox_agent.o +obj-$(CONFIG_SCMI_AGENT_SMCCC) += smccc_agent.o +obj-$(CONFIG_SCMI_AGENT_MAILBOX) += mailbox_agent.o +obj-$(CONFIG_SCMI_AGENT_OPTEE) += optee_agent.o obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o sandbox-scmi_devices.o diff --git a/drivers/firmware/scmi/mailbox_agent.c b/drivers/firmware/scmi/mailbox_agent.c index ea35e7e09ed..8e4af0c8faf 100644 --- a/drivers/firmware/scmi/mailbox_agent.c +++ b/drivers/firmware/scmi/mailbox_agent.c @@ -33,7 +33,7 @@ struct scmi_mbox_channel { static int scmi_mbox_process_msg(struct udevice *dev, struct scmi_msg *msg) { - struct scmi_mbox_channel *chan = dev_get_priv(dev); + struct scmi_mbox_channel *chan = dev_get_plat(dev); int ret; ret = scmi_write_msg_to_smt(dev, &chan->smt, msg); @@ -62,9 +62,9 @@ out: return ret; } -int scmi_mbox_probe(struct udevice *dev) +int scmi_mbox_of_to_plat(struct udevice *dev) { - struct scmi_mbox_channel *chan = dev_get_priv(dev); + struct scmi_mbox_channel *chan = dev_get_plat(dev); int ret; chan->timeout_us = TIMEOUT_US_10MS; @@ -72,17 +72,13 @@ int scmi_mbox_probe(struct udevice *dev) ret = mbox_get_by_index(dev, 0, &chan->mbox); if (ret) { dev_err(dev, "Failed to find mailbox: %d\n", ret); - goto out; + return ret; } ret = scmi_dt_get_smt_buffer(dev, &chan->smt); if (ret) dev_err(dev, "Failed to get shm resources: %d\n", ret); -out: - if (ret) - devm_kfree(dev, chan); - return ret; } @@ -99,7 +95,7 @@ U_BOOT_DRIVER(scmi_mbox) = { .name = "scmi-over-mailbox", .id = UCLASS_SCMI_AGENT, .of_match = scmi_mbox_ids, - .priv_auto = sizeof(struct scmi_mbox_channel), - .probe = scmi_mbox_probe, + .plat_auto = sizeof(struct scmi_mbox_channel), + .of_to_plat = scmi_mbox_of_to_plat, .ops = &scmi_mbox_ops, }; diff --git a/drivers/firmware/scmi/optee_agent.c b/drivers/firmware/scmi/optee_agent.c new file mode 100644 index 00000000000..1f265922343 --- /dev/null +++ b/drivers/firmware/scmi/optee_agent.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020-2021 Linaro Limited. + */ + +#define LOG_CATEGORY UCLASS_SCMI_AGENT + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <scmi_agent.h> +#include <scmi_agent-uclass.h> +#include <string.h> +#include <tee.h> +#include <asm/types.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <linux/arm-smccc.h> +#include <linux/bug.h> +#include <linux/compat.h> + +#include "smt.h" + +#define SCMI_SHM_SIZE 128 + +/** + * struct scmi_optee_channel - Description of an SCMI OP-TEE transport + * @channel_id: Channel identifier + * @smt: Shared memory buffer with synchronisation protocol + * @dyn_shm: True if using dynamically allocated shared memory + */ +struct scmi_optee_channel { + unsigned int channel_id; + struct scmi_smt smt; + bool dyn_shm; +}; + +/** + * struct channel_session - Aggreates SCMI service session context references + * @tee: OP-TEE device to invoke + * @tee_session: OP-TEE session identifier + * @tee_shm: Dynamically allocated OP-TEE shared memory, or NULL + * @channel_hdl: Channel handle provided by OP-TEE SCMI service + */ +struct channel_session { + struct udevice *tee; + u32 tee_session; + struct tee_shm *tee_shm; + u32 channel_hdl; +}; + +#define TA_SCMI_UUID { 0xa8cfe406, 0xd4f5, 0x4a2e, \ + { 0x9f, 0x8d, 0xa2, 0x5d, 0xc7, 0x54, 0xc0, 0x99 } } + +enum optee_smci_pta_cmd { + /* + * PTA_SCMI_CMD_CAPABILITIES - Get channel capabilities + * + * [out] value[0].a: Capability bit mask (enum pta_scmi_caps) + * [out] value[0].b: Extended capabilities or 0 + */ + PTA_SCMI_CMD_CAPABILITIES = 0, + + /* + * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL - Process SCMI message in SMT buffer + * + * [in] value[0].a: Channel handle + * + * Shared memory used for SCMI message/response exhange is expected + * already identified and bound to channel handle in both SCMI agent + * and SCMI server (OP-TEE) parts. + * The memory uses SMT header to carry SCMI meta-data (protocol ID and + * protocol message ID). + */ + PTA_SCMI_CMD_PROCESS_SMT_CHANNEL = 1, + + /* + * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE - Process SMT/SCMI message + * + * [in] value[0].a: Channel handle + * [in/out] memref[1]: Message/response buffer (SMT and SCMI payload) + * + * Shared memory used for SCMI message/response is a SMT buffer + * referenced by param[1]. It shall be 128 bytes large to fit response + * payload whatever message playload size. + * The memory uses SMT header to carry SCMI meta-data (protocol ID and + * protocol message ID). + */ + PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE = 2, + + /* + * PTA_SCMI_CMD_GET_CHANNEL - Get channel handle + * + * SCMI shm information are 0 if agent expects to use OP-TEE regular SHM + * + * [in] value[0].a: Channel identifier + * [out] value[0].a: Returned channel handle + * [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps) + */ + PTA_SCMI_CMD_GET_CHANNEL = 3, +}; + +/* + * OP-TEE SCMI service capabilities bit flags (32bit) + * + * PTA_SCMI_CAPS_SMT_HEADER + * When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in + * shared memory buffers to carry SCMI protocol synchronisation information. + */ +#define PTA_SCMI_CAPS_NONE 0 +#define PTA_SCMI_CAPS_SMT_HEADER BIT(0) + +static int open_channel(struct udevice *dev, struct channel_session *sess) +{ + const struct tee_optee_ta_uuid uuid = TA_SCMI_UUID; + struct scmi_optee_channel *chan = dev_get_plat(dev); + struct tee_open_session_arg sess_arg = { }; + struct tee_invoke_arg cmd_arg = { }; + struct tee_param param[1] = { }; + int ret; + + memset(sess, 0, sizeof(sess)); + + sess->tee = tee_find_device(NULL, NULL, NULL, NULL); + if (!sess->tee) + return -ENODEV; + + sess_arg.clnt_login = TEE_LOGIN_REE_KERNEL; + tee_optee_ta_uuid_to_octets(sess_arg.uuid, &uuid); + + ret = tee_open_session(sess->tee, &sess_arg, 0, NULL); + if (ret) { + dev_err(dev, "can't open session: %d\n", ret); + return ret; + } + + cmd_arg.func = PTA_SCMI_CMD_GET_CHANNEL; + cmd_arg.session = sess_arg.session; + + param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INOUT; + param[0].u.value.a = chan->channel_id; + param[0].u.value.b = PTA_SCMI_CAPS_SMT_HEADER; + + ret = tee_invoke_func(sess->tee, &cmd_arg, ARRAY_SIZE(param), param); + if (ret || cmd_arg.ret) { + dev_err(dev, "Invoke failed: %d, 0x%x\n", ret, cmd_arg.ret); + if (!ret) + ret = -EPROTO; + + tee_close_session(sess->tee, sess_arg.session); + return ret; + } + + sess->tee_session = sess_arg.session; + sess->channel_hdl = param[0].u.value.a; + + return 0; +} + +static void close_channel(struct channel_session *sess) +{ + tee_close_session(sess->tee, sess->tee_session); +} + +static int invoke_cmd(struct udevice *dev, struct channel_session *sess, + struct scmi_msg *msg) +{ + struct scmi_optee_channel *chan = dev_get_plat(dev); + struct tee_invoke_arg arg = { }; + struct tee_param param[2] = { }; + int ret; + + scmi_write_msg_to_smt(dev, &chan->smt, msg); + + arg.session = sess->tee_session; + param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = sess->channel_hdl; + + if (chan->dyn_shm) { + arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE; + param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT; + param[1].u.memref.shm = sess->tee_shm; + param[1].u.memref.size = SCMI_SHM_SIZE; + } else { + arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL; + } + + ret = tee_invoke_func(sess->tee, &arg, ARRAY_SIZE(param), param); + if (ret || arg.ret) { + if (!ret) + ret = -EPROTO; + } else { + ret = scmi_read_resp_from_smt(dev, &chan->smt, msg); + } + + scmi_clear_smt_channel(&chan->smt); + + return ret; +} + +static int prepare_shm(struct udevice *dev, struct channel_session *sess) +{ + struct scmi_optee_channel *chan = dev_get_plat(dev); + int ret; + + /* Static shm is already prepared by the firmware: nothing to do */ + if (!chan->dyn_shm) + return 0; + + chan->smt.size = SCMI_SHM_SIZE; + + ret = tee_shm_alloc(sess->tee, chan->smt.size, 0, &sess->tee_shm); + if (ret) { + dev_err(dev, "Failed to allocated shmem: %d\n", ret); + return ret; + } + + chan->smt.buf = sess->tee_shm->addr; + + /* Initialize shm buffer for message exchanges */ + scmi_clear_smt_channel(&chan->smt); + + return 0; +} + +static void release_shm(struct udevice *dev, struct channel_session *sess) +{ + struct scmi_optee_channel *chan = dev_get_plat(dev); + + if (chan->dyn_shm) + tee_shm_free(sess->tee_shm); +} + +static int scmi_optee_process_msg(struct udevice *dev, struct scmi_msg *msg) +{ + struct channel_session sess; + int ret; + + ret = open_channel(dev, &sess); + if (ret) + return ret; + + ret = prepare_shm(dev, &sess); + if (ret) + goto out; + + ret = invoke_cmd(dev, &sess, msg); + + release_shm(dev, &sess); + +out: + close_channel(&sess); + + return ret; +} + +static int scmi_optee_of_to_plat(struct udevice *dev) +{ + struct scmi_optee_channel *chan = dev_get_plat(dev); + int ret; + + if (dev_read_u32(dev, "linaro,optee-channel-id", &chan->channel_id)) { + dev_err(dev, "Missing property linaro,optee-channel-id\n"); + return -EINVAL; + } + + if (dev_read_prop(dev, "shmem", NULL)) { + ret = scmi_dt_get_smt_buffer(dev, &chan->smt); + if (ret) { + dev_err(dev, "Failed to get smt resources: %d\n", ret); + return ret; + } + chan->dyn_shm = false; + } else { + chan->dyn_shm = true; + } + + return 0; +} + +static int scmi_optee_probe(struct udevice *dev) +{ + struct channel_session sess; + int ret; + + /* Check OP-TEE service acknowledges the SCMI channel */ + ret = open_channel(dev, &sess); + if (!ret) + close_channel(&sess); + + return ret; +} + +static const struct udevice_id scmi_optee_ids[] = { + { .compatible = "linaro,scmi-optee" }, + { } +}; + +static const struct scmi_agent_ops scmi_optee_ops = { + .process_msg = scmi_optee_process_msg, +}; + +U_BOOT_DRIVER(scmi_optee) = { + .name = "scmi-over-optee", + .id = UCLASS_SCMI_AGENT, + .of_match = scmi_optee_ids, + .plat_auto = sizeof(struct scmi_optee_channel), + .of_to_plat = scmi_optee_of_to_plat, + .probe = scmi_optee_probe, + .flags = DM_FLAG_OS_PREPARE, + .ops = &scmi_optee_ops, +}; diff --git a/drivers/firmware/scmi/smccc_agent.c b/drivers/firmware/scmi/smccc_agent.c index f185891e8fd..5e166ca93ee 100644 --- a/drivers/firmware/scmi/smccc_agent.c +++ b/drivers/firmware/scmi/smccc_agent.c @@ -32,7 +32,7 @@ struct scmi_smccc_channel { static int scmi_smccc_process_msg(struct udevice *dev, struct scmi_msg *msg) { - struct scmi_smccc_channel *chan = dev_get_priv(dev); + struct scmi_smccc_channel *chan = dev_get_plat(dev); struct arm_smccc_res res; int ret; @@ -51,9 +51,9 @@ static int scmi_smccc_process_msg(struct udevice *dev, struct scmi_msg *msg) return ret; } -static int scmi_smccc_probe(struct udevice *dev) +static int scmi_smccc_of_to_plat(struct udevice *dev) { - struct scmi_smccc_channel *chan = dev_get_priv(dev); + struct scmi_smccc_channel *chan = dev_get_plat(dev); u32 func_id; int ret; @@ -65,12 +65,10 @@ static int scmi_smccc_probe(struct udevice *dev) chan->func_id = func_id; ret = scmi_dt_get_smt_buffer(dev, &chan->smt); - if (ret) { + if (ret) dev_err(dev, "Failed to get smt resources: %d\n", ret); - return ret; - } - return 0; + return ret; } static const struct udevice_id scmi_smccc_ids[] = { @@ -86,7 +84,7 @@ U_BOOT_DRIVER(scmi_smccc) = { .name = "scmi-over-smccc", .id = UCLASS_SCMI_AGENT, .of_match = scmi_smccc_ids, - .priv_auto = sizeof(struct scmi_smccc_channel), - .probe = scmi_smccc_probe, + .plat_auto = sizeof(struct scmi_smccc_channel), + .of_to_plat = scmi_smccc_of_to_plat, .ops = &scmi_smccc_ops, }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 40abc33772e..b41a755fc75 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -177,6 +177,11 @@ config HSDK_CREG_GPIO help This driver supports CREG GPIOs on Synopsys HSDK SOC. +config KIRKWOOD_GPIO + bool "Kirkwood GPIO driver" + help + This drdiver supports GPIOs on Kirkwood platforms + config LPC32XX_GPIO bool "LPC32XX GPIO driver" depends on DM diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index eafd801cdc3..9a3c8241bc6 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -40,6 +40,10 @@ DECLARE_GLOBAL_DATA_PTR; +#ifdef CONFIG_M68K +#define CONFIG_SYS_IMMR CONFIG_SYS_MBAR +#endif + #if !CONFIG_IS_ENABLED(DM_I2C) static const struct fsl_i2c_base *i2c_base[4] = { (struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET), diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index a17e55e9972..0b753f37bf4 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -38,6 +38,15 @@ config TPL_DM_KEYBOARD includes methods to start/stop the device, check for available input and update LEDs if the keyboard has them. +config KEYBOARD + bool "Enable legacy keyboard support (deprecated)" + help + Enable this to enable a custom keyboard support. + This simply calls drv_keyboard_init() which must be + defined in your board-specific files. This option is deprecated + and is only used by novena. For new boards, use driver model + instead. + config CROS_EC_KEYB bool "Enable Chrome OS EC keyboard support" depends on INPUT diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3bae0720058..a8baaeaf5cf 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -416,7 +416,7 @@ if I2C_EEPROM config SYS_I2C_EEPROM_ADDR_OVERFLOW hex "EEPROM Address Overflow" - default 0 + default 0x0 help EEPROM chips that implement "address overflow" are ones like Catalyst 24WC04/08/16 which has 9/10/11 bits of @@ -500,4 +500,7 @@ config ESM_PMIC Support ESM (Error Signal Monitor) on PMIC devices. ESM is used typically to reboot the board in error condition. +config FSL_IFC + bool + endmenu diff --git a/drivers/misc/fsl_portals.c b/drivers/misc/fsl_portals.c index 632430e4209..02bc3f86cae 100644 --- a/drivers/misc/fsl_portals.c +++ b/drivers/misc/fsl_portals.c @@ -106,7 +106,7 @@ static int fdt_qportal(void *blob, int off, int id, char *name, enum fsl_dpaa_dev dev, int create) { int childoff, dev_off, ret = 0; - u32 dev_handle; + unsigned int dev_handle; #ifdef CONFIG_FSL_CORENET int num; u32 liodns[2]; @@ -142,11 +142,9 @@ static int fdt_qportal(void *blob, int off, int id, char *name, if (childoff > 0) { dev_handle = fdt_get_phandle(blob, dev_off); if (dev_handle <= 0) { - dev_handle = fdt_alloc_phandle(blob); - ret = fdt_set_phandle(blob, dev_off, - dev_handle); - if (ret < 0) - return ret; + dev_handle = fdt_create_phandle(blob, dev_off); + if (!dev_handle) + return -FDT_ERR_NOPHANDLES; } ret = fdt_setprop(blob, childoff, "dev-handle", diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 3ee92d03ca2..b80e838066c 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -320,7 +320,7 @@ struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) struct blk_desc *desc; struct udevice *dev; - device_find_first_child(mmc->dev, &dev); + device_find_first_child_by_uclass(mmc->dev, UCLASS_BLK, &dev); if (!dev) return NULL; desc = dev_get_uclass_plat(dev); @@ -425,7 +425,7 @@ int mmc_unbind(struct udevice *dev) { struct udevice *bdev; - device_find_first_child(dev, &bdev); + device_find_first_child_by_uclass(dev, UCLASS_BLK, &bdev); if (bdev) { device_remove(bdev, DM_REMOVE_NORMAL); device_unbind(bdev); diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c index 8599f095bcd..97182ffd7f5 100644 --- a/drivers/mmc/mtk-sd.c +++ b/drivers/mmc/mtk-sd.c @@ -1724,6 +1724,20 @@ static int msdc_drv_bind(struct udevice *dev) return mmc_bind(dev, &plat->mmc, &plat->cfg); } +static int msdc_ops_wait_dat0(struct udevice *dev, int state, int timeout_us) +{ + struct msdc_host *host = dev_get_priv(dev); + int ret; + u32 reg; + + ret = readl_poll_sleep_timeout(&host->base->msdc_ps, reg, + !!(reg & MSDC_PS_DAT0) == !!state, + 1000, /* 1 ms */ + timeout_us); + + return ret; +} + static const struct dm_mmc_ops msdc_ops = { .send_cmd = msdc_ops_send_cmd, .set_ios = msdc_ops_set_ios, @@ -1732,6 +1746,7 @@ static const struct dm_mmc_ops msdc_ops = { #ifdef MMC_SUPPORTS_TUNING .execute_tuning = msdc_execute_tuning, #endif + .wait_dat0 = msdc_ops_wait_dat0, }; static const struct msdc_compatible mt7620_compat = { diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c index 895fbffecfc..451fe4a4e5a 100644 --- a/drivers/mmc/sandbox_mmc.c +++ b/drivers/mmc/sandbox_mmc.c @@ -9,23 +9,26 @@ #include <errno.h> #include <fdtdec.h> #include <log.h> +#include <malloc.h> #include <mmc.h> +#include <os.h> #include <asm/test.h> struct sandbox_mmc_plat { struct mmc_config cfg; struct mmc mmc; + const char *fname; }; -#define MMC_CSIZE 0 -#define MMC_CMULT 8 /* 8 because the card is high-capacity */ -#define MMC_BL_LEN_SHIFT 10 -#define MMC_BL_LEN BIT(MMC_BL_LEN_SHIFT) -#define MMC_CAPACITY (((MMC_CSIZE + 1) << (MMC_CMULT + 2)) \ - * MMC_BL_LEN) /* 1 MiB */ +#define MMC_CMULT 8 /* 8 because the card is high-capacity */ +#define MMC_BL_LEN_SHIFT 10 +#define MMC_BL_LEN BIT(MMC_BL_LEN_SHIFT) +#define SIZE_MULTIPLE ((1 << (MMC_CMULT + 2)) * MMC_BL_LEN) struct sandbox_mmc_priv { - u8 buf[MMC_CAPACITY]; + char *buf; + int csize; /* CSIZE value to report */ + int size; }; /** @@ -60,8 +63,8 @@ static int sandbox_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, case MMC_CMD_SEND_CSD: cmd->response[0] = 0; cmd->response[1] = (MMC_BL_LEN_SHIFT << 16) | - ((MMC_CSIZE >> 16) & 0x3f); - cmd->response[2] = (MMC_CSIZE & 0xffff) << 16; + ((priv->csize >> 16) & 0x3f); + cmd->response[2] = (priv->csize & 0xffff) << 16; cmd->response[3] = 0; break; case SD_CMD_SWITCH_FUNC: { @@ -143,6 +146,8 @@ static int sandbox_mmc_of_to_plat(struct udevice *dev) struct blk_desc *blk; int ret; + plat->fname = dev_read_string(dev, "filename"); + ret = mmc_of_parse(dev, cfg); if (ret) return ret; @@ -156,10 +161,46 @@ static int sandbox_mmc_of_to_plat(struct udevice *dev) static int sandbox_mmc_probe(struct udevice *dev) { struct sandbox_mmc_plat *plat = dev_get_plat(dev); + struct sandbox_mmc_priv *priv = dev_get_priv(dev); + int ret; + + if (plat->fname) { + ret = os_map_file(plat->fname, OS_O_RDWR | OS_O_CREAT, + (void **)&priv->buf, &priv->size); + if (ret) { + log_err("%s: Unable to map file '%s'\n", dev->name, + plat->fname); + return ret; + } + priv->csize = priv->size / SIZE_MULTIPLE - 1; + } else { + priv->csize = 0; + priv->size = (priv->csize + 1) * SIZE_MULTIPLE; /* 1 MiB */ + + priv->buf = malloc(priv->size); + if (!priv->buf) { + log_err("%s: Not enough memory (%x bytes)\n", + dev->name, priv->size); + return -ENOMEM; + } + } return mmc_init(&plat->mmc); } +static int sandbox_mmc_remove(struct udevice *dev) +{ + struct sandbox_mmc_plat *plat = dev_get_plat(dev); + struct sandbox_mmc_priv *priv = dev_get_priv(dev); + + if (plat->fname) + os_unmap(priv->buf, priv->size); + else + free(priv->buf); + + return 0; +} + static int sandbox_mmc_bind(struct udevice *dev) { struct sandbox_mmc_plat *plat = dev_get_plat(dev); @@ -196,6 +237,7 @@ U_BOOT_DRIVER(mmc_sandbox) = { .unbind = sandbox_mmc_unbind, .of_to_plat = sandbox_mmc_of_to_plat, .probe = sandbox_mmc_probe, + .remove = sandbox_mmc_remove, .priv_auto = sizeof(struct sandbox_mmc_priv), .plat_auto = sizeof(struct sandbox_mmc_plat), }; diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c index a3cdf7bcd9f..44bfc911af2 100644 --- a/drivers/mmc/stm32_sdmmc2.c +++ b/drivers/mmc/stm32_sdmmc2.c @@ -16,6 +16,7 @@ #include <asm/bitops.h> #include <asm/cache.h> #include <dm/device_compat.h> +#include <dm/pinctrl.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/libfdt.h> @@ -645,6 +646,66 @@ static const struct dm_mmc_ops stm32_sdmmc2_ops = { .host_power_cycle = stm32_sdmmc2_host_power_cycle, }; +static int stm32_sdmmc2_probe_level_translator(struct udevice *dev) +{ + struct stm32_sdmmc2_priv *priv = dev_get_priv(dev); + struct gpio_desc cmd_gpio; + struct gpio_desc ck_gpio; + struct gpio_desc ckin_gpio; + int clk_hi, clk_lo, ret; + + /* + * Assume the level translator is present if st,use-ckin is set. + * This is to cater for DTs which do not implement this test. + */ + priv->clk_reg_msk |= SDMMC_CLKCR_SELCLKRX_CKIN; + + ret = gpio_request_by_name(dev, "st,cmd-gpios", 0, &cmd_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) + goto exit_cmd; + + ret = gpio_request_by_name(dev, "st,ck-gpios", 0, &ck_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) + goto exit_ck; + + ret = gpio_request_by_name(dev, "st,ckin-gpios", 0, &ckin_gpio, + GPIOD_IS_IN); + if (ret) + goto exit_ckin; + + /* All GPIOs are valid, test whether level translator works */ + + /* Sample CKIN */ + clk_hi = !!dm_gpio_get_value(&ckin_gpio); + + /* Set CK low */ + dm_gpio_set_value(&ck_gpio, 0); + + /* Sample CKIN */ + clk_lo = !!dm_gpio_get_value(&ckin_gpio); + + /* Tristate all */ + dm_gpio_set_dir_flags(&cmd_gpio, GPIOD_IS_IN); + dm_gpio_set_dir_flags(&ck_gpio, GPIOD_IS_IN); + + /* Level translator is present if CK signal is propagated to CKIN */ + if (!clk_hi || clk_lo) + priv->clk_reg_msk &= ~SDMMC_CLKCR_SELCLKRX_CKIN; + + dm_gpio_free(dev, &ckin_gpio); + +exit_ckin: + dm_gpio_free(dev, &ck_gpio); +exit_ck: + dm_gpio_free(dev, &cmd_gpio); +exit_cmd: + pinctrl_select_state(dev, "default"); + + return 0; +} + static int stm32_sdmmc2_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); @@ -662,7 +723,7 @@ static int stm32_sdmmc2_probe(struct udevice *dev) if (dev_read_bool(dev, "st,sig-dir")) priv->pwr_reg_msk |= SDMMC_POWER_DIRPOL; if (dev_read_bool(dev, "st,use-ckin")) - priv->clk_reg_msk |= SDMMC_CLKCR_SELCLKRX_CKIN; + stm32_sdmmc2_probe_level_translator(dev); ret = clk_get_by_index(dev, 0, &priv->clk); if (ret) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index df9eae1691c..0e826c19298 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -9,10 +9,24 @@ config SYS_NAND_SELF_INIT This option, if enabled, provides more flexible and linux-like NAND initialization process. -config SYS_NAND_DRIVER_ECC_LAYOUT +config SPL_SYS_NAND_SELF_INIT + bool + depends on !SPL_NAND_SIMPLE + help + This option, if enabled, provides more flexible and linux-like + NAND initialization process, in SPL. + +config TPL_SYS_NAND_SELF_INIT bool + depends on TPL_NAND_SUPPORT + help + This option, if enabled, provides more flexible and linux-like + NAND initialization process, in SPL. + +config SYS_NAND_DRIVER_ECC_LAYOUT + bool "Omit standard ECC layouts to save space" help - Omit standard ECC layouts to safe space. Select this if your driver + Omit standard ECC layouts to save space. Select this if your driver is known to provide its own ECC layout. config SYS_NAND_USE_FLASH_BBT @@ -22,6 +36,7 @@ config SYS_NAND_USE_FLASH_BBT config NAND_ATMEL bool "Support Atmel NAND controller" + select SYS_NAND_SELF_INIT imply SYS_NAND_USE_FLASH_BBT help Enable this driver for NAND flash platforms using an Atmel NAND @@ -65,6 +80,7 @@ endif config NAND_BRCMNAND bool "Support Broadcom NAND controller" depends on OF_CONTROL && DM && DM_MTD + select SYS_NAND_SELF_INIT help Enable the driver for NAND flash on platforms using a Broadcom NAND controller. @@ -101,6 +117,7 @@ config NAND_BRCMNAND_63158 config NAND_DAVINCI bool "Support TI Davinci NAND controller" + select SYS_NAND_SELF_INIT if TARGET_DA850EVM help Enable this driver for NAND flash controllers available in TI Davinci and Keystone2 platforms @@ -128,17 +145,25 @@ config NAND_DENALI_DT config NAND_FSL_ELBC bool "Support Freescale Enhanced Local Bus Controller FCM NAND driver" + select TPL_SYS_NAND_SELF_INIT if TPL_NAND_SUPPORT + select SPL_SYS_NAND_SELF_INIT + select SYS_NAND_SELF_INIT depends on FSL_ELBC help Enable the Freescale Enhanced Local Bus Controller FCM NAND driver. config NAND_FSL_IFC bool "Support Freescale Integrated Flash Controller NAND driver" + select TPL_SYS_NAND_SELF_INIT if TPL_NAND_SUPPORT + select SPL_SYS_NAND_SELF_INIT + select SYS_NAND_SELF_INIT + select FSL_IFC help Enable the Freescale Integrated Flash Controller NAND driver. config NAND_LPC32XX_MLC bool "Support LPC32XX_MLC controller" + select SYS_NAND_SELF_INIT help Enable the LPC32XX MLC NAND controller. @@ -330,6 +355,7 @@ config NAND_SUNXI select SYS_NAND_SELF_INIT select SYS_NAND_U_BOOT_LOCATIONS select SPL_NAND_SUPPORT + select SPL_SYS_NAND_SELF_INIT imply CMD_NAND ---help--- Enable support for NAND. This option enables the standard and @@ -374,6 +400,7 @@ config NAND_MXC config NAND_MXS bool "MXS NAND support" depends on MX23 || MX28 || MX6 || MX7 || IMX8 || IMX8M + select SPL_SYS_NAND_SELF_INIT select SYS_NAND_SELF_INIT imply CMD_NAND select APBH_DMA @@ -406,6 +433,7 @@ config NAND_MXIC config NAND_ZYNQ bool "Support for Zynq Nand controller" + select SPL_SYS_NAND_SELF_INIT select SYS_NAND_SELF_INIT select DM_MTD imply CMD_NAND @@ -475,6 +503,14 @@ config ROCKCHIP_NAND NFC v800: RK3308, RV1108 NFC v900: PX30, RK3326 +config TEGRA_NAND + bool "Support for NAND controller on Tegra SoCs" + depends on ARCH_TEGRA + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + Enables support for NAND Flash chips on Tegra SoCs platforms. + comment "Generic NAND options" config SYS_NAND_BLOCK_SIZE diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index ef7ee395c0c..9158d94de25 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -788,7 +788,7 @@ static void davinci_nand_init(struct nand_chip *nand) nand->dev_ready = nand_davinci_dev_ready; } -#ifdef CONFIG_SYS_NAND_SELF_INIT +#if CONFIG_IS_ENABLED(SYS_NAND_SELF_INIT) static int davinci_nand_probe(struct udevice *dev) { struct nand_chip *nand = dev_get_priv(dev); diff --git a/drivers/mtd/nand/raw/nand.c b/drivers/mtd/nand/raw/nand.c index 59ad1392b0e..4b5560dd24c 100644 --- a/drivers/mtd/nand/raw/nand.c +++ b/drivers/mtd/nand/raw/nand.c @@ -76,7 +76,7 @@ int nand_register(int devnum, struct mtd_info *mtd) return 0; } -#ifndef CONFIG_SYS_NAND_SELF_INIT +#if !CONFIG_IS_ENABLED(SYS_NAND_SELF_INIT) static void nand_init_chip(int i) { struct nand_chip *nand = &nand_chip[i]; @@ -155,7 +155,7 @@ void nand_init(void) return; initialized = 1; -#ifdef CONFIG_SYS_NAND_SELF_INIT +#if CONFIG_IS_ENABLED(SYS_NAND_SELF_INIT) board_nand_init(); #else int i; diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 2f56d46a384..f350c7e5dc2 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -57,7 +57,7 @@ config SF_DEFAULT_CS config SF_DEFAULT_MODE hex "SPI Flash default mode (see include/spi.h)" depends on SPI_FLASH || DM_SPI_FLASH - default 0 + default 0x0 help The default mode may be provided by the platform to handle the common case when only a single serial diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c1a49173c28..71e0cbafb41 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1,6 +1,7 @@ source "drivers/net/phy/Kconfig" source "drivers/net/pfe_eth/Kconfig" source "drivers/net/fsl-mc/Kconfig" +source "drivers/net/bnxt/Kconfig" config ETH def_bool y @@ -554,6 +555,22 @@ config RTL8169 This driver supports Realtek 8169 series gigabit ethernet family of PCI/PCIe chipsets/adapters. +config SJA1105 + bool "NXP SJA1105 Ethernet switch family driver" + depends on DM_DSA && DM_SPI + select BITREVERSE + help + This is the driver for the NXP SJA1105 automotive Ethernet switch + family. These are 5-port devices and are managed over an SPI + interface. Probing is handled based on OF bindings. The driver + supports the following revisions: + - SJA1105E (Gen. 1, No TT-Ethernet) + - SJA1105T (Gen. 1, TT-Ethernet) + - SJA1105P (Gen. 2, No SGMII, No TT-Ethernet) + - SJA1105Q (Gen. 2, No SGMII, TT-Ethernet) + - SJA1105R (Gen. 2, SGMII, No TT-Ethernet) + - SJA1105S (Gen. 2, SGMII, TT-Ethernet) + config SMC911X bool "SMSC LAN911x and LAN921x controller driver" @@ -835,6 +852,13 @@ config FSL_LS_MDIO This driver supports the MDIO bus found on the Fman 10G Ethernet MACs and on the mEMAC (which supports both Clauses 22 and 45). +config ASPEED_MDIO + bool "Aspeed MDIO interface support" + depends on DM_MDIO + help + This driver supports the MDIO bus of Aspeed AST2600 SOC. The driver + currently supports Clause 22. + config MDIO_MUX_MMIOREG bool "MDIO MUX accessed as a MMIO register access" depends on DM_MDIO_MUX diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e4078d15a99..a6d0c23f02d 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -3,43 +3,53 @@ # (C) Copyright 2006 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -obj-y += phy/ -obj-$(CONFIG_ALTERA_TSE) += altera_tse.o obj-$(CONFIG_AG7XXX) += ag7xxx.o +obj-$(CONFIG_ALTERA_TSE) += altera_tse.o obj-$(CONFIG_ARMADA100_FEC) += armada100_fec.o +obj-$(CONFIG_ASPEED_MDIO) += aspeed_mdio.o obj-$(CONFIG_BCM6348_ETH) += bcm6348-eth.o obj-$(CONFIG_BCM6368_ETH) += bcm6368-eth.o obj-$(CONFIG_BCMGENET) += bcmgenet.o -obj-$(CONFIG_DRIVER_AX88180) += ax88180.o obj-$(CONFIG_BCM_SF2_ETH) += bcm-sf2-eth.o obj-$(CONFIG_BCM_SF2_ETH_GMAC) += bcm-sf2-eth-gmac.o +obj-$(CONFIG_BNXT_ETH) += bnxt/ obj-$(CONFIG_CALXEDA_XGMAC) += calxedaxgmac.o obj-$(CONFIG_CORTINA_NI_ENET) += cortina_ni.o obj-$(CONFIG_CS8900) += cs8900.o -obj-$(CONFIG_TULIP) += dc2114x.o -obj-$(CONFIG_ETH_DESIGNWARE) += designware.o -obj-$(CONFIG_ETH_DESIGNWARE_MESON8B) += dwmac_meson8b.o -obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac_socfpga.o -obj-$(CONFIG_ETH_DESIGNWARE_S700) += dwmac_s700.o -obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o +obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o obj-$(CONFIG_DNET) += dnet.o +obj-$(CONFIG_DRIVER_AX88180) += ax88180.o +obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o -obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o +obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o obj-$(CONFIG_E1000) += e1000.o 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_EP93XX) += ep93xx_eth.o obj-$(CONFIG_ETHOC) += ethoc.o +obj-$(CONFIG_ETH_DESIGNWARE) += designware.o +obj-$(CONFIG_ETH_DESIGNWARE_MESON8B) += dwmac_meson8b.o +obj-$(CONFIG_ETH_DESIGNWARE_S700) += dwmac_s700.o +obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac_socfpga.o +obj-$(CONFIG_ETH_SANDBOX) += sandbox.o +obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw-bus.o +obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw.o obj-$(CONFIG_FEC_MXC) += fec_mxc.o obj-$(CONFIG_FMAN_ENET) += fm/ +obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o obj-$(CONFIG_FSLDMAFEC) += fsl_mcdmafec.o mcfmii.o +obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o +obj-$(CONFIG_FSL_LS_MDIO) += fsl_ls_mdio.o +obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/ +obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/ +obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o +obj-$(CONFIG_FSL_PFE) += pfe_eth/ obj-$(CONFIG_FTGMAC100) += ftgmac100.o -obj-$(CONFIG_FTMAC110) += ftmac110.o obj-$(CONFIG_FTMAC100) += ftmac100.o +obj-$(CONFIG_FTMAC110) += ftmac110.o obj-$(CONFIG_GMAC_ROCKCHIP) += gmac_rockchip.o +obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o obj-$(CONFIG_KSZ9477) += ksz9477.o obj-$(CONFIG_LAN91C96) += lan91c96.o @@ -51,6 +61,8 @@ obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o obj-$(CONFIG_MDIO_MUX_MESON_G12A) += mdio_mux_meson_g12a.o obj-$(CONFIG_MDIO_MUX_MMIOREG) += mdio_mux_mmioreg.o obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o +obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o +obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o obj-$(CONFIG_MT7620_ETH) += mt7620-eth.o obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o @@ -60,44 +72,35 @@ obj-$(CONFIG_MVNETA) += mvneta.o obj-$(CONFIG_MVPP2) += mvpp2.o obj-$(CONFIG_NATSEMI) += natsemi.o obj-$(CONFIG_NETCONSOLE) += netconsole.o +obj-$(CONFIG_NET_OCTEONTX) += octeontx/ +obj-$(CONFIG_NET_OCTEONTX2) += octeontx2/ obj-$(CONFIG_NS8382X) += ns8382x.o +obj-$(CONFIG_OCTEONTX2_CGX_INTF) += octeontx2/cgx_intf.o +obj-$(CONFIG_OCTEONTX_SMI) += octeontx/smi.o obj-$(CONFIG_PCH_GBE) += pch_gbe.o obj-$(CONFIG_PCNET) += pcnet.o +obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o +obj-$(CONFIG_RENESAS_RAVB) += ravb.o obj-$(CONFIG_RTL8139) += rtl8139.o obj-$(CONFIG_RTL8169) += rtl8169.o -obj-$(CONFIG_ETH_SANDBOX) += sandbox.o -obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw.o -obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw-bus.o obj-$(CONFIG_SH_ETHER) += sh_eth.o -obj-$(CONFIG_RENESAS_RAVB) += ravb.o +obj-$(CONFIG_SJA1105) += sja1105.o obj-$(CONFIG_SMC91111) += smc91111.o obj-$(CONFIG_SMC911X) += smc911x.o +obj-$(CONFIG_SNI_AVE) += sni_ave.o +obj-$(CONFIG_SNI_NETSEC) += sni_netsec.o +obj-$(CONFIG_SUN4I_EMAC) += sunxi_emac.o +obj-$(CONFIG_SUN8I_EMAC) += sun8i_emac.o obj-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o -obj-$(CONFIG_NET_OCTEONTX) += octeontx/ -obj-$(CONFIG_NET_OCTEONTX2) += octeontx2/ -obj-$(CONFIG_OCTEONTX_SMI) += octeontx/smi.o -obj-$(CONFIG_OCTEONTX2_CGX_INTF) += octeontx2/cgx_intf.o -obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o +obj-$(CONFIG_TULIP) += dc2114x.o obj-$(CONFIG_ULI526X) += uli526x.o obj-$(CONFIG_VSC7385_ENET) += vsc7385.o +obj-$(CONFIG_VSC9953) += vsc9953.o obj-$(CONFIG_XILINX_AXIEMAC) += xilinx_axi_emac.o obj-$(CONFIG_XILINX_AXIMRMAC) += xilinx_axi_mrmac.o obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o obj-$(CONFIG_ZYNQ_GEM) += zynq_gem.o -obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/ -obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/ -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/ +obj-y += mscc_eswitch/ +obj-y += phy/ obj-y += qe/ -obj-$(CONFIG_SNI_AVE) += sni_ave.o -obj-$(CONFIG_SNI_NETSEC) += sni_netsec.o obj-y += ti/ -obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o -obj-y += mscc_eswitch/ -obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o -obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o -obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o -obj-$(CONFIG_FSL_LS_MDIO) += fsl_ls_mdio.o diff --git a/drivers/net/aspeed_mdio.c b/drivers/net/aspeed_mdio.c new file mode 100644 index 00000000000..a99715a7282 --- /dev/null +++ b/drivers/net/aspeed_mdio.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Aspeed MDIO driver + * + * (C) Copyright 2021 Aspeed Technology Inc. + * + * This file is inspired from the Linux kernel driver drivers/net/phy/mdio-aspeed.c + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <miiphy.h> +#include <net.h> +#include <reset.h> +#include <linux/bitops.h> +#include <linux/bitfield.h> +#include <linux/io.h> +#include <linux/iopoll.h> + +#define ASPEED_MDIO_CTRL 0x0 +#define ASPEED_MDIO_CTRL_FIRE BIT(31) +#define ASPEED_MDIO_CTRL_ST BIT(28) +#define ASPEED_MDIO_CTRL_ST_C45 0 +#define ASPEED_MDIO_CTRL_ST_C22 1 +#define ASPEED_MDIO_CTRL_OP GENMASK(27, 26) +#define MDIO_C22_OP_WRITE 0b01 +#define MDIO_C22_OP_READ 0b10 +#define ASPEED_MDIO_CTRL_PHYAD GENMASK(25, 21) +#define ASPEED_MDIO_CTRL_REGAD GENMASK(20, 16) +#define ASPEED_MDIO_CTRL_MIIWDATA GENMASK(15, 0) + +#define ASPEED_MDIO_DATA 0x4 +#define ASPEED_MDIO_DATA_MDC_THRES GENMASK(31, 24) +#define ASPEED_MDIO_DATA_MDIO_EDGE BIT(23) +#define ASPEED_MDIO_DATA_MDIO_LATCH GENMASK(22, 20) +#define ASPEED_MDIO_DATA_IDLE BIT(16) +#define ASPEED_MDIO_DATA_MIIRDATA GENMASK(15, 0) + +#define ASPEED_MDIO_TIMEOUT_US 1000 + +struct aspeed_mdio_priv { + void *base; +}; + +static int aspeed_mdio_read(struct udevice *mdio_dev, int addr, int devad, int reg) +{ + struct aspeed_mdio_priv *priv = dev_get_priv(mdio_dev); + u32 ctrl; + u32 data; + int rc; + + if (devad != MDIO_DEVAD_NONE) + return -EOPNOTSUPP; + + ctrl = ASPEED_MDIO_CTRL_FIRE + | FIELD_PREP(ASPEED_MDIO_CTRL_ST, ASPEED_MDIO_CTRL_ST_C22) + | FIELD_PREP(ASPEED_MDIO_CTRL_OP, MDIO_C22_OP_READ) + | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, addr) + | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, reg); + + writel(ctrl, priv->base + ASPEED_MDIO_CTRL); + + rc = readl_poll_timeout(priv->base + ASPEED_MDIO_DATA, data, + data & ASPEED_MDIO_DATA_IDLE, + ASPEED_MDIO_TIMEOUT_US); + + if (rc < 0) + return rc; + + return FIELD_GET(ASPEED_MDIO_DATA_MIIRDATA, data); +} + +static int aspeed_mdio_write(struct udevice *mdio_dev, int addr, int devad, int reg, u16 val) +{ + struct aspeed_mdio_priv *priv = dev_get_priv(mdio_dev); + u32 ctrl; + + if (devad != MDIO_DEVAD_NONE) + return -EOPNOTSUPP; + + ctrl = ASPEED_MDIO_CTRL_FIRE + | FIELD_PREP(ASPEED_MDIO_CTRL_ST, ASPEED_MDIO_CTRL_ST_C22) + | FIELD_PREP(ASPEED_MDIO_CTRL_OP, MDIO_C22_OP_WRITE) + | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, addr) + | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, reg) + | FIELD_PREP(ASPEED_MDIO_CTRL_MIIWDATA, val); + + writel(ctrl, priv->base + ASPEED_MDIO_CTRL); + + return readl_poll_timeout(priv->base + ASPEED_MDIO_CTRL, ctrl, + !(ctrl & ASPEED_MDIO_CTRL_FIRE), + ASPEED_MDIO_TIMEOUT_US); +} + +static const struct mdio_ops aspeed_mdio_ops = { + .read = aspeed_mdio_read, + .write = aspeed_mdio_write, +}; + +static int aspeed_mdio_probe(struct udevice *dev) +{ + struct aspeed_mdio_priv *priv = dev_get_priv(dev); + struct reset_ctl reset_ctl; + int ret = 0; + + priv->base = dev_read_addr_ptr(dev); + + ret = reset_get_by_index(dev, 0, &reset_ctl); + reset_deassert(&reset_ctl); + + return 0; +} + +static const struct udevice_id aspeed_mdio_ids[] = { + { .compatible = "aspeed,ast2600-mdio" }, + { } +}; + +U_BOOT_DRIVER(aspeed_mdio) = { + .name = "aspeed_mdio", + .id = UCLASS_MDIO, + .of_match = aspeed_mdio_ids, + .probe = aspeed_mdio_probe, + .ops = &aspeed_mdio_ops, + .plat_auto = sizeof(struct mdio_perdev_priv), + .priv_auto = sizeof(struct aspeed_mdio_priv), +}; diff --git a/drivers/net/bnxt/Kconfig b/drivers/net/bnxt/Kconfig new file mode 100644 index 00000000000..412ecd43033 --- /dev/null +++ b/drivers/net/bnxt/Kconfig @@ -0,0 +1,7 @@ +config BNXT_ETH + bool "BNXT PCI support" + depends on DM_ETH + select PCI_INIT_R + help + This driver implements support for bnxt pci controller + driver of ethernet class. diff --git a/drivers/net/bnxt/Makefile b/drivers/net/bnxt/Makefile new file mode 100644 index 00000000000..a9d6ce00d5e --- /dev/null +++ b/drivers/net/bnxt/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019-2021 Broadcom. + +# Broadcom nxe Ethernet driver +obj-y += bnxt.o diff --git a/drivers/net/bnxt/bnxt.c b/drivers/net/bnxt/bnxt.c new file mode 100644 index 00000000000..9844e96072e --- /dev/null +++ b/drivers/net/bnxt/bnxt.c @@ -0,0 +1,1708 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019-2021 Broadcom. + */ + +#include <common.h> + +#include <asm/io.h> +#include <dm.h> +#include <linux/delay.h> +#include <memalign.h> +#include <net.h> + +#include "bnxt.h" +#include "bnxt_dbg.h" + +#define bnxt_down_chip(bp) bnxt_hwrm_run(down_chip, bp, 0) +#define bnxt_bring_chip(bp) bnxt_hwrm_run(bring_chip, bp, 1) + +/* Broadcom ethernet driver PCI APIs. */ +static void bnxt_bring_pci(struct bnxt *bp) +{ + u16 cmd_reg = 0; + + dm_pci_read_config16(bp->pdev, PCI_VENDOR_ID, &bp->vendor_id); + dm_pci_read_config16(bp->pdev, PCI_DEVICE_ID, &bp->device_id); + dm_pci_read_config16(bp->pdev, PCI_SUBSYSTEM_VENDOR_ID, &bp->subsystem_vendor); + dm_pci_read_config16(bp->pdev, PCI_SUBSYSTEM_ID, &bp->subsystem_device); + dm_pci_read_config16(bp->pdev, PCI_COMMAND, &bp->cmd_reg); + dm_pci_read_config8(bp->pdev, PCI_INTERRUPT_LINE, &bp->irq); + bp->bar0 = dm_pci_map_bar(bp->pdev, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + bp->bar1 = dm_pci_map_bar(bp->pdev, PCI_BASE_ADDRESS_2, PCI_REGION_MEM); + bp->bar2 = dm_pci_map_bar(bp->pdev, PCI_BASE_ADDRESS_4, PCI_REGION_MEM); + cmd_reg = bp->cmd_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd_reg |= PCI_COMMAND_INTX_DISABLE; /* disable intr */ + dm_pci_write_config16(bp->pdev, PCI_COMMAND, cmd_reg); + dm_pci_read_config16(bp->pdev, PCI_COMMAND, &cmd_reg); + dbg_pci(bp, __func__, cmd_reg); +} + +int bnxt_free_rx_iob(struct bnxt *bp) +{ + unsigned int i; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_RX_IOB))) + return STATUS_SUCCESS; + + for (i = 0; i < bp->rx.buf_cnt; i++) { + if (bp->rx.iob[i]) { + free(bp->rx.iob[i]); + bp->rx.iob[i] = NULL; + } + } + + FLAG_RESET(bp->flag_hwrm, VALID_RX_IOB); + + return STATUS_SUCCESS; +} + +static void set_rx_desc(u8 *buf, void *iob, u16 cons_id, u32 iob_idx) +{ + struct rx_prod_pkt_bd *desc; + u16 off = cons_id * sizeof(struct rx_prod_pkt_bd); + + desc = (struct rx_prod_pkt_bd *)&buf[off]; + desc->flags_type = RX_PROD_PKT_BD_TYPE_RX_PROD_PKT; + desc->len = MAX_ETHERNET_PACKET_BUFFER_SIZE; + desc->opaque = iob_idx; + desc->dma.addr = virt_to_bus(iob); +} + +static int bnxt_alloc_rx_iob(struct bnxt *bp, u16 cons_id, u16 iob_idx) +{ + void *iob; + + iob = memalign(BNXT_DMA_ALIGNMENT, RX_STD_DMA_ALIGNED); + if (!iob) + return -ENOMEM; + + dbg_rx_iob(iob, iob_idx, cons_id); + set_rx_desc((u8 *)bp->rx.bd_virt, iob, cons_id, (u32)iob_idx); + bp->rx.iob[iob_idx] = iob; + + return 0; +} + +void bnxt_mm_init(struct bnxt *bp, const char *func) +{ + memset(bp->hwrm_addr_req, 0, REQ_BUFFER_SIZE); + memset(bp->hwrm_addr_resp, 0, RESP_BUFFER_SIZE); + memset(bp->cq.bd_virt, 0, CQ_RING_DMA_BUFFER_SIZE); + memset(bp->tx.bd_virt, 0, TX_RING_DMA_BUFFER_SIZE); + memset(bp->rx.bd_virt, 0, RX_RING_DMA_BUFFER_SIZE); + + bp->data_addr_mapping = virt_to_bus(bp->hwrm_addr_data); + bp->req_addr_mapping = virt_to_bus(bp->hwrm_addr_req); + bp->resp_addr_mapping = virt_to_bus(bp->hwrm_addr_resp); + bp->wait_link_timeout = LINK_DEFAULT_TIMEOUT; + bp->link_status = STATUS_LINK_DOWN; + bp->media_change = 1; + bp->mtu = MAX_ETHERNET_PACKET_BUFFER_SIZE; + bp->hwrm_max_req_len = HWRM_MAX_REQ_LEN; + bp->rx.buf_cnt = NUM_RX_BUFFERS; + bp->rx.ring_cnt = MAX_RX_DESC_CNT; + bp->tx.ring_cnt = MAX_TX_DESC_CNT; + bp->cq.ring_cnt = MAX_CQ_DESC_CNT; + bp->cq.completion_bit = 0x1; + bp->link_set = LINK_SPEED_DRV_100G; + dbg_mem(bp, func); +} + +void bnxt_free_mem(struct bnxt *bp) +{ + if (bp->cq.bd_virt) { + free(bp->cq.bd_virt); + bp->cq.bd_virt = NULL; + } + + if (bp->rx.bd_virt) { + free(bp->rx.bd_virt); + bp->rx.bd_virt = NULL; + } + + if (bp->tx.bd_virt) { + free(bp->tx.bd_virt); + bp->tx.bd_virt = NULL; + } + + if (bp->hwrm_addr_resp) { + free(bp->hwrm_addr_resp); + bp->resp_addr_mapping = 0; + bp->hwrm_addr_resp = NULL; + } + + if (bp->hwrm_addr_req) { + free(bp->hwrm_addr_req); + bp->req_addr_mapping = 0; + bp->hwrm_addr_req = NULL; + } + + if (bp->hwrm_addr_data) { + free(bp->hwrm_addr_data); + bp->data_addr_mapping = 0; + bp->hwrm_addr_data = NULL; + } + + dbg_mem_free_done(__func__); +} + +int bnxt_alloc_mem(struct bnxt *bp) +{ + bp->hwrm_addr_data = memalign(BNXT_DMA_ALIGNMENT, DMA_BUF_SIZE_ALIGNED); + bp->hwrm_addr_req = memalign(BNXT_DMA_ALIGNMENT, REQ_BUF_SIZE_ALIGNED); + bp->hwrm_addr_resp = MEM_HWRM_RESP; + + memset(&bp->tx, 0, sizeof(struct lm_tx_info_t)); + memset(&bp->rx, 0, sizeof(struct lm_rx_info_t)); + memset(&bp->cq, 0, sizeof(struct lm_cmp_info_t)); + + bp->tx.bd_virt = memalign(BNXT_DMA_ALIGNMENT, TX_RING_DMA_BUFFER_SIZE); + bp->rx.bd_virt = memalign(BNXT_DMA_ALIGNMENT, RX_RING_DMA_BUFFER_SIZE); + bp->cq.bd_virt = memalign(BNXT_DMA_ALIGNMENT, CQ_RING_DMA_BUFFER_SIZE); + + if (bp->hwrm_addr_req && + bp->hwrm_addr_resp && + bp->hwrm_addr_data && + bp->tx.bd_virt && + bp->rx.bd_virt && + bp->cq.bd_virt) { + bnxt_mm_init(bp, __func__); + return STATUS_SUCCESS; + } + + dbg_mem_alloc_fail(__func__); + bnxt_free_mem(bp); + + return -ENOMEM; +} + +static void hwrm_init(struct bnxt *bp, struct input *req, u16 cmd, u16 len) +{ + memset(req, 0, len); + req->req_type = cmd; + req->cmpl_ring = (u16)HWRM_NA_SIGNATURE; + req->target_id = (u16)HWRM_NA_SIGNATURE; + req->resp_addr = bp->resp_addr_mapping; + req->seq_id = bp->seq_id++; +} + +static void hwrm_write_req(struct bnxt *bp, void *req, u32 cnt) +{ + u32 i = 0; + + for (i = 0; i < cnt; i++) + writel(((u32 *)req)[i], bp->bar0 + GRC_COM_CHAN_BASE + (i * 4)); + + writel(0x1, (bp->bar0 + GRC_COM_CHAN_BASE + GRC_COM_CHAN_TRIG)); +} + +static void short_hwrm_cmd_req(struct bnxt *bp, u16 len) +{ + struct hwrm_short_input sreq; + + memset(&sreq, 0, sizeof(struct hwrm_short_input)); + sreq.req_type = (u16)((struct input *)bp->hwrm_addr_req)->req_type; + sreq.signature = SHORT_REQ_SIGNATURE_SHORT_CMD; + sreq.size = len; + sreq.req_addr = bp->req_addr_mapping; + dbg_short_cmd((u8 *)&sreq, __func__, sizeof(struct hwrm_short_input)); + hwrm_write_req(bp, &sreq, sizeof(struct hwrm_short_input) / 4); +} + +static int wait_resp(struct bnxt *bp, u32 tmo, u16 len, const char *func) +{ + struct input *req = (struct input *)bp->hwrm_addr_req; + struct output *resp = (struct output *)bp->hwrm_addr_resp; + u8 *ptr = (u8 *)resp; + u32 idx; + u32 wait_cnt = HWRM_CMD_DEFAULT_MULTIPLAYER((u32)tmo); + u16 resp_len = 0; + u16 ret = STATUS_TIMEOUT; + + if (len > bp->hwrm_max_req_len) + short_hwrm_cmd_req(bp, len); + else + hwrm_write_req(bp, req, (u32)(len / 4)); + + for (idx = 0; idx < wait_cnt; idx++) { + resp_len = resp->resp_len; + if (resp->seq_id == req->seq_id && resp->req_type == req->req_type && + ptr[resp_len - 1] == 1) { + bp->last_resp_code = resp->error_code; + ret = resp->error_code; + break; + } + + udelay(HWRM_CMD_POLL_WAIT_TIME); + } + + dbg_hw_cmd(bp, func, len, resp_len, tmo, ret); + + return (int)ret; +} + +static void bnxt_db_cq(struct bnxt *bp) +{ + writel(CQ_DOORBELL_KEY_IDX(bp->cq.cons_idx), bp->bar1); +} + +static void bnxt_db_rx(struct bnxt *bp, u32 idx) +{ + writel(RX_DOORBELL_KEY_RX | idx, bp->bar1); +} + +static void bnxt_db_tx(struct bnxt *bp, u32 idx) +{ + writel((u32)(TX_DOORBELL_KEY_TX | idx), bp->bar1); +} + +int iob_pad(void *packet, int length) +{ + if (length >= ETH_ZLEN) + return length; + + memset(((u8 *)packet + length), 0x00, (ETH_ZLEN - length)); + + return ETH_ZLEN; +} + +static inline u32 bnxt_tx_avail(struct bnxt *bp) +{ + barrier(); + + return TX_AVAIL(bp->tx.ring_cnt) - + ((bp->tx.prod_id - bp->tx.cons_id) & + (bp->tx.ring_cnt - 1)); +} + +void set_txq(struct bnxt *bp, int entry, dma_addr_t mapping, int len) +{ + struct tx_bd_short *prod_bd; + + prod_bd = (struct tx_bd_short *)BD_NOW(bp->tx.bd_virt, + entry, + sizeof(struct tx_bd_short)); + if (len < 512) + prod_bd->flags_type = TX_BD_SHORT_FLAGS_LHINT_LT512; + else if (len < 1024) + prod_bd->flags_type = TX_BD_SHORT_FLAGS_LHINT_LT1K; + else if (len < 2048) + prod_bd->flags_type = TX_BD_SHORT_FLAGS_LHINT_LT2K; + else + prod_bd->flags_type = TX_BD_SHORT_FLAGS_LHINT_GTE2K; + + prod_bd->flags_type |= TX_BD_FLAGS; + prod_bd->dma.addr = mapping; + prod_bd->len = len; + prod_bd->opaque = (u32)entry; + dump_tx_bd(prod_bd, (u16)(sizeof(struct tx_bd_short))); +} + +static void bnxt_tx_complete(struct bnxt *bp) +{ + bp->tx.cons_id = NEXT_IDX(bp->tx.cons_id, bp->tx.ring_cnt); + bp->tx.cnt++; + dump_tx_stat(bp); +} + +int post_rx_buffers(struct bnxt *bp) +{ + u16 cons_id = (bp->rx.cons_idx % bp->rx.ring_cnt); + u16 iob_idx; + + while (bp->rx.iob_cnt < bp->rx.buf_cnt) { + iob_idx = (cons_id % bp->rx.buf_cnt); + if (!bp->rx.iob[iob_idx]) { + if (bnxt_alloc_rx_iob(bp, cons_id, iob_idx) < 0) { + dbg_rx_alloc_iob_fail(iob_idx, cons_id); + break; + } + } + + cons_id = NEXT_IDX(cons_id, bp->rx.ring_cnt); + bp->rx.iob_cnt++; + } + + if (cons_id != bp->rx.cons_idx) { + dbg_rx_cid(bp->rx.cons_idx, cons_id); + bp->rx.cons_idx = cons_id; + bnxt_db_rx(bp, (u32)cons_id); + } + + FLAG_SET(bp->flag_hwrm, VALID_RX_IOB); + + return STATUS_SUCCESS; +} + +u8 bnxt_rx_drop(struct bnxt *bp, u8 *rx_buf, struct rx_pkt_cmpl_hi *rx_cmp_hi) +{ + u8 chksum_err = 0; + u8 i; + u16 error_flags; + + error_flags = (rx_cmp_hi->errors_v2 >> + RX_PKT_CMPL_ERRORS_BUFFER_ERROR_SFT); + if (rx_cmp_hi->errors_v2 == 0x20 || rx_cmp_hi->errors_v2 == 0x21) + chksum_err = 1; + + if (error_flags && !chksum_err) { + bp->rx.err++; + return 1; + } + + for (i = 0; i < 6; i++) { + if (rx_buf[6 + i] != bp->mac_set[i]) + break; + } + + if (i == 6) { + bp->rx.dropped++; + return 2; /* Drop the loopback packets */ + } + + return 0; +} + +static void bnxt_adv_cq_index(struct bnxt *bp, u16 count) +{ + u16 cons_idx = bp->cq.cons_idx + count; + + if (cons_idx >= MAX_CQ_DESC_CNT) { + /* Toggle completion bit when the ring wraps. */ + bp->cq.completion_bit ^= 1; + cons_idx = cons_idx - MAX_CQ_DESC_CNT; + } + + bp->cq.cons_idx = cons_idx; +} + +void bnxt_adv_rx_index(struct bnxt *bp, u8 *iob, u32 iob_idx) +{ + u16 cons_id = (bp->rx.cons_idx % bp->rx.ring_cnt); + + set_rx_desc((u8 *)bp->rx.bd_virt, (void *)iob, cons_id, iob_idx); + cons_id = NEXT_IDX(cons_id, bp->rx.ring_cnt); + if (cons_id != bp->rx.cons_idx) { + dbg_rx_cid(bp->rx.cons_idx, cons_id); + bp->rx.cons_idx = cons_id; + bnxt_db_rx(bp, (u32)cons_id); + } +} + +void rx_process(struct bnxt *bp, struct rx_pkt_cmpl *rx_cmp, + struct rx_pkt_cmpl_hi *rx_cmp_hi) +{ + u32 desc_idx = rx_cmp->opaque; + u8 *iob = bp->rx.iob[desc_idx]; + + dump_rx_bd(rx_cmp, rx_cmp_hi, desc_idx); + bp->rx.iob_len = rx_cmp->len; + bp->rx.iob_rx = iob; + if (bnxt_rx_drop(bp, iob, rx_cmp_hi)) + bp->rx.iob_recv = PKT_DROPPED; + else + bp->rx.iob_recv = PKT_RECEIVED; + + bp->rx.rx_cnt++; + + dbg_rxp(bp->rx.iob_rx, bp->rx.iob_len, bp->rx.iob_recv); + bnxt_adv_rx_index(bp, iob, desc_idx); + bnxt_adv_cq_index(bp, 2); /* Rx completion is 2 entries. */ +} + +static int bnxt_rx_complete(struct bnxt *bp, struct rx_pkt_cmpl *rx_cmp) +{ + struct rx_pkt_cmpl_hi *rx_cmp_hi; + u8 completion_bit = bp->cq.completion_bit; + + if (bp->cq.cons_idx == (bp->cq.ring_cnt - 1)) { + rx_cmp_hi = (struct rx_pkt_cmpl_hi *)bp->cq.bd_virt; + completion_bit ^= 0x1; /* Ring has wrapped. */ + } else { + rx_cmp_hi = (struct rx_pkt_cmpl_hi *)(rx_cmp + 1); + } + + if (!((rx_cmp_hi->errors_v2 & RX_PKT_CMPL_V2) ^ completion_bit)) + rx_process(bp, rx_cmp, rx_cmp_hi); + + return NO_MORE_CQ_BD_TO_SERVICE; +} + +static int bnxt_hwrm_ver_get(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_ver_get_input); + struct hwrm_ver_get_input *req; + struct hwrm_ver_get_output *resp; + int rc; + + req = (struct hwrm_ver_get_input *)bp->hwrm_addr_req; + resp = (struct hwrm_ver_get_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_VER_GET, cmd_len); + req->hwrm_intf_maj = HWRM_VERSION_MAJOR; + req->hwrm_intf_min = HWRM_VERSION_MINOR; + req->hwrm_intf_upd = HWRM_VERSION_UPDATE; + rc = wait_resp(bp, HWRM_CMD_DEFAULT_TIMEOUT, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + bp->hwrm_spec_code = + resp->hwrm_intf_maj_8b << 16 | + resp->hwrm_intf_min_8b << 8 | + resp->hwrm_intf_upd_8b; + bp->hwrm_cmd_timeout = (u32)resp->def_req_timeout; + if (!bp->hwrm_cmd_timeout) + bp->hwrm_cmd_timeout = (u32)HWRM_CMD_DEFAULT_TIMEOUT; + + if (resp->hwrm_intf_maj_8b >= 1) + bp->hwrm_max_req_len = resp->max_req_win_len; + + bp->chip_id = + resp->chip_rev << 24 | + resp->chip_metal << 16 | + resp->chip_bond_id << 8 | + resp->chip_platform_type; + bp->chip_num = resp->chip_num; + if ((resp->dev_caps_cfg & SHORT_CMD_SUPPORTED) && + (resp->dev_caps_cfg & SHORT_CMD_REQUIRED)) + FLAG_SET(bp->flags, BNXT_FLAG_HWRM_SHORT_CMD_SUPP); + + bp->hwrm_max_ext_req_len = resp->max_ext_req_len; + bp->fw_maj = resp->hwrm_fw_maj_8b; + bp->fw_min = resp->hwrm_fw_min_8b; + bp->fw_bld = resp->hwrm_fw_bld_8b; + bp->fw_rsvd = resp->hwrm_fw_rsvd_8b; + print_fw_ver(resp, bp->hwrm_cmd_timeout); + + return STATUS_SUCCESS; +} + +/* Broadcom ethernet driver Function HW cmds APIs. */ +static int bnxt_hwrm_func_resource_qcaps(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_func_resource_qcaps_input); + struct hwrm_func_resource_qcaps_input *req; + struct hwrm_func_resource_qcaps_output *resp; + int rc; + + req = (struct hwrm_func_resource_qcaps_input *)bp->hwrm_addr_req; + resp = (struct hwrm_func_resource_qcaps_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_FUNC_RESOURCE_QCAPS, cmd_len); + req->fid = (u16)HWRM_NA_SIGNATURE; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc != STATUS_SUCCESS) + return STATUS_SUCCESS; + + FLAG_SET(bp->flags, BNXT_FLAG_RESOURCE_QCAPS_SUPPORT); + /* VFs */ + bp->max_vfs = resp->max_vfs; + bp->vf_res_strategy = resp->vf_reservation_strategy; + /* vNICs */ + bp->min_vnics = resp->min_vnics; + bp->max_vnics = resp->max_vnics; + /* MSI-X */ + bp->max_msix = resp->max_msix; + /* Ring Groups */ + bp->min_hw_ring_grps = resp->min_hw_ring_grps; + bp->max_hw_ring_grps = resp->max_hw_ring_grps; + /* TX Rings */ + bp->min_tx_rings = resp->min_tx_rings; + bp->max_tx_rings = resp->max_tx_rings; + /* RX Rings */ + bp->min_rx_rings = resp->min_rx_rings; + bp->max_rx_rings = resp->max_rx_rings; + /* Completion Rings */ + bp->min_cp_rings = resp->min_cmpl_rings; + bp->max_cp_rings = resp->max_cmpl_rings; + /* RSS Contexts */ + bp->min_rsscos_ctxs = resp->min_rsscos_ctx; + bp->max_rsscos_ctxs = resp->max_rsscos_ctx; + /* L2 Contexts */ + bp->min_l2_ctxs = resp->min_l2_ctxs; + bp->max_l2_ctxs = resp->max_l2_ctxs; + /* Statistic Contexts */ + bp->min_stat_ctxs = resp->min_stat_ctx; + bp->max_stat_ctxs = resp->max_stat_ctx; + dbg_func_resource_qcaps(bp); + + return STATUS_SUCCESS; +} + +static u32 set_ring_info(struct bnxt *bp) +{ + u32 enables = 0; + + bp->num_cmpl_rings = DEFAULT_NUMBER_OF_CMPL_RINGS; + bp->num_tx_rings = DEFAULT_NUMBER_OF_TX_RINGS; + bp->num_rx_rings = DEFAULT_NUMBER_OF_RX_RINGS; + bp->num_hw_ring_grps = DEFAULT_NUMBER_OF_RING_GRPS; + bp->num_stat_ctxs = DEFAULT_NUMBER_OF_STAT_CTXS; + if (bp->min_cp_rings <= DEFAULT_NUMBER_OF_CMPL_RINGS) + bp->num_cmpl_rings = bp->min_cp_rings; + + if (bp->min_tx_rings <= DEFAULT_NUMBER_OF_TX_RINGS) + bp->num_tx_rings = bp->min_tx_rings; + + if (bp->min_rx_rings <= DEFAULT_NUMBER_OF_RX_RINGS) + bp->num_rx_rings = bp->min_rx_rings; + + if (bp->min_hw_ring_grps <= DEFAULT_NUMBER_OF_RING_GRPS) + bp->num_hw_ring_grps = bp->min_hw_ring_grps; + + if (bp->min_stat_ctxs <= DEFAULT_NUMBER_OF_STAT_CTXS) + bp->num_stat_ctxs = bp->min_stat_ctxs; + + print_num_rings(bp); + enables = (FUNC_CFG_REQ_ENABLES_NUM_CMPL_RINGS | + FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS | + FUNC_CFG_REQ_ENABLES_NUM_RX_RINGS | + FUNC_CFG_REQ_ENABLES_NUM_STAT_CTXS | + FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS); + + return enables; +} + +static void bnxt_hwrm_assign_resources(struct bnxt *bp) +{ + struct hwrm_func_cfg_input *req; + u32 enables = 0; + + if (FLAG_TEST(bp->flags, BNXT_FLAG_RESOURCE_QCAPS_SUPPORT)) + enables = set_ring_info(bp); + + req = (struct hwrm_func_cfg_input *)bp->hwrm_addr_req; + req->num_cmpl_rings = bp->num_cmpl_rings; + req->num_tx_rings = bp->num_tx_rings; + req->num_rx_rings = bp->num_rx_rings; + req->num_stat_ctxs = bp->num_stat_ctxs; + req->num_hw_ring_grps = bp->num_hw_ring_grps; + req->enables = enables; +} + +int bnxt_hwrm_nvm_flush(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_nvm_flush_input); + struct hwrm_nvm_flush_input *req; + int rc; + + req = (struct hwrm_nvm_flush_input *)bp->hwrm_addr_req; + + hwrm_init(bp, (void *)req, (u16)HWRM_NVM_FLUSH, cmd_len); + + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_func_qcaps_req(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_func_qcaps_input); + struct hwrm_func_qcaps_input *req; + struct hwrm_func_qcaps_output *resp; + int rc; + + req = (struct hwrm_func_qcaps_input *)bp->hwrm_addr_req; + resp = (struct hwrm_func_qcaps_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_FUNC_QCAPS, cmd_len); + req->fid = (u16)HWRM_NA_SIGNATURE; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + bp->fid = resp->fid; + bp->port_idx = (u8)resp->port_id; + + /* Get MAC address for this PF */ + memcpy(&bp->mac_addr[0], &resp->mac_address[0], ETH_ALEN); + + memcpy(&bp->mac_set[0], &bp->mac_addr[0], ETH_ALEN); + + print_func_qcaps(bp); + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_func_qcfg_req(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_func_qcfg_input); + struct hwrm_func_qcfg_input *req; + struct hwrm_func_qcfg_output *resp; + int rc; + + req = (struct hwrm_func_qcfg_input *)bp->hwrm_addr_req; + resp = (struct hwrm_func_qcfg_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_FUNC_QCFG, cmd_len); + req->fid = (u16)HWRM_NA_SIGNATURE; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + if (resp->flags & FUNC_QCFG_RESP_FLAGS_MULTI_HOST) + FLAG_SET(bp->flags, BNXT_FLAG_MULTI_HOST); + + if (resp->port_partition_type & + FUNC_QCFG_RESP_PORT_PARTITION_TYPE_NPAR1_0) + FLAG_SET(bp->flags, BNXT_FLAG_NPAR_MODE); + + bp->ordinal_value = (u8)resp->pci_id & 0x0F; + bp->stat_ctx_id = resp->stat_ctx_id; + memcpy(&bp->mac_addr[0], &resp->mac_address[0], ETH_ALEN); + print_func_qcfg(bp); + dbg_flags(__func__, bp->flags); + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_func_reset_req(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_func_reset_input); + struct hwrm_func_reset_input *req; + + req = (struct hwrm_func_reset_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_FUNC_RESET, cmd_len); + req->func_reset_level = FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETME; + + return wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); +} + +static int bnxt_hwrm_func_cfg_req(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_func_cfg_input); + struct hwrm_func_cfg_input *req; + + req = (struct hwrm_func_cfg_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_FUNC_CFG, cmd_len); + req->fid = (u16)HWRM_NA_SIGNATURE; + bnxt_hwrm_assign_resources(bp); + + return wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); +} + +static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_func_drv_rgtr_input); + struct hwrm_func_drv_rgtr_input *req; + int rc; + + req = (struct hwrm_func_drv_rgtr_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_FUNC_DRV_RGTR, cmd_len); + /* Register with HWRM */ + req->enables = FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE | + FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD | + FUNC_DRV_RGTR_REQ_ENABLES_VER; + req->async_event_fwd[0] |= 0x01; + req->os_type = FUNC_DRV_RGTR_REQ_OS_TYPE_OTHER; + req->ver_maj = DRIVER_VERSION_MAJOR; + req->ver_min = DRIVER_VERSION_MINOR; + req->ver_upd = DRIVER_VERSION_UPDATE; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_SET(bp->flag_hwrm, VALID_DRIVER_REG); + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_func_drv_unrgtr(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_func_drv_unrgtr_input); + struct hwrm_func_drv_unrgtr_input *req; + int rc; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_DRIVER_REG))) + return STATUS_SUCCESS; + + req = (struct hwrm_func_drv_unrgtr_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_FUNC_DRV_UNRGTR, cmd_len); + req->flags = FUNC_DRV_UNRGTR_REQ_FLAGS_PREPARE_FOR_SHUTDOWN; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_RESET(bp->flag_hwrm, VALID_DRIVER_REG); + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_cfa_l2_filter_alloc(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_cfa_l2_filter_alloc_input); + struct hwrm_cfa_l2_filter_alloc_input *req; + struct hwrm_cfa_l2_filter_alloc_output *resp; + int rc; + u32 flags = CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX; + u32 enables; + + req = (struct hwrm_cfa_l2_filter_alloc_input *)bp->hwrm_addr_req; + resp = (struct hwrm_cfa_l2_filter_alloc_output *)bp->hwrm_addr_resp; + enables = CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID | + CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR | + CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR_MASK; + + hwrm_init(bp, (void *)req, (u16)HWRM_CFA_L2_FILTER_ALLOC, cmd_len); + req->flags = flags; + req->enables = enables; + memcpy((char *)&req->l2_addr[0], (char *)&bp->mac_set[0], ETH_ALEN); + memset((char *)&req->l2_addr_mask[0], 0xff, ETH_ALEN); + memcpy((char *)&req->t_l2_addr[0], (char *)&bp->mac_set[0], ETH_ALEN); + memset((char *)&req->t_l2_addr_mask[0], 0xff, ETH_ALEN); + req->src_type = CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_NPORT; + req->src_id = (u32)bp->port_idx; + req->dst_id = bp->vnic_id; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_SET(bp->flag_hwrm, VALID_L2_FILTER); + bp->l2_filter_id = resp->l2_filter_id; + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_cfa_l2_filter_free(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_cfa_l2_filter_free_input); + struct hwrm_cfa_l2_filter_free_input *req; + int rc; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_L2_FILTER))) + return STATUS_SUCCESS; + + req = (struct hwrm_cfa_l2_filter_free_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_CFA_L2_FILTER_FREE, cmd_len); + req->l2_filter_id = bp->l2_filter_id; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_RESET(bp->flag_hwrm, VALID_L2_FILTER); + + return STATUS_SUCCESS; +} + +u32 bnxt_set_rx_mask(u32 rx_mask) +{ + u32 mask = 0; + + if (!rx_mask) + return mask; + mask = CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; + if (rx_mask != RX_MASK_ACCEPT_NONE) { + if (rx_mask & RX_MASK_ACCEPT_MULTICAST) + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_MCAST; + + if (rx_mask & RX_MASK_ACCEPT_ALL_MULTICAST) + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; + + if (rx_mask & RX_MASK_PROMISCUOUS_MODE) + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; + } + + return mask; +} + +static int bnxt_hwrm_set_rx_mask(struct bnxt *bp, u32 rx_mask) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_cfa_l2_set_rx_mask_input); + struct hwrm_cfa_l2_set_rx_mask_input *req; + u32 mask = bnxt_set_rx_mask(rx_mask); + + req = (struct hwrm_cfa_l2_set_rx_mask_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_CFA_L2_SET_RX_MASK, cmd_len); + req->vnic_id = bp->vnic_id; + req->mask = mask; + + return wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); +} + +static int bnxt_hwrm_port_mac_cfg(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_port_mac_cfg_input); + struct hwrm_port_mac_cfg_input *req; + + req = (struct hwrm_port_mac_cfg_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_PORT_MAC_CFG, cmd_len); + req->lpbk = PORT_MAC_CFG_REQ_LPBK_NONE; + + return wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); +} + +static int bnxt_hwrm_port_phy_qcfg(struct bnxt *bp, u16 idx) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_port_phy_qcfg_input); + struct hwrm_port_phy_qcfg_input *req; + struct hwrm_port_phy_qcfg_output *resp; + int rc; + + req = (struct hwrm_port_phy_qcfg_input *)bp->hwrm_addr_req; + resp = (struct hwrm_port_phy_qcfg_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_PORT_PHY_QCFG, cmd_len); + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + if (idx & SUPPORT_SPEEDS) + bp->support_speeds = resp->support_speeds; + + if (idx & DETECT_MEDIA) + bp->media_detect = resp->module_status; + + if (idx & PHY_SPEED) + bp->current_link_speed = resp->link_speed; + + if (idx & PHY_STATUS) { + if (resp->link == PORT_PHY_QCFG_RESP_LINK_LINK) + bp->link_status = STATUS_LINK_ACTIVE; + else + bp->link_status = STATUS_LINK_DOWN; + } + + return STATUS_SUCCESS; +} + +u16 set_link_speed_mask(u16 link_cap) +{ + u16 speed_mask = 0; + + if (link_cap & SPEED_CAPABILITY_DRV_100M) + speed_mask |= PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_100MB; + + if (link_cap & SPEED_CAPABILITY_DRV_1G) + speed_mask |= PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_1GB; + + if (link_cap & SPEED_CAPABILITY_DRV_10G) + speed_mask |= PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_10GB; + + if (link_cap & SPEED_CAPABILITY_DRV_25G) + speed_mask |= PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_25GB; + + if (link_cap & SPEED_CAPABILITY_DRV_40G) + speed_mask |= PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_40GB; + + if (link_cap & SPEED_CAPABILITY_DRV_50G) + speed_mask |= PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_50GB; + + if (link_cap & SPEED_CAPABILITY_DRV_100G) + speed_mask |= PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_100GB; + + return speed_mask; +} + +static int bnxt_hwrm_port_phy_cfg(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_port_phy_cfg_input); + struct hwrm_port_phy_cfg_input *req; + u32 flags; + u32 enables = 0; + u16 force_link_speed = 0; + u16 auto_link_speed_mask = 0; + u8 auto_mode = 0; + u8 auto_pause = 0; + u8 auto_duplex = 0; + + /* + * If multi_host or NPAR is set to TRUE, + * do not issue hwrm_port_phy_cfg + */ + if (FLAG_TEST(bp->flags, PORT_PHY_FLAGS)) { + dbg_flags(__func__, bp->flags); + return STATUS_SUCCESS; + } + + req = (struct hwrm_port_phy_cfg_input *)bp->hwrm_addr_req; + flags = PORT_PHY_CFG_REQ_FLAGS_FORCE | + PORT_PHY_CFG_REQ_FLAGS_RESET_PHY; + + switch (GET_MEDIUM_SPEED(bp->medium)) { + case MEDIUM_SPEED_1000MBPS: + force_link_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_1GB; + break; + case MEDIUM_SPEED_10GBPS: + force_link_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_10GB; + break; + case MEDIUM_SPEED_25GBPS: + force_link_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_25GB; + break; + case MEDIUM_SPEED_40GBPS: + force_link_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_40GB; + break; + case MEDIUM_SPEED_50GBPS: + force_link_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_50GB; + break; + case MEDIUM_SPEED_100GBPS: + force_link_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_100GB; + break; + default: + /* Enable AUTONEG by default */ + auto_mode = PORT_PHY_CFG_REQ_AUTO_MODE_SPEED_MASK; + flags &= ~PORT_PHY_CFG_REQ_FLAGS_FORCE; + enables |= PORT_PHY_CFG_REQ_ENABLES_AUTO_MODE | + PORT_PHY_CFG_REQ_ENABLES_AUTO_LINK_SPEED_MASK | + PORT_PHY_CFG_REQ_ENABLES_AUTO_DUPLEX | + PORT_PHY_CFG_REQ_ENABLES_AUTO_PAUSE; + auto_pause = PORT_PHY_CFG_REQ_AUTO_PAUSE_TX | + PORT_PHY_CFG_REQ_AUTO_PAUSE_RX; + auto_duplex = PORT_PHY_CFG_REQ_AUTO_DUPLEX_BOTH; + auto_link_speed_mask = bp->support_speeds; + break; + } + + hwrm_init(bp, (void *)req, (u16)HWRM_PORT_PHY_CFG, cmd_len); + req->flags = flags; + req->enables = enables; + req->port_id = bp->port_idx; + req->force_link_speed = force_link_speed; + req->auto_mode = auto_mode; + req->auto_duplex = auto_duplex; + req->auto_pause = auto_pause; + req->auto_link_speed_mask = auto_link_speed_mask; + + return wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); +} + +static int bnxt_qphy_link(struct bnxt *bp) +{ + u16 flag = QCFG_PHY_ALL; + + /* Query Link Status */ + if (bnxt_hwrm_port_phy_qcfg(bp, flag) != STATUS_SUCCESS) + return STATUS_FAILURE; + + if (bp->link_status != STATUS_LINK_ACTIVE) { + /* + * Configure link if it is not up. + * try to bring link up, but don't return + * failure if port_phy_cfg() fails + */ + bnxt_hwrm_port_phy_cfg(bp); + /* refresh link speed values after bringing link up */ + if (bnxt_hwrm_port_phy_qcfg(bp, flag) != STATUS_SUCCESS) + return STATUS_FAILURE; + } + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_stat_ctx_alloc(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_stat_ctx_alloc_input); + struct hwrm_stat_ctx_alloc_input *req; + struct hwrm_stat_ctx_alloc_output *resp; + int rc; + + req = (struct hwrm_stat_ctx_alloc_input *)bp->hwrm_addr_req; + resp = (struct hwrm_stat_ctx_alloc_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_STAT_CTX_ALLOC, cmd_len); + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_SET(bp->flag_hwrm, VALID_STAT_CTX); + bp->stat_ctx_id = (u16)resp->stat_ctx_id; + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_stat_ctx_free(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_stat_ctx_free_input); + struct hwrm_stat_ctx_free_input *req; + int rc; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_STAT_CTX))) + return STATUS_SUCCESS; + + req = (struct hwrm_stat_ctx_free_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_STAT_CTX_FREE, cmd_len); + req->stat_ctx_id = (u32)bp->stat_ctx_id; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_RESET(bp->flag_hwrm, VALID_STAT_CTX); + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_ring_free_grp(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_ring_grp_free_input); + struct hwrm_ring_grp_free_input *req; + int rc; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_RING_GRP))) + return STATUS_SUCCESS; + + req = (struct hwrm_ring_grp_free_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_RING_GRP_FREE, cmd_len); + req->ring_group_id = (u32)bp->ring_grp_id; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_RESET(bp->flag_hwrm, VALID_RING_GRP); + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_ring_alloc_grp(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_ring_grp_alloc_input); + struct hwrm_ring_grp_alloc_input *req; + struct hwrm_ring_grp_alloc_output *resp; + int rc; + + req = (struct hwrm_ring_grp_alloc_input *)bp->hwrm_addr_req; + resp = (struct hwrm_ring_grp_alloc_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_RING_GRP_ALLOC, cmd_len); + req->cr = bp->cq_ring_id; + req->rr = bp->rx_ring_id; + req->ar = (u16)HWRM_NA_SIGNATURE; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_SET(bp->flag_hwrm, VALID_RING_GRP); + bp->ring_grp_id = (u16)resp->ring_group_id; + + return STATUS_SUCCESS; +} + +int bnxt_hwrm_ring_free(struct bnxt *bp, u16 ring_id, u8 ring_type) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_ring_free_input); + struct hwrm_ring_free_input *req; + + req = (struct hwrm_ring_free_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_RING_FREE, cmd_len); + req->ring_type = ring_type; + req->ring_id = ring_id; + + return wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); +} + +static int bnxt_hwrm_ring_alloc(struct bnxt *bp, + dma_addr_t ring_map, + u16 length, + u16 ring_id, + u8 ring_type, + u8 int_mode) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_ring_alloc_input); + struct hwrm_ring_alloc_input *req; + struct hwrm_ring_alloc_output *resp; + int rc; + + req = (struct hwrm_ring_alloc_input *)bp->hwrm_addr_req; + resp = (struct hwrm_ring_alloc_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_RING_ALLOC, cmd_len); + req->ring_type = ring_type; + req->page_tbl_addr = ring_map; + req->page_size = LM_PAGE_SIZE; + req->length = (u32)length; + req->cmpl_ring_id = ring_id; + req->int_mode = int_mode; + if (ring_type == RING_ALLOC_REQ_RING_TYPE_TX) { + req->queue_id = TX_RING_QID; + } else if (ring_type == RING_ALLOC_REQ_RING_TYPE_RX) { + req->queue_id = RX_RING_QID; + req->enables = RING_ALLOC_REQ_ENABLES_RX_BUF_SIZE_VALID; + req->rx_buf_size = MAX_ETHERNET_PACKET_BUFFER_SIZE; + } + + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + if (ring_type == RING_ALLOC_REQ_RING_TYPE_L2_CMPL) { + FLAG_SET(bp->flag_hwrm, VALID_RING_CQ); + bp->cq_ring_id = resp->ring_id; + } else if (ring_type == RING_ALLOC_REQ_RING_TYPE_TX) { + FLAG_SET(bp->flag_hwrm, VALID_RING_TX); + bp->tx_ring_id = resp->ring_id; + } else if (ring_type == RING_ALLOC_REQ_RING_TYPE_RX) { + FLAG_SET(bp->flag_hwrm, VALID_RING_RX); + bp->rx_ring_id = resp->ring_id; + } + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_ring_alloc_cq(struct bnxt *bp) +{ + return bnxt_hwrm_ring_alloc(bp, + virt_to_bus(bp->cq.bd_virt), + bp->cq.ring_cnt, + 0, + RING_ALLOC_REQ_RING_TYPE_L2_CMPL, + BNXT_CQ_INTR_MODE()); +} + +static int bnxt_hwrm_ring_alloc_tx(struct bnxt *bp) +{ + return bnxt_hwrm_ring_alloc(bp, + virt_to_bus(bp->tx.bd_virt), + bp->tx.ring_cnt, bp->cq_ring_id, + RING_ALLOC_REQ_RING_TYPE_TX, + BNXT_INTR_MODE()); +} + +static int bnxt_hwrm_ring_alloc_rx(struct bnxt *bp) +{ + return bnxt_hwrm_ring_alloc(bp, + virt_to_bus(bp->rx.bd_virt), + bp->rx.ring_cnt, + bp->cq_ring_id, + RING_ALLOC_REQ_RING_TYPE_RX, + BNXT_INTR_MODE()); +} + +static int bnxt_hwrm_ring_free_cq(struct bnxt *bp) +{ + int ret = STATUS_SUCCESS; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_RING_CQ))) + return ret; + + ret = RING_FREE(bp, bp->cq_ring_id, RING_FREE_REQ_RING_TYPE_L2_CMPL); + if (ret == STATUS_SUCCESS) + FLAG_RESET(bp->flag_hwrm, VALID_RING_CQ); + + return ret; +} + +static int bnxt_hwrm_ring_free_tx(struct bnxt *bp) +{ + int ret = STATUS_SUCCESS; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_RING_TX))) + return ret; + + ret = RING_FREE(bp, bp->tx_ring_id, RING_FREE_REQ_RING_TYPE_TX); + if (ret == STATUS_SUCCESS) + FLAG_RESET(bp->flag_hwrm, VALID_RING_TX); + + return ret; +} + +static int bnxt_hwrm_ring_free_rx(struct bnxt *bp) +{ + int ret = STATUS_SUCCESS; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_RING_RX))) + return ret; + + ret = RING_FREE(bp, bp->rx_ring_id, RING_FREE_REQ_RING_TYPE_RX); + if (ret == STATUS_SUCCESS) + FLAG_RESET(bp->flag_hwrm, VALID_RING_RX); + + return ret; +} + +static int bnxt_hwrm_vnic_alloc(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_vnic_alloc_input); + struct hwrm_vnic_alloc_input *req; + struct hwrm_vnic_alloc_output *resp; + int rc; + + req = (struct hwrm_vnic_alloc_input *)bp->hwrm_addr_req; + resp = (struct hwrm_vnic_alloc_output *)bp->hwrm_addr_resp; + hwrm_init(bp, (void *)req, (u16)HWRM_VNIC_ALLOC, cmd_len); + req->flags = VNIC_ALLOC_REQ_FLAGS_DEFAULT; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_SET(bp->flag_hwrm, VALID_VNIC_ID); + bp->vnic_id = resp->vnic_id; + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_vnic_free(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_vnic_free_input); + struct hwrm_vnic_free_input *req; + int rc; + + if (!(FLAG_TEST(bp->flag_hwrm, VALID_VNIC_ID))) + return STATUS_SUCCESS; + + req = (struct hwrm_vnic_free_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_VNIC_FREE, cmd_len); + req->vnic_id = bp->vnic_id; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + if (rc) + return STATUS_FAILURE; + + FLAG_RESET(bp->flag_hwrm, VALID_VNIC_ID); + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_vnic_cfg(struct bnxt *bp) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_vnic_cfg_input); + struct hwrm_vnic_cfg_input *req; + + req = (struct hwrm_vnic_cfg_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_VNIC_CFG, cmd_len); + req->enables = VNIC_CFG_REQ_ENABLES_MRU; + req->mru = bp->mtu; + req->enables |= VNIC_CFG_REQ_ENABLES_DFLT_RING_GRP; + req->dflt_ring_grp = bp->ring_grp_id; + req->vnic_id = bp->vnic_id; + + return wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); +} + +static int set_phy_speed(struct bnxt *bp) +{ + char name[20]; + u16 flag = PHY_STATUS | PHY_SPEED | DETECT_MEDIA; + + /* Query Link Status */ + if (bnxt_hwrm_port_phy_qcfg(bp, flag) != STATUS_SUCCESS) + return STATUS_FAILURE; + + switch (bp->current_link_speed) { + case PORT_PHY_QCFG_RESP_LINK_SPEED_100GB: + sprintf(name, "%s %s", str_100, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_50GB: + sprintf(name, "%s %s", str_50, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_40GB: + sprintf(name, "%s %s", str_40, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_25GB: + sprintf(name, "%s %s", str_25, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_20GB: + sprintf(name, "%s %s", str_20, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_10GB: + sprintf(name, "%s %s", str_10, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_2_5GB: + sprintf(name, "%s %s", str_2_5, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_2GB: + sprintf(name, "%s %s", str_2, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_1GB: + sprintf(name, "%s %s", str_1, str_gbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_100MB: + sprintf(name, "%s %s", str_100, str_mbps); + break; + case PORT_PHY_QCFG_RESP_LINK_SPEED_10MB: + sprintf(name, "%s %s", str_10, str_mbps); + break; + default: + sprintf(name, "%s %x", str_unknown, bp->current_link_speed); + } + + dbg_phy_speed(bp, name); + + return STATUS_SUCCESS; +} + +static int set_phy_link(struct bnxt *bp, u32 tmo) +{ + int ret; + + set_phy_speed(bp); + dbg_link_status(bp); + ret = STATUS_FAILURE; + if (bp->link_status == STATUS_LINK_ACTIVE) { + dbg_link_state(bp, tmo); + ret = STATUS_SUCCESS; + } + + return ret; +} + +static int get_phy_link(struct bnxt *bp) +{ + u16 flag = PHY_STATUS | PHY_SPEED | DETECT_MEDIA; + + dbg_chip_info(bp); + /* Query Link Status */ + if (bnxt_hwrm_port_phy_qcfg(bp, flag) != STATUS_SUCCESS) + return STATUS_FAILURE; + + set_phy_link(bp, 100); + + return STATUS_SUCCESS; +} + +static int bnxt_hwrm_set_async_event(struct bnxt *bp) +{ + int rc; + u16 cmd_len = (u16)sizeof(struct hwrm_func_cfg_input); + struct hwrm_func_cfg_input *req; + + req = (struct hwrm_func_cfg_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_FUNC_CFG, cmd_len); + req->fid = (u16)HWRM_NA_SIGNATURE; + req->enables = FUNC_CFG_REQ_ENABLES_ASYNC_EVENT_CR; + req->async_event_cr = bp->cq_ring_id; + rc = wait_resp(bp, bp->hwrm_cmd_timeout, cmd_len, __func__); + + return rc; +} + +int bnxt_hwrm_get_nvmem(struct bnxt *bp, + u16 data_len, + u16 option_num, + u16 dimensions, + u16 index_0) +{ + u16 cmd_len = (u16)sizeof(struct hwrm_nvm_get_variable_input); + struct hwrm_nvm_get_variable_input *req; + + req = (struct hwrm_nvm_get_variable_input *)bp->hwrm_addr_req; + hwrm_init(bp, (void *)req, (u16)HWRM_NVM_GET_VARIABLE, cmd_len); + req->dest_data_addr = bp->data_addr_mapping; + req->data_len = data_len; + req->option_num = option_num; + req->dimensions = dimensions; + req->index_0 = index_0; + + return wait_resp(bp, + HWRM_CMD_FLASH_MULTIPLAYER(bp->hwrm_cmd_timeout), + cmd_len, + __func__); +} + +static void set_medium(struct bnxt *bp) +{ + switch (bp->link_set & LINK_SPEED_DRV_MASK) { + case LINK_SPEED_DRV_1G: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_1000MBPS); + break; + case LINK_SPEED_DRV_2_5G: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_2500MBPS); + break; + case LINK_SPEED_DRV_10G: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_10GBPS); + break; + case LINK_SPEED_DRV_25G: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_25GBPS); + break; + case LINK_SPEED_DRV_40G: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_40GBPS); + break; + case LINK_SPEED_DRV_50G: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_50GBPS); + break; + case LINK_SPEED_DRV_100G: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_100GBPS); + break; + case LINK_SPEED_DRV_200G: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_200GBPS); + break; + case LINK_SPEED_DRV_AUTONEG: + bp->medium = SET_MEDIUM_SPEED(bp, MEDIUM_SPEED_AUTONEG); + break; + default: + bp->medium = SET_MEDIUM_DUPLEX(bp, MEDIUM_FULL_DUPLEX); + break; + } +} + +static int bnxt_hwrm_get_link_speed(struct bnxt *bp) +{ + u32 *ptr32 = (u32 *)bp->hwrm_addr_data; + + if (bnxt_hwrm_get_nvmem(bp, + 4, + (u16)LINK_SPEED_DRV_NUM, + 1, + (u16)bp->port_idx) != STATUS_SUCCESS) + return STATUS_FAILURE; + + bp->link_set = *ptr32; + bp->link_set &= SPEED_DRV_MASK; + set_medium(bp); + + return STATUS_SUCCESS; +} + +typedef int (*hwrm_func_t)(struct bnxt *bp); + +hwrm_func_t down_chip[] = { + bnxt_hwrm_cfa_l2_filter_free, /* Free l2 filter */ + bnxt_free_rx_iob, /* Free rx iob */ + bnxt_hwrm_vnic_free, /* Free vnic */ + bnxt_hwrm_ring_free_grp, /* Free ring group */ + bnxt_hwrm_ring_free_rx, /* Free rx ring */ + bnxt_hwrm_ring_free_tx, /* Free tx ring */ + bnxt_hwrm_ring_free_cq, /* Free CQ ring */ + bnxt_hwrm_stat_ctx_free, /* Free Stat ctx */ + bnxt_hwrm_func_drv_unrgtr, /* unreg driver */ + NULL, +}; + +hwrm_func_t bring_chip[] = { + bnxt_hwrm_ver_get, /* HWRM_VER_GET */ + bnxt_hwrm_func_reset_req, /* HWRM_FUNC_RESET */ + bnxt_hwrm_func_drv_rgtr, /* HWRM_FUNC_DRV_RGTR */ + bnxt_hwrm_func_resource_qcaps, /* HWRM_FUNC_RESOURCE_QCAPS */ + bnxt_hwrm_func_qcfg_req, /* HWRM_FUNC_QCFG */ + bnxt_hwrm_func_qcaps_req, /* HWRM_FUNC_QCAPS */ + bnxt_hwrm_get_link_speed, /* HWRM_NVM_GET_VARIABLE - 203 */ + bnxt_hwrm_port_mac_cfg, /* HWRM_PORT_MAC_CFG */ + bnxt_qphy_link, /* HWRM_PORT_PHY_QCFG */ + bnxt_hwrm_func_cfg_req, /* HWRM_FUNC_CFG - ring resource*/ + bnxt_hwrm_stat_ctx_alloc, /* Allocate Stat Ctx ID */ + bnxt_hwrm_ring_alloc_cq, /* Allocate CQ Ring */ + bnxt_hwrm_ring_alloc_tx, /* Allocate Tx ring */ + bnxt_hwrm_ring_alloc_rx, /* Allocate Rx Ring */ + bnxt_hwrm_ring_alloc_grp, /* Create Ring Group */ + post_rx_buffers, /* Post RX buffers */ + bnxt_hwrm_set_async_event, /* ENABLES_ASYNC_EVENT_CR */ + bnxt_hwrm_vnic_alloc, /* Alloc VNIC */ + bnxt_hwrm_vnic_cfg, /* Config VNIC */ + bnxt_hwrm_cfa_l2_filter_alloc, /* Alloc L2 Filter */ + get_phy_link, /* Get Physical Link */ + NULL, +}; + +int bnxt_hwrm_run(hwrm_func_t cmds[], struct bnxt *bp, int flag) +{ + hwrm_func_t *ptr; + int ret; + int status = STATUS_SUCCESS; + + for (ptr = cmds; *ptr; ++ptr) { + ret = (*ptr)(bp); + if (ret) { + status = STATUS_FAILURE; + /* Continue till all cleanup routines are called */ + if (flag) + return STATUS_FAILURE; + } + } + + return status; +} + +/* Broadcom ethernet driver Network interface APIs. */ +static int bnxt_start(struct udevice *dev) +{ + struct bnxt *bp = dev_get_priv(dev); + + if (bnxt_hwrm_set_rx_mask(bp, RX_MASK) != STATUS_SUCCESS) + return STATUS_FAILURE; + + bp->card_en = true; + return STATUS_SUCCESS; +} + +static int bnxt_send(struct udevice *dev, void *packet, int length) +{ + struct bnxt *bp = dev_get_priv(dev); + int len; + u16 entry; + dma_addr_t mapping; + + if (bnxt_tx_avail(bp) < 1) { + dbg_no_tx_bd(); + return -ENOBUFS; + } + + entry = bp->tx.prod_id; + len = iob_pad(packet, length); + mapping = virt_to_bus(packet); + set_txq(bp, entry, mapping, len); + entry = NEXT_IDX(entry, bp->tx.ring_cnt); + dump_tx_pkt(packet, mapping, len); + bnxt_db_tx(bp, (u32)entry); + bp->tx.prod_id = entry; + bp->tx.cnt_req++; + bnxt_tx_complete(bp); + + return 0; +} + +static void bnxt_link_evt(struct bnxt *bp, struct cmpl_base *cmp) +{ + struct hwrm_async_event_cmpl *evt; + + evt = (struct hwrm_async_event_cmpl *)cmp; + switch (evt->event_id) { + case ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE: + if (evt->event_data1 & 0x01) + bp->link_status = STATUS_LINK_ACTIVE; + else + bp->link_status = STATUS_LINK_DOWN; + + set_phy_link(bp, 0); + break; + default: + break; + } +} + +static int bnxt_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct bnxt *bp = dev_get_priv(dev); + struct cmpl_base *cmp; + u16 old_cons_idx = bp->cq.cons_idx; + int done = SERVICE_NEXT_CQ_BD; + u32 cq_type; + + while (done == SERVICE_NEXT_CQ_BD) { + cmp = (struct cmpl_base *)BD_NOW(bp->cq.bd_virt, + bp->cq.cons_idx, + sizeof(struct cmpl_base)); + if ((cmp->info3_v & CMPL_BASE_V) ^ bp->cq.completion_bit) + break; + + cq_type = cmp->type & CMPL_BASE_TYPE_MASK; + dump_evt((u8 *)cmp, cq_type, bp->cq.cons_idx); + dump_CQ(cmp, bp->cq.cons_idx); + switch (cq_type) { + case CMPL_BASE_TYPE_HWRM_ASYNC_EVENT: + bnxt_link_evt(bp, cmp); + fallthrough; + case CMPL_BASE_TYPE_TX_L2: + case CMPL_BASE_TYPE_STAT_EJECT: + bnxt_adv_cq_index(bp, 1); + break; + case CMPL_BASE_TYPE_RX_L2: + done = bnxt_rx_complete(bp, (struct rx_pkt_cmpl *)cmp); + break; + default: + done = NO_MORE_CQ_BD_TO_SERVICE; + break; + } + } + + if (bp->cq.cons_idx != old_cons_idx) + bnxt_db_cq(bp); + + if (bp->rx.iob_recv == PKT_RECEIVED) { + *packetp = bp->rx.iob_rx; + return bp->rx.iob_len; + } + + return -EAGAIN; +} + +static void bnxt_stop(struct udevice *dev) +{ + struct bnxt *bp = dev_get_priv(dev); + + if (bp->card_en) { + bnxt_hwrm_set_rx_mask(bp, 0); + bp->card_en = false; + } +} + +static int bnxt_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct bnxt *bp = dev_get_priv(dev); + + dbg_rx_pkt(bp, __func__, packet, length); + bp->rx.iob_recv = PKT_DONE; + bp->rx.iob_len = 0; + bp->rx.iob_rx = NULL; + + return 0; +} + +static int bnxt_read_rom_hwaddr(struct udevice *dev) +{ + struct eth_pdata *plat = dev_get_plat(dev); + struct bnxt *bp = dev_get_priv(dev); + + memcpy(plat->enetaddr, bp->mac_set, ETH_ALEN); + + return 0; +} + +static const struct eth_ops bnxt_eth_ops = { + .start = bnxt_start, + .send = bnxt_send, + .recv = bnxt_recv, + .stop = bnxt_stop, + .free_pkt = bnxt_free_pkt, + .read_rom_hwaddr = bnxt_read_rom_hwaddr, +}; + +static const struct udevice_id bnxt_eth_ids[] = { + { .compatible = "broadcom,nxe" }, + { } +}; + +static int bnxt_eth_bind(struct udevice *dev) +{ + char name[20]; + + sprintf(name, "bnxt_eth%u", dev_seq(dev)); + + return device_set_name(dev, name); +} + +static int bnxt_eth_probe(struct udevice *dev) +{ + struct bnxt *bp = dev_get_priv(dev); + int ret; + + ret = bnxt_alloc_mem(bp); + if (ret) { + printf("*** error: bnxt_alloc_mem failed! ***\n"); + return ret; + } + + bp->cardnum = dev_seq(dev); + bp->name = dev->name; + bp->pdev = (struct udevice *)dev; + + bnxt_bring_pci(bp); + + ret = bnxt_bring_chip(bp); + if (ret) { + printf("*** error: bnxt_bring_chip failed! ***\n"); + return -ENODATA; + } + + return 0; +} + +static int bnxt_eth_remove(struct udevice *dev) +{ + struct bnxt *bp = dev_get_priv(dev); + + bnxt_down_chip(bp); + bnxt_free_mem(bp); + + return 0; +} + +static struct pci_device_id bnxt_nics[] = { + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NXT_57320)}, + {} +}; + +U_BOOT_DRIVER(eth_bnxt) = { + .name = "eth_bnxt", + .id = UCLASS_ETH, + .of_match = bnxt_eth_ids, + .bind = bnxt_eth_bind, + .probe = bnxt_eth_probe, + .remove = bnxt_eth_remove, + .ops = &bnxt_eth_ops, + .priv_auto = sizeof(struct bnxt), + .plat_auto = sizeof(struct eth_pdata), + .flags = DM_FLAG_ACTIVE_DMA, +}; + +U_BOOT_PCI_DEVICE(eth_bnxt, bnxt_nics); diff --git a/drivers/net/bnxt/bnxt.h b/drivers/net/bnxt/bnxt.h new file mode 100644 index 00000000000..6c648272af1 --- /dev/null +++ b/drivers/net/bnxt/bnxt.h @@ -0,0 +1,390 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019-2021 Broadcom. + */ + +#ifndef _BNXT_H_ +#define _BNXT_H_ + +#include <pci.h> +#include <linux/if_ether.h> + +#include "bnxt_hsi.h" + +union dma_addr64_t { + dma_addr_t addr; + u64 as_u64; +}; + +#define DRIVER_VERSION_MAJOR 1 +#define DRIVER_VERSION_MINOR 0 +#define DRIVER_VERSION_UPDATE 0 + +/* Broadcom ethernet driver defines. */ +#define FLAG_SET(f, b) ((f) |= (b)) +#define FLAG_TEST(f, b) ((f) & (b)) +#define FLAG_RESET(f, b) ((f) &= ~(b)) +#define BNXT_FLAG_HWRM_SHORT_CMD_SUPP BIT(0) +#define BNXT_FLAG_HWRM_SHORT_CMD_REQ BIT(1) +#define BNXT_FLAG_RESOURCE_QCAPS_SUPPORT BIT(2) +#define BNXT_FLAG_MULTI_HOST BIT(3) +#define BNXT_FLAG_NPAR_MODE BIT(4) +/******************************************************************************* + * Status codes. + ******************************************************************************/ +#define STATUS_SUCCESS 0 +#define STATUS_FAILURE 1 +#define STATUS_LINK_ACTIVE 4 +#define STATUS_LINK_DOWN 5 +#define STATUS_TIMEOUT 0xffff +/******************************************************************************* + * Receive filter masks. + ******************************************************************************/ +#define RX_MASK_ACCEPT_NONE 0x0000 +#define RX_MASK_ACCEPT_MULTICAST 0x0002 +#define RX_MASK_ACCEPT_ALL_MULTICAST 0x0004 +#define RX_MASK_ACCEPT_BROADCAST 0x0008 +#define RX_MASK_PROMISCUOUS_MODE 0x10000 +/******************************************************************************* + * media speed. + ******************************************************************************/ +#define MEDIUM_SPEED_AUTONEG 0x0000L +#define MEDIUM_SPEED_1000MBPS 0x0300L +#define MEDIUM_SPEED_2500MBPS 0x0400L +#define MEDIUM_SPEED_10GBPS 0x0600L +#define MEDIUM_SPEED_25GBPS 0x0800L +#define MEDIUM_SPEED_40GBPS 0x0900L +#define MEDIUM_SPEED_50GBPS 0x0a00L +#define MEDIUM_SPEED_100GBPS 0x0b00L +#define MEDIUM_SPEED_200GBPS 0x0c00L +#define MEDIUM_SPEED_MASK 0xff00L +#define GET_MEDIUM_SPEED(m) ((m) & MEDIUM_SPEED_MASK) +#define SET_MEDIUM_SPEED(bp, s) (((bp)->medium & ~MEDIUM_SPEED_MASK) | (s)) +#define MEDIUM_UNKNOWN_DUPLEX 0x00000L +#define MEDIUM_FULL_DUPLEX 0x00000L +#define MEDIUM_HALF_DUPLEX 0x10000L +#define GET_MEDIUM_DUPLEX(m) ((m) & MEDIUM_HALF_DUPLEX) +#define SET_MEDIUM_DUPLEX(bp, d) (((bp)->medium & ~MEDIUM_HALF_DUPLEX) | (d)) +#define MEDIUM_SELECTIVE_AUTONEG 0x01000000L +#define GET_MEDIUM_AUTONEG_MODE(m) ((m) & 0xff000000L) +#define GRC_COM_CHAN_BASE 0 +#define GRC_COM_CHAN_TRIG 0x100 +#define HWRM_CMD_DEFAULT_TIMEOUT 500 /* in Miliseconds */ +#define HWRM_CMD_POLL_WAIT_TIME 100 /* In MicroeSconds */ +#define HWRM_CMD_DEFAULT_MULTIPLAYER(a) ((a) * 10) +#define HWRM_CMD_FLASH_MULTIPLAYER(a) ((a) * 100) +#define HWRM_CMD_FLASH_ERASE_MULTIPLAYER(a) ((a) * 1000) +#define MAX_ETHERNET_PACKET_BUFFER_SIZE 1536 +#define DEFAULT_NUMBER_OF_CMPL_RINGS 0x01 +#define DEFAULT_NUMBER_OF_TX_RINGS 0x01 +#define DEFAULT_NUMBER_OF_RX_RINGS 0x01 +#define DEFAULT_NUMBER_OF_RING_GRPS 0x01 +#define DEFAULT_NUMBER_OF_STAT_CTXS 0x01 +#define NUM_RX_BUFFERS 512 +#define MAX_RX_DESC_CNT 1024 +#define MAX_TX_DESC_CNT 512 +#define MAX_CQ_DESC_CNT 2048 +#define TX_RING_DMA_BUFFER_SIZE (MAX_TX_DESC_CNT * sizeof(struct tx_bd_short)) +#define RX_RING_DMA_BUFFER_SIZE \ + (MAX_RX_DESC_CNT * sizeof(struct rx_prod_pkt_bd)) +#define CQ_RING_DMA_BUFFER_SIZE (MAX_CQ_DESC_CNT * sizeof(struct cmpl_base)) +#define BNXT_DMA_ALIGNMENT 256 //64 +#define REQ_BUFFER_SIZE 1024 +#define RESP_BUFFER_SIZE 1024 +#define DMA_BUFFER_SIZE 1024 +#define LM_PAGE_BITS 8 +#define BNXT_RX_STD_DMA_SZ 1536 +#define NEXT_IDX(N, S) (((N) + 1) & ((S) - 1)) +#define BD_NOW(bd, entry, len) (&((u8 *)(bd))[(entry) * (len)]) +#define BNXT_CQ_INTR_MODE() RING_ALLOC_REQ_INT_MODE_POLL +#define BNXT_INTR_MODE() RING_ALLOC_REQ_INT_MODE_POLL +/* Set default link timeout period to 500 millseconds */ +#define LINK_DEFAULT_TIMEOUT 500 +#define RX_MASK \ + (RX_MASK_ACCEPT_BROADCAST | \ + RX_MASK_ACCEPT_ALL_MULTICAST | \ + RX_MASK_ACCEPT_MULTICAST) +#define TX_RING_QID ((u16)bp->port_idx * 10) +#define RX_RING_QID 0 +#define LM_PAGE_SIZE LM_PAGE_BITS +#define virt_to_bus(a) ((dma_addr_t)(a)) +#define REQ_BUF_SIZE_ALIGNED ALIGN(REQ_BUFFER_SIZE, BNXT_DMA_ALIGNMENT) +#define RESP_BUF_SIZE_ALIGNED ALIGN(RESP_BUFFER_SIZE, BNXT_DMA_ALIGNMENT) +#define DMA_BUF_SIZE_ALIGNED ALIGN(DMA_BUFFER_SIZE, BNXT_DMA_ALIGNMENT) +#define RX_STD_DMA_ALIGNED ALIGN(BNXT_RX_STD_DMA_SZ, BNXT_DMA_ALIGNMENT) +#define PCI_COMMAND_INTX_DISABLE 0x0400 /* Interrupt disable */ +#define TX_AVAIL(r) ((r) - 1) +#define NO_MORE_CQ_BD_TO_SERVICE 1 +#define SERVICE_NEXT_CQ_BD 0 +#define PHY_STATUS 0x0001 +#define PHY_SPEED 0x0002 +#define DETECT_MEDIA 0x0004 +#define SUPPORT_SPEEDS 0x0008 +#define str_1 "1" +#define str_2 "2" +#define str_2_5 "2.5" +#define str_10 "10" +#define str_20 "20" +#define str_25 "25" +#define str_40 "40" +#define str_50 "50" +#define str_100 "100" +#define str_gbps "Gbps" +#define str_mbps "Mbps" +#define str_unknown "Unknown" +/* Broadcom ethernet driver nvm defines. */ +/* nvm cfg 1 - MAC settings */ +#define FUNC_MAC_ADDR_NUM 1 +/* nvm cfg 203 - u32 link_settings */ +#define LINK_SPEED_DRV_NUM 203 +#define LINK_SPEED_DRV_MASK 0x0000000F +#define LINK_SPEED_DRV_SHIFT 0 +#define LINK_SPEED_DRV_AUTONEG 0x0 +#define LINK_SPEED_DRV_1G 0x1 +#define LINK_SPEED_DRV_10G 0x2 +#define LINK_SPEED_DRV_25G 0x3 +#define LINK_SPEED_DRV_40G 0x4 +#define LINK_SPEED_DRV_50G 0x5 +#define LINK_SPEED_DRV_100G 0x6 +#define LINK_SPEED_DRV_200G 0x7 +#define LINK_SPEED_DRV_2_5G 0xE +#define LINK_SPEED_DRV_100M 0xF +/* nvm cfg 201 - u32 speed_cap_mask */ +#define SPEED_CAPABILITY_DRV_1G 0x1 +#define SPEED_CAPABILITY_DRV_10G 0x2 +#define SPEED_CAPABILITY_DRV_25G 0x4 +#define SPEED_CAPABILITY_DRV_40G 0x8 +#define SPEED_CAPABILITY_DRV_50G 0x10 +#define SPEED_CAPABILITY_DRV_100G 0x20 +#define SPEED_CAPABILITY_DRV_100M 0x8000 +/* nvm cfg 202 */ +/* nvm cfg 205 */ +#define LINK_SPEED_FW_NUM 205 +/* nvm cfg 210 */ +/* nvm cfg 211 */ +/* nvm cfg 213 */ +#define SPEED_DRV_MASK LINK_SPEED_DRV_MASK +/****************************************************************************** + * Doorbell info. + *****************************************************************************/ +#define RX_DOORBELL_KEY_RX (0x1UL << 28) +#define TX_DOORBELL_KEY_TX (0x0UL << 28) + +#define CMPL_DOORBELL_IDX_VALID 0x4000000UL +#define CMPL_DOORBELL_KEY_CMPL (0x2UL << 28) + +/****************************************************************************** + * Transmit info. + *****************************************************************************/ +struct tx_bd_short { + u16 flags_type; +#define TX_BD_SHORT_TYPE_TX_BD_SHORT 0x0UL +#define TX_BD_SHORT_FLAGS_PACKET_END 0x40UL +#define TX_BD_SHORT_FLAGS_NO_CMPL 0x80UL +#define TX_BD_SHORT_FLAGS_BD_CNT_SFT 8 +#define TX_BD_SHORT_FLAGS_LHINT_LT512 (0x0UL << 13) +#define TX_BD_SHORT_FLAGS_LHINT_LT1K (0x1UL << 13) +#define TX_BD_SHORT_FLAGS_LHINT_LT2K (0x2UL << 13) +#define TX_BD_SHORT_FLAGS_LHINT_GTE2K (0x3UL << 13) +#define TX_BD_SHORT_FLAGS_COAL_NOW 0x8000UL + u16 len; + u32 opaque; + union dma_addr64_t dma; +}; + +struct lm_tx_info_t { + void *bd_virt; + u16 prod_id; /* Tx producer index. */ + u16 cons_id; + u16 ring_cnt; + u32 cnt; /* Tx statistics. */ + u32 cnt_req; +}; + +struct cmpl_base { + u16 type; +#define CMPL_BASE_TYPE_MASK 0x3fUL +#define CMPL_BASE_TYPE_TX_L2 0x0UL +#define CMPL_BASE_TYPE_RX_L2 0x11UL +#define CMPL_BASE_TYPE_STAT_EJECT 0x1aUL +#define CMPL_BASE_TYPE_HWRM_ASYNC_EVENT 0x2eUL + u16 info1; + u32 info2; + u32 info3_v; +#define CMPL_BASE_V 0x1UL + u32 info4; +}; + +struct lm_cmp_info_t { + void *bd_virt; + u16 cons_idx; + u16 ring_cnt; + u8 completion_bit; + u8 res[3]; +}; + +struct rx_pkt_cmpl { + u16 flags_type; + u16 len; + u32 opaque; + u8 agg_bufs_v1; + u8 rss_hash_type; + u8 payload_offset; + u8 unused1; + u32 rss_hash; +}; + +struct rx_pkt_cmpl_hi { + u32 flags2; + u32 metadata; + u16 errors_v2; +#define RX_PKT_CMPL_V2 0x1UL +#define RX_PKT_CMPL_ERRORS_BUFFER_ERROR_SFT 1 + u16 cfa_code; + u32 reorder; +}; + +struct rx_prod_pkt_bd { + u16 flags_type; +#define RX_PROD_PKT_BD_TYPE_RX_PROD_PKT 0x4UL + u16 len; + u32 opaque; + union dma_addr64_t dma; +}; + +struct lm_rx_info_t { + void *bd_virt; + void *iob[NUM_RX_BUFFERS]; + void *iob_rx; + u16 iob_len; + u16 iob_recv; + u16 iob_cnt; + u16 buf_cnt; /* Total Rx buffer descriptors. */ + u16 ring_cnt; + u16 cons_idx; /* Last processed consumer index. */ + u32 rx_cnt; + u32 rx_buf_cnt; + u32 err; + u32 crc; + u32 dropped; +}; + +#define VALID_DRIVER_REG 0x0001 +#define VALID_STAT_CTX 0x0002 +#define VALID_RING_CQ 0x0004 +#define VALID_RING_TX 0x0008 +#define VALID_RING_RX 0x0010 +#define VALID_RING_GRP 0x0020 +#define VALID_VNIC_ID 0x0040 +#define VALID_RX_IOB 0x0080 +#define VALID_L2_FILTER 0x0100 + +enum RX_FLAGS { + PKT_DONE = 0, + PKT_RECEIVED = 1, + PKT_DROPPED = 2, +}; + +struct bnxt { + struct udevice *pdev; + const char *name; + unsigned int cardnum; + void *hwrm_addr_req; + void *hwrm_addr_resp; + void *hwrm_addr_data; + dma_addr_t data_addr_mapping; + dma_addr_t req_addr_mapping; + dma_addr_t resp_addr_mapping; + struct lm_tx_info_t tx; /* Tx info. */ + struct lm_rx_info_t rx; /* Rx info. */ + struct lm_cmp_info_t cq; /* completion info. */ + u16 last_resp_code; + u16 seq_id; + u32 flag_hwrm; + u32 flags; + u16 vendor_id; + u16 device_id; + u16 subsystem_vendor; + u16 subsystem_device; + u16 cmd_reg; + u8 irq; + void __iomem *bar0; + void __iomem *bar1; + void __iomem *bar2; + u16 chip_num; + /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ + u32 chip_id; + u32 hwrm_cmd_timeout; + u16 hwrm_spec_code; + u16 hwrm_max_req_len; + u16 hwrm_max_ext_req_len; + u8 fw_maj; + u8 fw_min; + u8 fw_bld; + u8 fw_rsvd; + u8 mac_addr[ETH_ALEN]; /* HW MAC address */ + u8 mac_set[ETH_ALEN]; /* NVM Configured MAC */ + u16 fid; + u8 port_idx; + u8 ordinal_value; + u16 mtu; + u16 ring_grp_id; + u16 cq_ring_id; + u16 tx_ring_id; + u16 rx_ring_id; + u16 current_link_speed; + u16 link_status; + u16 wait_link_timeout; + u64 l2_filter_id; + u16 vnic_id; + u16 stat_ctx_id; + u32 medium; + u16 support_speeds; + u32 link_set; + u8 media_detect; + u8 media_change; + u16 max_vfs; + u16 vf_res_strategy; + u16 min_vnics; + u16 max_vnics; + u16 max_msix; + u16 min_hw_ring_grps; + u16 max_hw_ring_grps; + u16 min_tx_rings; + u16 max_tx_rings; + u16 min_rx_rings; + u16 max_rx_rings; + u16 min_cp_rings; + u16 max_cp_rings; + u16 min_rsscos_ctxs; + u16 max_rsscos_ctxs; + u16 min_l2_ctxs; + u16 max_l2_ctxs; + u16 min_stat_ctxs; + u16 max_stat_ctxs; + u16 num_cmpl_rings; + u16 num_tx_rings; + u16 num_rx_rings; + u16 num_stat_ctxs; + u16 num_hw_ring_grps; + bool card_en; +}; + +#define SHORT_CMD_SUPPORTED VER_GET_RESP_DEV_CAPS_CFG_SHORT_CMD_SUPPORTED +#define SHORT_CMD_REQUIRED VER_GET_RESP_DEV_CAPS_CFG_SHORT_CMD_REQUIRED +#define CQ_DOORBELL_KEY_IDX(a) \ + (CMPL_DOORBELL_KEY_CMPL | \ + CMPL_DOORBELL_IDX_VALID | \ + (u32)(a)) +#define TX_BD_FLAGS \ + (TX_BD_SHORT_TYPE_TX_BD_SHORT | \ + TX_BD_SHORT_FLAGS_NO_CMPL | \ + TX_BD_SHORT_FLAGS_COAL_NOW | \ + TX_BD_SHORT_FLAGS_PACKET_END | \ + (1 << TX_BD_SHORT_FLAGS_BD_CNT_SFT)) +#define MEM_HWRM_RESP memalign(BNXT_DMA_ALIGNMENT, RESP_BUF_SIZE_ALIGNED) +#define PORT_PHY_FLAGS (BNXT_FLAG_NPAR_MODE | BNXT_FLAG_MULTI_HOST) +#define RING_FREE(bp, rid, flag) bnxt_hwrm_ring_free(bp, rid, flag) +#define QCFG_PHY_ALL (SUPPORT_SPEEDS | DETECT_MEDIA | PHY_SPEED | PHY_STATUS) + +#endif /* _BNXT_H_ */ diff --git a/drivers/net/bnxt/bnxt_dbg.h b/drivers/net/bnxt/bnxt_dbg.h new file mode 100644 index 00000000000..e9e9f6efefd --- /dev/null +++ b/drivers/net/bnxt/bnxt_dbg.h @@ -0,0 +1,536 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019-2021 Broadcom. + */ + +#ifndef _BXNT_DBG_H_ +#define _BXNT_DBG_H_ + +/* Adjust commented out lines below to enable debug. */ +/* #define DEBUG_PCI */ +/* #define DEBUG_MEMORY */ +/* #define DEBUG_LINK */ +/* #define DEBUG_CHIP */ +/* #define DEBUG_FAIL */ +/* #define DEBUG_HWRM_CMDS */ +/* #define DEBUG_HWRM_DUMP */ +/* #define DEBUG_CQ */ +/* #define DEBUG_CQ_DUMP */ +/* #define DEBUG_TX */ +/* #define DEBUG_TX_DUMP */ +/* #define DEBUG_RX */ +/* #define DEBUG_RX_DUMP */ + +#if \ + defined(DEBUG_PCI) || \ + defined(DEBUG_MEMORY) || \ + defined(DEBUG_LINK) || \ + defined(DEBUG_CHIP) || \ + defined(DEBUG_FAIL) || \ + defined(DEBUG_HWRM_CMDS) || \ + defined(DEBUG_HWRM_DUMP) || \ + defined(DEBUG_CQ) || \ + defined(DEBUG_CQ_DUMP) || \ + defined(DEBUG_TX) || \ + defined(DEBUG_TX_DUMP) || \ + defined(DEBUG_RX) || \ + defined(DEBUG_RX_DUMP) +#define DEBUG_DEFAULT +#endif + +#if defined(DEBUG_DEFAULT) +#define dbg_prn printf +#define MAX_CHAR_SIZE(a) (u32)((1 << (a)) - 1) +#define DISP_U8 0x00 +#define DISP_U16 0x01 +#define DISP_U32 0x02 +#define DISP_U64 0x03 + +void dumpmemory1(u8 *buffer, u32 length, u8 flag) +{ + u32 jj = 0; + u8 i, c; + + printf("\n %p:", buffer); + for (jj = 0; jj < 16; jj++) { + if (!(jj & MAX_CHAR_SIZE(flag))) + printf(" "); + if (jj < length) + printf("%02x", buffer[jj]); + else + printf(" "); + if ((jj & 0xF) == 0xF) { + printf(" "); + for (i = 0; i < 16; i++) { + if (i < length) { + c = buffer[jj + i - 15]; + if (c >= 0x20 && c < 0x7F) + ; + else + c = '.'; + printf("%c", c); + } + } + } + } +} + +void dump_mem(u8 *buffer, u32 length, u8 flag) +{ + u32 length16, remlen, jj; + + length16 = length & 0xFFFFFFF0; + remlen = length & 0xF; + for (jj = 0; jj < length16; jj += 16) + dumpmemory1((u8 *)&buffer[jj], 16, flag); + if (remlen) + dumpmemory1((u8 *)&buffer[length16], remlen, flag); + if (length16 || remlen) + printf("\n"); +} +#endif + +#if defined(DEBUG_PCI) +void dbg_pci(struct bnxt *bp, const char *func, u16 cmd_reg) +{ + printf("- %s()\n", func); + printf(" Vendor id : %04X\n", bp->vendor_id); + printf(" Device id : %04X\n", bp->device_id); + printf(" Irq : %d\n", bp->irq); + printf(" PCI Command Reg : %04X %04X\n", bp->cmd_reg, cmd_reg); + printf(" Sub Vendor id : %04X\n", bp->subsystem_vendor); + printf(" Sub Device id : %04X\n", bp->subsystem_device); + printf(" BAR (0) : %p\n", bp->bar0); + printf(" BAR (1) : %p\n", bp->bar1); + printf(" BAR (2) : %p\n", bp->bar2); +} +#else +#define dbg_pci(bp, func, creg) +#endif + +#if defined(DEBUG_MEMORY) +void dbg_mem(struct bnxt *bp, const char *func) +{ + printf("- %s()\n", func); + printf(" bp Addr : %p", bp); + printf(" Len %4d", (u16)sizeof(struct bnxt)); + printf(" phy %llx\n", virt_to_bus(bp)); + printf(" bp->hwrm_req_addr : %p", bp->hwrm_addr_req); + printf(" Len %4d", (u16)REQ_BUFFER_SIZE); + printf(" phy %llx\n", bp->req_addr_mapping); + printf(" bp->hwrm_resp_addr : %p", bp->hwrm_addr_resp); + printf(" Len %4d", (u16)RESP_BUFFER_SIZE); + printf(" phy %llx\n", bp->resp_addr_mapping); + printf(" bp->tx.bd_virt : %p", bp->tx.bd_virt); + printf(" Len %4d", (u16)TX_RING_DMA_BUFFER_SIZE); + printf(" phy %llx\n", virt_to_bus(bp->tx.bd_virt)); + printf(" bp->rx.bd_virt : %p", bp->rx.bd_virt); + printf(" Len %4d", (u16)RX_RING_DMA_BUFFER_SIZE); + printf(" phy %llx\n", virt_to_bus(bp->rx.bd_virt)); + printf(" bp->cq.bd_virt : %p", bp->cq.bd_virt); + printf(" Len %4d", (u16)CQ_RING_DMA_BUFFER_SIZE); + printf(" phy %llx\n", virt_to_bus(bp->cq.bd_virt)); +} +#else +#define dbg_mem(bp, func) +#endif + +#if defined(DEBUG_CHIP) +void print_fw_ver(struct hwrm_ver_get_output *resp, u32 tmo) +{ + if (resp->hwrm_intf_maj_8b < 1) { + dbg_prn(" HWRM interface %d.%d.%d is older than 1.0.0.\n", + resp->hwrm_intf_maj_8b, resp->hwrm_intf_min_8b, + resp->hwrm_intf_upd_8b); + dbg_prn(" Update FW with HWRM interface 1.0.0 or newer.\n"); + } + dbg_prn(" FW Version : %d.%d.%d.%d\n", + resp->hwrm_fw_maj_8b, resp->hwrm_fw_min_8b, + resp->hwrm_fw_bld_8b, resp->hwrm_fw_rsvd_8b); + printf(" cmd timeout : %d\n", tmo); +} + +void dbg_func_resource_qcaps(struct bnxt *bp) +{ + /* Ring Groups */ + printf(" min_hw_ring_grps : %d\n", bp->min_hw_ring_grps); + printf(" max_hw_ring_grps : %d\n", bp->max_hw_ring_grps); + /* TX Rings */ + printf(" min_tx_rings : %d\n", bp->min_tx_rings); + printf(" max_tx_rings : %d\n", bp->max_tx_rings); + /* RX Rings */ + printf(" min_rx_rings : %d\n", bp->min_rx_rings); + printf(" max_rx_rings : %d\n", bp->max_rx_rings); + /* Completion Rings */ + printf(" min_cq_rings : %d\n", bp->min_cp_rings); + printf(" max_cq_rings : %d\n", bp->max_cp_rings); + /* Statistic Contexts */ + printf(" min_stat_ctxs : %d\n", bp->min_stat_ctxs); + printf(" max_stat_ctxs : %d\n", bp->max_stat_ctxs); +} + +void print_func_qcaps(struct bnxt *bp) +{ + printf(" Port Number : %d\n", bp->port_idx); + printf(" fid : 0x%04x\n", bp->fid); + dbg_prn(" PF MAC : %02x:%02x:%02x:%02x:%02x:%02x\n", + bp->mac_addr[0], + bp->mac_addr[1], + bp->mac_addr[2], + bp->mac_addr[3], + bp->mac_addr[4], + bp->mac_addr[5]); +} + +void print_func_qcfg(struct bnxt *bp) +{ + printf(" ordinal_value : %d\n", bp->ordinal_value); + printf(" stat_ctx_id : %x\n", bp->stat_ctx_id); + dbg_prn(" FW MAC : %02x:%02x:%02x:%02x:%02x:%02x\n", + bp->mac_addr[0], + bp->mac_addr[1], + bp->mac_addr[2], + bp->mac_addr[3], + bp->mac_addr[4], + bp->mac_addr[5]); +} + +void dbg_set_speed(u32 speed) +{ + u32 speed1 = ((speed & LINK_SPEED_DRV_MASK) >> LINK_SPEED_DRV_SHIFT); + + printf(" Set Link Speed : "); + switch (speed & LINK_SPEED_DRV_MASK) { + case LINK_SPEED_DRV_1G: + printf("1 GBPS"); + break; + case LINK_SPEED_DRV_10G: + printf("10 GBPS"); + break; + case LINK_SPEED_DRV_25G: + printf("25 GBPS"); + break; + case LINK_SPEED_DRV_40G: + printf("40 GBPS"); + break; + case LINK_SPEED_DRV_50G: + printf("50 GBPS"); + break; + case LINK_SPEED_DRV_100G: + printf("100 GBPS"); + break; + case LINK_SPEED_DRV_AUTONEG: + printf("AUTONEG"); + break; + default: + printf("%x", speed1); + break; + } + printf("\n"); +} + +void dbg_chip_info(struct bnxt *bp) +{ + printf(" Stat Ctx ID : %d\n", bp->stat_ctx_id); + printf(" Grp ID : %d\n", bp->ring_grp_id); + printf(" CQ Ring Id : %d\n", bp->cq_ring_id); + printf(" Tx Ring Id : %d\n", bp->tx_ring_id); + printf(" Rx ring Id : %d\n", bp->rx_ring_id); +} + +void print_num_rings(struct bnxt *bp) +{ + printf(" num_cmpl_rings : %d\n", bp->num_cmpl_rings); + printf(" num_tx_rings : %d\n", bp->num_tx_rings); + printf(" num_rx_rings : %d\n", bp->num_rx_rings); + printf(" num_ring_grps : %d\n", bp->num_hw_ring_grps); + printf(" num_stat_ctxs : %d\n", bp->num_stat_ctxs); +} + +void dbg_flags(const char *func, u32 flags) +{ + printf("- %s()\n", func); + printf(" bp->flags : 0x%04x\n", flags); +} +#else +#define print_fw_ver(resp, tmo) +#define dbg_func_resource_qcaps(bp) +#define print_func_qcaps(bp) +#define print_func_qcfg(bp) +#define dbg_set_speed(speed) +#define dbg_chip_info(bp) +#define print_num_rings(bp) +#define dbg_flags(func, flags) +#endif + +#if defined(DEBUG_HWRM_CMDS) || defined(DEBUG_FAIL) +void dump_hwrm_req(struct bnxt *bp, const char *func, u32 len, u32 tmo) +{ + dbg_prn("- %s(0x%04x) cmd_len %d cmd_tmo %d", + func, (u16)((struct input *)bp->hwrm_addr_req)->req_type, + len, tmo); +#if defined(DEBUG_HWRM_DUMP) + dump_mem((u8 *)bp->hwrm_addr_req, len, DISP_U8); +#else + printf("\n"); +#endif +} + +void debug_resp(struct bnxt *bp, const char *func, u32 resp_len, u16 err) +{ + dbg_prn("- %s(0x%04x) - ", + func, (u16)((struct input *)bp->hwrm_addr_req)->req_type); + if (err == STATUS_SUCCESS) + printf("Done"); + else if (err != STATUS_TIMEOUT) + printf("Fail err 0x%04x", err); + else + printf("timedout"); +#if defined(DEBUG_HWRM_DUMP) + if (err != STATUS_TIMEOUT) + dump_mem((u8 *)bp->hwrm_addr_resp, resp_len, DISP_U8); + else + printf("\n"); +#else + printf("\n"); +#endif +} + +void dbg_hw_cmd(struct bnxt *bp, + const char *func, u16 cmd_len, + u16 resp_len, u32 cmd_tmo, u16 err) +{ +#if !defined(DEBUG_HWRM_CMDS) + if (err && err != STATUS_TIMEOUT) +#endif + { + dump_hwrm_req(bp, func, cmd_len, cmd_tmo); + debug_resp(bp, func, resp_len, err); + } +} +#else +#define dbg_hw_cmd(bp, func, cmd_len, resp_len, cmd_tmo, err) +#endif + +#if defined(DEBUG_HWRM_CMDS) +void dbg_short_cmd(u8 *req, const char *func, u32 len) +{ + struct hwrm_short_input *sreq; + + sreq = (struct hwrm_short_input *)req; + dbg_prn("- %s(0x%04x) short_cmd_len %d", + func, + sreq->req_type, + (int)len); +#if defined(DEBUG_HWRM_DUMP) + dump_mem((u8 *)sreq, len, DISP_U8); +#else + printf("\n"); +#endif +} +#else +#define dbg_short_cmd(sreq, func, len) +#endif + +#if defined(DEBUG_RX) +void dump_rx_bd(struct rx_pkt_cmpl *rx_cmp, + struct rx_pkt_cmpl_hi *rx_cmp_hi, + u32 desc_idx) +{ + printf(" RX desc_idx %d\n", desc_idx); + printf("- rx_cmp %llx", virt_to_bus(rx_cmp)); +#if defined(DEBUG_RX_DUMP) + dump_mem((u8 *)rx_cmp, (u32)sizeof(struct rx_pkt_cmpl), DISP_U8); +#else + printf("\n"); +#endif + printf("- rx_cmp_hi %llx", virt_to_bus(rx_cmp_hi)); +#if defined(DEBUG_RX_DUMP) + dump_mem((u8 *)rx_cmp_hi, (u32)sizeof(struct rx_pkt_cmpl_hi), DISP_U8); +#else + printf("\n"); +#endif +} + +void dbg_rxp(u8 *iob, u16 rx_len, u16 flag) +{ + printf("- RX iob %llx Len %d ", virt_to_bus(iob), rx_len); + if (flag == PKT_RECEIVED) + printf(" PKT RECEIVED"); + else if (flag == PKT_DROPPED) + printf(" PKT DROPPED"); +#if defined(DEBUG_RX_DUMP) + dump_mem(iob, (u32)rx_len, DISP_U8); +#else + printf("\n"); +#endif +} + +void dbg_rx_cid(u16 idx, u16 cid) +{ + dbg_prn("- RX old cid %d new cid %d\n", idx, cid); +} + +void dbg_rx_alloc_iob_fail(u16 idx, u16 cid) +{ + dbg_prn(" Rx alloc_iob (%d) failed", idx); + dbg_prn(" for cons_id %d\n", cid); +} + +void dbg_rx_iob(void *iob, u16 idx, u16 cid) +{ + dbg_prn(" Rx alloc_iob (%d) %p bd_virt (%d)\n", + idx, iob, cid); +} + +void dbg_rx_pkt(struct bnxt *bp, const char *func, uchar *pkt, int len) +{ + if (bp->rx.iob_recv == PKT_RECEIVED) { + dbg_prn("- %s: %llx %d\n", func, + virt_to_bus(pkt), len); + } +} +#else +#define dump_rx_bd(rx_cmp, rx_cmp_hi, desc_idx) +#define dbg_rxp(iob, rx_len, flag) +#define dbg_rx_cid(idx, cid) +#define dbg_rx_alloc_iob_fail(idx, cid) +#define dbg_rx_iob(iob, idx, cid) +#define dbg_rx_pkt(bp, func, pkt, len) +#endif + +#if defined(DEBUG_CQ) +void dump_CQ(struct cmpl_base *cmp, u16 cons_idx) +{ + printf("- CQ Type "); + + switch (cmp->type & CMPL_BASE_TYPE_MASK) { + case CMPL_BASE_TYPE_STAT_EJECT: + printf("(se)"); + break; + case CMPL_BASE_TYPE_HWRM_ASYNC_EVENT: + printf("(ae)"); + break; + case CMPL_BASE_TYPE_TX_L2: + printf("(tx)"); + break; + case CMPL_BASE_TYPE_RX_L2: + printf("(rx)"); + break; + default: + printf("%04x", (u16)(cmp->type & CMPL_BASE_TYPE_MASK)); + break; + } + printf(" cid %d", cons_idx); +#if defined(DEBUG_CQ_DUMP) + dump_mem((u8 *)cmp, (u32)sizeof(struct cmpl_base), DISP_U8); +#else + printf("\n"); +#endif +} +#else +#define dump_CQ(cq, id) +#endif + +#if defined(DEBUG_TX) +void dump_tx_stat(struct bnxt *bp) +{ + printf(" TX stats cnt %d req_cnt %d", bp->tx.cnt, bp->tx.cnt_req); + printf(" prod_id %d cons_id %d\n", bp->tx.prod_id, bp->tx.cons_id); +} + +void dump_tx_pkt(void *packet, dma_addr_t mapping, int len) +{ + printf(" TX Addr %llx Size %d", mapping, len); +#if defined(DEBUG_TX_DUMP) + dump_mem((u8 *)packet, len, DISP_U8); +#else + printf("\n"); +#endif +} + +void dump_tx_bd(struct tx_bd_short *tx_bd, u16 len) +{ + printf(" Tx BD Addr %llx Size %d", virt_to_bus(tx_bd), len); +#if defined(DEBUG_TX_DUMP) + dump_mem((u8 *)tx_bd, (u32)len, DISP_U8); +#else + printf("\n"); +#endif +} + +void dbg_no_tx_bd(void) +{ + printf(" Tx ring full\n"); +} +#else +#define dump_tx_stat(bp) +#define dump_tx_pkt(packet, mapping, len) +#define dump_tx_bd(prod_bd, len) +#define dbg_no_tx_bd() +#endif + +#if defined(DEBUG_MEMORY) +void dbg_mem_free_done(const char *func) +{ + printf("- %s - Done\n", func); +} +#else +#define dbg_mem_free_done(func) +#endif + +#if defined(DEBUG_FAIL) +void dbg_mem_alloc_fail(const char *func) +{ + printf("- %s() Fail\n", func); +} +#else +#define dbg_mem_alloc_fail(func) +#endif + +#if defined(DEBUG_LINK) +static void dump_evt(u8 *cmp, u32 type, u16 cid) +{ + u32 size = sizeof(struct cmpl_base); + u8 c = 'C'; + + switch (type) { + case CMPL_BASE_TYPE_HWRM_ASYNC_EVENT: + break; + default: + return; + } + dbg_prn("- %cQ Type (ae) cid %d", c, cid); + dump_mem(cmp, size, DISP_U8); +} + +void dbg_link_status(struct bnxt *bp) +{ + dbg_prn(" Port(%d) : Link", bp->port_idx); + if (bp->link_status == STATUS_LINK_ACTIVE) { + dbg_prn("Up"); + } else { + dbg_prn("Down\n"); + dbg_prn(" media_detect : %x", bp->media_detect); + } + dbg_prn("\n"); +} + +void dbg_link_state(struct bnxt *bp, u32 tmo) +{ + if (bp->link_status == STATUS_LINK_ACTIVE) + printf(" Link wait time : %d ms\n", tmo); +} + +void dbg_phy_speed(struct bnxt *bp, char *name) +{ + printf(" Current Speed : %s\n", name); +} +#else +#define dump_evt(cmp, ty, cid) +#define dbg_link_status(bp) +#define dbg_link_state(bp, tmo) +#define dbg_phy_speed(bp, name) +#endif + +#endif /* _BXNT_DBG_H_ */ diff --git a/drivers/net/bnxt/bnxt_hsi.h b/drivers/net/bnxt/bnxt_hsi.h new file mode 100644 index 00000000000..81cc5da9e4d --- /dev/null +++ b/drivers/net/bnxt/bnxt_hsi.h @@ -0,0 +1,889 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + /* + * Copyright 2019-2021 Broadcom. + */ + +#ifndef _BNXT_HSI_H_ +#define _BNXT_HSI_H_ + +/* input (size:128b/16B) */ +struct input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; +}; + +/* output (size:64b/8B) */ +struct output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; +}; + +/* hwrm_short_input (size:128b/16B) */ +struct hwrm_short_input { + __le16 req_type; + __le16 signature; +#define SHORT_REQ_SIGNATURE_SHORT_CMD 0x4321UL + __le16 unused_0; + __le16 size; + __le64 req_addr; +}; + +#define HWRM_VER_GET 0x0UL +#define HWRM_FUNC_RESET 0x11UL +#define HWRM_FUNC_QCAPS 0x15UL +#define HWRM_FUNC_QCFG 0x16UL +#define HWRM_FUNC_CFG 0x17UL +#define HWRM_FUNC_DRV_UNRGTR 0x1aUL +#define HWRM_FUNC_DRV_RGTR 0x1dUL +#define HWRM_PORT_PHY_CFG 0x20UL +#define HWRM_PORT_MAC_CFG 0x21UL +#define HWRM_PORT_PHY_QCFG 0x27UL +#define HWRM_VNIC_ALLOC 0x40UL +#define HWRM_VNIC_FREE 0x41UL +#define HWRM_VNIC_CFG 0x42UL +#define HWRM_RING_ALLOC 0x50UL +#define HWRM_RING_FREE 0x51UL +#define HWRM_RING_GRP_ALLOC 0x60UL +#define HWRM_RING_GRP_FREE 0x61UL +#define HWRM_CFA_L2_FILTER_ALLOC 0x90UL +#define HWRM_CFA_L2_FILTER_FREE 0x91UL +#define HWRM_CFA_L2_SET_RX_MASK 0x93UL +#define HWRM_STAT_CTX_ALLOC 0xb0UL +#define HWRM_STAT_CTX_FREE 0xb1UL +#define HWRM_FUNC_RESOURCE_QCAPS 0x190UL +#define HWRM_NVM_FLUSH 0xfff0UL +#define HWRM_NVM_GET_VARIABLE 0xfff1UL +#define HWRM_NVM_SET_VARIABLE 0xfff2UL + +#define HWRM_NA_SIGNATURE ((__le32)(-1)) +#define HWRM_MAX_REQ_LEN 128 +#define HWRM_VERSION_MAJOR 1 +#define HWRM_VERSION_MINOR 10 +#define HWRM_VERSION_UPDATE 0 + +/* hwrm_ver_get_input (size:192b/24B) */ +struct hwrm_ver_get_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + u8 hwrm_intf_maj; + u8 hwrm_intf_min; + u8 hwrm_intf_upd; + u8 unused_0[5]; +}; + +/* hwrm_ver_get_output (size:1408b/176B) */ +struct hwrm_ver_get_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 hwrm_intf_maj_8b; + u8 hwrm_intf_min_8b; + u8 hwrm_intf_upd_8b; + u8 hwrm_intf_rsvd_8b; + u8 hwrm_fw_maj_8b; + u8 hwrm_fw_min_8b; + u8 hwrm_fw_bld_8b; + u8 hwrm_fw_rsvd_8b; + u8 mgmt_fw_maj_8b; + u8 mgmt_fw_min_8b; + u8 mgmt_fw_bld_8b; + u8 mgmt_fw_rsvd_8b; + u8 netctrl_fw_maj_8b; + u8 netctrl_fw_min_8b; + u8 netctrl_fw_bld_8b; + u8 netctrl_fw_rsvd_8b; + __le32 dev_caps_cfg; +#define VER_GET_RESP_DEV_CAPS_CFG_SHORT_CMD_SUPPORTED 0x4UL +#define VER_GET_RESP_DEV_CAPS_CFG_SHORT_CMD_REQUIRED 0x8UL + u8 roce_fw_maj_8b; + u8 roce_fw_min_8b; + u8 roce_fw_bld_8b; + u8 roce_fw_rsvd_8b; + char hwrm_fw_name[16]; + char mgmt_fw_name[16]; + char netctrl_fw_name[16]; + u8 reserved2[16]; + char roce_fw_name[16]; + __le16 chip_num; + u8 chip_rev; + u8 chip_metal; + u8 chip_bond_id; + u8 chip_platform_type; + __le16 max_req_win_len; + __le16 max_resp_len; + __le16 def_req_timeout; + u8 flags; + u8 unused_0[2]; + u8 always_1; + __le16 hwrm_intf_major; + __le16 hwrm_intf_minor; + __le16 hwrm_intf_build; + __le16 hwrm_intf_patch; + __le16 hwrm_fw_major; + __le16 hwrm_fw_minor; + __le16 hwrm_fw_build; + __le16 hwrm_fw_patch; + __le16 mgmt_fw_major; + __le16 mgmt_fw_minor; + __le16 mgmt_fw_build; + __le16 mgmt_fw_patch; + __le16 netctrl_fw_major; + __le16 netctrl_fw_minor; + __le16 netctrl_fw_build; + __le16 netctrl_fw_patch; + __le16 roce_fw_major; + __le16 roce_fw_minor; + __le16 roce_fw_build; + __le16 roce_fw_patch; + __le16 max_ext_req_len; + u8 unused_1[5]; + u8 valid; +}; + +/* hwrm_async_event_cmpl (size:128b/16B) */ +struct hwrm_async_event_cmpl { + __le16 type; + __le16 event_id; +#define ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE 0x0UL + __le32 event_data2; + u8 opaque_v; + u8 timestamp_lo; + __le16 timestamp_hi; + __le32 event_data1; +}; + +/* hwrm_func_reset_input (size:192b/24B) */ +struct hwrm_func_reset_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 enables; + __le16 vf_id; + u8 func_reset_level; +#define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETME 0x1UL + u8 unused_0; +}; + +/* hwrm_func_qcaps_input (size:192b/24B) */ +struct hwrm_func_qcaps_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 fid; + u8 unused_0[6]; +}; + +/* hwrm_func_qcaps_output (size:640b/80B) */ +struct hwrm_func_qcaps_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 fid; + __le16 port_id; + __le32 flags; + u8 mac_address[6]; + __le16 max_rsscos_ctx; + __le16 max_cmpl_rings; + __le16 max_tx_rings; + __le16 max_rx_rings; + __le16 max_l2_ctxs; + __le16 max_vnics; + __le16 first_vf_id; + __le16 max_vfs; + __le16 max_stat_ctx; + __le32 max_encap_records; + __le32 max_decap_records; + __le32 max_tx_em_flows; + __le32 max_tx_wm_flows; + __le32 max_rx_em_flows; + __le32 max_rx_wm_flows; + __le32 max_mcast_filters; + __le32 max_flow_id; + __le32 max_hw_ring_grps; + __le16 max_sp_tx_rings; + u8 unused_0; + u8 valid; +}; + +/* hwrm_func_qcfg_input (size:192b/24B) */ +struct hwrm_func_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 fid; + u8 unused_0[6]; +}; + +/* hwrm_func_qcfg_output (size:704b/88B) */ +struct hwrm_func_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 fid; + __le16 port_id; + __le16 vlan; + __le16 flags; +#define FUNC_QCFG_RESP_FLAGS_MULTI_HOST 0x20UL + u8 mac_address[6]; + __le16 pci_id; + __le16 alloc_rsscos_ctx; + __le16 alloc_cmpl_rings; + __le16 alloc_tx_rings; + __le16 alloc_rx_rings; + __le16 alloc_l2_ctx; + __le16 alloc_vnics; + __le16 mtu; + __le16 mru; + __le16 stat_ctx_id; + u8 port_partition_type; +#define FUNC_QCFG_RESP_PORT_PARTITION_TYPE_NPAR1_0 0x2UL + u8 port_pf_cnt; + __le16 dflt_vnic_id; + __le16 max_mtu_configured; + __le32 min_bw; + __le32 max_bw; + u8 evb_mode; + u8 options; + __le16 alloc_vfs; + __le32 alloc_mcast_filters; + __le32 alloc_hw_ring_grps; + __le16 alloc_sp_tx_rings; + __le16 alloc_stat_ctx; + __le16 alloc_msix; + __le16 registered_vfs; + u8 unused_1[3]; + u8 always_1; + __le32 reset_addr_poll; + u8 unused_2[3]; + u8 valid; +}; + +/* hwrm_func_cfg_input (size:704b/88B) */ +struct hwrm_func_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 fid; + __le16 num_msix; + __le32 flags; + __le32 enables; +#define FUNC_CFG_REQ_ENABLES_NUM_CMPL_RINGS 0x8UL +#define FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS 0x10UL +#define FUNC_CFG_REQ_ENABLES_NUM_RX_RINGS 0x20UL +#define FUNC_CFG_REQ_ENABLES_NUM_STAT_CTXS 0x100UL +#define FUNC_CFG_REQ_ENABLES_ASYNC_EVENT_CR 0x4000UL +#define FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS 0x80000UL + __le16 mtu; + __le16 mru; + __le16 num_rsscos_ctxs; + __le16 num_cmpl_rings; + __le16 num_tx_rings; + __le16 num_rx_rings; + __le16 num_l2_ctxs; + __le16 num_vnics; + __le16 num_stat_ctxs; + __le16 num_hw_ring_grps; + u8 dflt_mac_addr[6]; + __le16 dflt_vlan; + __be32 dflt_ip_addr[4]; + __le32 min_bw; + __le32 max_bw; + __le16 async_event_cr; + u8 vlan_antispoof_mode; + u8 allowed_vlan_pris; + u8 evb_mode; + u8 options; + __le16 num_mcast_filters; +}; + +/* hwrm_func_drv_rgtr_input (size:896b/112B) */ +struct hwrm_func_drv_rgtr_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; + __le32 enables; +#define FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE 0x1UL +#define FUNC_DRV_RGTR_REQ_ENABLES_VER 0x2UL +#define FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD 0x10UL + __le16 os_type; +#define FUNC_DRV_RGTR_REQ_OS_TYPE_OTHER 0x1UL + u8 ver_maj_8b; + u8 ver_min_8b; + u8 ver_upd_8b; + u8 unused_0[3]; + __le32 timestamp; + u8 unused_1[4]; + __le32 vf_req_fwd[8]; + __le32 async_event_fwd[8]; + __le16 ver_maj; + __le16 ver_min; + __le16 ver_upd; + __le16 ver_patch; +}; + +/* hwrm_func_drv_unrgtr_input (size:192b/24B) */ +struct hwrm_func_drv_unrgtr_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; +#define FUNC_DRV_UNRGTR_REQ_FLAGS_PREPARE_FOR_SHUTDOWN 0x1UL + u8 unused_0[4]; +}; + +/* hwrm_func_resource_qcaps_input (size:192b/24B) */ +struct hwrm_func_resource_qcaps_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 fid; + u8 unused_0[6]; +}; + +/* hwrm_func_resource_qcaps_output (size:448b/56B) */ +struct hwrm_func_resource_qcaps_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 max_vfs; + __le16 max_msix; + __le16 vf_reservation_strategy; + __le16 min_rsscos_ctx; + __le16 max_rsscos_ctx; + __le16 min_cmpl_rings; + __le16 max_cmpl_rings; + __le16 min_tx_rings; + __le16 max_tx_rings; + __le16 min_rx_rings; + __le16 max_rx_rings; + __le16 min_l2_ctxs; + __le16 max_l2_ctxs; + __le16 min_vnics; + __le16 max_vnics; + __le16 min_stat_ctx; + __le16 max_stat_ctx; + __le16 min_hw_ring_grps; + __le16 max_hw_ring_grps; + __le16 max_tx_scheduler_inputs; + __le16 flags; + u8 unused_0[5]; + u8 valid; +}; + +/* hwrm_func_vlan_qcfg_input (size:192b/24B) */ +struct hwrm_func_vlan_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 fid; + u8 unused_0[6]; +}; + +/* hwrm_port_phy_cfg_input (size:448b/56B) */ +struct hwrm_port_phy_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; +#define PORT_PHY_CFG_REQ_FLAGS_RESET_PHY 0x1UL +#define PORT_PHY_CFG_REQ_FLAGS_FORCE 0x4UL + __le32 enables; +#define PORT_PHY_CFG_REQ_ENABLES_AUTO_MODE 0x1UL +#define PORT_PHY_CFG_REQ_ENABLES_AUTO_DUPLEX 0x2UL +#define PORT_PHY_CFG_REQ_ENABLES_AUTO_PAUSE 0x4UL +#define PORT_PHY_CFG_REQ_ENABLES_AUTO_LINK_SPEED_MASK 0x10UL + __le16 port_id; + __le16 force_link_speed; +#define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_1GB 0xaUL +#define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_10GB 0x64UL +#define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_25GB 0xfaUL +#define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_40GB 0x190UL +#define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_50GB 0x1f4UL +#define PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_100GB 0x3e8UL + u8 auto_mode; +#define PORT_PHY_CFG_REQ_AUTO_MODE_SPEED_MASK 0x4UL + u8 auto_duplex; +#define PORT_PHY_CFG_REQ_AUTO_DUPLEX_BOTH 0x2UL + u8 auto_pause; +#define PORT_PHY_CFG_REQ_AUTO_PAUSE_TX 0x1UL +#define PORT_PHY_CFG_REQ_AUTO_PAUSE_RX 0x2UL + u8 unused_0; + __le16 auto_link_speed; + __le16 auto_link_speed_mask; +#define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_100MB 0x2UL +#define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_1GB 0x8UL +#define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_10GB 0x40UL +#define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_25GB 0x100UL +#define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_40GB 0x200UL +#define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_50GB 0x400UL +#define PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_MASK_100GB 0x800UL + u8 wirespeed; + u8 lpbk; + u8 force_pause; + u8 unused_1; + __le32 preemphasis; + __le16 eee_link_speed_mask; + u8 unused_2[2]; + __le32 tx_lpi_timer; + __le32 unused_3; +}; + +/* hwrm_port_phy_qcfg_input (size:192b/24B) */ +struct hwrm_port_phy_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 port_id; + u8 unused_0[6]; +}; + +/* hwrm_port_phy_qcfg_output (size:768b/96B) */ +struct hwrm_port_phy_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 link; +#define PORT_PHY_QCFG_RESP_LINK_LINK 0x2UL + u8 unused_0; + __le16 link_speed; +#define PORT_PHY_QCFG_RESP_LINK_SPEED_100MB 0x1UL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_1GB 0xaUL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_2GB 0x14UL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_2_5GB 0x19UL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_10GB 0x64UL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_20GB 0xc8UL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_25GB 0xfaUL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_40GB 0x190UL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_50GB 0x1f4UL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_100GB 0x3e8UL +#define PORT_PHY_QCFG_RESP_LINK_SPEED_10MB 0xffffUL + u8 duplex_cfg; + u8 pause; + __le16 support_speeds; +#define PORT_QCFG_SUPPORT_SPEEDS_100MBHD 0x1UL +#define PORT_QCFG_SUPPORT_SPEEDS_100MB 0x2UL +#define PORT_QCFG_SUPPORT_SPEEDS_1GBHD 0x4UL +#define PORT_QCFG_SUPPORT_SPEEDS_1GB 0x8UL +#define PORT_QCFG_SUPPORT_SPEEDS_2GB 0x10UL +#define PORT_QCFG_SUPPORT_SPEEDS_2_5GB 0x20UL +#define PORT_QCFG_SUPPORT_SPEEDS_10GB 0x40UL +#define PORT_QCFG_SUPPORT_SPEEDS_20GB 0x80UL +#define PORT_QCFG_SUPPORT_SPEEDS_25GB 0x100UL +#define PORT_QCFG_SUPPORT_SPEEDS_50GB 0x400UL +#define PORT_QCFG_SUPPORT_SPEEDS_100GB 0x800UL +#define PORT_QCFG_SUPPORT_SPEEDS_200GB 0x4000UL + __le16 force_link_speed; + u8 auto_mode; + u8 auto_pause; + __le16 auto_link_speed; + __le16 auto_link_speed_mask; + u8 wirespeed; + u8 lpbk; + u8 force_pause; + u8 module_status; + __le32 preemphasis; + u8 phy_maj; + u8 phy_min; + u8 phy_bld; + u8 phy_type; + u8 media_type; + u8 xcvr_pkg_type; + u8 eee_config_phy_addr; + u8 parallel_detect; + __le16 link_partner_adv_speeds; + u8 link_partner_adv_auto_mode; + u8 link_partner_adv_pause; + __le16 adv_eee_link_speed_mask; + __le16 link_partner_adv_eee_link_speed_mask; + __le32 xcvr_identifier_type_tx_lpi_timer; + __le16 fec_cfg; + u8 duplex_state; + u8 option_flags; + char phy_vendor_name[16]; + char phy_vendor_partnumber[16]; + u8 unused_2[7]; + u8 valid; +}; + +/* hwrm_port_mac_cfg_input (size:320b/40B) */ +struct hwrm_port_mac_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; + __le32 enables; + __le16 port_id; + u8 ipg; + u8 lpbk; +#define PORT_MAC_CFG_REQ_LPBK_NONE 0x0UL + u8 vlan_pri2cos_map_pri; + u8 reserved1; + u8 tunnel_pri2cos_map_pri; + u8 dscp2pri_map_pri; + __le16 rx_ts_capture_ptp_msg_type; + __le16 tx_ts_capture_ptp_msg_type; + u8 cos_field_cfg; + u8 unused_0[3]; +}; + +/* hwrm_vnic_alloc_input (size:192b/24B) */ +struct hwrm_vnic_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; +#define VNIC_ALLOC_REQ_FLAGS_DEFAULT 0x1UL + u8 unused_0[4]; +}; + +/* hwrm_vnic_alloc_output (size:128b/16B) */ +struct hwrm_vnic_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 vnic_id; + u8 unused_0[3]; + u8 valid; +}; + +/* hwrm_vnic_free_input (size:192b/24B) */ +struct hwrm_vnic_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 vnic_id; + u8 unused_0[4]; +}; + +/* hwrm_vnic_cfg_input (size:320b/40B) */ +struct hwrm_vnic_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; + __le32 enables; +#define VNIC_CFG_REQ_ENABLES_DFLT_RING_GRP 0x1UL +#define VNIC_CFG_REQ_ENABLES_MRU 0x10UL + __le16 vnic_id; + __le16 dflt_ring_grp; + __le16 rss_rule; + __le16 cos_rule; + __le16 lb_rule; + __le16 mru; + __le16 default_rx_ring_id; + __le16 default_cmpl_ring_id; +}; + +/* hwrm_ring_alloc_input (size:704b/88B) */ +struct hwrm_ring_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 enables; +#define RING_ALLOC_REQ_ENABLES_RX_BUF_SIZE_VALID 0x100UL + u8 ring_type; +#define RING_ALLOC_REQ_RING_TYPE_L2_CMPL 0x0UL +#define RING_ALLOC_REQ_RING_TYPE_TX 0x1UL +#define RING_ALLOC_REQ_RING_TYPE_RX 0x2UL + u8 unused_0; + __le16 flags; + __le64 page_tbl_addr; + __le32 fbo; + u8 page_size; + u8 page_tbl_depth; + u8 unused_1[2]; + __le32 length; + __le16 logical_id; + __le16 cmpl_ring_id; + __le16 queue_id; + __le16 rx_buf_size; + __le16 rx_ring_id; + __le16 nq_ring_id; + __le16 ring_arb_cfg; + __le16 unused_3; + __le32 reserved3; + __le32 stat_ctx_id; + __le32 reserved4; + __le32 max_bw; + u8 int_mode; +#define RING_ALLOC_REQ_INT_MODE_POLL 0x3UL + u8 unused_4[3]; + __le64 cq_handle; +}; + +/* hwrm_ring_alloc_output (size:128b/16B) */ +struct hwrm_ring_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 ring_id; + __le16 logical_ring_id; + u8 unused_0[3]; + u8 valid; +}; + +/* hwrm_ring_free_input (size:192b/24B) */ +struct hwrm_ring_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + u8 ring_type; +#define RING_FREE_REQ_RING_TYPE_L2_CMPL 0x0UL +#define RING_FREE_REQ_RING_TYPE_TX 0x1UL +#define RING_FREE_REQ_RING_TYPE_RX 0x2UL + u8 unused_0; + __le16 ring_id; + u8 unused_1[4]; +}; + +/* hwrm_ring_grp_alloc_input (size:192b/24B) */ +struct hwrm_ring_grp_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 cr; + __le16 rr; + __le16 ar; + __le16 sc; +}; + +/* hwrm_ring_grp_alloc_output (size:128b/16B) */ +struct hwrm_ring_grp_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 ring_group_id; + u8 unused_0[3]; + u8 valid; +}; + +/* hwrm_ring_grp_free_input (size:192b/24B) */ +struct hwrm_ring_grp_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 ring_group_id; + u8 unused_0[4]; +}; + +/* hwrm_cfa_l2_filter_alloc_input (size:768b/96B) */ +struct hwrm_cfa_l2_filter_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; +#define CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX 0x1UL + __le32 enables; +#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR 0x1UL +#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR_MASK 0x2UL +#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x8000UL + u8 l2_addr[6]; + u8 unused_0[2]; + u8 l2_addr_mask[6]; + __le16 l2_ovlan; + __le16 l2_ovlan_mask; + __le16 l2_ivlan; + __le16 l2_ivlan_mask; + u8 unused_1[2]; + u8 t_l2_addr[6]; + u8 unused_2[2]; + u8 t_l2_addr_mask[6]; + __le16 t_l2_ovlan; + __le16 t_l2_ovlan_mask; + __le16 t_l2_ivlan; + __le16 t_l2_ivlan_mask; + u8 src_type; +#define CFA_L2_FILTER_ALLOC_REQ_SRC_TYPE_NPORT 0x0UL + u8 unused_3; + __le32 src_id; + u8 tunnel_type; + u8 unused_4; + __le16 dst_id; + __le16 mirror_vnic_id; + u8 pri_hint; + u8 unused_5; + __le32 unused_6; + __le64 l2_filter_id_hint; +}; + +/* hwrm_cfa_l2_filter_alloc_output (size:192b/24B) */ +struct hwrm_cfa_l2_filter_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le64 l2_filter_id; + __le32 flow_id; + u8 unused_0[3]; + u8 valid; +}; + +/* hwrm_cfa_l2_filter_free_input (size:192b/24B) */ +struct hwrm_cfa_l2_filter_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 l2_filter_id; +}; + +/* hwrm_cfa_l2_set_rx_mask_input (size:448b/56B) */ +struct hwrm_cfa_l2_set_rx_mask_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 vnic_id; + __le32 mask; +#define CFA_L2_SET_RX_MASK_REQ_MASK_MCAST 0x2UL +#define CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST 0x4UL +#define CFA_L2_SET_RX_MASK_REQ_MASK_BCAST 0x8UL +#define CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS 0x10UL + __le64 mc_tbl_addr; + __le32 num_mc_entries; + u8 unused_0[4]; + __le64 vlan_tag_tbl_addr; + __le32 num_vlan_tags; + u8 unused_1[4]; +}; + +/* hwrm_stat_ctx_alloc_input (size:256b/32B) */ +struct hwrm_stat_ctx_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 stats_dma_addr; + __le32 update_period_ms; + u8 stat_ctx_flags; + u8 unused_0[3]; +}; + +/* hwrm_stat_ctx_alloc_output (size:128b/16B) */ +struct hwrm_stat_ctx_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 stat_ctx_id; + u8 unused_0[3]; + u8 valid; +}; + +/* hwrm_stat_ctx_free_input (size:192b/24B) */ +struct hwrm_stat_ctx_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 stat_ctx_id; + u8 unused_0[4]; +}; + +/* hwrm_nvm_flush_input (size:128b/16B) */ +struct hwrm_nvm_flush_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; +}; + +/* hwrm_nvm_get_variable_input (size:320b/40B) */ +struct hwrm_nvm_get_variable_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 dest_data_addr; + __le16 data_len; + __le16 option_num; + __le16 dimensions; + __le16 index_0; + __le16 index_1; + __le16 index_2; + __le16 index_3; + u8 flags; + u8 unused_0; +}; + +/* hwrm_nvm_set_variable_input (size:320b/40B) */ +struct hwrm_nvm_set_variable_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 src_data_addr; + __le16 data_len; + __le16 option_num; + __le16 dimensions; + __le16 index_0; + __le16 index_1; + __le16 index_2; + __le16 index_3; + u8 flags; + u8 unused_0; +}; + +#endif /* _BNXT_HSI_H_ */ diff --git a/drivers/net/fec_mxc.h b/drivers/net/fec_mxc.h index 1c0d0e5b8f8..48faa33d66e 100644 --- a/drivers/net/fec_mxc.h +++ b/drivers/net/fec_mxc.h @@ -272,7 +272,7 @@ struct fec_priv { struct clk clk_ref; struct clk clk_ptp; u32 clk_rate; - char promisc; + bool promisc; }; /** diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c index 7d51be1f723..f8256126405 100644 --- a/drivers/net/fm/fm.c +++ b/drivers/net/fm/fm.c @@ -387,8 +387,8 @@ int fm_init_common(int index, struct ccsr_fman *reg) struct udevice *new; /* speed and mode will be read from DT */ - ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, - CONFIG_ENV_SPI_CS, 0, 0, &new); + ret = spi_flash_probe_bus_cs(CONFIG_SF_DEFAULT_BUS, + CONFIG_SF_DEFAULT_CS, 0, 0, &new); ucode_flash = dev_get_uclass_priv(new); #else @@ -474,7 +474,7 @@ int fm_init_common(int index, struct ccsr_fman *reg) struct udevice *new; /* speed and mode will be read from DT */ - ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + ret = spi_flash_probe_bus_cs(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, 0, 0, &new); ucode_flash = dev_get_uclass_priv(new); diff --git a/drivers/net/mscc_eswitch/felix_switch.c b/drivers/net/mscc_eswitch/felix_switch.c index 551fc2c9f96..60b2e8f32d4 100644 --- a/drivers/net/mscc_eswitch/felix_switch.c +++ b/drivers/net/mscc_eswitch/felix_switch.c @@ -16,6 +16,7 @@ */ #include <dm/device_compat.h> +#include <dm/of_extra.h> #include <linux/delay.h> #include <net/dsa.h> #include <asm/io.h> @@ -39,7 +40,9 @@ #define FELIX_IS2 0x060000 #define FELIX_GMII(port) (0x100000 + (port) * 0x10000) #define FELIX_QSYS 0x200000 - +#define FELIX_DEVCPU_GCB 0x070000 +#define FELIX_DEVCPU_GCB_SOFT_RST (FELIX_DEVCPU_GCB + 0x00000004) +#define SOFT_SWC_RST BIT(0) #define FELIX_SYS_SYSTEM (FELIX_SYS + 0x00000E00) #define FELIX_SYS_SYSTEM_EN BIT(0) #define FELIX_SYS_RAM_CTRL (FELIX_SYS + 0x00000F24) @@ -210,17 +213,14 @@ static int felix_init_sxgmii(struct mii_dev *imdio, int pidx) static void felix_start_pcs(struct udevice *dev, int port, struct phy_device *phy, struct mii_dev *imdio) { - bool autoneg = true; - - if (phy->phy_id == PHY_FIXED_ID || - phy->interface == PHY_INTERFACE_MODE_2500BASEX) - autoneg = false; + ofnode node = dsa_port_get_ofnode(dev, port); + bool inband_an = ofnode_eth_uses_inband_aneg(node); switch (phy->interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_2500BASEX: case PHY_INTERFACE_MODE_QSGMII: - felix_init_sgmii(imdio, port, autoneg); + felix_init_sgmii(imdio, port, inband_an); break; case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_USXGMII: @@ -239,6 +239,15 @@ static void felix_init(struct udevice *dev) void *base = priv->regs_base; int timeout = 100; + /* Switch core reset */ + out_le32(base + FELIX_DEVCPU_GCB_SOFT_RST, SOFT_SWC_RST); + while (in_le32(base + FELIX_DEVCPU_GCB_SOFT_RST) & SOFT_SWC_RST && + --timeout) + udelay(10); + if (in_le32(base + FELIX_DEVCPU_GCB_SOFT_RST) & SOFT_SWC_RST) + dev_err(dev, "Timeout waiting for switch core reset\n"); + timeout = 100; + /* Init core memories */ out_le32(base + FELIX_SYS_RAM_CTRL, FELIX_SYS_RAM_CTRL_INIT); while (in_le32(base + FELIX_SYS_RAM_CTRL) & FELIX_SYS_RAM_CTRL_INIT && diff --git a/drivers/net/pfe_eth/Kconfig b/drivers/net/pfe_eth/Kconfig index a13b331a508..b2724ee3e84 100644 --- a/drivers/net/pfe_eth/Kconfig +++ b/drivers/net/pfe_eth/Kconfig @@ -9,4 +9,20 @@ config SYS_FSL_PFE_ADDR hex "PFE base address" default 0x04000000 +config SYS_FSL_PFE_SPI_BUS + int "Value of SPI flash bus for PFE firmware" + default SF_DEFAULT_BUS + +config SYS_FSL_PFE_SPI_CS + int "Value of SPI flash chip select for PFE firmware" + default SF_DEFAULT_CS + +config SYS_FSL_PFE_SPI_MAX_HZ + int "Value of SPI flash max frequency for PFE firmware" + default SF_DEFAULT_SPEED + +config SYS_FSL_PFE_SPI_MODE + hex "Value of SPI flash work mode for PFE firmware" + default SF_DEFAULT_MODE + endif diff --git a/drivers/net/pfe_eth/pfe_firmware.c b/drivers/net/pfe_eth/pfe_firmware.c index ad5bc3c8624..93e5ea55c78 100644 --- a/drivers/net/pfe_eth/pfe_firmware.c +++ b/drivers/net/pfe_eth/pfe_firmware.c @@ -179,10 +179,10 @@ int pfe_spi_flash_init(void) if (!addr) return -ENOMEM; - ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, - CONFIG_ENV_SPI_CS, - CONFIG_ENV_SPI_MAX_HZ, - CONFIG_ENV_SPI_MODE, + ret = spi_flash_probe_bus_cs(CONFIG_SYS_FSL_PFE_SPI_BUS, + CONFIG_SYS_FSL_PFE_SPI_CS, + CONFIG_SYS_FSL_PFE_SPI_MAX_HZ, + CONFIG_SYS_FSL_PFE_SPI_MODE, &new); if (ret) { printf("SF: failed to probe spi\n"); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 68ee7d7a2dd..e69cd8a4b31 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -214,16 +214,6 @@ config PHY_NXP_C45_TJA11XX 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/cortina.c b/drivers/net/phy/cortina.c index 2ac02952450..778d93e609c 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -159,8 +159,8 @@ void cs4340_upload_firmware(struct phy_device *phydev) struct spi_flash *ucode_flash; addr = malloc(CONFIG_CORTINA_FW_LENGTH); - ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, - CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + ucode_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); if (!ucode_flash) { puts("SF: probe for Cortina ucode failed\n"); } else { @@ -212,8 +212,8 @@ void cs4340_upload_firmware(struct phy_device *phydev) struct spi_flash *ucode_flash; addr = malloc(CONFIG_CORTINA_FW_LENGTH); - ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, - CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + ucode_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); if (!ucode_flash) { puts("SF: probe for Cortina ucode failed\n"); } else { diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index d1a643cf5a0..f9482b21a01 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -19,6 +19,7 @@ /* Microsemi PHY ID's */ #define PHY_ID_VSC8530 0x00070560 #define PHY_ID_VSC8531 0x00070570 +#define PHY_ID_VSC8502 0x00070630 #define PHY_ID_VSC8540 0x00070760 #define PHY_ID_VSC8541 0x00070770 #define PHY_ID_VSC8574 0x000704a0 @@ -1513,6 +1514,50 @@ static int vsc8584_config(struct phy_device *phydev) return vsc8584_config_init(phydev); } +static int vsc8502_config(struct phy_device *phydev) +{ + bool rgmii_rx_delay = false, rgmii_tx_delay = false; + u16 reg = 0; + int ret; + + /* Assume nothing needs to be done for the default GMII/MII mode */ + if (!phy_interface_is_rgmii(phydev)) + return 0; + + /* Set Extended PHY Control 1 register to RGMII */ + phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_EXT_PHY_CNTL_1_REG, + BIT(13) | BIT(12)); + + /* Soft reset required after changing PHY mode from the default + * of GMII/MII + */ + ret = mscc_phy_soft_reset(phydev); + if (ret) + return ret; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + rgmii_rx_delay = true; + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + rgmii_tx_delay = true; + + phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_EXT2); + + if (rgmii_rx_delay) + reg |= VSC_PHY_RGMII_DELAY_2000_PS << RGMII_RX_CLK_DELAY_POS; + if (rgmii_tx_delay) + reg |= VSC_PHY_RGMII_DELAY_2000_PS << RGMII_TX_CLK_DELAY_POS; + + phy_write(phydev, MDIO_DEVAD_NONE, MSCC_PHY_RGMII_CNTL_REG, reg); + + phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_STD); + + return 0; +} + static struct phy_driver VSC8530_driver = { .name = "Microsemi VSC8530", .uid = PHY_ID_VSC8530, @@ -1533,6 +1578,16 @@ static struct phy_driver VSC8531_driver = { .shutdown = &genphy_shutdown, }; +static struct phy_driver VSC8502_driver = { + .name = "Microsemi VSC8502", + .uid = PHY_ID_VSC8502, + .mask = 0x000ffff0, + .features = PHY_GBIT_FEATURES, + .config = &vsc8502_config, + .startup = &mscc_startup, + .shutdown = &genphy_shutdown, +}; + static struct phy_driver VSC8540_driver = { .name = "Microsemi VSC8540", .uid = PHY_ID_VSC8540, @@ -1577,6 +1632,7 @@ int phy_mscc_init(void) { phy_register(&VSC8530_driver); phy_register(&VSC8531_driver); + phy_register(&VSC8502_driver); phy_register(&VSC8540_driver); phy_register(&VSC8541_driver); phy_register(&VSC8574_driver); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index b1b1fa50809..24c3ea59bbb 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #define PHY_RTL8211x_FORCE_MASTER BIT(1) -#define PHY_RTL8211E_PINE64_GIGABIT_FIX BIT(2) #define PHY_RTL8211F_FORCE_EEE_RXC_ON BIT(3) #define PHY_RTL8201F_S700_RMII_TIMINGS BIT(4) @@ -49,10 +48,10 @@ #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_CONFREG 0x1c +#define MIIM_RTL8211E_CTRL_DELAY BIT(13) +#define MIIM_RTL8211E_TX_DELAY BIT(12) +#define MIIM_RTL8211E_RX_DELAY BIT(11) #define MIIM_RTL8211E_EXT_PAGE_SELECT 0x1e @@ -108,10 +107,6 @@ static int rtl8211b_probe(struct phy_device *phydev) static int rtl8211e_probe(struct phy_device *phydev) { -#ifdef CONFIG_RTL8211E_PINE64_GIGABIT_FIX - phydev->flags |= PHY_RTL8211E_PINE64_GIGABIT_FIX; -#endif - return 0; } @@ -154,22 +149,6 @@ 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); @@ -201,6 +180,44 @@ static int rtl8201f_config(struct phy_device *phydev) return 0; } +static int rtl8211e_config(struct phy_device *phydev) +{ + int reg, val; + + /* enable TX/RX delay for rgmii-* modes, and disable them for rgmii. */ + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + val = MIIM_RTL8211E_CTRL_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + val = MIIM_RTL8211E_CTRL_DELAY | MIIM_RTL8211E_TX_DELAY | + MIIM_RTL8211E_RX_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + val = MIIM_RTL8211E_CTRL_DELAY | MIIM_RTL8211E_RX_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + val = MIIM_RTL8211E_CTRL_DELAY | MIIM_RTL8211E_TX_DELAY; + break; + default: /* the rest of the modes imply leaving delays as is. */ + goto default_delay; + } + + 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); + reg &= ~(MIIM_RTL8211E_TX_DELAY | MIIM_RTL8211E_RX_DELAY); + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211E_CONFREG, reg | val); + + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211F_PAGE_SELECT, 0); + +default_delay: + genphy_config_aneg(phydev); + + return 0; +} + static int rtl8211f_config(struct phy_device *phydev) { u16 reg; @@ -410,7 +427,7 @@ static struct phy_driver RTL8211E_driver = { .mask = 0xffffff, .features = PHY_GBIT_FEATURES, .probe = &rtl8211e_probe, - .config = &rtl8211x_config, + .config = &rtl8211e_config, .startup = &rtl8211e_startup, .shutdown = &genphy_shutdown, }; diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c new file mode 100644 index 00000000000..17bab33eddb --- /dev/null +++ b/drivers/net/sja1105.c @@ -0,0 +1,3376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2016-2018 NXP + * Copyright 2018, Sensor-Technik Wiedemann GmbH + * Copyright 2018-2019, Vladimir Oltean <olteanv@gmail.com> + * Copyright 2020-2021 NXP + * + * Ported from Linux (drivers/net/dsa/sja1105/). + */ + +#include <common.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/bitrev.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/if_ether.h> +#include <linux/if_vlan.h> +#include <linux/types.h> +#include <net/dsa.h> +#include <stdlib.h> +#include <spi.h> +#include <miiphy.h> +#include <dm/of_extra.h> + +enum packing_op { + PACK, + UNPACK, +}; + +#define ETHER_CRC32_POLY 0x04C11DB7 +#define ETH_P_SJA1105 0xdadb +#define SJA1105_NUM_PORTS 5 +#define SJA1110_NUM_PORTS 11 +#define SJA1105_MAX_NUM_PORTS SJA1110_NUM_PORTS +#define SJA1105_NUM_TC 8 +#define SJA1105ET_FDB_BIN_SIZE 4 +#define SJA1105_SIZE_CGU_CMD 4 +#define SJA1105_SIZE_RESET_CMD 4 +#define SJA1105_SIZE_MDIO_CMD 4 +#define SJA1105_SIZE_SPI_MSG_HEADER 4 +#define SJA1105_SIZE_SPI_MSG_MAXLEN (64 * 4) +#define SJA1105_SIZE_DEVICE_ID 4 +#define SJA1105_SIZE_TABLE_HEADER 12 +#define SJA1105_SIZE_L2_POLICING_ENTRY 8 +#define SJA1105_SIZE_VLAN_LOOKUP_ENTRY 8 +#define SJA1110_SIZE_VLAN_LOOKUP_ENTRY 12 +#define SJA1105_SIZE_L2_FORWARDING_ENTRY 8 +#define SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY 12 +#define SJA1105_SIZE_XMII_PARAMS_ENTRY 4 +#define SJA1110_SIZE_XMII_PARAMS_ENTRY 8 +#define SJA1105ET_SIZE_MAC_CONFIG_ENTRY 28 +#define SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY 40 +#define SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY 32 +#define SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY 44 +#define SJA1110_SIZE_GENERAL_PARAMS_ENTRY 56 + +#define SJA1105_MAX_L2_LOOKUP_COUNT 1024 +#define SJA1105_MAX_L2_POLICING_COUNT 45 +#define SJA1110_MAX_L2_POLICING_COUNT 110 +#define SJA1105_MAX_VLAN_LOOKUP_COUNT 4096 +#define SJA1105_MAX_L2_FORWARDING_COUNT 13 +#define SJA1110_MAX_L2_FORWARDING_COUNT 19 +#define SJA1105_MAX_MAC_CONFIG_COUNT 5 +#define SJA1110_MAX_MAC_CONFIG_COUNT 11 +#define SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT 1 +#define SJA1105_MAX_GENERAL_PARAMS_COUNT 1 +#define SJA1105_MAX_XMII_PARAMS_COUNT 1 + +#define SJA1105_MAX_FRAME_MEMORY 929 + +#define SJA1105E_DEVICE_ID 0x9C00000Cull +#define SJA1105T_DEVICE_ID 0x9E00030Eull +#define SJA1105PR_DEVICE_ID 0xAF00030Eull +#define SJA1105QS_DEVICE_ID 0xAE00030Eull +#define SJA1110_DEVICE_ID 0xB700030Full + +#define SJA1105ET_PART_NO 0x9A83 +#define SJA1105P_PART_NO 0x9A84 +#define SJA1105Q_PART_NO 0x9A85 +#define SJA1105R_PART_NO 0x9A86 +#define SJA1105S_PART_NO 0x9A87 +#define SJA1110A_PART_NO 0x1110 +#define SJA1110B_PART_NO 0x1111 +#define SJA1110C_PART_NO 0x1112 +#define SJA1110D_PART_NO 0x1113 + +#define SJA1110_ACU 0x1c4400 +#define SJA1110_RGU 0x1c6000 +#define SJA1110_CGU 0x1c6400 + +#define SJA1110_SPI_ADDR(x) ((x) / 4) +#define SJA1110_ACU_ADDR(x) (SJA1110_ACU + SJA1110_SPI_ADDR(x)) +#define SJA1110_CGU_ADDR(x) (SJA1110_CGU + SJA1110_SPI_ADDR(x)) +#define SJA1110_RGU_ADDR(x) (SJA1110_RGU + SJA1110_SPI_ADDR(x)) + +#define SJA1105_RSV_ADDR 0xffffffffffffffffull + +#define SJA1110_PCS_BANK_REG SJA1110_SPI_ADDR(0x3fc) + +#define DSA_8021Q_DIR_TX BIT(11) +#define DSA_8021Q_PORT_SHIFT 0 +#define DSA_8021Q_PORT_MASK GENMASK(3, 0) +#define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \ + DSA_8021Q_PORT_MASK) + +#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) + +/* XPCS registers */ + +/* VR MII MMD registers offsets */ +#define DW_VR_MII_DIG_CTRL1 0x8000 +#define DW_VR_MII_AN_CTRL 0x8001 +#define DW_VR_MII_DIG_CTRL2 0x80e1 + +/* VR_MII_DIG_CTRL1 */ +#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9) + +/* VR_MII_DIG_CTRL2 */ +#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4) + +/* VR_MII_AN_CTRL */ +#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3 +#define DW_VR_MII_TX_CONFIG_MASK BIT(3) +#define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0 +#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1 +#define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1) +#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2 + +/* PMA registers */ + +/* LANE_DRIVER1_0 register */ +#define SJA1110_LANE_DRIVER1_0 0x8038 +#define SJA1110_TXDRV(x) (((x) << 12) & GENMASK(14, 12)) + +/* LANE_DRIVER2_0 register */ +#define SJA1110_LANE_DRIVER2_0 0x803a +#define SJA1110_TXDRVTRIM_LSB(x) ((x) & GENMASK_ULL(15, 0)) + +/* LANE_DRIVER2_1 register */ +#define SJA1110_LANE_DRIVER2_1 0x803b +#define SJA1110_LANE_DRIVER2_1_RSV BIT(9) +#define SJA1110_TXDRVTRIM_MSB(x) (((x) & GENMASK_ULL(23, 16)) >> 16) + +/* LANE_TRIM register */ +#define SJA1110_LANE_TRIM 0x8040 +#define SJA1110_TXTEN BIT(11) +#define SJA1110_TXRTRIM(x) (((x) << 8) & GENMASK(10, 8)) +#define SJA1110_TXPLL_BWSEL BIT(7) +#define SJA1110_RXTEN BIT(6) +#define SJA1110_RXRTRIM(x) (((x) << 3) & GENMASK(5, 3)) +#define SJA1110_CDR_GAIN BIT(2) +#define SJA1110_ACCOUPLE_RXVCM_EN BIT(0) + +/* LANE_DATAPATH_1 register */ +#define SJA1110_LANE_DATAPATH_1 0x8037 + +/* POWERDOWN_ENABLE register */ +#define SJA1110_POWERDOWN_ENABLE 0x8041 +#define SJA1110_TXPLL_PD BIT(12) +#define SJA1110_TXPD BIT(11) +#define SJA1110_RXPKDETEN BIT(10) +#define SJA1110_RXCH_PD BIT(9) +#define SJA1110_RXBIAS_PD BIT(8) +#define SJA1110_RESET_SER_EN BIT(7) +#define SJA1110_RESET_SER BIT(6) +#define SJA1110_RESET_DES BIT(5) +#define SJA1110_RCVEN BIT(4) + +/* RXPLL_CTRL0 register */ +#define SJA1110_RXPLL_CTRL0 0x8065 +#define SJA1110_RXPLL_FBDIV(x) (((x) << 2) & GENMASK(9, 2)) + +/* RXPLL_CTRL1 register */ +#define SJA1110_RXPLL_CTRL1 0x8066 +#define SJA1110_RXPLL_REFDIV(x) ((x) & GENMASK(4, 0)) + +/* TXPLL_CTRL0 register */ +#define SJA1110_TXPLL_CTRL0 0x806d +#define SJA1110_TXPLL_FBDIV(x) ((x) & GENMASK(11, 0)) + +/* TXPLL_CTRL1 register */ +#define SJA1110_TXPLL_CTRL1 0x806e +#define SJA1110_TXPLL_REFDIV(x) ((x) & GENMASK(5, 0)) + +/* RX_DATA_DETECT register */ +#define SJA1110_RX_DATA_DETECT 0x8045 + +/* RX_CDR_CTLE register */ +#define SJA1110_RX_CDR_CTLE 0x8042 + +/* UM10944.pdf Page 11, Table 2. Configuration Blocks */ +enum { + BLKID_L2_POLICING = 0x06, + BLKID_VLAN_LOOKUP = 0x07, + BLKID_L2_FORWARDING = 0x08, + BLKID_MAC_CONFIG = 0x09, + BLKID_L2_FORWARDING_PARAMS = 0x0E, + BLKID_GENERAL_PARAMS = 0x11, + BLKID_XMII_PARAMS = 0x4E, +}; + +enum sja1105_blk_idx { + BLK_IDX_L2_POLICING = 0, + BLK_IDX_VLAN_LOOKUP, + BLK_IDX_L2_FORWARDING, + BLK_IDX_MAC_CONFIG, + BLK_IDX_L2_FORWARDING_PARAMS, + BLK_IDX_GENERAL_PARAMS, + BLK_IDX_XMII_PARAMS, + BLK_IDX_MAX, +}; + +struct sja1105_general_params_entry { + u64 mac_fltres1; + u64 mac_fltres0; + u64 mac_flt1; + u64 mac_flt0; + u64 casc_port; + u64 host_port; + u64 mirr_port; + u64 tpid; + u64 tpid2; +}; + +struct sja1105_vlan_lookup_entry { + u64 vmemb_port; + u64 vlan_bc; + u64 tag_port; + u64 vlanid; + u64 type_entry; /* SJA1110 only */ +}; + +struct sja1105_l2_forwarding_entry { + u64 bc_domain; + u64 reach_port; + u64 fl_domain; +}; + +struct sja1105_l2_forwarding_params_entry { + u64 part_spc[SJA1105_NUM_TC]; +}; + +struct sja1105_l2_policing_entry { + u64 sharindx; + u64 smax; + u64 rate; + u64 maxlen; + u64 partition; +}; + +struct sja1105_mac_config_entry { + u64 top[SJA1105_NUM_TC]; + u64 base[SJA1105_NUM_TC]; + u64 enabled[SJA1105_NUM_TC]; + u64 speed; + u64 vlanid; + u64 egress; + u64 ingress; +}; + +struct sja1105_xmii_params_entry { + u64 phy_mac[SJA1105_MAX_NUM_PORTS]; + u64 xmii_mode[SJA1105_MAX_NUM_PORTS]; + u64 special[SJA1105_MAX_NUM_PORTS]; +}; + +struct sja1105_table_header { + u64 block_id; + u64 len; + u64 crc; +}; + +struct sja1105_table_ops { + size_t (*packing)(void *buf, void *entry_ptr, enum packing_op op); + size_t unpacked_entry_size; + size_t packed_entry_size; + size_t max_entry_count; +}; + +struct sja1105_table { + const struct sja1105_table_ops *ops; + size_t entry_count; + void *entries; +}; + +struct sja1105_static_config { + u64 device_id; + struct sja1105_table tables[BLK_IDX_MAX]; +}; + +struct sja1105_xpcs_cfg { + bool inband_an; + int speed; +}; + +struct sja1105_private { + struct sja1105_static_config static_config; + bool rgmii_rx_delay[SJA1105_MAX_NUM_PORTS]; + bool rgmii_tx_delay[SJA1105_MAX_NUM_PORTS]; + u16 pvid[SJA1105_MAX_NUM_PORTS]; + struct sja1105_xpcs_cfg xpcs_cfg[SJA1105_MAX_NUM_PORTS]; + struct mii_dev *mdio_pcs; + const struct sja1105_info *info; + struct udevice *dev; +}; + +typedef enum { + SPI_READ = 0, + SPI_WRITE = 1, +} sja1105_spi_rw_mode_t; + +typedef enum { + XMII_MAC = 0, + XMII_PHY = 1, +} sja1105_mii_role_t; + +typedef enum { + XMII_MODE_MII = 0, + XMII_MODE_RMII = 1, + XMII_MODE_RGMII = 2, + XMII_MODE_SGMII = 3, +} sja1105_phy_interface_t; + +enum { + SJA1105_SPEED_AUTO, + SJA1105_SPEED_10MBPS, + SJA1105_SPEED_100MBPS, + SJA1105_SPEED_1000MBPS, + SJA1105_SPEED_MAX, +}; + +enum sja1110_vlan_type { + SJA1110_VLAN_INVALID = 0, + SJA1110_VLAN_C_TAG = 1, /* Single inner VLAN tag */ + SJA1110_VLAN_S_TAG = 2, /* Single outer VLAN tag */ + SJA1110_VLAN_D_TAG = 3, /* Double tagged, use outer tag for lookup */ +}; + +/* Keeps the different addresses between E/T and P/Q/R/S */ +struct sja1105_regs { + u64 device_id; + u64 prod_id; + u64 status; + u64 port_control; + u64 rgu; + u64 config; + u64 rmii_pll1; + u64 pad_mii_tx[SJA1105_MAX_NUM_PORTS]; + u64 pad_mii_rx[SJA1105_MAX_NUM_PORTS]; + u64 pad_mii_id[SJA1105_MAX_NUM_PORTS]; + u64 cgu_idiv[SJA1105_MAX_NUM_PORTS]; + u64 mii_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 mii_rx_clk[SJA1105_MAX_NUM_PORTS]; + u64 mii_ext_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 mii_ext_rx_clk[SJA1105_MAX_NUM_PORTS]; + u64 rgmii_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 rmii_ref_clk[SJA1105_MAX_NUM_PORTS]; + u64 rmii_ext_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 pcs_base[SJA1105_MAX_NUM_PORTS]; +}; + +struct sja1105_info { + u64 device_id; + u64 part_no; + const struct sja1105_table_ops *static_ops; + const struct sja1105_regs *regs; + int (*reset_cmd)(struct sja1105_private *priv); + int (*setup_rgmii_delay)(struct sja1105_private *priv, int port); + int (*pcs_mdio_read)(struct mii_dev *bus, int phy, int mmd, int reg); + int (*pcs_mdio_write)(struct mii_dev *bus, int phy, int mmd, int reg, + u16 val); + int (*pma_config)(struct sja1105_private *priv, int port); + const char *name; + bool supports_mii[SJA1105_MAX_NUM_PORTS]; + bool supports_rmii[SJA1105_MAX_NUM_PORTS]; + bool supports_rgmii[SJA1105_MAX_NUM_PORTS]; + bool supports_sgmii[SJA1105_MAX_NUM_PORTS]; + const u64 port_speed[SJA1105_SPEED_MAX]; +}; + +struct sja1105_chunk { + u8 *buf; + size_t len; + u64 reg_addr; +}; + +struct sja1105_spi_message { + u64 access; + u64 read_count; + u64 address; +}; + +/* Common structure for CFG_PAD_MIIx_RX and CFG_PAD_MIIx_TX */ +struct sja1105_cfg_pad_mii { + u64 d32_os; + u64 d32_ih; + u64 d32_ipud; + u64 d10_ih; + u64 d10_os; + u64 d10_ipud; + u64 ctrl_os; + u64 ctrl_ih; + u64 ctrl_ipud; + u64 clk_os; + u64 clk_ih; + u64 clk_ipud; +}; + +struct sja1105_cfg_pad_mii_id { + u64 rxc_stable_ovr; + u64 rxc_delay; + u64 rxc_bypass; + u64 rxc_pd; + u64 txc_stable_ovr; + u64 txc_delay; + u64 txc_bypass; + u64 txc_pd; +}; + +struct sja1105_cgu_idiv { + u64 clksrc; + u64 autoblock; + u64 idiv; + u64 pd; +}; + +struct sja1105_cgu_pll_ctrl { + u64 pllclksrc; + u64 msel; + u64 autoblock; + u64 psel; + u64 direct; + u64 fbsel; + u64 bypass; + u64 pd; +}; + +enum { + CLKSRC_MII0_TX_CLK = 0x00, + CLKSRC_MII0_RX_CLK = 0x01, + CLKSRC_MII1_TX_CLK = 0x02, + CLKSRC_MII1_RX_CLK = 0x03, + CLKSRC_MII2_TX_CLK = 0x04, + CLKSRC_MII2_RX_CLK = 0x05, + CLKSRC_MII3_TX_CLK = 0x06, + CLKSRC_MII3_RX_CLK = 0x07, + CLKSRC_MII4_TX_CLK = 0x08, + CLKSRC_MII4_RX_CLK = 0x09, + CLKSRC_PLL0 = 0x0B, + CLKSRC_PLL1 = 0x0E, + CLKSRC_IDIV0 = 0x11, + CLKSRC_IDIV1 = 0x12, + CLKSRC_IDIV2 = 0x13, + CLKSRC_IDIV3 = 0x14, + CLKSRC_IDIV4 = 0x15, +}; + +struct sja1105_cgu_mii_ctrl { + u64 clksrc; + u64 autoblock; + u64 pd; +}; + +static int get_reverse_lsw32_offset(int offset, size_t len) +{ + int closest_multiple_of_4; + int word_index; + + word_index = offset / 4; + closest_multiple_of_4 = word_index * 4; + offset -= closest_multiple_of_4; + word_index = (len / 4) - word_index - 1; + return word_index * 4 + offset; +} + +/* Simplified version of the "packing" function from Linux, adapted + * to support only sja1105's quirk: QUIRK_LSW32_IS_FIRST + */ +static void sja1105_packing(void *pbuf, u64 *uval, int startbit, int endbit, + size_t pbuflen, enum packing_op op) +{ + int plogical_first_u8, plogical_last_u8, box; + + if (op == UNPACK) + *uval = 0; + + plogical_first_u8 = startbit / 8; + plogical_last_u8 = endbit / 8; + + for (box = plogical_first_u8; box >= plogical_last_u8; box--) { + int box_start_bit, box_end_bit, box_addr; + int proj_start_bit, proj_end_bit; + u64 proj_mask; + u8 box_mask; + + if (box == plogical_first_u8) + box_start_bit = startbit % 8; + else + box_start_bit = 7; + if (box == plogical_last_u8) + box_end_bit = endbit % 8; + else + box_end_bit = 0; + + proj_start_bit = ((box * 8) + box_start_bit) - endbit; + proj_end_bit = ((box * 8) + box_end_bit) - endbit; + proj_mask = GENMASK_ULL(proj_start_bit, proj_end_bit); + box_mask = GENMASK_ULL(box_start_bit, box_end_bit); + + box_addr = pbuflen - box - 1; + box_addr = get_reverse_lsw32_offset(box_addr, pbuflen); + + if (op == UNPACK) { + u64 pval; + + /* Read from pbuf, write to uval */ + pval = ((u8 *)pbuf)[box_addr] & box_mask; + + pval >>= box_end_bit; + pval <<= proj_end_bit; + *uval &= ~proj_mask; + *uval |= pval; + } else { + u64 pval; + + /* Write to pbuf, read from uval */ + pval = (*uval) & proj_mask; + pval >>= proj_end_bit; + + pval <<= box_end_bit; + ((u8 *)pbuf)[box_addr] &= ~box_mask; + ((u8 *)pbuf)[box_addr] |= pval; + } + } +} + +static u32 crc32_add(u32 crc, u8 byte) +{ + u32 byte32 = bitrev32(byte); + int i; + + for (i = 0; i < 8; i++) { + if ((crc ^ byte32) & BIT(31)) { + crc <<= 1; + crc ^= ETHER_CRC32_POLY; + } else { + crc <<= 1; + } + byte32 <<= 1; + } + return crc; +} + +/* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */ +static uint32_t sja1105_crc32(void *buf, size_t len) +{ + unsigned int i; + u64 chunk; + u32 crc; + + /* seed */ + crc = 0xFFFFFFFF; + for (i = 0; i < len; i += 4) { + sja1105_packing(buf + i, &chunk, 31, 0, 4, UNPACK); + crc = crc32_add(crc, chunk & 0xFF); + crc = crc32_add(crc, (chunk >> 8) & 0xFF); + crc = crc32_add(crc, (chunk >> 16) & 0xFF); + crc = crc32_add(crc, (chunk >> 24) & 0xFF); + } + return bitrev32(~crc); +} + +static void sja1105_spi_message_pack(void *buf, struct sja1105_spi_message *msg) +{ + const int size = SJA1105_SIZE_SPI_MSG_HEADER; + + memset(buf, 0, size); + + sja1105_packing(buf, &msg->access, 31, 31, size, PACK); + sja1105_packing(buf, &msg->read_count, 30, 25, size, PACK); + sja1105_packing(buf, &msg->address, 24, 4, size, PACK); +} + +static int sja1105_xfer_buf(const struct sja1105_private *priv, + sja1105_spi_rw_mode_t rw, u64 reg_addr, + u8 *buf, size_t len) +{ + struct udevice *dev = priv->dev; + struct sja1105_chunk chunk = { + .len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN), + .reg_addr = reg_addr, + .buf = buf, + }; + int num_chunks; + int rc, i; + + rc = dm_spi_claim_bus(dev); + if (rc) + return rc; + + num_chunks = DIV_ROUND_UP(len, SJA1105_SIZE_SPI_MSG_MAXLEN); + + for (i = 0; i < num_chunks; i++) { + u8 hdr_buf[SJA1105_SIZE_SPI_MSG_HEADER]; + struct sja1105_spi_message msg; + u8 *rx_buf = NULL; + u8 *tx_buf = NULL; + + /* Populate the transfer's header buffer */ + msg.address = chunk.reg_addr; + msg.access = rw; + if (rw == SPI_READ) + msg.read_count = chunk.len / 4; + else + /* Ignored */ + msg.read_count = 0; + sja1105_spi_message_pack(hdr_buf, &msg); + rc = dm_spi_xfer(dev, SJA1105_SIZE_SPI_MSG_HEADER * 8, hdr_buf, + NULL, SPI_XFER_BEGIN); + if (rc) + goto out; + + /* Populate the transfer's data buffer */ + if (rw == SPI_READ) + rx_buf = chunk.buf; + else + tx_buf = chunk.buf; + rc = dm_spi_xfer(dev, chunk.len * 8, tx_buf, rx_buf, + SPI_XFER_END); + if (rc) + goto out; + + /* Calculate next chunk */ + chunk.buf += chunk.len; + chunk.reg_addr += chunk.len / 4; + chunk.len = min_t(size_t, (ptrdiff_t)(buf + len - chunk.buf), + SJA1105_SIZE_SPI_MSG_MAXLEN); + } + +out: + dm_spi_release_bus(dev); + + return rc; +} + +static int sja1105et_reset_cmd(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0}; + const int size = SJA1105_SIZE_RESET_CMD; + u64 cold_rst = 1; + + sja1105_packing(packed_buf, &cold_rst, 3, 3, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf, + SJA1105_SIZE_RESET_CMD); +} + +static int sja1105pqrs_reset_cmd(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0}; + const int size = SJA1105_SIZE_RESET_CMD; + u64 cold_rst = 1; + + sja1105_packing(packed_buf, &cold_rst, 2, 2, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf, + SJA1105_SIZE_RESET_CMD); +} + +static int sja1110_reset_cmd(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0}; + const int size = SJA1105_SIZE_RESET_CMD; + u64 switch_rst = 1; + + /* Only reset the switch core. + * A full cold reset would re-enable the BASE_MCSS_CLOCK PLL which + * would turn on the microcontroller, potentially letting it execute + * code which could interfere with our configuration. + */ + sja1105_packing(packed_buf, &switch_rst, 20, 20, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf, + SJA1105_SIZE_RESET_CMD); +} + +static size_t sja1105et_general_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY; + struct sja1105_general_params_entry *entry = entry_ptr; + + sja1105_packing(buf, &entry->mac_fltres1, 311, 264, size, op); + sja1105_packing(buf, &entry->mac_fltres0, 263, 216, size, op); + sja1105_packing(buf, &entry->mac_flt1, 215, 168, size, op); + sja1105_packing(buf, &entry->mac_flt0, 167, 120, size, op); + sja1105_packing(buf, &entry->casc_port, 115, 113, size, op); + sja1105_packing(buf, &entry->host_port, 112, 110, size, op); + sja1105_packing(buf, &entry->mirr_port, 109, 107, size, op); + sja1105_packing(buf, &entry->tpid, 42, 27, size, op); + sja1105_packing(buf, &entry->tpid2, 25, 10, size, op); + return size; +} + +static size_t sja1110_general_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_general_params_entry *entry = entry_ptr; + const size_t size = SJA1110_SIZE_GENERAL_PARAMS_ENTRY; + + sja1105_packing(buf, &entry->mac_fltres1, 438, 391, size, op); + sja1105_packing(buf, &entry->mac_fltres0, 390, 343, size, op); + sja1105_packing(buf, &entry->mac_flt1, 342, 295, size, op); + sja1105_packing(buf, &entry->mac_flt0, 294, 247, size, op); + sja1105_packing(buf, &entry->casc_port, 242, 232, size, op); + sja1105_packing(buf, &entry->host_port, 231, 228, size, op); + sja1105_packing(buf, &entry->mirr_port, 227, 224, size, op); + sja1105_packing(buf, &entry->tpid2, 159, 144, size, op); + sja1105_packing(buf, &entry->tpid, 142, 127, size, op); + return size; +} + +static size_t +sja1105pqrs_general_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY; + struct sja1105_general_params_entry *entry = entry_ptr; + + sja1105_packing(buf, &entry->mac_fltres1, 343, 296, size, op); + sja1105_packing(buf, &entry->mac_fltres0, 295, 248, size, op); + sja1105_packing(buf, &entry->mac_flt1, 247, 200, size, op); + sja1105_packing(buf, &entry->mac_flt0, 199, 152, size, op); + sja1105_packing(buf, &entry->casc_port, 147, 145, size, op); + sja1105_packing(buf, &entry->host_port, 144, 142, size, op); + sja1105_packing(buf, &entry->mirr_port, 141, 139, size, op); + sja1105_packing(buf, &entry->tpid, 74, 59, size, op); + sja1105_packing(buf, &entry->tpid2, 57, 42, size, op); + return size; +} + +static size_t +sja1105_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY; + struct sja1105_l2_forwarding_params_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 13; i < SJA1105_NUM_TC; i++, offset += 10) + sja1105_packing(buf, &entry->part_spc[i], + offset + 9, offset + 0, size, op); + return size; +} + +static size_t +sja1110_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_forwarding_params_entry *entry = entry_ptr; + const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY; + int offset, i; + + for (i = 0, offset = 5; i < 8; i++, offset += 11) + sja1105_packing(buf, &entry->part_spc[i], + offset + 10, offset + 0, size, op); + return size; +} + +static size_t sja1105_l2_forwarding_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY; + struct sja1105_l2_forwarding_entry *entry = entry_ptr; + + sja1105_packing(buf, &entry->bc_domain, 63, 59, size, op); + sja1105_packing(buf, &entry->reach_port, 58, 54, size, op); + sja1105_packing(buf, &entry->fl_domain, 53, 49, size, op); + return size; +} + +static size_t sja1110_l2_forwarding_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_forwarding_entry *entry = entry_ptr; + const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY; + + sja1105_packing(buf, &entry->bc_domain, 63, 53, size, op); + sja1105_packing(buf, &entry->reach_port, 52, 42, size, op); + sja1105_packing(buf, &entry->fl_domain, 41, 31, size, op); + return size; +} + +static size_t sja1105_l2_policing_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_policing_entry *entry = entry_ptr; + const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY; + + sja1105_packing(buf, &entry->sharindx, 63, 58, size, op); + sja1105_packing(buf, &entry->smax, 57, 42, size, op); + sja1105_packing(buf, &entry->rate, 41, 26, size, op); + sja1105_packing(buf, &entry->maxlen, 25, 15, size, op); + sja1105_packing(buf, &entry->partition, 14, 12, size, op); + return size; +} + +static size_t sja1110_l2_policing_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_policing_entry *entry = entry_ptr; + const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY; + + sja1105_packing(buf, &entry->sharindx, 63, 57, size, op); + sja1105_packing(buf, &entry->smax, 56, 39, size, op); + sja1105_packing(buf, &entry->rate, 38, 21, size, op); + sja1105_packing(buf, &entry->maxlen, 20, 10, size, op); + sja1105_packing(buf, &entry->partition, 9, 7, size, op); + return size; +} + +static size_t sja1105et_mac_config_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY; + struct sja1105_mac_config_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 72; i < SJA1105_NUM_TC; i++, offset += 19) { + sja1105_packing(buf, &entry->enabled[i], + offset + 0, offset + 0, size, op); + sja1105_packing(buf, &entry->base[i], + offset + 9, offset + 1, size, op); + sja1105_packing(buf, &entry->top[i], + offset + 18, offset + 10, size, op); + } + sja1105_packing(buf, &entry->speed, 66, 65, size, op); + sja1105_packing(buf, &entry->vlanid, 21, 10, size, op); + sja1105_packing(buf, &entry->egress, 2, 2, size, op); + sja1105_packing(buf, &entry->ingress, 1, 1, size, op); + return size; +} + +static size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY; + struct sja1105_mac_config_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 104; i < SJA1105_NUM_TC; i++, offset += 19) { + sja1105_packing(buf, &entry->enabled[i], + offset + 0, offset + 0, size, op); + sja1105_packing(buf, &entry->base[i], + offset + 9, offset + 1, size, op); + sja1105_packing(buf, &entry->top[i], + offset + 18, offset + 10, size, op); + } + sja1105_packing(buf, &entry->speed, 98, 97, size, op); + sja1105_packing(buf, &entry->vlanid, 53, 42, size, op); + sja1105_packing(buf, &entry->egress, 32, 32, size, op); + sja1105_packing(buf, &entry->ingress, 31, 31, size, op); + return size; +} + +static size_t sja1110_mac_config_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY; + struct sja1105_mac_config_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 104; i < 8; i++, offset += 19) { + sja1105_packing(buf, &entry->enabled[i], + offset + 0, offset + 0, size, op); + sja1105_packing(buf, &entry->base[i], + offset + 9, offset + 1, size, op); + sja1105_packing(buf, &entry->top[i], + offset + 18, offset + 10, size, op); + } + sja1105_packing(buf, &entry->speed, 98, 96, size, op); + sja1105_packing(buf, &entry->vlanid, 52, 41, size, op); + sja1105_packing(buf, &entry->egress, 31, 31, size, op); + sja1105_packing(buf, &entry->ingress, 30, 30, size, op); + return size; +} + +static size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY; + struct sja1105_vlan_lookup_entry *entry = entry_ptr; + + sja1105_packing(buf, &entry->vmemb_port, 53, 49, size, op); + sja1105_packing(buf, &entry->vlan_bc, 48, 44, size, op); + sja1105_packing(buf, &entry->tag_port, 43, 39, size, op); + sja1105_packing(buf, &entry->vlanid, 38, 27, size, op); + return size; +} + +static size_t sja1110_vlan_lookup_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_vlan_lookup_entry *entry = entry_ptr; + const size_t size = SJA1110_SIZE_VLAN_LOOKUP_ENTRY; + + sja1105_packing(buf, &entry->vmemb_port, 73, 63, size, op); + sja1105_packing(buf, &entry->vlan_bc, 62, 52, size, op); + sja1105_packing(buf, &entry->tag_port, 51, 41, size, op); + sja1105_packing(buf, &entry->type_entry, 40, 39, size, op); + sja1105_packing(buf, &entry->vlanid, 38, 27, size, op); + return size; +} + +static size_t sja1105_xmii_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_XMII_PARAMS_ENTRY; + struct sja1105_xmii_params_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 17; i < SJA1105_NUM_PORTS; i++, offset += 3) { + sja1105_packing(buf, &entry->xmii_mode[i], + offset + 1, offset + 0, size, op); + sja1105_packing(buf, &entry->phy_mac[i], + offset + 2, offset + 2, size, op); + } + return size; +} + +static size_t sja1110_xmii_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1110_SIZE_XMII_PARAMS_ENTRY; + struct sja1105_xmii_params_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 20; i < SJA1110_NUM_PORTS; i++, offset += 4) { + sja1105_packing(buf, &entry->xmii_mode[i], + offset + 1, offset + 0, size, op); + sja1105_packing(buf, &entry->phy_mac[i], + offset + 2, offset + 2, size, op); + sja1105_packing(buf, &entry->special[i], + offset + 3, offset + 3, size, op); + } + return size; +} + +static size_t sja1105_table_header_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_TABLE_HEADER; + struct sja1105_table_header *entry = entry_ptr; + + sja1105_packing(buf, &entry->block_id, 31, 24, size, op); + sja1105_packing(buf, &entry->len, 55, 32, size, op); + sja1105_packing(buf, &entry->crc, 95, 64, size, op); + return size; +} + +static void +sja1105_table_header_pack_with_crc(void *buf, struct sja1105_table_header *hdr) +{ + /* First pack the table as-is, then calculate the CRC, and + * finally put the proper CRC into the packed buffer + */ + memset(buf, 0, SJA1105_SIZE_TABLE_HEADER); + sja1105_table_header_packing(buf, hdr, PACK); + hdr->crc = sja1105_crc32(buf, SJA1105_SIZE_TABLE_HEADER - 4); + sja1105_packing(buf + SJA1105_SIZE_TABLE_HEADER - 4, &hdr->crc, + 31, 0, 4, PACK); +} + +static void sja1105_table_write_crc(u8 *table_start, u8 *crc_ptr) +{ + u64 computed_crc; + int len_bytes; + + len_bytes = (uintptr_t)(crc_ptr - table_start); + computed_crc = sja1105_crc32(table_start, len_bytes); + sja1105_packing(crc_ptr, &computed_crc, 31, 0, 4, PACK); +} + +/* The block IDs that the switches support are unfortunately sparse, so keep a + * mapping table to "block indices" and translate back and forth. + */ +static const u64 blk_id_map[BLK_IDX_MAX] = { + [BLK_IDX_L2_POLICING] = BLKID_L2_POLICING, + [BLK_IDX_VLAN_LOOKUP] = BLKID_VLAN_LOOKUP, + [BLK_IDX_L2_FORWARDING] = BLKID_L2_FORWARDING, + [BLK_IDX_MAC_CONFIG] = BLKID_MAC_CONFIG, + [BLK_IDX_L2_FORWARDING_PARAMS] = BLKID_L2_FORWARDING_PARAMS, + [BLK_IDX_GENERAL_PARAMS] = BLKID_GENERAL_PARAMS, + [BLK_IDX_XMII_PARAMS] = BLKID_XMII_PARAMS, +}; + +static void +sja1105_static_config_pack(void *buf, struct sja1105_static_config *config) +{ + struct sja1105_table_header header = {0}; + enum sja1105_blk_idx i; + u8 *p = buf; + int j; + + sja1105_packing(p, &config->device_id, 31, 0, 4, PACK); + p += SJA1105_SIZE_DEVICE_ID; + + for (i = 0; i < BLK_IDX_MAX; i++) { + const struct sja1105_table *table; + u8 *table_start; + + table = &config->tables[i]; + if (!table->entry_count) + continue; + + header.block_id = blk_id_map[i]; + header.len = table->entry_count * + table->ops->packed_entry_size / 4; + sja1105_table_header_pack_with_crc(p, &header); + p += SJA1105_SIZE_TABLE_HEADER; + table_start = p; + for (j = 0; j < table->entry_count; j++) { + u8 *entry_ptr = table->entries; + + entry_ptr += j * table->ops->unpacked_entry_size; + memset(p, 0, table->ops->packed_entry_size); + table->ops->packing(p, entry_ptr, PACK); + p += table->ops->packed_entry_size; + } + sja1105_table_write_crc(table_start, p); + p += 4; + } + /* Final header: + * Block ID does not matter + * Length of 0 marks that header is final + * CRC will be replaced on-the-fly + */ + header.block_id = 0; + header.len = 0; + header.crc = 0xDEADBEEF; + memset(p, 0, SJA1105_SIZE_TABLE_HEADER); + sja1105_table_header_packing(p, &header, PACK); +} + +static size_t +sja1105_static_config_get_length(const struct sja1105_static_config *config) +{ + unsigned int header_count; + enum sja1105_blk_idx i; + unsigned int sum; + + /* Ending header */ + header_count = 1; + sum = SJA1105_SIZE_DEVICE_ID; + + /* Tables (headers and entries) */ + for (i = 0; i < BLK_IDX_MAX; i++) { + const struct sja1105_table *table; + + table = &config->tables[i]; + if (table->entry_count) + header_count++; + + sum += table->ops->packed_entry_size * table->entry_count; + } + /* Headers have an additional CRC at the end */ + sum += header_count * (SJA1105_SIZE_TABLE_HEADER + 4); + /* Last header does not have an extra CRC because there is no data */ + sum -= 4; + + return sum; +} + +/* Compatibility matrices */ +static const struct sja1105_table_ops sja1105et_table_ops[BLK_IDX_MAX] = { + [BLK_IDX_L2_POLICING] = { + .packing = sja1105_l2_policing_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_policing_entry), + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY, + .max_entry_count = SJA1105_MAX_L2_POLICING_COUNT, + }, + [BLK_IDX_VLAN_LOOKUP] = { + .packing = sja1105_vlan_lookup_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_vlan_lookup_entry), + .packed_entry_size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY, + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, + }, + [BLK_IDX_L2_FORWARDING] = { + .packing = sja1105_l2_forwarding_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT, + }, + [BLK_IDX_MAC_CONFIG] = { + .packing = sja1105et_mac_config_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_mac_config_entry), + .packed_entry_size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY, + .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, + }, + [BLK_IDX_L2_FORWARDING_PARAMS] = { + .packing = sja1105_l2_forwarding_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_params_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + }, + [BLK_IDX_GENERAL_PARAMS] = { + .packing = sja1105et_general_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_general_params_entry), + .packed_entry_size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, + }, + [BLK_IDX_XMII_PARAMS] = { + .packing = sja1105_xmii_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry), + .packed_entry_size = SJA1105_SIZE_XMII_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT, + }, +}; + +static const struct sja1105_table_ops sja1105pqrs_table_ops[BLK_IDX_MAX] = { + [BLK_IDX_L2_POLICING] = { + .packing = sja1105_l2_policing_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_policing_entry), + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY, + .max_entry_count = SJA1105_MAX_L2_POLICING_COUNT, + }, + [BLK_IDX_VLAN_LOOKUP] = { + .packing = sja1105_vlan_lookup_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_vlan_lookup_entry), + .packed_entry_size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY, + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, + }, + [BLK_IDX_L2_FORWARDING] = { + .packing = sja1105_l2_forwarding_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT, + }, + [BLK_IDX_MAC_CONFIG] = { + .packing = sja1105pqrs_mac_config_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_mac_config_entry), + .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY, + .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, + }, + [BLK_IDX_L2_FORWARDING_PARAMS] = { + .packing = sja1105_l2_forwarding_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_params_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + }, + [BLK_IDX_GENERAL_PARAMS] = { + .packing = sja1105pqrs_general_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_general_params_entry), + .packed_entry_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, + }, + [BLK_IDX_XMII_PARAMS] = { + .packing = sja1105_xmii_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry), + .packed_entry_size = SJA1105_SIZE_XMII_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT, + }, +}; + +static const struct sja1105_table_ops sja1110_table_ops[BLK_IDX_MAX] = { + [BLK_IDX_L2_POLICING] = { + .packing = sja1110_l2_policing_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_policing_entry), + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY, + .max_entry_count = SJA1110_MAX_L2_POLICING_COUNT, + }, + [BLK_IDX_VLAN_LOOKUP] = { + .packing = sja1110_vlan_lookup_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_vlan_lookup_entry), + .packed_entry_size = SJA1110_SIZE_VLAN_LOOKUP_ENTRY, + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, + }, + [BLK_IDX_L2_FORWARDING] = { + .packing = sja1110_l2_forwarding_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY, + .max_entry_count = SJA1110_MAX_L2_FORWARDING_COUNT, + }, + [BLK_IDX_MAC_CONFIG] = { + .packing = sja1110_mac_config_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_mac_config_entry), + .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY, + .max_entry_count = SJA1110_MAX_MAC_CONFIG_COUNT, + }, + [BLK_IDX_L2_FORWARDING_PARAMS] = { + .packing = sja1110_l2_forwarding_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_forwarding_params_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + }, + [BLK_IDX_GENERAL_PARAMS] = { + .packing = sja1110_general_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_general_params_entry), + .packed_entry_size = SJA1110_SIZE_GENERAL_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, + }, + [BLK_IDX_XMII_PARAMS] = { + .packing = sja1110_xmii_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry), + .packed_entry_size = SJA1110_SIZE_XMII_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT, + }, +}; + +static int sja1105_init_mii_settings(struct sja1105_private *priv) +{ + struct sja1105_table *table; + + table = &priv->static_config.tables[BLK_IDX_XMII_PARAMS]; + + table->entries = calloc(SJA1105_MAX_XMII_PARAMS_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + /* Table will be populated at runtime */ + table->entry_count = SJA1105_MAX_XMII_PARAMS_COUNT; + + return 0; +} + +static void sja1105_setup_tagging(struct sja1105_private *priv, int port) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev); + struct sja1105_vlan_lookup_entry *vlan; + int cpu = pdata->cpu_port; + + /* The CPU port is implicitly configured by + * configuring the front-panel ports + */ + if (port == cpu) + return; + + vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; + + priv->pvid[port] = DSA_8021Q_DIR_TX | DSA_8021Q_PORT(port); + + vlan[port].vmemb_port = BIT(port) | BIT(cpu); + vlan[port].vlan_bc = BIT(port) | BIT(cpu); + vlan[port].tag_port = BIT(cpu); + vlan[port].vlanid = priv->pvid[port]; + vlan[port].type_entry = SJA1110_VLAN_D_TAG; +} + +static int sja1105_init_vlan(struct sja1105_private *priv) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev); + struct sja1105_table *table; + int port; + + table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; + + table->entries = calloc(pdata->num_ports, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = pdata->num_ports; + + for (port = 0; port < pdata->num_ports; port++) + sja1105_setup_tagging(priv, port); + + return 0; +} + +static void +sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd, + int from, int to) +{ + l2_fwd[from].bc_domain |= BIT(to); + l2_fwd[from].reach_port |= BIT(to); + l2_fwd[from].fl_domain |= BIT(to); +} + +static int sja1105_init_l2_forwarding(struct sja1105_private *priv) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev); + struct sja1105_l2_forwarding_entry *l2fwd; + struct sja1105_table *table; + int cpu = pdata->cpu_port; + int i; + + table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING]; + + table->entries = calloc(SJA1105_MAX_L2_FORWARDING_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = SJA1105_MAX_L2_FORWARDING_COUNT; + + l2fwd = table->entries; + + /* First 5 entries define the forwarding rules */ + for (i = 0; i < pdata->num_ports; i++) { + if (i == cpu) + continue; + + sja1105_port_allow_traffic(l2fwd, i, cpu); + sja1105_port_allow_traffic(l2fwd, cpu, i); + } + /* Next 8 entries define VLAN PCP mapping from ingress to egress. + * Leave them unpopulated (implicitly 0) but present. + */ + return 0; +} + +static int sja1105_init_l2_forwarding_params(struct sja1105_private *priv) +{ + struct sja1105_l2_forwarding_params_entry default_l2fwd_params = { + /* Use a single memory partition for all ingress queues */ + .part_spc = { SJA1105_MAX_FRAME_MEMORY, 0, 0, 0, 0, 0, 0, 0 }, + }; + struct sja1105_table *table; + + table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS]; + + table->entries = calloc(SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT; + + /* This table only has a single entry */ + ((struct sja1105_l2_forwarding_params_entry *)table->entries)[0] = + default_l2fwd_params; + + return 0; +} + +static int sja1105_init_general_params(struct sja1105_private *priv) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev); + struct sja1105_general_params_entry default_general_params = { + /* No frame trapping */ + .mac_fltres1 = 0x0, + .mac_flt1 = 0xffffffffffff, + .mac_fltres0 = 0x0, + .mac_flt0 = 0xffffffffffff, + .host_port = pdata->num_ports, + /* No mirroring => specify an out-of-range port value */ + .mirr_port = pdata->num_ports, + /* No link-local trapping => specify an out-of-range port value + */ + .casc_port = pdata->num_ports, + /* Force the switch to see all traffic as untagged. */ + .tpid = ETH_P_SJA1105, + .tpid2 = ETH_P_SJA1105, + }; + struct sja1105_table *table; + + table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; + + table->entries = calloc(SJA1105_MAX_GENERAL_PARAMS_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT; + + /* This table only has a single entry */ + ((struct sja1105_general_params_entry *)table->entries)[0] = + default_general_params; + + return 0; +} + +static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing, + int index, int mtu) +{ + policing[index].sharindx = index; + policing[index].smax = 65535; /* Burst size in bytes */ + policing[index].rate = SJA1105_RATE_MBPS(1000); + policing[index].maxlen = mtu; + policing[index].partition = 0; +} + +static int sja1105_init_l2_policing(struct sja1105_private *priv) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev); + struct sja1105_l2_policing_entry *policing; + struct sja1105_table *table; + int cpu = pdata->cpu_port; + int i, j, k; + + table = &priv->static_config.tables[BLK_IDX_L2_POLICING]; + + table->entries = calloc(SJA1105_MAX_L2_POLICING_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = SJA1105_MAX_L2_POLICING_COUNT; + + policing = table->entries; + + /* k sweeps through all unicast policers (0-39). + * bcast sweeps through policers 40-44. + */ + for (i = 0, k = 0; i < pdata->num_ports; i++) { + int bcast = (pdata->num_ports * SJA1105_NUM_TC) + i; + int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; + + if (i == cpu) + mtu += VLAN_HLEN; + + for (j = 0; j < SJA1105_NUM_TC; j++, k++) + sja1105_setup_policer(policing, k, mtu); + + /* Set up this port's policer for broadcast traffic */ + sja1105_setup_policer(policing, bcast, mtu); + } + return 0; +} + +static int sja1105_init_mac_settings(struct sja1105_private *priv) +{ + struct sja1105_mac_config_entry default_mac = { + /* Enable 1 priority queue on egress. */ + .top = {0x1FF, 0, 0, 0, 0, 0, 0}, + .base = {0x0, 0, 0, 0, 0, 0, 0, 0}, + .enabled = {1, 0, 0, 0, 0, 0, 0, 0}, + /* Will be overridden in sja1105_port_enable. */ + .speed = priv->info->port_speed[SJA1105_SPEED_AUTO], + .egress = true, + .ingress = true, + }; + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev); + struct sja1105_mac_config_entry *mac; + struct sja1105_table *table; + int port; + + table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG]; + + table->entries = calloc(pdata->num_ports, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = pdata->num_ports; + + mac = table->entries; + + for (port = 0; port < pdata->num_ports; port++) { + mac[port] = default_mac; + /* Internal VLAN (pvid) to apply to untagged ingress */ + mac[port].vlanid = priv->pvid[port]; + } + + return 0; +} + +static int sja1105_static_config_init(struct sja1105_private *priv) +{ + struct sja1105_static_config *config = &priv->static_config; + const struct sja1105_table_ops *static_ops = priv->info->static_ops; + u64 device_id = priv->info->device_id; + enum sja1105_blk_idx i; + int rc; + + *config = (struct sja1105_static_config) {0}; + + /* Transfer static_ops array from priv into per-table ops + * for handier access + */ + for (i = 0; i < BLK_IDX_MAX; i++) + config->tables[i].ops = &static_ops[i]; + + config->device_id = device_id; + + /* Build initial static configuration, to be fixed up during runtime */ + rc = sja1105_init_vlan(priv); + if (rc < 0) + return rc; + rc = sja1105_init_mac_settings(priv); + if (rc < 0) + return rc; + rc = sja1105_init_mii_settings(priv); + if (rc < 0) + return rc; + rc = sja1105_init_l2_forwarding(priv); + if (rc < 0) + return rc; + rc = sja1105_init_l2_forwarding_params(priv); + if (rc < 0) + return rc; + rc = sja1105_init_l2_policing(priv); + if (rc < 0) + return rc; + rc = sja1105_init_general_params(priv); + if (rc < 0) + return rc; + + return 0; +} + +static void sja1105_static_config_free(struct sja1105_static_config *config) +{ + enum sja1105_blk_idx i; + + for (i = 0; i < BLK_IDX_MAX; i++) { + if (config->tables[i].entry_count) { + free(config->tables[i].entries); + config->tables[i].entry_count = 0; + } + } +} + +static void sja1105_cgu_idiv_packing(void *buf, struct sja1105_cgu_idiv *idiv, + enum packing_op op) +{ + const int size = 4; + + sja1105_packing(buf, &idiv->clksrc, 28, 24, size, op); + sja1105_packing(buf, &idiv->autoblock, 11, 11, size, op); + sja1105_packing(buf, &idiv->idiv, 5, 2, size, op); + sja1105_packing(buf, &idiv->pd, 0, 0, size, op); +} + +static int sja1105_cgu_idiv_config(struct sja1105_private *priv, int port, + bool enabled, int factor) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + struct sja1105_cgu_idiv idiv; + + if (regs->cgu_idiv[port] == SJA1105_RSV_ADDR) + return 0; + + if (enabled && factor != 1 && factor != 10) + return -ERANGE; + + /* Payload for packed_buf */ + idiv.clksrc = 0x0A; /* 25MHz */ + idiv.autoblock = 1; /* Block clk automatically */ + idiv.idiv = factor - 1; /* Divide by 1 or 10 */ + idiv.pd = enabled ? 0 : 1; /* Power down? */ + sja1105_cgu_idiv_packing(packed_buf, &idiv, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->cgu_idiv[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static void +sja1105_cgu_mii_control_packing(void *buf, struct sja1105_cgu_mii_ctrl *cmd, + enum packing_op op) +{ + const int size = 4; + + sja1105_packing(buf, &cmd->clksrc, 28, 24, size, op); + sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op); + sja1105_packing(buf, &cmd->pd, 0, 0, size, op); +} + +static int sja1105_cgu_mii_tx_clk_config(struct sja1105_private *priv, + int port, sja1105_mii_role_t role) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl mii_tx_clk; + const int mac_clk_sources[] = { + CLKSRC_MII0_TX_CLK, + CLKSRC_MII1_TX_CLK, + CLKSRC_MII2_TX_CLK, + CLKSRC_MII3_TX_CLK, + CLKSRC_MII4_TX_CLK, + }; + const int phy_clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, + CLKSRC_IDIV3, + CLKSRC_IDIV4, + }; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + int clksrc; + + if (regs->mii_tx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + if (role == XMII_MAC) + clksrc = mac_clk_sources[port]; + else + clksrc = phy_clk_sources[port]; + + /* Payload for packed_buf */ + mii_tx_clk.clksrc = clksrc; + mii_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + mii_tx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &mii_tx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_tx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int +sja1105_cgu_mii_rx_clk_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + struct sja1105_cgu_mii_ctrl mii_rx_clk; + const int clk_sources[] = { + CLKSRC_MII0_RX_CLK, + CLKSRC_MII1_RX_CLK, + CLKSRC_MII2_RX_CLK, + CLKSRC_MII3_RX_CLK, + CLKSRC_MII4_RX_CLK, + }; + + if (regs->mii_rx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + mii_rx_clk.clksrc = clk_sources[port]; + mii_rx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + mii_rx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &mii_rx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_rx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int +sja1105_cgu_mii_ext_tx_clk_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl mii_ext_tx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + const int clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, + CLKSRC_IDIV3, + CLKSRC_IDIV4, + }; + + if (regs->mii_ext_tx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + mii_ext_tx_clk.clksrc = clk_sources[port]; + mii_ext_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + mii_ext_tx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &mii_ext_tx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_ext_tx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int +sja1105_cgu_mii_ext_rx_clk_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl mii_ext_rx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + const int clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, + CLKSRC_IDIV3, + CLKSRC_IDIV4, + }; + + if (regs->mii_ext_rx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + mii_ext_rx_clk.clksrc = clk_sources[port]; + mii_ext_rx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + mii_ext_rx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &mii_ext_rx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_ext_rx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1105_mii_clocking_setup(struct sja1105_private *priv, int port, + sja1105_mii_role_t role) +{ + int rc; + + rc = sja1105_cgu_idiv_config(priv, port, (role == XMII_PHY), 1); + if (rc < 0) + return rc; + + rc = sja1105_cgu_mii_tx_clk_config(priv, port, role); + if (rc < 0) + return rc; + + rc = sja1105_cgu_mii_rx_clk_config(priv, port); + if (rc < 0) + return rc; + + if (role == XMII_PHY) { + rc = sja1105_cgu_mii_ext_tx_clk_config(priv, port); + if (rc < 0) + return rc; + + rc = sja1105_cgu_mii_ext_rx_clk_config(priv, port); + if (rc < 0) + return rc; + } + return 0; +} + +static void +sja1105_cgu_pll_control_packing(void *buf, struct sja1105_cgu_pll_ctrl *cmd, + enum packing_op op) +{ + const int size = 4; + + sja1105_packing(buf, &cmd->pllclksrc, 28, 24, size, op); + sja1105_packing(buf, &cmd->msel, 23, 16, size, op); + sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op); + sja1105_packing(buf, &cmd->psel, 9, 8, size, op); + sja1105_packing(buf, &cmd->direct, 7, 7, size, op); + sja1105_packing(buf, &cmd->fbsel, 6, 6, size, op); + sja1105_packing(buf, &cmd->bypass, 1, 1, size, op); + sja1105_packing(buf, &cmd->pd, 0, 0, size, op); +} + +static int sja1105_cgu_rgmii_tx_clk_config(struct sja1105_private *priv, + int port, u64 speed) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl txc; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + int clksrc; + + if (regs->rgmii_tx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) { + clksrc = CLKSRC_PLL0; + } else { + int clk_sources[] = {CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2, + CLKSRC_IDIV3, CLKSRC_IDIV4}; + clksrc = clk_sources[port]; + } + + /* RGMII: 125MHz for 1000, 25MHz for 100, 2.5MHz for 10 */ + txc.clksrc = clksrc; + /* Autoblock clk while changing clksrc */ + txc.autoblock = 1; + /* Power Down off => enabled */ + txc.pd = 0; + sja1105_cgu_mii_control_packing(packed_buf, &txc, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgmii_tx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +/* AGU */ +static void +sja1105_cfg_pad_mii_packing(void *buf, struct sja1105_cfg_pad_mii *cmd, + enum packing_op op) +{ + const int size = 4; + + sja1105_packing(buf, &cmd->d32_os, 28, 27, size, op); + sja1105_packing(buf, &cmd->d32_ih, 26, 26, size, op); + sja1105_packing(buf, &cmd->d32_ipud, 25, 24, size, op); + sja1105_packing(buf, &cmd->d10_os, 20, 19, size, op); + sja1105_packing(buf, &cmd->d10_ih, 18, 18, size, op); + sja1105_packing(buf, &cmd->d10_ipud, 17, 16, size, op); + sja1105_packing(buf, &cmd->ctrl_os, 12, 11, size, op); + sja1105_packing(buf, &cmd->ctrl_ih, 10, 10, size, op); + sja1105_packing(buf, &cmd->ctrl_ipud, 9, 8, size, op); + sja1105_packing(buf, &cmd->clk_os, 4, 3, size, op); + sja1105_packing(buf, &cmd->clk_ih, 2, 2, size, op); + sja1105_packing(buf, &cmd->clk_ipud, 1, 0, size, op); +} + +static void +sja1110_cfg_pad_mii_id_packing(void *buf, struct sja1105_cfg_pad_mii_id *cmd, + enum packing_op op) +{ + const int size = SJA1105_SIZE_CGU_CMD; + u64 range = 4; + + /* Fields RXC_RANGE and TXC_RANGE select the input frequency range: + * 0 = 2.5MHz + * 1 = 25MHz + * 2 = 50MHz + * 3 = 125MHz + * 4 = Automatically determined by port speed. + * There's no point in defining a structure different than the one for + * SJA1105, so just hardcode the frequency range to automatic, just as + * before. + */ + sja1105_packing(buf, &cmd->rxc_stable_ovr, 26, 26, size, op); + sja1105_packing(buf, &cmd->rxc_delay, 25, 21, size, op); + sja1105_packing(buf, &range, 20, 18, size, op); + sja1105_packing(buf, &cmd->rxc_bypass, 17, 17, size, op); + sja1105_packing(buf, &cmd->rxc_pd, 16, 16, size, op); + sja1105_packing(buf, &cmd->txc_stable_ovr, 10, 10, size, op); + sja1105_packing(buf, &cmd->txc_delay, 9, 5, size, op); + sja1105_packing(buf, &range, 4, 2, size, op); + sja1105_packing(buf, &cmd->txc_bypass, 1, 1, size, op); + sja1105_packing(buf, &cmd->txc_pd, 0, 0, size, op); +} + +static int sja1105_rgmii_cfg_pad_tx_config(struct sja1105_private *priv, + int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cfg_pad_mii pad_mii_tx = {0}; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + + if (regs->pad_mii_tx[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload */ + pad_mii_tx.d32_os = 3; /* TXD[3:2] output stage: */ + /* high noise/high speed */ + pad_mii_tx.d10_os = 3; /* TXD[1:0] output stage: */ + /* high noise/high speed */ + pad_mii_tx.d32_ipud = 2; /* TXD[3:2] input stage: */ + /* plain input (default) */ + pad_mii_tx.d10_ipud = 2; /* TXD[1:0] input stage: */ + /* plain input (default) */ + pad_mii_tx.ctrl_os = 3; /* TX_CTL / TX_ER output stage */ + pad_mii_tx.ctrl_ipud = 2; /* TX_CTL / TX_ER input stage (default) */ + pad_mii_tx.clk_os = 3; /* TX_CLK output stage */ + pad_mii_tx.clk_ih = 0; /* TX_CLK input hysteresis (default) */ + pad_mii_tx.clk_ipud = 2; /* TX_CLK input stage (default) */ + sja1105_cfg_pad_mii_packing(packed_buf, &pad_mii_tx, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_tx[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1105_cfg_pad_rx_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cfg_pad_mii pad_mii_rx = {0}; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + + if (regs->pad_mii_rx[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload */ + pad_mii_rx.d32_ih = 0; /* RXD[3:2] input stage hysteresis: */ + /* non-Schmitt (default) */ + pad_mii_rx.d32_ipud = 2; /* RXD[3:2] input weak pull-up/down */ + /* plain input (default) */ + pad_mii_rx.d10_ih = 0; /* RXD[1:0] input stage hysteresis: */ + /* non-Schmitt (default) */ + pad_mii_rx.d10_ipud = 2; /* RXD[1:0] input weak pull-up/down */ + /* plain input (default) */ + pad_mii_rx.ctrl_ih = 0; /* RX_DV/CRS_DV/RX_CTL and RX_ER */ + /* input stage hysteresis: */ + /* non-Schmitt (default) */ + pad_mii_rx.ctrl_ipud = 3; /* RX_DV/CRS_DV/RX_CTL and RX_ER */ + /* input stage weak pull-up/down: */ + /* pull-down */ + pad_mii_rx.clk_os = 2; /* RX_CLK/RXC output stage: */ + /* medium noise/fast speed (default) */ + pad_mii_rx.clk_ih = 0; /* RX_CLK/RXC input hysteresis: */ + /* non-Schmitt (default) */ + pad_mii_rx.clk_ipud = 2; /* RX_CLK/RXC input pull-up/down: */ + /* plain input (default) */ + sja1105_cfg_pad_mii_packing(packed_buf, &pad_mii_rx, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_rx[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static void +sja1105_cfg_pad_mii_id_packing(void *buf, struct sja1105_cfg_pad_mii_id *cmd, + enum packing_op op) +{ + const int size = SJA1105_SIZE_CGU_CMD; + + sja1105_packing(buf, &cmd->rxc_stable_ovr, 15, 15, size, op); + sja1105_packing(buf, &cmd->rxc_delay, 14, 10, size, op); + sja1105_packing(buf, &cmd->rxc_bypass, 9, 9, size, op); + sja1105_packing(buf, &cmd->rxc_pd, 8, 8, size, op); + sja1105_packing(buf, &cmd->txc_stable_ovr, 7, 7, size, op); + sja1105_packing(buf, &cmd->txc_delay, 6, 2, size, op); + sja1105_packing(buf, &cmd->txc_bypass, 1, 1, size, op); + sja1105_packing(buf, &cmd->txc_pd, 0, 0, size, op); +} + +/* Valid range in degrees is an integer between 73.8 and 101.7 */ +static u64 sja1105_rgmii_delay(u64 phase) +{ + /* UM11040.pdf: The delay in degree phase is 73.8 + delay_tune * 0.9. + * To avoid floating point operations we'll multiply by 10 + * and get 1 decimal point precision. + */ + phase *= 10; + return (phase - 738) / 9; +} + +static int sja1105pqrs_setup_rgmii_delay(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cfg_pad_mii_id pad_mii_id = {0}; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + int rc; + + if (priv->rgmii_rx_delay[port]) + pad_mii_id.rxc_delay = sja1105_rgmii_delay(90); + if (priv->rgmii_tx_delay[port]) + pad_mii_id.txc_delay = sja1105_rgmii_delay(90); + + /* Stage 1: Turn the RGMII delay lines off. */ + pad_mii_id.rxc_bypass = 1; + pad_mii_id.rxc_pd = 1; + pad_mii_id.txc_bypass = 1; + pad_mii_id.txc_pd = 1; + sja1105_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK); + + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port], + packed_buf, SJA1105_SIZE_CGU_CMD); + if (rc < 0) + return rc; + + /* Stage 2: Turn the RGMII delay lines on. */ + if (priv->rgmii_rx_delay[port]) { + pad_mii_id.rxc_bypass = 0; + pad_mii_id.rxc_pd = 0; + } + if (priv->rgmii_tx_delay[port]) { + pad_mii_id.txc_bypass = 0; + pad_mii_id.txc_pd = 0; + } + sja1105_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1110_setup_rgmii_delay(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cfg_pad_mii_id pad_mii_id = {0}; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + + pad_mii_id.rxc_pd = 1; + pad_mii_id.txc_pd = 1; + + if (priv->rgmii_rx_delay[port]) { + pad_mii_id.rxc_delay = sja1105_rgmii_delay(90); + /* The "BYPASS" bit in SJA1110 is actually a "don't bypass" */ + pad_mii_id.rxc_bypass = 1; + pad_mii_id.rxc_pd = 0; + } + + if (priv->rgmii_tx_delay[port]) { + pad_mii_id.txc_delay = sja1105_rgmii_delay(90); + pad_mii_id.txc_bypass = 1; + pad_mii_id.txc_pd = 0; + } + + sja1110_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1105_rgmii_clocking_setup(struct sja1105_private *priv, int port, + sja1105_mii_role_t role) +{ + struct sja1105_mac_config_entry *mac; + struct udevice *dev = priv->dev; + u64 speed; + int rc; + + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; + speed = mac[port].speed; + + if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) { + /* 1000Mbps, IDIV disabled (125 MHz) */ + rc = sja1105_cgu_idiv_config(priv, port, false, 1); + } else if (speed == priv->info->port_speed[SJA1105_SPEED_100MBPS]) { + /* 100Mbps, IDIV enabled, divide by 1 (25 MHz) */ + rc = sja1105_cgu_idiv_config(priv, port, true, 1); + } else if (speed == priv->info->port_speed[SJA1105_SPEED_10MBPS]) { + /* 10Mbps, IDIV enabled, divide by 10 (2.5 MHz) */ + rc = sja1105_cgu_idiv_config(priv, port, true, 10); + } else if (speed == priv->info->port_speed[SJA1105_SPEED_AUTO]) { + /* Skip CGU configuration if there is no speed available + * (e.g. link is not established yet) + */ + dev_dbg(dev, "Speed not available, skipping CGU config\n"); + return 0; + } else { + rc = -EINVAL; + } + + if (rc < 0) { + dev_err(dev, "Failed to configure idiv\n"); + return rc; + } + rc = sja1105_cgu_rgmii_tx_clk_config(priv, port, speed); + if (rc < 0) { + dev_err(dev, "Failed to configure RGMII Tx clock\n"); + return rc; + } + rc = sja1105_rgmii_cfg_pad_tx_config(priv, port); + if (rc < 0) { + dev_err(dev, "Failed to configure Tx pad registers\n"); + return rc; + } + + if (!priv->info->setup_rgmii_delay) + return 0; + + return priv->info->setup_rgmii_delay(priv, port); +} + +static int sja1105_cgu_rmii_ref_clk_config(struct sja1105_private *priv, + int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + struct sja1105_cgu_mii_ctrl ref_clk; + const int clk_sources[] = { + CLKSRC_MII0_TX_CLK, + CLKSRC_MII1_TX_CLK, + CLKSRC_MII2_TX_CLK, + CLKSRC_MII3_TX_CLK, + CLKSRC_MII4_TX_CLK, + }; + + if (regs->rmii_ref_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + ref_clk.clksrc = clk_sources[port]; + ref_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + ref_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &ref_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_ref_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int +sja1105_cgu_rmii_ext_tx_clk_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_cgu_mii_ctrl ext_tx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + + if (regs->rmii_ext_tx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + ext_tx_clk.clksrc = CLKSRC_PLL1; + ext_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + ext_tx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &ext_tx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_ext_tx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1105_cgu_rmii_pll_config(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + struct sja1105_cgu_pll_ctrl pll = {0}; + int rc; + + if (regs->rmii_pll1 == SJA1105_RSV_ADDR) + return 0; + + /* Step 1: PLL1 setup for 50Mhz */ + pll.pllclksrc = 0xA; + pll.msel = 0x1; + pll.autoblock = 0x1; + pll.psel = 0x1; + pll.direct = 0x0; + pll.fbsel = 0x1; + pll.bypass = 0x0; + pll.pd = 0x1; + + sja1105_cgu_pll_control_packing(packed_buf, &pll, PACK); + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_pll1, packed_buf, + SJA1105_SIZE_CGU_CMD); + if (rc < 0) + return rc; + + /* Step 2: Enable PLL1 */ + pll.pd = 0x0; + + sja1105_cgu_pll_control_packing(packed_buf, &pll, PACK); + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_pll1, packed_buf, + SJA1105_SIZE_CGU_CMD); + return rc; +} + +static int sja1105_rmii_clocking_setup(struct sja1105_private *priv, int port, + sja1105_mii_role_t role) +{ + int rc; + + /* AH1601.pdf chapter 2.5.1. Sources */ + if (role == XMII_MAC) { + /* Configure and enable PLL1 for 50Mhz output */ + rc = sja1105_cgu_rmii_pll_config(priv); + if (rc < 0) + return rc; + } + /* Disable IDIV for this port */ + rc = sja1105_cgu_idiv_config(priv, port, false, 1); + if (rc < 0) + return rc; + /* Source to sink mappings */ + rc = sja1105_cgu_rmii_ref_clk_config(priv, port); + if (rc < 0) + return rc; + if (role == XMII_MAC) { + rc = sja1105_cgu_rmii_ext_tx_clk_config(priv, port); + if (rc < 0) + return rc; + } + return 0; +} + +static int sja1105_pcs_read(struct sja1105_private *priv, int addr, + int devad, int regnum) +{ + return priv->mdio_pcs->read(priv->mdio_pcs, addr, devad, regnum); +} + +static int sja1105_pcs_write(struct sja1105_private *priv, int addr, + int devad, int regnum, u16 val) +{ + return priv->mdio_pcs->write(priv->mdio_pcs, addr, devad, regnum, val); +} + +/* In NXP SJA1105, the PCS is integrated with a PMA that has the TX lane + * polarity inverted by default (PLUS is MINUS, MINUS is PLUS). To obtain + * normal non-inverted behavior, the TX lane polarity must be inverted in the + * PCS, via the DIGITAL_CONTROL_2 register. + */ +static int sja1105_pma_config(struct sja1105_private *priv, int port) +{ + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + DW_VR_MII_DIG_CTRL2, + DW_VR_MII_DIG_CTRL2_TX_POL_INV); +} + +static int sja1110_pma_config(struct sja1105_private *priv, int port) +{ + u16 txpll_fbdiv = 0x19, txpll_refdiv = 0x1; + u16 rxpll_fbdiv = 0x19, rxpll_refdiv = 0x1; + u16 rx_cdr_ctle = 0x212a; + u16 val; + int rc; + + /* Program TX PLL feedback divider and reference divider settings for + * correct oscillation frequency. + */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_TXPLL_CTRL0, + SJA1110_TXPLL_FBDIV(txpll_fbdiv)); + if (rc < 0) + return rc; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_TXPLL_CTRL1, + SJA1110_TXPLL_REFDIV(txpll_refdiv)); + if (rc < 0) + return rc; + + /* Program transmitter amplitude and disable amplitude trimming */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_LANE_DRIVER1_0, SJA1110_TXDRV(0x5)); + if (rc < 0) + return rc; + + val = SJA1110_TXDRVTRIM_LSB(0xffffffull); + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_LANE_DRIVER2_0, val); + if (rc < 0) + return rc; + + val = SJA1110_TXDRVTRIM_MSB(0xffffffull) | SJA1110_LANE_DRIVER2_1_RSV; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_LANE_DRIVER2_1, val); + if (rc < 0) + return rc; + + /* Enable input and output resistor terminations for low BER. */ + val = SJA1110_ACCOUPLE_RXVCM_EN | SJA1110_CDR_GAIN | + SJA1110_RXRTRIM(4) | SJA1110_RXTEN | SJA1110_TXPLL_BWSEL | + SJA1110_TXRTRIM(3) | SJA1110_TXTEN; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_LANE_TRIM, + val); + if (rc < 0) + return rc; + + /* Select PCS as transmitter data source. */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_LANE_DATAPATH_1, 0); + if (rc < 0) + return rc; + + /* Program RX PLL feedback divider and reference divider for correct + * oscillation frequency. + */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_RXPLL_CTRL0, + SJA1110_RXPLL_FBDIV(rxpll_fbdiv)); + if (rc < 0) + return rc; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_RXPLL_CTRL1, + SJA1110_RXPLL_REFDIV(rxpll_refdiv)); + if (rc < 0) + return rc; + + /* Program threshold for receiver signal detector. + * Enable control of RXPLL by receiver signal detector to disable RXPLL + * when an input signal is not present. + */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_RX_DATA_DETECT, 0x0005); + if (rc < 0) + return rc; + + /* Enable TX and RX PLLs and circuits. + * Release reset of PMA to enable data flow to/from PCS. + */ + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, + SJA1110_POWERDOWN_ENABLE); + if (rc < 0) + return rc; + + val = rc & ~(SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD | + SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN | + SJA1110_RESET_SER | SJA1110_RESET_DES); + val |= SJA1110_RXPKDETEN | SJA1110_RCVEN; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, + SJA1110_POWERDOWN_ENABLE, val); + if (rc < 0) + return rc; + + /* Program continuous-time linear equalizer (CTLE) settings. */ + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_RX_CDR_CTLE, + rx_cdr_ctle); + if (rc < 0) + return rc; + + return 0; +} + +static int sja1105_xpcs_config_aneg_c37_sgmii(struct sja1105_private *priv, + int port) +{ + int rc; + + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1); + if (rc < 0) + return rc; + rc &= ~MDIO_AN_CTRL1_ENABLE; + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1, + rc); + if (rc < 0) + return rc; + + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); + if (rc < 0) + return rc; + + rc &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK); + rc |= (DW_VR_MII_PCS_MODE_C37_SGMII << + DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & + DW_VR_MII_PCS_MODE_MASK); + rc |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII << + DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & + DW_VR_MII_TX_CONFIG_MASK); + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, + rc); + if (rc < 0) + return rc; + + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); + if (rc < 0) + return rc; + + if (priv->xpcs_cfg[port].inband_an) + rc |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; + else + rc &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; + + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, + rc); + if (rc < 0) + return rc; + + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1); + if (rc < 0) + return rc; + + if (priv->xpcs_cfg[port].inband_an) + rc |= MDIO_AN_CTRL1_ENABLE; + else + rc &= ~MDIO_AN_CTRL1_ENABLE; + + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1, rc); +} + +static int sja1105_xpcs_link_up_sgmii(struct sja1105_private *priv, int port) +{ + int val = BMCR_FULLDPLX; + + if (priv->xpcs_cfg[port].inband_an) + return 0; + + switch (priv->xpcs_cfg[port].speed) { + case SPEED_1000: + val = BMCR_SPEED1000; + break; + case SPEED_100: + val = BMCR_SPEED100; + break; + case SPEED_10: + val = BMCR_SPEED10; + break; + default: + dev_err(priv->dev, "Invalid PCS speed %d\n", + priv->xpcs_cfg[port].speed); + return -EINVAL; + } + + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1, val); +} + +static int sja1105_sgmii_setup(struct sja1105_private *priv, int port) +{ + int rc; + + rc = sja1105_xpcs_config_aneg_c37_sgmii(priv, port); + if (rc) + return rc; + + rc = sja1105_xpcs_link_up_sgmii(priv, port); + if (rc) + return rc; + + return priv->info->pma_config(priv, port); +} + +static int sja1105_clocking_setup_port(struct sja1105_private *priv, int port) +{ + struct sja1105_xmii_params_entry *mii; + sja1105_phy_interface_t phy_mode; + sja1105_mii_role_t role; + int rc; + + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; + + /* RGMII etc */ + phy_mode = mii->xmii_mode[port]; + /* MAC or PHY, for applicable types (not RGMII) */ + role = mii->phy_mac[port]; + + switch (phy_mode) { + case XMII_MODE_MII: + rc = sja1105_mii_clocking_setup(priv, port, role); + break; + case XMII_MODE_RMII: + rc = sja1105_rmii_clocking_setup(priv, port, role); + break; + case XMII_MODE_RGMII: + rc = sja1105_rgmii_clocking_setup(priv, port, role); + break; + case XMII_MODE_SGMII: + rc = sja1105_sgmii_setup(priv, port); + break; + default: + return -EINVAL; + } + if (rc) + return rc; + + /* Internally pull down the RX_DV/CRS_DV/RX_CTL and RX_ER inputs */ + return sja1105_cfg_pad_rx_config(priv, port); +} + +static int sja1105_clocking_setup(struct sja1105_private *priv) +{ + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev); + int port, rc; + + for (port = 0; port < pdata->num_ports; port++) { + rc = sja1105_clocking_setup_port(priv, port); + if (rc < 0) + return rc; + } + return 0; +} + +static int sja1105_pcs_mdio_read(struct mii_dev *bus, int phy, int mmd, int reg) +{ + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0}; + struct sja1105_private *priv = bus->priv; + const int size = SJA1105_SIZE_MDIO_CMD; + u64 addr, tmp; + int rc; + + if (mmd == MDIO_DEVAD_NONE) + return -ENODEV; + + if (!priv->info->supports_sgmii[phy]) + return -ENODEV; + + addr = (mmd << 16) | (reg & GENMASK(15, 0)); + + if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2) + return 0xffff; + + rc = sja1105_xfer_buf(priv, SPI_READ, addr, packed_buf, size); + if (rc < 0) + return rc; + + sja1105_packing(packed_buf, &tmp, 31, 0, size, UNPACK); + + return tmp & 0xffff; +} + +static int sja1105_pcs_mdio_write(struct mii_dev *bus, int phy, int mmd, + int reg, u16 val) +{ + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0}; + struct sja1105_private *priv = bus->priv; + const int size = SJA1105_SIZE_MDIO_CMD; + u64 addr, tmp; + + if (mmd == MDIO_DEVAD_NONE) + return -ENODEV; + + if (!priv->info->supports_sgmii[phy]) + return -ENODEV; + + addr = (mmd << 16) | (reg & GENMASK(15, 0)); + tmp = val; + + if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2) + return -ENODEV; + + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, addr, packed_buf, size); +} + +static int sja1110_pcs_mdio_read(struct mii_dev *bus, int phy, int mmd, int reg) +{ + struct sja1105_private *priv = bus->priv; + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0}; + const int size = SJA1105_SIZE_MDIO_CMD; + int offset, bank; + u64 addr, tmp; + int rc; + + if (mmd == MDIO_DEVAD_NONE) + return -ENODEV; + + if (regs->pcs_base[phy] == SJA1105_RSV_ADDR) + return -ENODEV; + + addr = (mmd << 16) | (reg & GENMASK(15, 0)); + + bank = addr >> 8; + offset = addr & GENMASK(7, 0); + + /* This addressing scheme reserves register 0xff for the bank address + * register, so that can never be addressed. + */ + if (offset == 0xff) + return -ENODEV; + + tmp = bank; + + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK); + + rc = sja1105_xfer_buf(priv, SPI_WRITE, + regs->pcs_base[phy] + SJA1110_PCS_BANK_REG, + packed_buf, size); + if (rc < 0) + return rc; + + rc = sja1105_xfer_buf(priv, SPI_READ, regs->pcs_base[phy] + offset, + packed_buf, size); + if (rc < 0) + return rc; + + sja1105_packing(packed_buf, &tmp, 31, 0, size, UNPACK); + + return tmp & 0xffff; +} + +static int sja1110_pcs_mdio_write(struct mii_dev *bus, int phy, int mmd, + int reg, u16 val) +{ + struct sja1105_private *priv = bus->priv; + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0}; + const int size = SJA1105_SIZE_MDIO_CMD; + int offset, bank; + u64 addr, tmp; + int rc; + + if (mmd == MDIO_DEVAD_NONE) + return -ENODEV; + + if (regs->pcs_base[phy] == SJA1105_RSV_ADDR) + return -ENODEV; + + addr = (mmd << 16) | (reg & GENMASK(15, 0)); + + bank = addr >> 8; + offset = addr & GENMASK(7, 0); + + /* This addressing scheme reserves register 0xff for the bank address + * register, so that can never be addressed. + */ + if (offset == 0xff) + return -ENODEV; + + tmp = bank; + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK); + + rc = sja1105_xfer_buf(priv, SPI_WRITE, + regs->pcs_base[phy] + SJA1110_PCS_BANK_REG, + packed_buf, size); + if (rc < 0) + return rc; + + tmp = val; + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pcs_base[phy] + offset, + packed_buf, size); +} + +static int sja1105_mdiobus_register(struct sja1105_private *priv) +{ + struct udevice *dev = priv->dev; + struct mii_dev *bus; + int rc; + + if (!priv->info->pcs_mdio_read || !priv->info->pcs_mdio_write) + return 0; + + bus = mdio_alloc(); + if (!bus) + return -ENOMEM; + + snprintf(bus->name, MDIO_NAME_LEN, "%s-pcs", dev->name); + bus->read = priv->info->pcs_mdio_read; + bus->write = priv->info->pcs_mdio_write; + bus->priv = priv; + + rc = mdio_register(bus); + if (rc) { + mdio_free(bus); + return rc; + } + + priv->mdio_pcs = bus; + + return 0; +} + +static void sja1105_mdiobus_unregister(struct sja1105_private *priv) +{ + if (!priv->mdio_pcs) + return; + + mdio_unregister(priv->mdio_pcs); + mdio_free(priv->mdio_pcs); +} + +static const struct sja1105_regs sja1105et_regs = { + .device_id = 0x0, + .prod_id = 0x100BC3, + .status = 0x1, + .port_control = 0x11, + .config = 0x020000, + .rgu = 0x100440, + /* UM10944.pdf, Table 86, ACU Register overview */ + .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808}, + .pad_mii_rx = {0x100801, 0x100803, 0x100805, 0x100807, 0x100809}, + .rmii_pll1 = 0x10000A, + .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F}, + /* UM10944.pdf, Table 78, CGU Register overview */ + .mii_tx_clk = {0x100013, 0x10001A, 0x100021, 0x100028, 0x10002F}, + .mii_rx_clk = {0x100014, 0x10001B, 0x100022, 0x100029, 0x100030}, + .mii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034}, + .mii_ext_rx_clk = {0x100019, 0x100020, 0x100027, 0x10002E, 0x100035}, + .rgmii_tx_clk = {0x100016, 0x10001D, 0x100024, 0x10002B, 0x100032}, + .rmii_ref_clk = {0x100015, 0x10001C, 0x100023, 0x10002A, 0x100031}, + .rmii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034}, +}; + +static const struct sja1105_regs sja1105pqrs_regs = { + .device_id = 0x0, + .prod_id = 0x100BC3, + .status = 0x1, + .port_control = 0x12, + .config = 0x020000, + .rgu = 0x100440, + /* UM10944.pdf, Table 86, ACU Register overview */ + .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808}, + .pad_mii_rx = {0x100801, 0x100803, 0x100805, 0x100807, 0x100809}, + .pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814}, + .rmii_pll1 = 0x10000A, + .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F}, + /* UM11040.pdf, Table 114 */ + .mii_tx_clk = {0x100013, 0x100019, 0x10001F, 0x100025, 0x10002B}, + .mii_rx_clk = {0x100014, 0x10001A, 0x100020, 0x100026, 0x10002C}, + .mii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F}, + .mii_ext_rx_clk = {0x100018, 0x10001E, 0x100024, 0x10002A, 0x100030}, + .rgmii_tx_clk = {0x100016, 0x10001C, 0x100022, 0x100028, 0x10002E}, + .rmii_ref_clk = {0x100015, 0x10001B, 0x100021, 0x100027, 0x10002D}, + .rmii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F}, +}; + +static const struct sja1105_regs sja1110_regs = { + .device_id = SJA1110_SPI_ADDR(0x0), + .prod_id = SJA1110_ACU_ADDR(0xf00), + .status = SJA1110_SPI_ADDR(0x4), + .port_control = SJA1110_SPI_ADDR(0x50), /* actually INHIB_TX */ + .config = 0x020000, + .rgu = SJA1110_RGU_ADDR(0x100), /* Reset Control Register 0 */ + /* Ports 2 and 3 are capable of xMII, but there isn't anything to + * configure in the CGU/ACU for them. + */ + .pad_mii_tx = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR}, + .pad_mii_rx = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR}, + .pad_mii_id = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1110_ACU_ADDR(0x18), SJA1110_ACU_ADDR(0x28), + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR}, + .rmii_pll1 = SJA1105_RSV_ADDR, + .cgu_idiv = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .mii_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .mii_rx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .mii_ext_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .mii_ext_rx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .rgmii_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .rmii_ref_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .rmii_ext_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR}, + .pcs_base = {SJA1105_RSV_ADDR, 0x1c1400, 0x1c1800, 0x1c1c00, 0x1c2000, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, +}; + +enum sja1105_switch_id { + SJA1105E = 0, + SJA1105T, + SJA1105P, + SJA1105Q, + SJA1105R, + SJA1105S, + SJA1110A, + SJA1110B, + SJA1110C, + SJA1110D, + SJA1105_MAX_SWITCH_ID, +}; + +static const struct sja1105_info sja1105_info[] = { + [SJA1105E] = { + .device_id = SJA1105E_DEVICE_ID, + .part_no = SJA1105ET_PART_NO, + .static_ops = sja1105et_table_ops, + .reset_cmd = sja1105et_reset_cmd, + .regs = &sja1105et_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .name = "SJA1105E", + }, + [SJA1105T] = { + .device_id = SJA1105T_DEVICE_ID, + .part_no = SJA1105ET_PART_NO, + .static_ops = sja1105et_table_ops, + .reset_cmd = sja1105et_reset_cmd, + .regs = &sja1105et_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .name = "SJA1105T", + }, + [SJA1105P] = { + .device_id = SJA1105PR_DEVICE_ID, + .part_no = SJA1105P_PART_NO, + .static_ops = sja1105pqrs_table_ops, + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, + .reset_cmd = sja1105pqrs_reset_cmd, + .regs = &sja1105pqrs_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .name = "SJA1105P", + }, + [SJA1105Q] = { + .device_id = SJA1105QS_DEVICE_ID, + .part_no = SJA1105Q_PART_NO, + .static_ops = sja1105pqrs_table_ops, + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, + .reset_cmd = sja1105pqrs_reset_cmd, + .regs = &sja1105pqrs_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .name = "SJA1105Q", + }, + [SJA1105R] = { + .device_id = SJA1105PR_DEVICE_ID, + .part_no = SJA1105R_PART_NO, + .static_ops = sja1105pqrs_table_ops, + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, + .reset_cmd = sja1105pqrs_reset_cmd, + .regs = &sja1105pqrs_regs, + .pcs_mdio_read = sja1105_pcs_mdio_read, + .pcs_mdio_write = sja1105_pcs_mdio_write, + .pma_config = sja1105_pma_config, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .supports_sgmii = {false, false, false, false, true}, + .name = "SJA1105R", + }, + [SJA1105S] = { + .device_id = SJA1105QS_DEVICE_ID, + .part_no = SJA1105S_PART_NO, + .static_ops = sja1105pqrs_table_ops, + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, + .reset_cmd = sja1105pqrs_reset_cmd, + .regs = &sja1105pqrs_regs, + .pcs_mdio_read = sja1105_pcs_mdio_read, + .pcs_mdio_write = sja1105_pcs_mdio_write, + .pma_config = sja1105_pma_config, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .supports_sgmii = {false, false, false, false, true}, + .name = "SJA1105S", + }, + [SJA1110A] = { + .device_id = SJA1110_DEVICE_ID, + .part_no = SJA1110A_PART_NO, + .static_ops = sja1110_table_ops, + .setup_rgmii_delay = sja1110_setup_rgmii_delay, + .reset_cmd = sja1110_reset_cmd, + .regs = &sja1110_regs, + .pcs_mdio_read = sja1110_pcs_mdio_read, + .pcs_mdio_write = sja1110_pcs_mdio_write, + .pma_config = sja1110_pma_config, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 4, + [SJA1105_SPEED_100MBPS] = 3, + [SJA1105_SPEED_1000MBPS] = 2, + }, + .supports_mii = {true, true, true, true, false, + true, true, true, true, true, true}, + .supports_rmii = {false, false, true, true, false, + false, false, false, false, false, false}, + .supports_rgmii = {false, false, true, true, false, + false, false, false, false, false, false}, + .supports_sgmii = {false, true, true, true, true, + false, false, false, false, false, false}, + .name = "SJA1110A", + }, + [SJA1110B] = { + .device_id = SJA1110_DEVICE_ID, + .part_no = SJA1110B_PART_NO, + .static_ops = sja1110_table_ops, + .setup_rgmii_delay = sja1110_setup_rgmii_delay, + .reset_cmd = sja1110_reset_cmd, + .regs = &sja1110_regs, + .pcs_mdio_read = sja1110_pcs_mdio_read, + .pcs_mdio_write = sja1110_pcs_mdio_write, + .pma_config = sja1110_pma_config, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 4, + [SJA1105_SPEED_100MBPS] = 3, + [SJA1105_SPEED_1000MBPS] = 2, + }, + .supports_mii = {true, true, true, true, false, + true, true, true, true, true, false}, + .supports_rmii = {false, false, true, true, false, + false, false, false, false, false, false}, + .supports_rgmii = {false, false, true, true, false, + false, false, false, false, false, false}, + .supports_sgmii = {false, false, false, true, true, + false, false, false, false, false, false}, + .name = "SJA1110B", + }, + [SJA1110C] = { + .device_id = SJA1110_DEVICE_ID, + .part_no = SJA1110C_PART_NO, + .static_ops = sja1110_table_ops, + .setup_rgmii_delay = sja1110_setup_rgmii_delay, + .reset_cmd = sja1110_reset_cmd, + .regs = &sja1110_regs, + .pcs_mdio_read = sja1110_pcs_mdio_read, + .pcs_mdio_write = sja1110_pcs_mdio_write, + .pma_config = sja1110_pma_config, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 4, + [SJA1105_SPEED_100MBPS] = 3, + [SJA1105_SPEED_1000MBPS] = 2, + }, + .supports_mii = {true, true, true, true, false, + true, true, true, false, false, false}, + .supports_rmii = {false, false, true, true, false, + false, false, false, false, false, false}, + .supports_rgmii = {false, false, true, true, false, + false, false, false, false, false, false}, + .supports_sgmii = {false, false, false, false, true, + false, false, false, false, false, false}, + .name = "SJA1110C", + }, + [SJA1110D] = { + .device_id = SJA1110_DEVICE_ID, + .part_no = SJA1110D_PART_NO, + .static_ops = sja1110_table_ops, + .setup_rgmii_delay = sja1110_setup_rgmii_delay, + .reset_cmd = sja1110_reset_cmd, + .regs = &sja1110_regs, + .pcs_mdio_read = sja1110_pcs_mdio_read, + .pcs_mdio_write = sja1110_pcs_mdio_write, + .pma_config = sja1110_pma_config, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 4, + [SJA1105_SPEED_100MBPS] = 3, + [SJA1105_SPEED_1000MBPS] = 2, + }, + .supports_mii = {true, false, true, false, false, + true, true, true, false, false, false}, + .supports_rmii = {false, false, true, false, false, + false, false, false, false, false, false}, + .supports_rgmii = {false, false, true, false, false, + false, false, false, false, false, false}, + .supports_sgmii = {false, true, true, true, true, + false, false, false, false, false, false}, + .name = "SJA1110D", + }, +}; + +struct sja1105_status { + u64 configs; + u64 crcchkl; + u64 ids; + u64 crcchkg; +}; + +static void sja1105_status_unpack(void *buf, struct sja1105_status *status) +{ + sja1105_packing(buf, &status->configs, 31, 31, 4, UNPACK); + sja1105_packing(buf, &status->crcchkl, 30, 30, 4, UNPACK); + sja1105_packing(buf, &status->ids, 29, 29, 4, UNPACK); + sja1105_packing(buf, &status->crcchkg, 28, 28, 4, UNPACK); +} + +static int sja1105_status_get(struct sja1105_private *priv, + struct sja1105_status *status) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[4]; + int rc; + + rc = sja1105_xfer_buf(priv, SPI_READ, regs->status, packed_buf, 4); + if (rc < 0) + return rc; + + sja1105_status_unpack(packed_buf, status); + + return 0; +} + +/* Not const because unpacking priv->static_config into buffers and preparing + * for upload requires the recalculation of table CRCs and updating the + * structures with these. + */ +static int +static_config_buf_prepare_for_upload(struct sja1105_private *priv, + void *config_buf, int buf_len) +{ + struct sja1105_static_config *config = &priv->static_config; + struct sja1105_table_header final_header; + char *final_header_ptr; + int crc_len; + + /* Write Device ID and config tables to config_buf */ + sja1105_static_config_pack(config_buf, config); + /* Recalculate CRC of the last header (right now 0xDEADBEEF). + * Don't include the CRC field itself. + */ + crc_len = buf_len - 4; + /* Read the whole table header */ + final_header_ptr = config_buf + buf_len - SJA1105_SIZE_TABLE_HEADER; + sja1105_table_header_packing(final_header_ptr, &final_header, UNPACK); + /* Modify */ + final_header.crc = sja1105_crc32(config_buf, crc_len); + /* Rewrite */ + sja1105_table_header_packing(final_header_ptr, &final_header, PACK); + + return 0; +} + +static int sja1105_static_config_upload(struct sja1105_private *priv) +{ + struct sja1105_static_config *config = &priv->static_config; + const struct sja1105_regs *regs = priv->info->regs; + struct sja1105_status status; + u8 *config_buf; + int buf_len; + int rc; + + buf_len = sja1105_static_config_get_length(config); + config_buf = calloc(buf_len, sizeof(char)); + if (!config_buf) + return -ENOMEM; + + rc = static_config_buf_prepare_for_upload(priv, config_buf, buf_len); + if (rc < 0) { + printf("Invalid config, cannot upload\n"); + rc = -EINVAL; + goto out; + } + /* Put the SJA1105 in programming mode */ + rc = priv->info->reset_cmd(priv); + if (rc < 0) { + printf("Failed to reset switch\n"); + goto out; + } + /* Wait for the switch to come out of reset */ + udelay(1000); + /* Upload the static config to the device */ + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->config, + config_buf, buf_len); + if (rc < 0) { + printf("Failed to upload config\n"); + goto out; + } + /* Check that SJA1105 responded well to the config upload */ + rc = sja1105_status_get(priv, &status); + if (rc < 0) + goto out; + + if (status.ids == 1) { + printf("Mismatch between hardware and static config device id. " + "Wrote 0x%llx, wants 0x%llx\n", + config->device_id, priv->info->device_id); + rc = -EIO; + goto out; + } + if (status.crcchkl == 1 || status.crcchkg == 1) { + printf("Switch reported invalid CRC on static config\n"); + rc = -EIO; + goto out; + } + if (status.configs == 0) { + printf("Switch reported that config is invalid\n"); + rc = -EIO; + goto out; + } + +out: + free(config_buf); + return rc; +} + +static int sja1105_static_config_reload(struct sja1105_private *priv) +{ + int rc; + + rc = sja1105_static_config_upload(priv); + if (rc < 0) { + printf("Failed to load static config: %d\n", rc); + return rc; + } + + /* Configure the CGU (PHY link modes and speeds) */ + rc = sja1105_clocking_setup(priv); + if (rc < 0) { + printf("Failed to configure MII clocking: %d\n", rc); + return rc; + } + + return 0; +} + +static int sja1105_port_probe(struct udevice *dev, int port, + struct phy_device *phy) +{ + struct sja1105_private *priv = dev_get_priv(dev); + ofnode node = dsa_port_get_ofnode(dev, port); + phy_interface_t phy_mode = phy->interface; + + priv->xpcs_cfg[port].inband_an = ofnode_eth_uses_inband_aneg(node); + + if (phy_mode == PHY_INTERFACE_MODE_MII || + phy_mode == PHY_INTERFACE_MODE_RMII) { + phy->supported &= PHY_BASIC_FEATURES; + phy->advertising &= PHY_BASIC_FEATURES; + } else { + phy->supported &= PHY_GBIT_FEATURES; + phy->advertising &= PHY_GBIT_FEATURES; + } + + return phy_config(phy); +} + +static int sja1105_port_enable(struct udevice *dev, int port, + struct phy_device *phy) +{ + struct sja1105_private *priv = dev_get_priv(dev); + phy_interface_t phy_mode = phy->interface; + struct sja1105_xmii_params_entry *mii; + struct sja1105_mac_config_entry *mac; + int rc; + + rc = phy_startup(phy); + if (rc) + return rc; + + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_MII: + if (!priv->info->supports_mii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_MII; + break; + case PHY_INTERFACE_MODE_RMII: + if (!priv->info->supports_rmii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_RMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + if (!priv->info->supports_rgmii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_RGMII; + break; + case PHY_INTERFACE_MODE_SGMII: + if (!priv->info->supports_sgmii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_SGMII; + mii->special[port] = true; + break; +unsupported: + default: + dev_err(dev, "Unsupported PHY mode %d on port %d!\n", + phy_mode, port); + return -EINVAL; + } + + /* RevMII, RevRMII not supported */ + mii->phy_mac[port] = XMII_MAC; + + /* Let the PHY handle the RGMII delays, if present. */ + if (phy->phy_id == PHY_FIXED_ID) { + if (phy_mode == PHY_INTERFACE_MODE_RGMII_RXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + priv->rgmii_rx_delay[port] = true; + + if (phy_mode == PHY_INTERFACE_MODE_RGMII_TXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + priv->rgmii_tx_delay[port] = true; + + if ((priv->rgmii_rx_delay[port] || + priv->rgmii_tx_delay[port]) && + !priv->info->setup_rgmii_delay) { + printf("Chip does not support internal RGMII delays\n"); + return -EINVAL; + } + } + + if (mii->xmii_mode[port] == XMII_MODE_SGMII) { + mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; + priv->xpcs_cfg[port].speed = phy->speed; + } else if (phy->speed == SPEED_1000) { + mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; + } else if (phy->speed == SPEED_100) { + mac[port].speed = priv->info->port_speed[SJA1105_SPEED_100MBPS]; + } else if (phy->speed == SPEED_10) { + mac[port].speed = priv->info->port_speed[SJA1105_SPEED_10MBPS]; + } else { + printf("Invalid PHY speed %d on port %d\n", phy->speed, port); + return -EINVAL; + } + + return sja1105_static_config_reload(priv); +} + +static void sja1105_port_disable(struct udevice *dev, int port, + struct phy_device *phy) +{ + phy_shutdown(phy); +} + +static int sja1105_xmit(struct udevice *dev, int port, void *packet, int length) +{ + struct sja1105_private *priv = dev_get_priv(dev); + u8 *from = (u8 *)packet + VLAN_HLEN; + struct vlan_ethhdr *hdr = packet; + u8 *dest = (u8 *)packet; + + memmove(dest, from, 2 * ETH_ALEN); + hdr->h_vlan_proto = htons(ETH_P_SJA1105); + hdr->h_vlan_TCI = htons(priv->pvid[port]); + + return 0; +} + +static int sja1105_rcv(struct udevice *dev, int *port, void *packet, int length) +{ + struct vlan_ethhdr *hdr = packet; + u8 *dest = packet + VLAN_HLEN; + u8 *from = packet; + + if (ntohs(hdr->h_vlan_proto) != ETH_P_SJA1105) + return -EINVAL; + + *port = ntohs(hdr->h_vlan_TCI) & DSA_8021Q_PORT_MASK; + memmove(dest, from, 2 * ETH_ALEN); + + return 0; +} + +static const struct dsa_ops sja1105_dsa_ops = { + .port_probe = sja1105_port_probe, + .port_enable = sja1105_port_enable, + .port_disable = sja1105_port_disable, + .xmit = sja1105_xmit, + .rcv = sja1105_rcv, +}; + +static int sja1105_init(struct sja1105_private *priv) +{ + int rc; + + rc = sja1105_static_config_init(priv); + if (rc) { + printf("Failed to initialize static config: %d\n", rc); + return rc; + } + + rc = sja1105_mdiobus_register(priv); + if (rc) { + printf("Failed to register MDIO bus: %d\n", rc); + goto err_mdiobus_register; + } + + return 0; + +err_mdiobus_register: + sja1105_static_config_free(&priv->static_config); + + return rc; +} + +static int sja1105_check_device_id(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->info->regs; + u8 packed_buf[SJA1105_SIZE_DEVICE_ID] = {0}; + enum sja1105_switch_id id; + u64 device_id; + u64 part_no; + int rc; + + rc = sja1105_xfer_buf(priv, SPI_READ, regs->device_id, packed_buf, + SJA1105_SIZE_DEVICE_ID); + if (rc < 0) + return rc; + + sja1105_packing(packed_buf, &device_id, 31, 0, SJA1105_SIZE_DEVICE_ID, + UNPACK); + + if (device_id != priv->info->device_id) { + printf("Expected device ID 0x%llx but read 0x%llx\n", + priv->info->device_id, device_id); + return -ENODEV; + } + + rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, packed_buf, + SJA1105_SIZE_DEVICE_ID); + if (rc < 0) + return rc; + + sja1105_packing(packed_buf, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID, + UNPACK); + + for (id = 0; id < SJA1105_MAX_SWITCH_ID; id++) { + const struct sja1105_info *info = &sja1105_info[id]; + + /* Is what's been probed in our match table at all? */ + if (info->device_id != device_id || info->part_no != part_no) + continue; + + /* But is it what's in the device tree? */ + if (priv->info->device_id != device_id || + priv->info->part_no != part_no) { + printf("Device tree specifies chip %s but found %s, please fix it!\n", + priv->info->name, info->name); + /* It isn't. No problem, pick that up. */ + priv->info = info; + } + + return 0; + } + + printf("Unexpected {device ID, part number}: 0x%llx 0x%llx\n", + device_id, part_no); + + return -ENODEV; +} + +static int sja1105_probe(struct udevice *dev) +{ + enum sja1105_switch_id id = dev_get_driver_data(dev); + struct sja1105_private *priv = dev_get_priv(dev); + int rc; + + if (ofnode_valid(dev_ofnode(dev)) && + !ofnode_is_available(dev_ofnode(dev))) { + dev_dbg(dev, "switch disabled\n"); + return -ENODEV; + } + + priv->info = &sja1105_info[id]; + priv->dev = dev; + + rc = sja1105_check_device_id(priv); + if (rc < 0) { + dev_err(dev, "Device ID check failed: %d\n", rc); + return rc; + } + + dsa_set_tagging(dev, VLAN_HLEN, 0); + + return sja1105_init(priv); +} + +static int sja1105_remove(struct udevice *dev) +{ + struct sja1105_private *priv = dev_get_priv(dev); + + sja1105_mdiobus_unregister(priv); + sja1105_static_config_free(&priv->static_config); + + return 0; +} + +static const struct udevice_id sja1105_ids[] = { + { .compatible = "nxp,sja1105e", .data = SJA1105E }, + { .compatible = "nxp,sja1105t", .data = SJA1105T }, + { .compatible = "nxp,sja1105p", .data = SJA1105P }, + { .compatible = "nxp,sja1105q", .data = SJA1105Q }, + { .compatible = "nxp,sja1105r", .data = SJA1105R }, + { .compatible = "nxp,sja1105s", .data = SJA1105S }, + { .compatible = "nxp,sja1110a", .data = SJA1110A }, + { .compatible = "nxp,sja1110b", .data = SJA1110B }, + { .compatible = "nxp,sja1110c", .data = SJA1110C }, + { .compatible = "nxp,sja1110d", .data = SJA1110D }, + { } +}; + +U_BOOT_DRIVER(sja1105) = { + .name = "sja1105", + .id = UCLASS_DSA, + .of_match = sja1105_ids, + .probe = sja1105_probe, + .remove = sja1105_remove, + .ops = &sja1105_dsa_ops, + .priv_auto = sizeof(struct sja1105_private), +}; diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 0ce97656715..beca886b256 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -156,6 +156,19 @@ static int tsec_mcast_addr(struct udevice *dev, const u8 *mcast_mac, int join) return 0; } +static int __maybe_unused tsec_set_promisc(struct udevice *dev, bool enable) +{ + struct tsec_private *priv = dev_get_priv(dev); + struct tsec __iomem *regs = priv->regs; + + if (enable) + setbits_be32(®s->rctrl, RCTRL_PROM); + else + clrbits_be32(®s->rctrl, RCTRL_PROM); + + return 0; +} + /* * Initialized required registers to appropriate values, zeroing * those we don't care about (unless zero is bad, in which case, @@ -186,8 +199,6 @@ static void init_registers(struct tsec __iomem *regs) out_be32(®s->hash.gaddr6, 0); out_be32(®s->hash.gaddr7, 0); - out_be32(®s->rctrl, 0x00000000); - /* Init RMON mib registers */ memset((void *)®s->rmon, 0, sizeof(regs->rmon)); @@ -432,7 +443,7 @@ static void tsec_halt(struct udevice *dev) * of the eTSEC port initialization sequence, * the eTSEC Rx logic may not be properly initialized. */ -void redundant_init(struct tsec_private *priv) +static void redundant_init(struct tsec_private *priv) { struct tsec __iomem *regs = priv->regs; uint t, count = 0; @@ -454,7 +465,7 @@ void redundant_init(struct tsec_private *priv) 0x71, 0x72}; /* Enable promiscuous mode */ - setbits_be32(®s->rctrl, 0x8); + setbits_be32(®s->rctrl, RCTRL_PROM); /* Enable loopback mode */ setbits_be32(®s->maccfg1, MACCFG1_LOOPBACK); /* Enable transmit and receive */ @@ -506,7 +517,7 @@ void redundant_init(struct tsec_private *priv) if (fail) panic("eTSEC init fail!\n"); /* Disable promiscuous mode */ - clrbits_be32(®s->rctrl, 0x8); + clrbits_be32(®s->rctrl, RCTRL_PROM); /* Disable loopback mode */ clrbits_be32(®s->maccfg1, MACCFG1_LOOPBACK); } @@ -932,6 +943,7 @@ static const struct eth_ops tsec_ops = { .free_pkt = tsec_free_pkt, .stop = tsec_halt, .mcast = tsec_mcast_addr, + .set_promisc = tsec_set_promisc, }; static struct tsec_data etsec2_data = { diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index cc139af6cb5..42f8cb6be0d 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -19,6 +19,12 @@ 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 SYS_PCI_64BIT + bool "Enable 64-bit PCI resources" + default y if PPC + help + Enable 64-bit PCI resource access. + config PCI_AARDVARK bool "Enable Aardvark PCIe driver" depends on DM_GPIO diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4a131bf5ca4..04f623652f0 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_PCI) += pci_auto_common.o pci_common.o obj-$(CONFIG_PCIE_ECAM_GENERIC) += pcie_ecam_generic.o obj-$(CONFIG_PCIE_ECAM_SYNQUACER) += pcie_ecam_synquacer.o -obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o obj-$(CONFIG_PCI_MPC85XX) += pci_mpc85xx.o obj-$(CONFIG_PCI_MSC01) += pci_msc01.o diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c deleted file mode 100644 index c544af2a0b7..00000000000 --- a/drivers/pci/fsl_pci_init.c +++ /dev/null @@ -1,936 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2007-2012 Freescale Semiconductor, Inc. - */ - -#include <common.h> -#include <env.h> -#include <init.h> -#include <log.h> -#include <malloc.h> -#include <asm/fsl_serdes.h> -#include <asm/global_data.h> -#include <linux/delay.h> - -DECLARE_GLOBAL_DATA_PTR; - -/* - * PCI/PCIE Controller initialization for mpc85xx/mpc86xx soc's - * - * Initialize controller and call the common driver/pci pci_hose_scan to - * scan for bridges and devices. - * - * Hose fields which need to be pre-initialized by board specific code: - * regions[] - * first_busno - * - * Fields updated: - * last_busno - */ - -#include <pci.h> -#include <asm/io.h> -#include <asm/fsl_pci.h> - -#define MAX_PCI_REGIONS 7 - -#ifndef CONFIG_SYS_PCI_MEMORY_BUS -#define CONFIG_SYS_PCI_MEMORY_BUS 0 -#endif - -#ifndef CONFIG_SYS_PCI_MEMORY_PHYS -#define CONFIG_SYS_PCI_MEMORY_PHYS 0 -#endif - -#if defined(CONFIG_SYS_PCI_64BIT) && !defined(CONFIG_SYS_PCI64_MEMORY_BUS) -#define CONFIG_SYS_PCI64_MEMORY_BUS (64ull*1024*1024*1024) -#endif - -/* Setup one inbound ATMU window. - * - * We let the caller decide what the window size should be - */ -static void set_inbound_window(volatile pit_t *pi, - struct pci_region *r, - u64 size) -{ - u32 sz = (__ilog2_u64(size) - 1); -#ifdef CONFIG_SYS_FSL_ERRATUM_A005434 - u32 flag = 0; -#else - u32 flag = PIWAR_LOCAL; -#endif - - flag |= PIWAR_EN | PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; - - out_be32(&pi->pitar, r->phys_start >> 12); - out_be32(&pi->piwbar, r->bus_start >> 12); -#ifdef CONFIG_SYS_PCI_64BIT - out_be32(&pi->piwbear, r->bus_start >> 44); -#else - out_be32(&pi->piwbear, 0); -#endif - if (r->flags & PCI_REGION_PREFETCH) - flag |= PIWAR_PF; - out_be32(&pi->piwar, flag | sz); -} - -int fsl_setup_hose(struct pci_controller *hose, unsigned long addr) -{ - volatile ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *) addr; - - /* Reset hose to make sure its in a clean state */ - memset(hose, 0, sizeof(struct pci_controller)); - - hose->regions = (struct pci_region *) - calloc(1, MAX_PCI_REGIONS * sizeof(struct pci_region)); - - pci_setup_indirect(hose, (u32)&pci->cfg_addr, (u32)&pci->cfg_data); - - return fsl_is_pci_agent(hose); -} - -static int fsl_pci_setup_inbound_windows(struct pci_controller *hose, - u64 out_lo, u8 pcie_cap, - volatile pit_t *pi) -{ - struct pci_region *r = hose->regions + hose->region_count; - u64 sz = min((u64)gd->ram_size, (1ull << 32)); - - phys_addr_t phys_start = CONFIG_SYS_PCI_MEMORY_PHYS; - pci_addr_t bus_start = CONFIG_SYS_PCI_MEMORY_BUS; - pci_size_t pci_sz; - - /* we have no space available for inbound memory mapping */ - if (bus_start > out_lo) { - printf ("no space for inbound mapping of memory\n"); - return 0; - } - - /* limit size */ - if ((bus_start + sz) > out_lo) { - sz = out_lo - bus_start; - debug ("limiting size to %llx\n", sz); - } - - pci_sz = 1ull << __ilog2_u64(sz); - /* - * we can overlap inbound/outbound windows on PCI-E since RX & TX - * links a separate - */ - if ((pcie_cap == PCI_CAP_ID_EXP) && (pci_sz < sz)) { - debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n", - (u64)bus_start, (u64)phys_start, (u64)sz); - pci_set_region(r, bus_start, phys_start, sz, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | - PCI_REGION_PREFETCH); - - /* if we aren't an exact power of two match, pci_sz is smaller - * round it up to the next power of two. We report the actual - * size to pci region tracking. - */ - if (pci_sz != sz) - sz = 2ull << __ilog2_u64(sz); - - set_inbound_window(pi--, r++, sz); - sz = 0; /* make sure we dont set the R2 window */ - } else { - debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n", - (u64)bus_start, (u64)phys_start, (u64)pci_sz); - pci_set_region(r, bus_start, phys_start, pci_sz, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | - PCI_REGION_PREFETCH); - set_inbound_window(pi--, r++, pci_sz); - - sz -= pci_sz; - bus_start += pci_sz; - phys_start += pci_sz; - - pci_sz = 1ull << __ilog2_u64(sz); - if (sz) { - debug ("R1 bus_start: %llx phys_start: %llx size: %llx\n", - (u64)bus_start, (u64)phys_start, (u64)pci_sz); - pci_set_region(r, bus_start, phys_start, pci_sz, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | - PCI_REGION_PREFETCH); - set_inbound_window(pi--, r++, pci_sz); - sz -= pci_sz; - bus_start += pci_sz; - phys_start += pci_sz; - } - } - -#if defined(CONFIG_PHYS_64BIT) && defined(CONFIG_SYS_PCI_64BIT) - /* - * On 64-bit capable systems, set up a mapping for all of DRAM - * in high pci address space. - */ - pci_sz = 1ull << __ilog2_u64(gd->ram_size); - /* round up to the next largest power of two */ - if (gd->ram_size > pci_sz) - pci_sz = 1ull << (__ilog2_u64(gd->ram_size) + 1); - debug ("R64 bus_start: %llx phys_start: %llx size: %llx\n", - (u64)CONFIG_SYS_PCI64_MEMORY_BUS, - (u64)CONFIG_SYS_PCI_MEMORY_PHYS, - (u64)pci_sz); - pci_set_region(r, - CONFIG_SYS_PCI64_MEMORY_BUS, - CONFIG_SYS_PCI_MEMORY_PHYS, - pci_sz, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | - PCI_REGION_PREFETCH); - set_inbound_window(pi--, r++, pci_sz); -#else - pci_sz = 1ull << __ilog2_u64(sz); - if (sz) { - debug ("R2 bus_start: %llx phys_start: %llx size: %llx\n", - (u64)bus_start, (u64)phys_start, (u64)pci_sz); - pci_set_region(r, bus_start, phys_start, pci_sz, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | - PCI_REGION_PREFETCH); - sz -= pci_sz; - bus_start += pci_sz; - phys_start += pci_sz; - set_inbound_window(pi--, r++, pci_sz); - } -#endif - -#ifdef CONFIG_PHYS_64BIT - if (sz && (((u64)gd->ram_size) < (1ull << 32))) - printf("Was not able to map all of memory via " - "inbound windows -- %lld remaining\n", sz); -#endif - - hose->region_count = r - hose->regions; - - return 1; -} - -#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER -static void fsl_pcie_boot_master(pit_t *pi) -{ - /* configure inbound window for slave's u-boot image */ - debug("PCIEBOOT - MASTER: Inbound window for slave's image; " - "Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n", - (u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS, - (u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS1, - CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE); - struct pci_region r_inbound; - u32 sz_inbound = __ilog2_u64(CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE) - - 1; - pci_set_region(&r_inbound, - CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS1, - CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS, - sz_inbound, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); - - set_inbound_window(pi--, &r_inbound, - CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE); - - /* configure inbound window for slave's u-boot image */ - debug("PCIEBOOT - MASTER: Inbound window for slave's image; " - "Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n", - (u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS, - (u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS2, - CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE); - pci_set_region(&r_inbound, - CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS2, - CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS, - sz_inbound, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); - - set_inbound_window(pi--, &r_inbound, - CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE); - - /* configure inbound window for slave's ucode and ENV */ - debug("PCIEBOOT - MASTER: Inbound window for slave's " - "ucode and ENV; " - "Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n", - (u64)CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_PHYS, - (u64)CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_BUS, - CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE); - sz_inbound = __ilog2_u64(CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE) - - 1; - pci_set_region(&r_inbound, - CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_BUS, - CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_PHYS, - sz_inbound, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); - - set_inbound_window(pi--, &r_inbound, - CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE); -} - -static void fsl_pcie_boot_master_release_slave(int port) -{ - unsigned long release_addr; - - /* now release slave's core 0 */ - switch (port) { - case 1: - release_addr = CONFIG_SYS_PCIE1_MEM_VIRT - + CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET; - break; -#ifdef CONFIG_SYS_PCIE2_MEM_VIRT - case 2: - release_addr = CONFIG_SYS_PCIE2_MEM_VIRT - + CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET; - break; -#endif -#ifdef CONFIG_SYS_PCIE3_MEM_VIRT - case 3: - release_addr = CONFIG_SYS_PCIE3_MEM_VIRT - + CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET; - break; -#endif - default: - release_addr = 0; - break; - } - if (release_addr != 0) { - out_be32((void *)release_addr, - CONFIG_SRIO_PCIE_BOOT_RELEASE_MASK); - debug("PCIEBOOT - MASTER: " - "Release slave successfully! Now the slave should start up!\n"); - } else { - debug("PCIEBOOT - MASTER: " - "Release slave failed!\n"); - } -} -#endif - -void fsl_pci_init(struct pci_controller *hose, struct fsl_pci_info *pci_info) -{ - u32 cfg_addr = (u32)&((ccsr_fsl_pci_t *)pci_info->regs)->cfg_addr; - u32 cfg_data = (u32)&((ccsr_fsl_pci_t *)pci_info->regs)->cfg_data; - u16 temp16; - u32 temp32; - u32 block_rev; - int enabled, r, inbound = 0; - u16 ltssm; - u8 temp8, pcie_cap; - int pcie_cap_pos; - int pci_dcr; - int pci_dsr; - int pci_lsr; - -#if defined(CONFIG_FSL_PCIE_DISABLE_ASPM) - int pci_lcr; -#endif - - volatile ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *)cfg_addr; - struct pci_region *reg = hose->regions + hose->region_count; - pci_dev_t dev = PCI_BDF(hose->first_busno, 0, 0); - - /* Initialize ATMU registers based on hose regions and flags */ - volatile pot_t *po = &pci->pot[1]; /* skip 0 */ - volatile pit_t *pi; - - u64 out_hi = 0, out_lo = -1ULL; - u32 pcicsrbar, pcicsrbar_sz; - - pci_setup_indirect(hose, cfg_addr, cfg_data); - -#ifdef PEX_CCB_DIV - /* Configure the PCIE controller core clock ratio */ - pci_hose_write_config_dword(hose, dev, 0x440, - ((gd->bus_clk / 1000000) * - (16 / PEX_CCB_DIV)) / 333); -#endif - block_rev = in_be32(&pci->block_rev1); - if (PEX_IP_BLK_REV_2_2 <= block_rev) { - pi = &pci->pit[2]; /* 0xDC0 */ - } else { - pi = &pci->pit[3]; /* 0xDE0 */ - } - - /* Handle setup of outbound windows first */ - for (r = 0; r < hose->region_count; r++) { - unsigned long flags = hose->regions[r].flags; - u32 sz = (__ilog2_u64((u64)hose->regions[r].size) - 1); - - flags &= PCI_REGION_SYS_MEMORY|PCI_REGION_TYPE; - if (flags != PCI_REGION_SYS_MEMORY) { - u64 start = hose->regions[r].bus_start; - u64 end = start + hose->regions[r].size; - - out_be32(&po->powbar, hose->regions[r].phys_start >> 12); - out_be32(&po->potar, start >> 12); -#ifdef CONFIG_SYS_PCI_64BIT - out_be32(&po->potear, start >> 44); -#else - out_be32(&po->potear, 0); -#endif - if (hose->regions[r].flags & PCI_REGION_IO) { - out_be32(&po->powar, POWAR_EN | sz | - POWAR_IO_READ | POWAR_IO_WRITE); - } else { - out_be32(&po->powar, POWAR_EN | sz | - POWAR_MEM_READ | POWAR_MEM_WRITE); - out_lo = min(start, out_lo); - out_hi = max(end, out_hi); - } - po++; - } - } - debug("Outbound memory range: %llx:%llx\n", out_lo, out_hi); - - /* setup PCSRBAR/PEXCSRBAR */ - pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, 0xffffffff); - pci_hose_read_config_dword (hose, dev, PCI_BASE_ADDRESS_0, &pcicsrbar_sz); - pcicsrbar_sz = ~pcicsrbar_sz + 1; - - if (out_hi < (0x100000000ull - pcicsrbar_sz) || - (out_lo > 0x100000000ull)) - pcicsrbar = 0x100000000ull - pcicsrbar_sz; - else - pcicsrbar = (out_lo - pcicsrbar_sz) & -pcicsrbar_sz; - pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, pcicsrbar); - - out_lo = min(out_lo, (u64)pcicsrbar); - - debug("PCICSRBAR @ 0x%x\n", pcicsrbar); - - pci_set_region(reg++, pcicsrbar, CONFIG_SYS_CCSRBAR_PHYS, - pcicsrbar_sz, PCI_REGION_SYS_MEMORY); - hose->region_count++; - - /* see if we are a PCIe or PCI controller */ - pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP); - pci_dcr = pcie_cap_pos + 0x08; - pci_dsr = pcie_cap_pos + 0x0a; - pci_lsr = pcie_cap_pos + 0x12; - - pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap); - -#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER - /* boot from PCIE --master */ - char *s = env_get("bootmaster"); - char pcie[6]; - sprintf(pcie, "PCIE%d", pci_info->pci_num); - - if (s && (strcmp(s, pcie) == 0)) { - debug("PCIEBOOT - MASTER: Master port [ %d ] for pcie boot.\n", - pci_info->pci_num); - fsl_pcie_boot_master((pit_t *)pi); - } else { - /* inbound */ - inbound = fsl_pci_setup_inbound_windows(hose, - out_lo, pcie_cap, pi); - } -#else - /* inbound */ - inbound = fsl_pci_setup_inbound_windows(hose, out_lo, pcie_cap, pi); -#endif - - for (r = 0; r < hose->region_count; r++) - debug("PCI reg:%d %016llx:%016llx %016llx %08lx\n", r, - (u64)hose->regions[r].phys_start, - (u64)hose->regions[r].bus_start, - (u64)hose->regions[r].size, - hose->regions[r].flags); - - pci_register_hose(hose); - pciauto_config_init(hose); /* grab pci_{mem,prefetch,io} */ - hose->current_busno = hose->first_busno; - - out_be32(&pci->pedr, 0xffffffff); /* Clear any errors */ - out_be32(&pci->peer, ~0x20140); /* Enable All Error Interrupts except - * - Master abort (pci) - * - Master PERR (pci) - * - ICCA (PCIe) - */ - pci_hose_read_config_dword(hose, dev, pci_dcr, &temp32); - temp32 |= 0xf000e; /* set URR, FER, NFER (but not CER) */ - pci_hose_write_config_dword(hose, dev, pci_dcr, temp32); - -#if defined(CONFIG_FSL_PCIE_DISABLE_ASPM) - pci_lcr = pcie_cap_pos + 0x10; - temp32 = 0; - pci_hose_read_config_dword(hose, dev, pci_lcr, &temp32); - temp32 &= ~0x03; /* Disable ASPM */ - pci_hose_write_config_dword(hose, dev, pci_lcr, temp32); - udelay(1); -#endif - if (pcie_cap == PCI_CAP_ID_EXP) { - if (block_rev >= PEX_IP_BLK_REV_3_0) { -#define PEX_CSR0_LTSSM_MASK 0xFC -#define PEX_CSR0_LTSSM_SHIFT 2 - ltssm = (in_be32(&pci->pex_csr0) - & PEX_CSR0_LTSSM_MASK) >> PEX_CSR0_LTSSM_SHIFT; - enabled = (ltssm == 0x11) ? 1 : 0; -#ifdef CONFIG_FSL_PCIE_RESET - int i; - /* assert PCIe reset */ - setbits_be32(&pci->pdb_stat, 0x08000000); - (void) in_be32(&pci->pdb_stat); - udelay(1000); - /* clear PCIe reset */ - clrbits_be32(&pci->pdb_stat, 0x08000000); - asm("sync;isync"); - for (i = 0; i < 100 && ltssm < PCI_LTSSM_L0; i++) { - pci_hose_read_config_word(hose, dev, PCI_LTSSM, - <ssm); - udelay(1000); - } -#endif - } else { - /* pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm); */ - /* enabled = ltssm >= PCI_LTSSM_L0; */ - pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm); - enabled = ltssm >= PCI_LTSSM_L0; - -#ifdef CONFIG_FSL_PCIE_RESET - if (ltssm == 1) { - int i; - debug("....PCIe link error. " "LTSSM=0x%02x.", ltssm); - /* assert PCIe reset */ - setbits_be32(&pci->pdb_stat, 0x08000000); - (void) in_be32(&pci->pdb_stat); - udelay(100); - debug(" Asserting PCIe reset @%p = %x\n", - &pci->pdb_stat, in_be32(&pci->pdb_stat)); - /* clear PCIe reset */ - clrbits_be32(&pci->pdb_stat, 0x08000000); - asm("sync;isync"); - for (i=0; i<100 && ltssm < PCI_LTSSM_L0; i++) { - pci_hose_read_config_word(hose, dev, PCI_LTSSM, - <ssm); - udelay(1000); - debug("....PCIe link error. " - "LTSSM=0x%02x.\n", ltssm); - } - enabled = ltssm >= PCI_LTSSM_L0; - - /* we need to re-write the bar0 since a reset will - * clear it - */ - pci_hose_write_config_dword(hose, dev, - PCI_BASE_ADDRESS_0, pcicsrbar); - } -#endif - } - -#ifdef CONFIG_SYS_P4080_ERRATUM_PCIE_A003 - if (enabled == 0) { - serdes_corenet_t *srds_regs = (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR; - temp32 = in_be32(&srds_regs->srdspccr0); - - if ((temp32 >> 28) == 3) { - int i; - - out_be32(&srds_regs->srdspccr0, 2 << 28); - setbits_be32(&pci->pdb_stat, 0x08000000); - in_be32(&pci->pdb_stat); - udelay(100); - clrbits_be32(&pci->pdb_stat, 0x08000000); - asm("sync;isync"); - for (i=0; i < 100 && ltssm < PCI_LTSSM_L0; i++) { - pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm); - udelay(1000); - } - enabled = ltssm >= PCI_LTSSM_L0; - } - } -#endif - if (!enabled) { - /* Let the user know there's no PCIe link for root - * complex. for endpoint, the link may not setup, so - * print undetermined. - */ - if (fsl_is_pci_agent(hose)) - printf("undetermined, regs @ 0x%lx\n", pci_info->regs); - else - printf("no link, regs @ 0x%lx\n", pci_info->regs); - hose->last_busno = hose->first_busno; - return; - } - - out_be32(&pci->pme_msg_det, 0xffffffff); - out_be32(&pci->pme_msg_int_en, 0xffffffff); - - /* Print the negotiated PCIe link width */ - pci_hose_read_config_word(hose, dev, pci_lsr, &temp16); - printf("x%d gen%d, regs @ 0x%lx\n", (temp16 & 0x3f0) >> 4, - (temp16 & 0xf), pci_info->regs); - - hose->current_busno++; /* Start scan with secondary */ - pciauto_prescan_setup_bridge(hose, dev, hose->current_busno); - } - -#ifdef CONFIG_SYS_FSL_ERRATUM_A007815 - /* The Read-Only Write Enable bit defaults to 1 instead of 0. - * Set to 0 to protect the read-only registers. - */ - clrbits_be32(&pci->dbi_ro_wr_en, 0x01); -#endif - - /* Use generic setup_device to initialize standard pci regs, - * but do not allocate any windows since any BAR found (such - * as PCSRBAR) is not in this cpu's memory space. - */ - pciauto_setup_device(hose, dev, 0, hose->pci_mem, - hose->pci_prefetch, hose->pci_io); - - if (inbound) { - pci_hose_read_config_word(hose, dev, PCI_COMMAND, &temp16); - pci_hose_write_config_word(hose, dev, PCI_COMMAND, - temp16 | PCI_COMMAND_MEMORY); - } - -#ifndef CONFIG_PCI_NOSCAN - if (!fsl_is_pci_agent(hose)) { - debug(" Scanning PCI bus %02x\n", - hose->current_busno); - hose->last_busno = pci_hose_scan_bus(hose, hose->current_busno); - } else { - debug(" Not scanning PCI bus %02x. PI=%x\n", - hose->current_busno, temp8); - hose->last_busno = hose->current_busno; - } - - /* if we are PCIe - update limit regs and subordinate busno - * for the virtual P2P bridge - */ - if (pcie_cap == PCI_CAP_ID_EXP) { - pciauto_postscan_setup_bridge(hose, dev, hose->last_busno); - } -#else - hose->last_busno = hose->current_busno; -#endif - - /* Clear all error indications */ - if (pcie_cap == PCI_CAP_ID_EXP) - out_be32(&pci->pme_msg_det, 0xffffffff); - out_be32(&pci->pedr, 0xffffffff); - - pci_hose_read_config_word(hose, dev, pci_dsr, &temp16); - if (temp16) { - pci_hose_write_config_word(hose, dev, pci_dsr, 0xffff); - } - - pci_hose_read_config_word (hose, dev, PCI_SEC_STATUS, &temp16); - if (temp16) { - pci_hose_write_config_word(hose, dev, PCI_SEC_STATUS, 0xffff); - } -} - -int fsl_is_pci_agent(struct pci_controller *hose) -{ - int pcie_cap_pos; - u8 pcie_cap; - pci_dev_t dev = PCI_BDF(hose->first_busno, 0, 0); - - pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP); - pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap); - if (pcie_cap == PCI_CAP_ID_EXP) { - u8 header_type; - - pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, - &header_type); - return (header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL; - } else { - u8 prog_if; - - pci_hose_read_config_byte(hose, dev, PCI_CLASS_PROG, &prog_if); - /* Programming Interface (PCI_CLASS_PROG) - * 0 == pci host or pcie root-complex, - * 1 == pci agent or pcie end-point - */ - return (prog_if == FSL_PROG_IF_AGENT); - } -} - -int fsl_pci_init_port(struct fsl_pci_info *pci_info, - struct pci_controller *hose, int busno) -{ - volatile ccsr_fsl_pci_t *pci; - struct pci_region *r; - pci_dev_t dev = PCI_BDF(busno,0,0); - int pcie_cap_pos; - u8 pcie_cap; - - pci = (ccsr_fsl_pci_t *) pci_info->regs; - - /* on non-PCIe controllers we don't have pme_msg_det so this code - * should do nothing since the read will return 0 - */ - if (in_be32(&pci->pme_msg_det)) { - out_be32(&pci->pme_msg_det, 0xffffffff); - debug (" with errors. Clearing. Now 0x%08x", - pci->pme_msg_det); - } - - r = hose->regions + hose->region_count; - - /* outbound memory */ - pci_set_region(r++, - pci_info->mem_bus, - pci_info->mem_phys, - pci_info->mem_size, - PCI_REGION_MEM); - - /* outbound io */ - pci_set_region(r++, - pci_info->io_bus, - pci_info->io_phys, - pci_info->io_size, - PCI_REGION_IO); - - hose->region_count = r - hose->regions; - hose->first_busno = busno; - - fsl_pci_init(hose, pci_info); - - if (fsl_is_pci_agent(hose)) { - fsl_pci_config_unlock(hose); - hose->last_busno = hose->first_busno; -#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER - } else { - /* boot from PCIE --master releases slave's core 0 */ - char *s = env_get("bootmaster"); - char pcie[6]; - sprintf(pcie, "PCIE%d", pci_info->pci_num); - - if (s && (strcmp(s, pcie) == 0)) - fsl_pcie_boot_master_release_slave(pci_info->pci_num); -#endif - } - - pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP); - pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap); - printf("PCI%s%x: Bus %02x - %02x\n", pcie_cap == PCI_CAP_ID_EXP ? - "e" : "", pci_info->pci_num, - hose->first_busno, hose->last_busno); - return(hose->last_busno + 1); -} - -/* Enable inbound PCI config cycles for agent/endpoint interface */ -void fsl_pci_config_unlock(struct pci_controller *hose) -{ - pci_dev_t dev = PCI_BDF(hose->first_busno,0,0); - int pcie_cap_pos; - u8 pcie_cap; - u16 pbfr; - - if (!fsl_is_pci_agent(hose)) - return; - - pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP); - pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap); - if (pcie_cap != 0x0) { - ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *)hose->cfg_addr; - u32 block_rev = in_be32(&pci->block_rev1); - /* PCIe - set CFG_READY bit of Configuration Ready Register */ - if (block_rev >= PEX_IP_BLK_REV_3_0) - setbits_be32(&pci->config, FSL_PCIE_V3_CFG_RDY); - else - pci_hose_write_config_byte(hose, dev, - FSL_PCIE_CFG_RDY, 0x1); - } else { - /* PCI - clear ACL bit of PBFR */ - pci_hose_read_config_word(hose, dev, FSL_PCI_PBFR, &pbfr); - pbfr &= ~0x20; - pci_hose_write_config_word(hose, dev, FSL_PCI_PBFR, pbfr); - } -} - -#if defined(CONFIG_PCIE1) || defined(CONFIG_PCIE2) || \ - defined(CONFIG_PCIE3) || defined(CONFIG_PCIE4) -int fsl_configure_pcie(struct fsl_pci_info *info, - struct pci_controller *hose, - const char *connected, int busno) -{ - int is_endpoint; - - set_next_law(info->mem_phys, law_size_bits(info->mem_size), info->law); - set_next_law(info->io_phys, law_size_bits(info->io_size), info->law); - - is_endpoint = fsl_setup_hose(hose, info->regs); - printf("PCIe%u: %s", info->pci_num, - is_endpoint ? "Endpoint" : "Root Complex"); - if (connected) - printf(" of %s", connected); - puts(", "); - - return fsl_pci_init_port(info, hose, busno); -} - -#if defined(CONFIG_FSL_CORENET) -#ifdef CONFIG_SYS_FSL_QORIQ_CHASSIS2 - #define _DEVDISR_PCIE1 FSL_CORENET_DEVDISR3_PCIE1 - #define _DEVDISR_PCIE2 FSL_CORENET_DEVDISR3_PCIE2 - #define _DEVDISR_PCIE3 FSL_CORENET_DEVDISR3_PCIE3 - #define _DEVDISR_PCIE4 FSL_CORENET_DEVDISR3_PCIE4 -#else - #define _DEVDISR_PCIE1 FSL_CORENET_DEVDISR_PCIE1 - #define _DEVDISR_PCIE2 FSL_CORENET_DEVDISR_PCIE2 - #define _DEVDISR_PCIE3 FSL_CORENET_DEVDISR_PCIE3 - #define _DEVDISR_PCIE4 FSL_CORENET_DEVDISR_PCIE4 -#endif - #define CONFIG_SYS_MPC8xxx_GUTS_ADDR CONFIG_SYS_MPC85xx_GUTS_ADDR -#elif defined(CONFIG_MPC85xx) - #define _DEVDISR_PCIE1 MPC85xx_DEVDISR_PCIE - #define _DEVDISR_PCIE2 MPC85xx_DEVDISR_PCIE2 - #define _DEVDISR_PCIE3 MPC85xx_DEVDISR_PCIE3 - #define _DEVDISR_PCIE4 0 - #define CONFIG_SYS_MPC8xxx_GUTS_ADDR CONFIG_SYS_MPC85xx_GUTS_ADDR -#elif defined(CONFIG_MPC86xx) - #define _DEVDISR_PCIE1 MPC86xx_DEVDISR_PCIE1 - #define _DEVDISR_PCIE2 MPC86xx_DEVDISR_PCIE2 - #define _DEVDISR_PCIE3 0 - #define _DEVDISR_PCIE4 0 - #define CONFIG_SYS_MPC8xxx_GUTS_ADDR \ - (&((immap_t *)CONFIG_SYS_IMMR)->im_gur) -#else -#error "No defines for DEVDISR_PCIE" -#endif - -/* Implement a dummy function for those platforms w/o SERDES */ -static const char *__board_serdes_name(enum srds_prtcl device) -{ - switch (device) { -#ifdef CONFIG_SYS_PCIE1_NAME - case PCIE1: - return CONFIG_SYS_PCIE1_NAME; -#endif -#ifdef CONFIG_SYS_PCIE2_NAME - case PCIE2: - return CONFIG_SYS_PCIE2_NAME; -#endif -#ifdef CONFIG_SYS_PCIE3_NAME - case PCIE3: - return CONFIG_SYS_PCIE3_NAME; -#endif -#ifdef CONFIG_SYS_PCIE4_NAME - case PCIE4: - return CONFIG_SYS_PCIE4_NAME; -#endif - default: - return NULL; - } - - return NULL; -} - -__attribute__((weak, alias("__board_serdes_name"))) const char * -board_serdes_name(enum srds_prtcl device); - -static u32 devdisr_mask[] = { - _DEVDISR_PCIE1, - _DEVDISR_PCIE2, - _DEVDISR_PCIE3, - _DEVDISR_PCIE4, -}; - -int fsl_pcie_init_ctrl(int busno, u32 devdisr, enum srds_prtcl dev, - struct fsl_pci_info *pci_info) -{ - struct pci_controller *hose; - int num = dev - PCIE1; - - hose = calloc(1, sizeof(struct pci_controller)); - if (!hose) - return busno; - - if (is_serdes_configured(dev) && !(devdisr & devdisr_mask[num])) { - busno = fsl_configure_pcie(pci_info, hose, - board_serdes_name(dev), busno); - } else { - printf("PCIe%d: disabled\n", num + 1); - } - - return busno; -} - -int fsl_pcie_init_board(int busno) -{ - struct fsl_pci_info pci_info; - ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC8xxx_GUTS_ADDR; - u32 devdisr; - u32 *addr; - -#ifdef CONFIG_SYS_FSL_QORIQ_CHASSIS2 - addr = &gur->devdisr3; -#else - addr = &gur->devdisr; -#endif - devdisr = in_be32(addr); - -#ifdef CONFIG_PCIE1 - SET_STD_PCIE_INFO(pci_info, 1); - busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE1, &pci_info); -#else - setbits_be32(addr, _DEVDISR_PCIE1); /* disable */ -#endif - -#ifdef CONFIG_PCIE2 - SET_STD_PCIE_INFO(pci_info, 2); - busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE2, &pci_info); -#else - setbits_be32(addr, _DEVDISR_PCIE2); /* disable */ -#endif - -#ifdef CONFIG_PCIE3 - SET_STD_PCIE_INFO(pci_info, 3); - busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE3, &pci_info); -#else - setbits_be32(addr, _DEVDISR_PCIE3); /* disable */ -#endif - -#ifdef CONFIG_PCIE4 - SET_STD_PCIE_INFO(pci_info, 4); - busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE4, &pci_info); -#else - setbits_be32(addr, _DEVDISR_PCIE4); /* disable */ -#endif - - return busno; -} -#else -int fsl_pcie_init_ctrl(int busno, u32 devdisr, enum srds_prtcl dev, - struct fsl_pci_info *pci_info) -{ - return busno; -} - -int fsl_pcie_init_board(int busno) -{ - return busno; -} -#endif - -#ifdef CONFIG_OF_BOARD_SETUP -#include <linux/libfdt.h> -#include <fdt_support.h> - -void ft_fsl_pci_setup(void *blob, const char *pci_compat, - unsigned long ctrl_addr) -{ - int off; - u32 bus_range[2]; - phys_addr_t p_ctrl_addr = (phys_addr_t)ctrl_addr; - struct pci_controller *hose; - - hose = find_hose_by_cfg_addr((void *)(ctrl_addr)); - - /* convert ctrl_addr to true physical address */ - p_ctrl_addr = (phys_addr_t)ctrl_addr - CONFIG_SYS_CCSRBAR; - p_ctrl_addr += CONFIG_SYS_CCSRBAR_PHYS; - - off = fdt_node_offset_by_compat_reg(blob, pci_compat, p_ctrl_addr); - - if (off < 0) - return; - - /* We assume a cfg_addr not being set means we didn't setup the controller */ - if ((hose == NULL) || (hose->cfg_addr == NULL)) { - fdt_del_node(blob, off); - } else { - bus_range[0] = 0; - bus_range[1] = hose->last_busno - hose->first_busno; - fdt_setprop(blob, off, "bus-range", &bus_range[0], 2*4); - fdt_pci_dma_ranges(blob, off, hose); - } -} -#endif diff --git a/drivers/pci/pci-aardvark.c b/drivers/pci/pci-aardvark.c index a92f00d0af5..22b6d8b6286 100644 --- a/drivers/pci/pci-aardvark.c +++ b/drivers/pci/pci-aardvark.c @@ -21,6 +21,7 @@ * * Author: Victor Gu <xigu@marvell.com> * Hezi Shahmoon <hezi.shahmoon@marvell.com> + * Pali Rohár <pali@kernel.org> * */ @@ -165,16 +166,6 @@ #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(b, d, f) (PCI_BDF(b, d, f) << 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 PIO_MAX_RETRIES 1500 #define PIO_WAIT_TIMEOUT 1000 @@ -467,7 +458,7 @@ static int pcie_advk_read_config(const struct udevice *bus, pci_dev_t bdf, advk_writel(pcie, reg, PIO_CTRL); /* Program the address registers */ - reg = PCIE_BDF(busno, PCI_DEV(bdf), PCI_FUNC(bdf)) | PCIE_CONF_REG(offset); + reg = PCIE_ECAM_OFFSET(busno, PCI_DEV(bdf), PCI_FUNC(bdf), (offset & ~0x3)); advk_writel(pcie, reg, PIO_ADDR_LS); advk_writel(pcie, 0, PIO_ADDR_MS); @@ -626,7 +617,7 @@ static int pcie_advk_write_config(struct udevice *bus, pci_dev_t bdf, advk_writel(pcie, reg, PIO_CTRL); /* Program the address registers */ - reg = PCIE_BDF(busno, PCI_DEV(bdf), PCI_FUNC(bdf)) | PCIE_CONF_REG(offset); + reg = PCIE_ECAM_OFFSET(busno, PCI_DEV(bdf), PCI_FUNC(bdf), (offset & ~0x3)); 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); diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 62a4df37a02..9248cbc294c 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -7,13 +7,13 @@ * Ported to U-Boot by: * Anton Schubert <anton.schubert@gmx.de> * Stefan Roese <sr@denx.de> + * Pali Rohár <pali@kernel.org> */ #include <common.h> #include <dm.h> #include <log.h> #include <malloc.h> -#include <asm/global_data.h> #include <dm/device-internal.h> #include <dm/lists.h> #include <dm/of_access.h> @@ -26,8 +26,6 @@ #include <linux/ioport.h> #include <linux/mbus.h> -DECLARE_GLOBAL_DATA_PTR; - /* PCIe unit register offsets */ #define SELECT(x, n) ((x >> n) & 1UL) @@ -313,7 +311,9 @@ static int mvebu_pcie_write_config(struct udevice *bus, pci_dev_t bdf, /* * Setup PCIE BARs and Address Decode Wins: - * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks + * BAR[0] -> internal registers + * BAR[1] -> covers all DRAM banks + * BAR[2] -> disabled * WIN[0-3] -> DRAM bank[0-3] */ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) @@ -364,6 +364,10 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) writel(0, pcie->base + PCIE_BAR_HI_OFF(1)); writel(((size - 1) & 0xffff0000) | 0x1, pcie->base + PCIE_BAR_CTRL_OFF(1)); + + /* Setup BAR[0] to internal registers. */ + writel(SOC_REGS_PHY_BASE, pcie->base + PCIE_BAR_LO_OFF(0)); + writel(0, pcie->base + PCIE_BAR_HI_OFF(0)); } static int mvebu_pcie_probe(struct udevice *dev) @@ -444,9 +448,9 @@ static int mvebu_pcie_probe(struct udevice *dev) if (mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr, (phys_addr_t)pcie->mem.start, - MBUS_PCI_MEM_SIZE)) { + resource_size(&pcie->mem))) { printf("PCIe unable to add mbus window for mem at %08x+%08x\n", - (u32)pcie->mem.start, MBUS_PCI_MEM_SIZE); + (u32)pcie->mem.start, (unsigned)resource_size(&pcie->mem)); } pcie->io.start = (u32)mvebu_pcie_iobase; @@ -455,9 +459,9 @@ static int mvebu_pcie_probe(struct udevice *dev) if (mvebu_mbus_add_window_by_id(pcie->io_target, pcie->io_attr, (phys_addr_t)pcie->io.start, - MBUS_PCI_IO_SIZE)) { + resource_size(&pcie->io))) { printf("PCIe unable to add mbus window for IO at %08x+%08x\n", - (u32)pcie->io.start, MBUS_PCI_IO_SIZE); + (u32)pcie->io.start, (unsigned)resource_size(&pcie->io)); } /* Setup windows and configure host bridge */ @@ -465,19 +469,15 @@ static int mvebu_pcie_probe(struct udevice *dev) /* PCI memory space */ pci_set_region(hose->regions + 0, pcie->mem.start, - pcie->mem.start, MBUS_PCI_MEM_SIZE, PCI_REGION_MEM); + pcie->mem.start, resource_size(&pcie->mem), PCI_REGION_MEM); pci_set_region(hose->regions + 1, 0, 0, gd->ram_size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); pci_set_region(hose->regions + 2, pcie->io.start, - pcie->io.start, MBUS_PCI_IO_SIZE, PCI_REGION_IO); + pcie->io.start, resource_size(&pcie->io), PCI_REGION_IO); hose->region_count = 3; - /* Set BAR0 to internal registers */ - writel(SOC_REGS_PHY_BASE, pcie->base + PCIE_BAR_LO_OFF(0)); - writel(0, pcie->base + PCIE_BAR_HI_OFF(0)); - /* PCI Bridge support 32-bit I/O and 64-bit prefetch mem addressing */ pcie->cfgcache[(PCI_IO_BASE - 0x10) / 4] = PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8); diff --git a/drivers/pci/pcie_ecam_generic.c b/drivers/pci/pcie_ecam_generic.c index e83e5aff206..1a9f9aec2ee 100644 --- a/drivers/pci/pcie_ecam_generic.c +++ b/drivers/pci/pcie_ecam_generic.c @@ -14,6 +14,8 @@ #include <asm/io.h> +#define TYPE_PCI 0x1 + /** * struct generic_ecam_pcie - generic_ecam PCIe controller state * @cfg_base: The base address of memory mapped configuration space @@ -46,10 +48,14 @@ static int pci_generic_ecam_conf_address(const struct udevice *bus, void *addr; addr = pcie->cfg_base; - addr += (PCI_BUS(bdf) - pcie->first_busno) << 20; - addr += PCI_DEV(bdf) << 15; - addr += PCI_FUNC(bdf) << 12; - addr += offset; + + if (dev_get_driver_data(bus) == TYPE_PCI) { + addr += ((PCI_BUS(bdf) - pcie->first_busno) << 16) | + (PCI_DEV(bdf) << 11) | (PCI_FUNC(bdf) << 8) | offset; + } else { + addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf) - pcie->first_busno, + PCI_DEV(bdf), PCI_FUNC(bdf), offset); + } *paddress = addr; return 0; @@ -158,7 +164,8 @@ static const struct dm_pci_ops pci_generic_ecam_ops = { }; static const struct udevice_id pci_generic_ecam_ids[] = { - { .compatible = "pci-host-ecam-generic" }, + { .compatible = "pci-host-ecam-generic" /* PCI-E */ }, + { .compatible = "pci-host-cam-generic", .data = TYPE_PCI }, { } }; diff --git a/drivers/pci/pcie_ecam_synquacer.c b/drivers/pci/pcie_ecam_synquacer.c index c6e7c59f8a6..e3e22891088 100644 --- a/drivers/pci/pcie_ecam_synquacer.c +++ b/drivers/pci/pcie_ecam_synquacer.c @@ -235,10 +235,8 @@ static int pci_synquacer_ecam_conf_address(const struct udevice *bus, void *addr; addr = pcie->cfg_base; - addr += (PCI_BUS(bdf) - pcie->first_busno) << 20; - addr += PCI_DEV(bdf) << 15; - addr += PCI_FUNC(bdf) << 12; - addr += offset; + addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf) - pcie->first_busno, + PCI_DEV(bdf), PCI_FUNC(bdf), offset); *paddress = addr; return 0; diff --git a/drivers/pci/pcie_layerscape_fixup.c b/drivers/pci/pcie_layerscape_fixup.c index 8a2a0e1f4a9..a47c9ef7c2f 100644 --- a/drivers/pci/pcie_layerscape_fixup.c +++ b/drivers/pci/pcie_layerscape_fixup.c @@ -584,9 +584,9 @@ static void ft_pcie_rc_fix(void *blob, struct ls_pcie_rc *pcie_rc) return; if (pcie_rc->enabled && pcie->mode == PCI_HEADER_TYPE_BRIDGE) - fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0); + fdt_set_node_status(blob, off, FDT_STATUS_OKAY); else - fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); + fdt_set_node_status(blob, off, FDT_STATUS_DISABLED); } static void ft_pcie_ep_fix(void *blob, struct ls_pcie_rc *pcie_rc) @@ -600,9 +600,9 @@ static void ft_pcie_ep_fix(void *blob, struct ls_pcie_rc *pcie_rc) return; if (pcie_rc->enabled && pcie->mode == PCI_HEADER_TYPE_NORMAL) - fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0); + fdt_set_node_status(blob, off, FDT_STATUS_OKAY); else - fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); + fdt_set_node_status(blob, off, FDT_STATUS_DISABLED); } static void ft_pcie_ls_setup(void *blob, struct ls_pcie_rc *pcie_rc) diff --git a/drivers/pci/pcie_layerscape_gen4_fixup.c b/drivers/pci/pcie_layerscape_gen4_fixup.c index 7d112341061..b2a45bf105c 100644 --- a/drivers/pci/pcie_layerscape_gen4_fixup.c +++ b/drivers/pci/pcie_layerscape_gen4_fixup.c @@ -193,9 +193,9 @@ static void ft_pcie_ep_layerscape_gen4_fix(void *blob, struct ls_pcie_g4 *pcie) } if (pcie->enabled && pcie->mode == PCI_HEADER_TYPE_NORMAL) - fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0); + fdt_set_node_status(blob, off, FDT_STATUS_OKAY); else - fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); + fdt_set_node_status(blob, off, FDT_STATUS_DISABLED); } static void ft_pcie_rc_layerscape_gen4_fix(void *blob, struct ls_pcie_g4 *pcie) @@ -214,9 +214,9 @@ static void ft_pcie_rc_layerscape_gen4_fix(void *blob, struct ls_pcie_g4 *pcie) } if (pcie->enabled && pcie->mode == PCI_HEADER_TYPE_BRIDGE) - fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0); + fdt_set_node_status(blob, off, FDT_STATUS_OKAY); else - fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); + fdt_set_node_status(blob, off, FDT_STATUS_DISABLED); } static void ft_pcie_layerscape_gen4_setup(void *blob, struct ls_pcie_g4 *pcie) diff --git a/drivers/pci/pcie_phytium.c b/drivers/pci/pcie_phytium.c index 752e1703215..a8072762542 100644 --- a/drivers/pci/pcie_phytium.c +++ b/drivers/pci/pcie_phytium.c @@ -36,9 +36,7 @@ static int phytium_pci_skip_dev(pci_dev_t parent) unsigned short capreg; unsigned char port_type; - addr += PCI_BUS(parent) << 20; - addr += PCI_DEV(parent) << 15; - addr += PCI_FUNC(parent) << 12; + addr += PCIE_ECAM_OFFSET(PCI_BUS(parent), PCI_DEV(parent), PCI_FUNC(parent), 0); pos = 0x34; while (1) { @@ -89,9 +87,7 @@ static int pci_phytium_conf_address(const struct udevice *bus, pci_dev_t bdf, bdf_parent = PCI_BDF((bus_no - 1), 0, 0); addr = pcie->cfg_base; - addr += PCI_BUS(bdf) << 20; - addr += PCI_DEV(bdf) << 15; - addr += PCI_FUNC(bdf) << 12; + addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), 0); if (bus_no > 0 && dev_no > 0) { if ((readb(addr + PCI_HEADER_TYPE) & 0x7f) != diff --git a/drivers/pci/pcie_rockchip.c b/drivers/pci/pcie_rockchip.c index b0c91c0f430..67039d2a29f 100644 --- a/drivers/pci/pcie_rockchip.c +++ b/drivers/pci/pcie_rockchip.c @@ -101,15 +101,6 @@ struct rockchip_pcie { struct phy pcie_phy; }; -static int rockchip_pcie_off_conf(pci_dev_t bdf, uint offset) -{ - unsigned int bus = PCI_BUS(bdf); - unsigned int dev = PCI_DEV(bdf); - unsigned int func = PCI_FUNC(bdf); - - return (bus << 20) | (dev << 15) | (func << 12) | (offset & ~0x3); -} - static int rockchip_pcie_rd_conf(const struct udevice *udev, pci_dev_t bdf, uint offset, ulong *valuep, enum pci_size_t size) @@ -117,7 +108,7 @@ static int rockchip_pcie_rd_conf(const struct udevice *udev, pci_dev_t bdf, struct rockchip_pcie *priv = dev_get_priv(udev); unsigned int bus = PCI_BUS(bdf); unsigned int dev = PCI_DEV(bdf); - int where = rockchip_pcie_off_conf(bdf, offset); + int where = PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), offset & ~0x3); ulong value; if (bus == priv->first_busno && dev == 0) { @@ -144,7 +135,7 @@ static int rockchip_pcie_wr_conf(struct udevice *udev, pci_dev_t bdf, struct rockchip_pcie *priv = dev_get_priv(udev); unsigned int bus = PCI_BUS(bdf); unsigned int dev = PCI_DEV(bdf); - int where = rockchip_pcie_off_conf(bdf, offset); + int where = PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), offset & ~0x3); ulong old; if (bus == priv->first_busno && dev == 0) { diff --git a/drivers/pci/pcie_xilinx.c b/drivers/pci/pcie_xilinx.c index ae9a65b0a9e..eb9ec97b74f 100644 --- a/drivers/pci/pcie_xilinx.c +++ b/drivers/pci/pcie_xilinx.c @@ -76,10 +76,7 @@ static int pcie_xilinx_config_address(const struct udevice *udev, pci_dev_t bdf, return -ENODEV; addr = pcie->cfg_base; - addr += bus << 20; - addr += dev << 15; - addr += func << 12; - addr += offset; + addr += PCIE_ECAM_OFFSET(bus, dev, func, offset); *paddress = addr; return 0; diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 141ece479fe..ef924e7af50 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -616,8 +616,8 @@ static int cdns_torrent_phy_probe(struct udevice *dev) /* Going through all the available subnodes or children*/ ofnode_for_each_subnode(child, dev_ofnode(dev)) { - /* PHY subnode name must be a 'link' */ - if (!ofnode_name_eq(child, "link")) + /* PHY subnode name must be a 'phy' */ + if (!ofnode_name_eq(child, "phy")) continue; cdns_phy->phys[node].lnk_rst = devm_reset_bulk_get_by_node(dev, child); diff --git a/drivers/phy/marvell/comphy_a3700.c b/drivers/phy/marvell/comphy_a3700.c index 047c8bb0452..41043535554 100644 --- a/drivers/phy/marvell/comphy_a3700.c +++ b/drivers/phy/marvell/comphy_a3700.c @@ -11,6 +11,7 @@ #include <asm/arch/cpu.h> #include <asm/arch/soc.h> #include <linux/delay.h> +#include <phy.h> #include "comphy_a3700.h" @@ -982,6 +983,138 @@ void comphy_dedicated_phys_init(void) debug_exit(); } +static int find_available_node_by_compatible(int offset, const char *compatible) +{ + do { + offset = fdt_node_offset_by_compatible(gd->fdt_blob, offset, + compatible); + } while (offset > 0 && !fdtdec_get_is_enabled(gd->fdt_blob, offset)); + + return offset; +} + +static bool comphy_a3700_find_lane(const int nodes[3], int node, + int port, int *lane, int *invert) +{ + int res, i, j; + + for (i = 0; ; i++) { + struct fdtdec_phandle_args args; + + res = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "phys", + "#phy-cells", 0, i, &args); + if (res) + return false; + + for (j = 0; j < 3; j++) { + if (nodes[j] >= 0 && args.node == nodes[j] && + (args.args_count >= 1 ? args.args[0] : 0) == port) { + *lane = j; + *invert = args.args_count >= 2 ? args.args[1] + : 0; + return true; + } + } + } + + return false; +} + +static void comphy_a3700_fill_cfg(struct chip_serdes_phy_config *cfg, + const int nodes[3], const char *compatible, + int type) +{ + int node, lane, port, speed, invert; + + port = (type == COMPHY_TYPE_SGMII1) ? 1 : 0; + + node = -1; + while (1) { + node = find_available_node_by_compatible(node, compatible); + if (node < 0) + return; + + if (comphy_a3700_find_lane(nodes, node, port, &lane, &invert)) + break; + } + + if (cfg->comphy_map_data[lane].type != COMPHY_TYPE_UNCONNECTED) { + printf("Error: More PHYs defined for lane %d, skipping\n", + lane); + return; + } + + if (type == COMPHY_TYPE_SGMII0 || type == COMPHY_TYPE_SGMII1) { + const char *phy_mode; + + phy_mode = fdt_getprop(gd->fdt_blob, node, "phy-mode", NULL); + if (phy_mode && + !strcmp(phy_mode, + phy_string_for_interface(PHY_INTERFACE_MODE_2500BASEX))) + speed = COMPHY_SPEED_3_125G; + else + speed = COMPHY_SPEED_1_25G; + } else if (type == COMPHY_TYPE_SATA0) { + speed = COMPHY_SPEED_6G; + } else { + speed = COMPHY_SPEED_5G; + } + + cfg->comphy_map_data[lane].type = type; + cfg->comphy_map_data[lane].speed = speed; + cfg->comphy_map_data[lane].invert = invert; +} + +static const fdt32_t comphy_a3700_mux_lane_order[3] = { + __constant_cpu_to_be32(1), + __constant_cpu_to_be32(0), + __constant_cpu_to_be32(2), +}; + +int comphy_a3700_init_serdes_map(int node, struct chip_serdes_phy_config *cfg) +{ + int comphy_nodes[3]; + int child, i; + + for (i = 0; i < ARRAY_SIZE(comphy_nodes); i++) + comphy_nodes[i] = -FDT_ERR_NOTFOUND; + + fdt_for_each_subnode(child, gd->fdt_blob, node) { + if (!fdtdec_get_is_enabled(gd->fdt_blob, child)) + continue; + + i = fdtdec_get_int(gd->fdt_blob, child, "reg", -1); + if (i < 0 || i >= ARRAY_SIZE(comphy_nodes)) + continue; + + comphy_nodes[i] = child; + } + + for (i = 0; i < ARRAY_SIZE(comphy_nodes); i++) { + cfg->comphy_map_data[i].type = COMPHY_TYPE_UNCONNECTED; + cfg->comphy_map_data[i].speed = COMPHY_SPEED_INVALID; + } + + comphy_a3700_fill_cfg(cfg, comphy_nodes, "marvell,armada3700-u3d", + COMPHY_TYPE_USB3_DEVICE); + comphy_a3700_fill_cfg(cfg, comphy_nodes, "marvell,armada3700-xhci", + COMPHY_TYPE_USB3_HOST0); + comphy_a3700_fill_cfg(cfg, comphy_nodes, "marvell,armada-3700-pcie", + COMPHY_TYPE_PEX0); + comphy_a3700_fill_cfg(cfg, comphy_nodes, "marvell,armada-3700-ahci", + COMPHY_TYPE_SATA0); + comphy_a3700_fill_cfg(cfg, comphy_nodes, "marvell,armada-3700-neta", + COMPHY_TYPE_SGMII0); + comphy_a3700_fill_cfg(cfg, comphy_nodes, "marvell,armada-3700-neta", + COMPHY_TYPE_SGMII1); + + cfg->comphy_lanes_count = 3; + cfg->comphy_mux_bitcount = 4; + cfg->comphy_mux_lane_order = comphy_a3700_mux_lane_order; + + return 0; +} + int comphy_a3700_init(struct chip_serdes_phy_config *chip_cfg, struct comphy_map *serdes_map) { diff --git a/drivers/phy/marvell/comphy_core.c b/drivers/phy/marvell/comphy_core.c index 2c9d7b2288d..233a973035b 100644 --- a/drivers/phy/marvell/comphy_core.c +++ b/drivers/phy/marvell/comphy_core.c @@ -86,11 +86,8 @@ __weak int comphy_update_map(struct comphy_map *serdes_map, int count) static int comphy_probe(struct udevice *dev) { - const void *blob = gd->fdt_blob; int node = dev_of_offset(dev); struct chip_serdes_phy_config *chip_cfg = dev_get_priv(dev); - int subnode; - int lane; int last_idx = 0; static int current_idx; int res; @@ -104,30 +101,14 @@ static int comphy_probe(struct udevice *dev) if (IS_ERR(chip_cfg->hpipe3_base_addr)) return PTR_ERR(chip_cfg->hpipe3_base_addr); - chip_cfg->comphy_lanes_count = fdtdec_get_int(blob, node, - "max-lanes", 0); - if (chip_cfg->comphy_lanes_count <= 0) { - dev_err(dev, "comphy max lanes is wrong\n"); - return -EINVAL; - } - - chip_cfg->comphy_mux_bitcount = fdtdec_get_int(blob, node, - "mux-bitcount", 0); - if (chip_cfg->comphy_mux_bitcount <= 0) { - dev_err(dev, "comphy mux bit count is wrong\n"); - return -EINVAL; - } - - chip_cfg->comphy_mux_lane_order = - fdtdec_locate_array(blob, node, "mux-lane-order", - chip_cfg->comphy_lanes_count); - if (device_is_compatible(dev, "marvell,comphy-armada-3700")) { + chip_cfg->comphy_init_map = comphy_a3700_init_serdes_map; chip_cfg->ptr_comphy_chip_init = comphy_a3700_init; chip_cfg->rx_training = NULL; } if (device_is_compatible(dev, "marvell,comphy-cp110")) { + chip_cfg->comphy_init_map = comphy_cp110_init_serdes_map; chip_cfg->ptr_comphy_chip_init = comphy_cp110_init; chip_cfg->rx_training = comphy_cp110_sfi_rx_training; } @@ -141,39 +122,9 @@ static int comphy_probe(struct udevice *dev) return -ENODEV; } - lane = 0; - fdt_for_each_subnode(subnode, blob, node) { - /* Skip disabled ports */ - if (!fdtdec_get_is_enabled(blob, subnode)) - continue; - - chip_cfg->comphy_map_data[lane].type = - fdtdec_get_int(blob, subnode, "phy-type", - COMPHY_TYPE_INVALID); - - if (chip_cfg->comphy_map_data[lane].type == - COMPHY_TYPE_INVALID) { - printf("no phy type for lane %d, setting lane as unconnected\n", - lane + 1); - continue; - } - - chip_cfg->comphy_map_data[lane].speed = - fdtdec_get_int(blob, subnode, "phy-speed", - COMPHY_SPEED_INVALID); - - chip_cfg->comphy_map_data[lane].invert = - fdtdec_get_int(blob, subnode, "phy-invert", - COMPHY_POLARITY_NO_INVERT); - - chip_cfg->comphy_map_data[lane].clk_src = - fdtdec_get_bool(blob, subnode, "clk-src"); - - chip_cfg->comphy_map_data[lane].end_point = - fdtdec_get_bool(blob, subnode, "end_point"); - - lane++; - } + res = chip_cfg->comphy_init_map(node, chip_cfg); + if (res < 0) + return res; res = comphy_update_map(chip_cfg->comphy_map_data, chip_cfg->comphy_lanes_count); if (res < 0) diff --git a/drivers/phy/marvell/comphy_core.h b/drivers/phy/marvell/comphy_core.h index 9bbd7f8f35d..d573776c05a 100644 --- a/drivers/phy/marvell/comphy_core.h +++ b/drivers/phy/marvell/comphy_core.h @@ -32,6 +32,7 @@ struct comphy_mux_data { struct chip_serdes_phy_config { struct comphy_mux_data *mux_data; + int (*comphy_init_map)(int, struct chip_serdes_phy_config *); int (*ptr_comphy_chip_init)(struct chip_serdes_phy_config *, struct comphy_map *); int (*rx_training)(struct chip_serdes_phy_config *, u32); @@ -85,9 +86,20 @@ static inline void reg_set16(void __iomem *addr, u16 data, u16 mask) /* SoC specific init functions */ #ifdef CONFIG_ARMADA_3700 +int comphy_a3700_init_serdes_map(int node, struct chip_serdes_phy_config *cfg); int comphy_a3700_init(struct chip_serdes_phy_config *ptr_chip_cfg, struct comphy_map *serdes_map); #else +static inline int +comphy_a3700_init_serdes_map(int node, struct chip_serdes_phy_config *cfg) +{ + /* + * This function should never be called in this configuration, so + * lets return an error here. + */ + return -1; +} + static inline int comphy_a3700_init(struct chip_serdes_phy_config *ptr_chip_cfg, struct comphy_map *serdes_map) { @@ -100,11 +112,22 @@ static inline int comphy_a3700_init(struct chip_serdes_phy_config *ptr_chip_cfg, #endif #ifdef CONFIG_ARMADA_8K +int comphy_cp110_init_serdes_map(int node, struct chip_serdes_phy_config *cfg); int comphy_cp110_init(struct chip_serdes_phy_config *ptr_chip_cfg, struct comphy_map *serdes_map); int comphy_cp110_sfi_rx_training(struct chip_serdes_phy_config *ptr_chip_cfg, u32 lane); #else +static inline int +comphy_cp110_init_serdes_map(int node, struct chip_serdes_phy_config *cfg) +{ + /* + * This function should never be called in this configuration, so + * lets return an error here. + */ + return -1; +} + static inline int comphy_cp110_init(struct chip_serdes_phy_config *ptr_chip_cfg, struct comphy_map *serdes_map) { diff --git a/drivers/phy/marvell/comphy_cp110.c b/drivers/phy/marvell/comphy_cp110.c index 4fe2dfcdd17..e063b51c6dd 100644 --- a/drivers/phy/marvell/comphy_cp110.c +++ b/drivers/phy/marvell/comphy_cp110.c @@ -554,6 +554,64 @@ void comphy_dedicated_phys_init(void) debug_exit(); } +int comphy_cp110_init_serdes_map(int node, struct chip_serdes_phy_config *cfg) +{ + int lane, subnode; + + cfg->comphy_lanes_count = fdtdec_get_int(gd->fdt_blob, node, + "max-lanes", 0); + if (cfg->comphy_lanes_count <= 0) { + printf("comphy max lanes is wrong\n"); + return -EINVAL; + } + + cfg->comphy_mux_bitcount = fdtdec_get_int(gd->fdt_blob, node, + "mux-bitcount", 0); + if (cfg->comphy_mux_bitcount <= 0) { + printf("comphy mux bit count is wrong\n"); + return -EINVAL; + } + + cfg->comphy_mux_lane_order = fdtdec_locate_array(gd->fdt_blob, node, + "mux-lane-order", + cfg->comphy_lanes_count); + + lane = 0; + fdt_for_each_subnode(subnode, gd->fdt_blob, node) { + /* Skip disabled ports */ + if (!fdtdec_get_is_enabled(gd->fdt_blob, subnode)) + continue; + + cfg->comphy_map_data[lane].type = + fdtdec_get_int(gd->fdt_blob, subnode, "phy-type", + COMPHY_TYPE_INVALID); + + if (cfg->comphy_map_data[lane].type == COMPHY_TYPE_INVALID) { + printf("no phy type for lane %d, setting lane as unconnected\n", + lane + 1); + continue; + } + + cfg->comphy_map_data[lane].speed = + fdtdec_get_int(gd->fdt_blob, subnode, "phy-speed", + COMPHY_SPEED_INVALID); + + cfg->comphy_map_data[lane].invert = + fdtdec_get_int(gd->fdt_blob, subnode, "phy-invert", + COMPHY_POLARITY_NO_INVERT); + + cfg->comphy_map_data[lane].clk_src = + fdtdec_get_bool(gd->fdt_blob, subnode, "clk-src"); + + cfg->comphy_map_data[lane].end_point = + fdtdec_get_bool(gd->fdt_blob, subnode, "end_point"); + + lane++; + } + + return 0; +} + int comphy_cp110_init(struct chip_serdes_phy_config *ptr_chip_cfg, struct comphy_map *serdes_map) { diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 30eaa376c8e..03946245c7d 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -145,6 +145,17 @@ config SPL_PINCONF_RECURSIVE if PINCTRL || SPL_PINCTRL +config PINCTRL_APPLE + bool "Apple pinctrl driver" + depends on DM && PINCTRL_GENERIC && ARCH_APPLE + default y + help + Support pin multiplexing on Apple SoCs. + + The driver is controlled by a device tree node which contains + both the GPIO definitions and pin control functions for each + available multiplex function. + config PINCTRL_AR933X bool "QCA/Athores ar933x pin control driver" depends on DM && SOC_AR933X @@ -291,6 +302,15 @@ config ASPEED_AST2500_PINCTRL uses Generic Pinctrl framework and is compatible with the Linux driver, i.e. it uses the same device tree configuration. +config ASPEED_AST2600_PINCTRL + bool "Aspeed AST2600 pin control driver" + depends on DM && PINCTRL_GENERIC && ASPEED_AST2600 + default y + help + Support pin multiplexing control on Aspeed ast2600 SoC. The driver + uses Generic Pinctrl framework and is compatible with the Linux + driver, i.e. it uses the same device tree configuration. + config PINCTRL_K210 bool "Kendryte K210 Fully-Programmable Input/Output Array driver" depends on DM && PINCTRL_GENERIC diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 05b71f2f134..fd736a7f640 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -3,6 +3,7 @@ obj-y += pinctrl-uclass.o obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o +obj-$(CONFIG_PINCTRL_APPLE) += pinctrl-apple.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o obj-y += nxp/ diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile index 2e6ed604c8f..a3e01ed1ca9 100644 --- a/drivers/pinctrl/aspeed/Makefile +++ b/drivers/pinctrl/aspeed/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ASPEED_AST2500_PINCTRL) += pinctrl_ast2500.o +obj-$(CONFIG_ASPEED_AST2600_PINCTRL) += pinctrl_ast2600.o diff --git a/drivers/pinctrl/aspeed/pinctrl_ast2600.c b/drivers/pinctrl/aspeed/pinctrl_ast2600.c new file mode 100644 index 00000000000..12cba83f6c0 --- /dev/null +++ b/drivers/pinctrl/aspeed/pinctrl_ast2600.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) ASPEED Technology Inc. + */ + +#include <common.h> +#include <errno.h> +#include <asm/arch/pinctrl.h> +#include <asm/arch/scu_ast2600.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/pinctrl.h> +#include <linux/bitops.h> +#include <linux/err.h> + +/* + * This driver works with very simple configuration that has the same name + * for group and function. This way it is compatible with the Linux Kernel + * driver. + */ +struct aspeed_sig_desc { + u32 offset; + u32 reg_set; + int clr; +}; + +struct aspeed_group_config { + char *group_name; + int ndescs; + struct aspeed_sig_desc *descs; +}; + +struct ast2600_pinctrl_priv { + struct ast2600_scu *scu; +}; + +static int ast2600_pinctrl_probe(struct udevice *dev) +{ + struct ast2600_pinctrl_priv *priv = dev_get_priv(dev); + struct udevice *clk_dev; + int ret = 0; + + /* find SCU base address from clock device */ + uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(aspeed_ast2600_scu), &clk_dev); + + if (ret) + return ret; + + priv->scu = dev_read_addr_ptr(clk_dev); + if (IS_ERR(priv->scu)) + return PTR_ERR(priv->scu); + + return 0; +} + +static struct aspeed_sig_desc i2c1_link[] = { + { 0x418, GENMASK(9, 8), 1 }, + { 0x4B8, GENMASK(9, 8), 0 }, +}; + +static struct aspeed_sig_desc i2c2_link[] = { + { 0x418, GENMASK(11, 10), 1 }, + { 0x4B8, GENMASK(11, 10), 0 }, +}; + +static struct aspeed_sig_desc i2c3_link[] = { + { 0x418, GENMASK(13, 12), 1 }, + { 0x4B8, GENMASK(13, 12), 0 }, +}; + +static struct aspeed_sig_desc i2c4_link[] = { + { 0x418, GENMASK(15, 14), 1 }, + { 0x4B8, GENMASK(15, 14), 0 }, +}; + +static struct aspeed_sig_desc i2c5_link[] = { + { 0x418, GENMASK(17, 16), 0 }, +}; + +static struct aspeed_sig_desc i2c6_link[] = { + { 0x418, GENMASK(19, 18), 0 }, +}; + +static struct aspeed_sig_desc i2c7_link[] = { + { 0x418, GENMASK(21, 20), 0 }, +}; + +static struct aspeed_sig_desc i2c8_link[] = { + { 0x418, GENMASK(23, 22), 0 }, +}; + +static struct aspeed_sig_desc i2c9_link[] = { + { 0x418, GENMASK(25, 24), 0 }, +}; + +static struct aspeed_sig_desc i2c10_link[] = { + { 0x418, GENMASK(27, 26), 0 }, +}; + +static struct aspeed_sig_desc i2c11_link[] = { + { 0x410, GENMASK(1, 0), 1 }, + { 0x4B0, GENMASK(1, 0), 0 }, +}; + +static struct aspeed_sig_desc i2c12_link[] = { + { 0x410, GENMASK(3, 2), 1 }, + { 0x4B0, GENMASK(3, 2), 0 }, +}; + +static struct aspeed_sig_desc i2c13_link[] = { + { 0x410, GENMASK(5, 4), 1 }, + { 0x4B0, GENMASK(5, 4), 0 }, +}; + +static struct aspeed_sig_desc i2c14_link[] = { + { 0x410, GENMASK(7, 6), 1 }, + { 0x4B0, GENMASK(7, 6), 0 }, +}; + +static struct aspeed_sig_desc i2c15_link[] = { + { 0x414, GENMASK(29, 28), 1 }, + { 0x4B4, GENMASK(29, 28), 0 }, +}; + +static struct aspeed_sig_desc i2c16_link[] = { + { 0x414, GENMASK(31, 30), 1 }, + { 0x4B4, GENMASK(31, 30), 0 }, +}; + +static struct aspeed_sig_desc mac1_link[] = { + { 0x410, BIT(4), 0 }, + { 0x470, BIT(4), 1 }, +}; + +static struct aspeed_sig_desc mac2_link[] = { + { 0x410, BIT(5), 0 }, + { 0x470, BIT(5), 1 }, +}; + +static struct aspeed_sig_desc mac3_link[] = { + { 0x410, BIT(6), 0 }, + { 0x470, BIT(6), 1 }, +}; + +static struct aspeed_sig_desc mac4_link[] = { + { 0x410, BIT(7), 0 }, + { 0x470, BIT(7), 1 }, +}; + +static struct aspeed_sig_desc rgmii1[] = { + { 0x500, BIT(6), 0 }, + { 0x400, GENMASK(11, 0), 0 }, +}; + +static struct aspeed_sig_desc rgmii2[] = { + { 0x500, BIT(7), 0 }, + { 0x400, GENMASK(23, 12), 0 }, +}; + +static struct aspeed_sig_desc rgmii3[] = { + { 0x510, BIT(0), 0 }, + { 0x410, GENMASK(27, 16), 0 }, +}; + +static struct aspeed_sig_desc rgmii4[] = { + { 0x510, BIT(1), 0 }, + { 0x410, GENMASK(31, 28), 1 }, + { 0x4b0, GENMASK(31, 28), 0 }, + { 0x474, GENMASK(7, 0), 1 }, + { 0x414, GENMASK(7, 0), 1 }, + { 0x4b4, GENMASK(7, 0), 0 }, +}; + +static struct aspeed_sig_desc rmii1[] = { + { 0x504, BIT(6), 0 }, + { 0x400, GENMASK(3, 0), 0 }, + { 0x400, GENMASK(11, 6), 0 }, +}; + +static struct aspeed_sig_desc rmii2[] = { + { 0x504, BIT(7), 0 }, + { 0x400, GENMASK(15, 12), 0 }, + { 0x400, GENMASK(23, 18), 0 }, +}; + +static struct aspeed_sig_desc rmii3[] = { + { 0x514, BIT(0), 0 }, + { 0x410, GENMASK(27, 22), 0 }, + { 0x410, GENMASK(19, 16), 0 }, +}; + +static struct aspeed_sig_desc rmii4[] = { + { 0x514, BIT(1), 0 }, + { 0x410, GENMASK(7, 2), 1 }, + { 0x410, GENMASK(31, 28), 1 }, + { 0x414, GENMASK(7, 2), 1 }, + { 0x4B0, GENMASK(31, 28), 0 }, + { 0x4B4, GENMASK(7, 2), 0 }, +}; + +static struct aspeed_sig_desc rmii1_rclk_oe[] = { + { 0x340, BIT(29), 0 }, +}; + +static struct aspeed_sig_desc rmii2_rclk_oe[] = { + { 0x340, BIT(30), 0 }, +}; + +static struct aspeed_sig_desc rmii3_rclk_oe[] = { + { 0x350, BIT(29), 0 }, +}; + +static struct aspeed_sig_desc rmii4_rclk_oe[] = { + { 0x350, BIT(30), 0 }, +}; + +static struct aspeed_sig_desc mdio1_link[] = { + { 0x430, BIT(17) | BIT(16), 0 }, +}; + +static struct aspeed_sig_desc mdio2_link[] = { + { 0x470, BIT(13) | BIT(12), 1 }, + { 0x410, BIT(13) | BIT(12), 0 }, +}; + +static struct aspeed_sig_desc mdio3_link[] = { + { 0x470, BIT(1) | BIT(0), 1 }, + { 0x410, BIT(1) | BIT(0), 0 }, +}; + +static struct aspeed_sig_desc mdio4_link[] = { + { 0x470, BIT(3) | BIT(2), 1 }, + { 0x410, BIT(3) | BIT(2), 0 }, +}; + +static struct aspeed_sig_desc sdio2_link[] = { + { 0x414, GENMASK(23, 16), 1 }, + { 0x4B4, GENMASK(23, 16), 0 }, + { 0x450, BIT(1), 0 }, +}; + +static struct aspeed_sig_desc sdio1_link[] = { + { 0x414, GENMASK(15, 8), 0 }, +}; + +/* when sdio1 8bits, sdio2 can't use */ +static struct aspeed_sig_desc sdio1_8bit_link[] = { + { 0x414, GENMASK(15, 8), 0 }, + { 0x4b4, GENMASK(21, 18), 0 }, + { 0x450, BIT(3), 0 }, + { 0x450, BIT(1), 1 }, +}; + +static struct aspeed_sig_desc emmc_link[] = { + { 0x400, GENMASK(31, 24), 0 }, +}; + +static struct aspeed_sig_desc emmcg8_link[] = { + { 0x400, GENMASK(31, 24), 0 }, + { 0x404, GENMASK(3, 0), 0 }, +/* set SCU504 to clear the strap bits in SCU500 */ + { 0x504, BIT(3), 0 }, + { 0x504, BIT(5), 0 }, +}; + +static struct aspeed_sig_desc fmcquad_link[] = { + { 0x438, GENMASK(5, 4), 0 }, +}; + +static struct aspeed_sig_desc spi1_link[] = { + { 0x438, GENMASK(13, 11), 0 }, +}; + +static struct aspeed_sig_desc spi1abr_link[] = { + { 0x438, BIT(9), 0 }, +}; + +static struct aspeed_sig_desc spi1cs1_link[] = { + { 0x438, BIT(8), 0 }, +}; + +static struct aspeed_sig_desc spi1wp_link[] = { + { 0x438, BIT(10), 0 }, +}; + +static struct aspeed_sig_desc spi1quad_link[] = { + { 0x438, GENMASK(15, 14), 0 }, +}; + +static struct aspeed_sig_desc spi2_link[] = { + { 0x434, GENMASK(29, 27) | BIT(24), 0 }, +}; + +static struct aspeed_sig_desc spi2cs1_link[] = { + { 0x434, BIT(25), 0 }, +}; + +static struct aspeed_sig_desc spi2cs2_link[] = { + { 0x434, BIT(26), 0 }, +}; + +static struct aspeed_sig_desc spi2quad_link[] = { + { 0x434, GENMASK(31, 30), 0 }, +}; + +static struct aspeed_sig_desc fsi1[] = { + { 0xd48, GENMASK(21, 20), 0 }, +}; + +static struct aspeed_sig_desc fsi2[] = { + { 0xd48, GENMASK(23, 22), 0 }, +}; + +static struct aspeed_sig_desc usb2ad_link[] = { + { 0x440, BIT(24), 0 }, + { 0x440, BIT(25), 1 }, +}; + +static struct aspeed_sig_desc usb2ah_link[] = { + { 0x440, BIT(24), 1 }, + { 0x440, BIT(25), 0 }, +}; + +static struct aspeed_sig_desc usb2bh_link[] = { + { 0x440, BIT(28), 1 }, + { 0x440, BIT(29), 0 }, +}; + +static struct aspeed_sig_desc pcie0rc_link[] = { + { 0x40, BIT(21), 0 }, +}; + +static struct aspeed_sig_desc pcie1rc_link[] = { + { 0x40, BIT(19), 0 }, /* SSPRST# output enable */ + { 0x500, BIT(24), 0 }, /* dedicate rc reset */ +}; + +static const struct aspeed_group_config ast2600_groups[] = { + { "MAC1LINK", ARRAY_SIZE(mac1_link), mac1_link }, + { "MAC2LINK", ARRAY_SIZE(mac2_link), mac2_link }, + { "MAC3LINK", ARRAY_SIZE(mac3_link), mac3_link }, + { "MAC4LINK", ARRAY_SIZE(mac4_link), mac4_link }, + { "RGMII1", ARRAY_SIZE(rgmii1), rgmii1 }, + { "RGMII2", ARRAY_SIZE(rgmii2), rgmii2 }, + { "RGMII3", ARRAY_SIZE(rgmii3), rgmii3 }, + { "RGMII4", ARRAY_SIZE(rgmii4), rgmii4 }, + { "RMII1", ARRAY_SIZE(rmii1), rmii1 }, + { "RMII2", ARRAY_SIZE(rmii2), rmii2 }, + { "RMII3", ARRAY_SIZE(rmii3), rmii3 }, + { "RMII4", ARRAY_SIZE(rmii4), rmii4 }, + { "RMII1RCLK", ARRAY_SIZE(rmii1_rclk_oe), rmii1_rclk_oe }, + { "RMII2RCLK", ARRAY_SIZE(rmii2_rclk_oe), rmii2_rclk_oe }, + { "RMII3RCLK", ARRAY_SIZE(rmii3_rclk_oe), rmii3_rclk_oe }, + { "RMII4RCLK", ARRAY_SIZE(rmii4_rclk_oe), rmii4_rclk_oe }, + { "MDIO1", ARRAY_SIZE(mdio1_link), mdio1_link }, + { "MDIO2", ARRAY_SIZE(mdio2_link), mdio2_link }, + { "MDIO3", ARRAY_SIZE(mdio3_link), mdio3_link }, + { "MDIO4", ARRAY_SIZE(mdio4_link), mdio4_link }, + { "SD1", ARRAY_SIZE(sdio1_link), sdio1_link }, + { "SD1_8bits", ARRAY_SIZE(sdio1_8bit_link), sdio1_8bit_link }, + { "SD2", ARRAY_SIZE(sdio2_link), sdio2_link }, + { "EMMC", ARRAY_SIZE(emmc_link), emmc_link }, + { "EMMCG8", ARRAY_SIZE(emmcg8_link), emmcg8_link }, + { "FMCQUAD", ARRAY_SIZE(fmcquad_link), fmcquad_link }, + { "SPI1", ARRAY_SIZE(spi1_link), spi1_link }, + { "SPI1ABR", ARRAY_SIZE(spi1abr_link), spi1abr_link }, + { "SPI1CS1", ARRAY_SIZE(spi1cs1_link), spi1cs1_link }, + { "SPI1WP", ARRAY_SIZE(spi1wp_link), spi1wp_link }, + { "SPI1QUAD", ARRAY_SIZE(spi1quad_link), spi1quad_link }, + { "SPI2", ARRAY_SIZE(spi2_link), spi2_link }, + { "SPI2CS1", ARRAY_SIZE(spi2cs1_link), spi2cs1_link }, + { "SPI2CS2", ARRAY_SIZE(spi2cs2_link), spi2cs2_link }, + { "SPI2QUAD", ARRAY_SIZE(spi2quad_link), spi2quad_link }, + { "I2C1", ARRAY_SIZE(i2c1_link), i2c1_link }, + { "I2C2", ARRAY_SIZE(i2c2_link), i2c2_link }, + { "I2C3", ARRAY_SIZE(i2c3_link), i2c3_link }, + { "I2C4", ARRAY_SIZE(i2c4_link), i2c4_link }, + { "I2C5", ARRAY_SIZE(i2c5_link), i2c5_link }, + { "I2C6", ARRAY_SIZE(i2c6_link), i2c6_link }, + { "I2C7", ARRAY_SIZE(i2c7_link), i2c7_link }, + { "I2C8", ARRAY_SIZE(i2c8_link), i2c8_link }, + { "I2C9", ARRAY_SIZE(i2c9_link), i2c9_link }, + { "I2C10", ARRAY_SIZE(i2c10_link), i2c10_link }, + { "I2C11", ARRAY_SIZE(i2c11_link), i2c11_link }, + { "I2C12", ARRAY_SIZE(i2c12_link), i2c12_link }, + { "I2C13", ARRAY_SIZE(i2c13_link), i2c13_link }, + { "I2C14", ARRAY_SIZE(i2c14_link), i2c14_link }, + { "I2C15", ARRAY_SIZE(i2c15_link), i2c15_link }, + { "I2C16", ARRAY_SIZE(i2c16_link), i2c16_link }, + { "FSI1", ARRAY_SIZE(fsi1), fsi1 }, + { "FSI2", ARRAY_SIZE(fsi2), fsi2 }, + { "USB2AD", ARRAY_SIZE(usb2ad_link), usb2ad_link }, + { "USB2AH", ARRAY_SIZE(usb2ah_link), usb2ah_link }, + { "USB2BH", ARRAY_SIZE(usb2bh_link), usb2bh_link }, + { "PCIE0RC", ARRAY_SIZE(pcie0rc_link), pcie0rc_link }, + { "PCIE1RC", ARRAY_SIZE(pcie1rc_link), pcie1rc_link }, +}; + +static int ast2600_pinctrl_get_groups_count(struct udevice *dev) +{ + debug("PINCTRL: get_(functions/groups)_count\n"); + + return ARRAY_SIZE(ast2600_groups); +} + +static const char *ast2600_pinctrl_get_group_name(struct udevice *dev, + unsigned selector) +{ + debug("PINCTRL: get_(function/group)_name %u\n", selector); + + return ast2600_groups[selector].group_name; +} + +static int ast2600_pinctrl_group_set(struct udevice *dev, unsigned selector, unsigned func_selector) +{ + struct ast2600_pinctrl_priv *priv = dev_get_priv(dev); + const struct aspeed_group_config *config; + const struct aspeed_sig_desc *descs; + u32 ctrl_reg = (u32)priv->scu; + u32 i; + + debug("PINCTRL: group_set <%u, %u>\n", selector, func_selector); + if (selector >= ARRAY_SIZE(ast2600_groups)) + return -EINVAL; + + config = &ast2600_groups[selector]; + for (i = 0; i < config->ndescs; i++) { + descs = &config->descs[i]; + if (descs->clr) + clrbits_le32((u32)ctrl_reg + descs->offset, descs->reg_set); + else + setbits_le32((u32)ctrl_reg + descs->offset, descs->reg_set); + } + + return 0; +} + +static struct pinctrl_ops ast2600_pinctrl_ops = { + .set_state = pinctrl_generic_set_state, + .get_groups_count = ast2600_pinctrl_get_groups_count, + .get_group_name = ast2600_pinctrl_get_group_name, + .get_functions_count = ast2600_pinctrl_get_groups_count, + .get_function_name = ast2600_pinctrl_get_group_name, + .pinmux_group_set = ast2600_pinctrl_group_set, +}; + +static const struct udevice_id ast2600_pinctrl_ids[] = { + { .compatible = "aspeed,g6-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(pinctrl_aspeed) = { + .name = "aspeed_ast2600_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = ast2600_pinctrl_ids, + .priv_auto = sizeof(struct ast2600_pinctrl_priv), + .ops = &ast2600_pinctrl_ops, + .probe = ast2600_pinctrl_probe, +}; diff --git a/drivers/pinctrl/pinctrl-apple.c b/drivers/pinctrl/pinctrl-apple.c new file mode 100644 index 00000000000..62476358c34 --- /dev/null +++ b/drivers/pinctrl/pinctrl-apple.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2021 Mark Kettenis <kettenis@openbsd.org> + */ + +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/pinctrl.h> +#include <dt-bindings/pinctrl/apple.h> +#include <asm/io.h> +#include <asm-generic/gpio.h> +#include <linux/bitfield.h> + +struct apple_pinctrl_priv { + void *base; + int pin_count; +}; + +#define REG_GPIO(x) (4 * (x)) +#define REG_GPIO_DATA BIT(0) +#define REG_GPIO_MODE GENMASK(3, 1) +#define REG_GPIO_OUT 1 +#define REG_GPIO_PERIPH GENMASK(6, 5) +#define REG_GPIO_INPUT_ENABLE BIT(9) + +static void apple_pinctrl_config_pin(struct apple_pinctrl_priv *priv, + unsigned pin, u32 clr, u32 set) +{ + unsigned reg = REG_GPIO(pin); + u32 old, new; + + old = readl(priv->base + REG_GPIO(pin)); + new = (old & ~clr) | set; + writel(new, priv->base + reg); +} + +static int apple_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev->parent); + + return !!(readl(priv->base + REG_GPIO(offset)) & REG_GPIO_DATA); +} + +static int apple_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev->parent); + + apple_pinctrl_config_pin(priv, offset, REG_GPIO_DATA, + value ? REG_GPIO_DATA : 0); + return 0; +} + +static int apple_gpio_get_direction(struct udevice *dev, unsigned offset) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev->parent); + u32 reg = readl(priv->base + REG_GPIO(offset)); + + if (FIELD_GET(REG_GPIO_MODE, reg) == REG_GPIO_OUT) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +} + +static int apple_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev->parent); + + apple_pinctrl_config_pin(priv, offset, + REG_GPIO_PERIPH | REG_GPIO_MODE, + REG_GPIO_INPUT_ENABLE); + return 0; +} + +static int apple_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev->parent); + u32 set = (value ? REG_GPIO_DATA : 0); + + apple_pinctrl_config_pin(priv, offset, REG_GPIO_DATA | + REG_GPIO_PERIPH | REG_GPIO_MODE, + set | FIELD_PREP(REG_GPIO_MODE, REG_GPIO_OUT)); + return 0; +} + +static int apple_gpio_probe(struct udevice *dev) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev->parent); + struct gpio_dev_priv *uc_priv; + + uc_priv = dev_get_uclass_priv(dev); + uc_priv->bank_name = "gpio"; + uc_priv->gpio_count = priv->pin_count; + + return 0; +} + +static struct dm_gpio_ops apple_gpio_ops = { + .get_value = apple_gpio_get_value, + .set_value = apple_gpio_set_value, + .get_function = apple_gpio_get_direction, + .direction_input = apple_gpio_direction_input, + .direction_output = apple_gpio_direction_output, +}; + +static struct driver apple_gpio_driver = { + .name = "apple_gpio", + .id = UCLASS_GPIO, + .probe = apple_gpio_probe, + .ops = &apple_gpio_ops, +}; + +static int apple_pinctrl_get_pins_count(struct udevice *dev) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev); + + return priv->pin_count; +} + +static const char *apple_pinctrl_get_pin_name(struct udevice *dev, + unsigned selector) +{ + static char pin_name[PINNAME_SIZE]; + + snprintf(pin_name, PINNAME_SIZE, "pin%d", selector); + return pin_name; +} + +static int apple_pinctrl_get_pin_muxing(struct udevice *dev, unsigned selector, + char *buf, int size) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev); + + if (readl(priv->base + REG_GPIO(selector)) & REG_GPIO_PERIPH) + strncpy(buf, "periph", size); + else + strncpy(buf, "gpio", size); + return 0; +} + +static int apple_pinctrl_pinmux_set(struct udevice *dev, unsigned pin_selector, + unsigned func_selector) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev); + + apple_pinctrl_config_pin(priv, pin_selector, + REG_GPIO_DATA | REG_GPIO_MODE, + FIELD_PREP(REG_GPIO_PERIPH, func_selector) | + REG_GPIO_INPUT_ENABLE); + return 0; +} + +static int apple_pinctrl_pinmux_property_set(struct udevice *dev, + u32 pinmux_group) +{ + unsigned pin_selector = APPLE_PIN(pinmux_group); + unsigned func_selector = APPLE_FUNC(pinmux_group); + int ret; + + ret = apple_pinctrl_pinmux_set(dev, pin_selector, func_selector); + return ret ? ret : pin_selector; +} + +static int apple_pinctrl_probe(struct udevice *dev) +{ + struct apple_pinctrl_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args args; + struct udevice *child; + + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) + return -EINVAL; + + if (!dev_read_phandle_with_args(dev, "gpio-ranges", + NULL, 3, 0, &args)) + priv->pin_count = args.args[2]; + + device_bind(dev, &apple_gpio_driver, "apple_gpio", NULL, + dev_ofnode(dev), &child); + + return 0; +} + +static struct pinctrl_ops apple_pinctrl_ops = { + .set_state = pinctrl_generic_set_state, + .get_pins_count = apple_pinctrl_get_pins_count, + .get_pin_name = apple_pinctrl_get_pin_name, + .pinmux_set = apple_pinctrl_pinmux_set, + .pinmux_property_set = apple_pinctrl_pinmux_property_set, + .get_pin_muxing = apple_pinctrl_get_pin_muxing, +}; + +static const struct udevice_id apple_pinctrl_ids[] = { + { .compatible = "apple,pinctrl" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(pinctrl_apple) = { + .name = "apple_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = apple_pinctrl_ids, + .priv_auto = sizeof(struct apple_pinctrl_priv), + .ops = &apple_pinctrl_ops, + .probe = apple_pinctrl_probe, +}; diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c index fe7a59d4313..509e2a80e9a 100644 --- a/drivers/pinctrl/pinctrl-stmfx.c +++ b/drivers/pinctrl/pinctrl-stmfx.c @@ -5,8 +5,12 @@ * Driver for STMicroelectronics Multi-Function eXpander (STMFX) GPIO expander * based on Linux driver : pinctrl/pinctrl-stmfx.c */ + +#define LOG_CATEGORY UCLASS_PINCTRL + #include <common.h> #include <dm.h> +#include <log.h> #include <i2c.h> #include <asm/gpio.h> #include <dm/device.h> diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c index b3142bf4e1f..3ddeaf4adcb 100644 --- a/drivers/power/regulator/scmi_regulator.c +++ b/drivers/power/regulator/scmi_regulator.c @@ -2,6 +2,9 @@ /* * Copyright (C) 2020-2021 Linaro Limited */ + +#define LOG_CATEGORY UCLASS_REGULATOR + #include <common.h> #include <dm.h> #include <errno.h> diff --git a/drivers/ram/stm32mp1/Makefile b/drivers/ram/stm32mp1/Makefile index e1e9135603a..71ded6bed40 100644 --- a/drivers/ram/stm32mp1/Makefile +++ b/drivers/ram/stm32mp1/Makefile @@ -8,7 +8,6 @@ obj-y += stm32mp1_ddr.o obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) += stm32mp1_interactive.o obj-$(CONFIG_STM32MP1_DDR_TESTS) += stm32mp1_tests.o -obj-$(CONFIG_STM32MP1_DDR_TUNING) += stm32mp1_tuning.o ifneq ($(DDR_INTERACTIVE),) CFLAGS_stm32mp1_interactive.o += -DCONFIG_STM32MP1_DDR_INTERACTIVE_FORCE=y diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c index 0457166b127..4d78aa5cb13 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr.c +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c @@ -68,7 +68,6 @@ struct reg_desc { #define DDRPHY_REG_REG_SIZE 11 /* st,phy-reg */ #define DDRPHY_REG_TIMING_SIZE 10 /* st,phy-timing */ -#define DDRPHY_REG_CAL_SIZE 12 /* st,phy-cal */ #define DDRCTL_REG_REG(x) DDRCTL_REG(x, stm32mp1_ddrctrl_reg) static const struct reg_desc ddr_reg[DDRCTL_REG_REG_SIZE] = { @@ -178,22 +177,6 @@ static const struct reg_desc ddrphy_timing[DDRPHY_REG_TIMING_SIZE] = { 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_SIZE] = { - 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), -}; - /************************************************************** * DYNAMIC REGISTERS: only used for debug purpose (read/modify) **************************************************************/ @@ -218,12 +201,24 @@ static const struct reg_desc ddrphy_dyn[] = { DDRPHY_REG_DYN(zq0sr1), DDRPHY_REG_DYN(dx0gsr0), DDRPHY_REG_DYN(dx0gsr1), + DDRPHY_REG_DYN(dx0dllcr), + DDRPHY_REG_DYN(dx0dqtr), + DDRPHY_REG_DYN(dx0dqstr), DDRPHY_REG_DYN(dx1gsr0), DDRPHY_REG_DYN(dx1gsr1), + DDRPHY_REG_DYN(dx1dllcr), + DDRPHY_REG_DYN(dx1dqtr), + DDRPHY_REG_DYN(dx1dqstr), DDRPHY_REG_DYN(dx2gsr0), DDRPHY_REG_DYN(dx2gsr1), + DDRPHY_REG_DYN(dx2dllcr), + DDRPHY_REG_DYN(dx2dqtr), + DDRPHY_REG_DYN(dx2dqstr), DDRPHY_REG_DYN(dx3gsr0), DDRPHY_REG_DYN(dx3gsr1), + DDRPHY_REG_DYN(dx3dllcr), + DDRPHY_REG_DYN(dx3dqtr), + DDRPHY_REG_DYN(dx3dqstr), }; #define DDRPHY_REG_DYN_SIZE ARRAY_SIZE(ddrphy_dyn) @@ -240,7 +235,6 @@ enum reg_type { REG_MAP, REGPHY_REG, REGPHY_TIMING, - REGPHY_CAL, #ifdef CONFIG_STM32MP1_DDR_INTERACTIVE /* dynamic registers => managed in driver or not changed, * can be dumped in interactive mode @@ -264,8 +258,6 @@ struct ddr_reg_info { 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, DDRCTL_REG_REG_SIZE, DDR_BASE}, @@ -279,8 +271,6 @@ const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = { "static", ddrphy_reg, DDRPHY_REG_REG_SIZE, DDRPHY_BASE}, [REGPHY_TIMING] = { "timing", ddrphy_timing, DDRPHY_REG_TIMING_SIZE, DDRPHY_BASE}, -[REGPHY_CAL] = { - "cal", ddrphy_cal, DDRPHY_REG_CAL_SIZE, DDRPHY_BASE}, #ifdef CONFIG_STM32MP1_DDR_INTERACTIVE [REG_DYN] = { "dyn", ddr_dyn, DDR_REG_DYN_SIZE, DDR_BASE}, @@ -456,9 +446,6 @@ static u32 get_par_addr(const struct stm32mp1_ddr_config *config, case REGPHY_TIMING: par_addr = (u32)&config->p_timing; break; - case REGPHY_CAL: - par_addr = (u32)&config->p_cal; - break; case REG_DYN: case REGPHY_DYN: case REG_TYPE_NB: @@ -570,7 +557,7 @@ static void ddrphy_idone_wait(struct stm32mp1_ddrphy *phy) (u32)&phy->pgsr, pgsr, ret); } -void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, u32 pir) +static void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, u32 pir) { pir |= DDRPHYC_PIR_INIT; writel(pir, &phy->pir); @@ -639,7 +626,7 @@ static void wait_operating_mode(struct ddr_info *priv, int mode) log_debug("[0x%08x] stat = 0x%08x\n", (u32)&priv->ctl->stat, stat); } -void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl) +static void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl) { start_sw_done(ctl); /* quasi-dynamic register update*/ @@ -650,8 +637,8 @@ void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl) wait_sw_done_ack(ctl); } -void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, - u32 rfshctl3, u32 pwrctl) +static void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, + u32 rfshctl3, u32 pwrctl) { start_sw_done(ctl); if (!(rfshctl3 & DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH)) @@ -774,8 +761,6 @@ start: */ set_reg(priv, REGPHY_REG, &config->p_reg); set_reg(priv, REGPHY_TIMING, &config->p_timing); - if (config->p_cal_present) - set_reg(priv, REGPHY_CAL, &config->p_cal); if (INTERACTIVE(STEP_PHY_INIT)) goto start; @@ -810,32 +795,32 @@ start: wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); - if (config->p_cal_present) { - log_debug("DDR DQS training skipped.\n"); - } else { - log_debug("DDR DQS training : "); + log_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); + 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); + /* RVTRN is excuted only on LPDDR2/LPDDR3 */ + if (config->c_reg.mstr & DDRCTRL_MSTR_DDR3) + pir = DDRPHYC_PIR_QSTRN; + else + pir = DDRPHYC_PIR_QSTRN | DDRPHYC_PIR_RVTRN; + stm32mp1_ddrphy_init(priv->phy, pir); /* 11. monitor PUB PGSR.IDONE to poll cpmpletion of training sequence */ - ddrphy_idone_wait(priv->phy); + 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); - } /* if (config->p_cal_present) */ + 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); diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.h b/drivers/ram/stm32mp1/stm32mp1_ddr.h index 4998f044394..861efff92be 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr.h +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.h @@ -140,21 +140,6 @@ struct stm32mp1_ddrphy_timing { 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; u32 speed; /* in kHZ */ @@ -169,16 +154,9 @@ struct stm32mp1_ddr_config { struct stm32mp1_ddrctrl_perf c_perf; struct stm32mp1_ddrphy_reg p_reg; struct stm32mp1_ddrphy_timing p_timing; - struct stm32mp1_ddrphy_cal p_cal; - bool p_cal_present; }; int stm32mp1_ddr_clk_enable(struct ddr_info *priv, u32 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, diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h index 3c8885a9657..f1a26e31f6c 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h +++ b/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h @@ -6,8 +6,9 @@ #ifndef _RAM_STM32MP1_DDR_REGS_H #define _RAM_STM32MP1_DDR_REGS_H -/* DDR3/LPDDR2/LPDDR3 Controller (DDRCTRL) registers */ #include <linux/bitops.h> + +/* DDR3/LPDDR2/LPDDR3 Controller (DDRCTRL) registers */ struct stm32mp1_ddrctl { u32 mstr ; /* 0x0 Master*/ u32 stat; /* 0x4 Operating Mode Status*/ @@ -238,6 +239,7 @@ struct stm32mp1_ddrphy { #define DDRCTRL_MSTR_LPDDR2 BIT(2) #define DDRCTRL_MSTR_LPDDR3 BIT(3) #define DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK GENMASK(13, 12) +#define DDRCTRL_MSTR_DATA_BUS_WIDTH_SHIFT 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) @@ -275,25 +277,6 @@ struct stm32mp1_ddrphy { #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) @@ -309,13 +292,9 @@ struct stm32mp1_ddrphy { #define DDRPHYC_PIR_DRAMRST BIT(5) #define DDRPHYC_PIR_DRAMINIT BIT(6) #define DDRPHYC_PIR_QSTRN BIT(7) +#define DDRPHYC_PIR_RVTRN BIT(8) #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) @@ -324,43 +303,6 @@ struct stm32mp1_ddrphy { #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_DLLSRST BIT(30) -#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) diff --git a/drivers/ram/stm32mp1/stm32mp1_interactive.c b/drivers/ram/stm32mp1/stm32mp1_interactive.c index 8c2310ac906..f0fe7e61e33 100644 --- a/drivers/ram/stm32mp1/stm32mp1_interactive.c +++ b/drivers/ram/stm32mp1/stm32mp1_interactive.c @@ -32,7 +32,6 @@ enum ddr_command { DDR_CMD_NEXT, DDR_CMD_GO, DDR_CMD_TEST, - DDR_CMD_TUNING, DDR_CMD_UNKNOWN, }; @@ -60,9 +59,6 @@ enum ddr_command stm32mp1_get_command(char *cmd, int argc) #ifdef CONFIG_STM32MP1_DDR_TESTS [DDR_CMD_TEST] = "test", #endif -#ifdef CONFIG_STM32MP1_DDR_TUNING - [DDR_CMD_TUNING] = "tuning", -#endif }; /* min and max number of argument */ const char cmd_arg[DDR_CMD_UNKNOWN][2] = { @@ -79,9 +75,6 @@ enum ddr_command stm32mp1_get_command(char *cmd, int argc) #ifdef CONFIG_STM32MP1_DDR_TESTS [DDR_CMD_TEST] = { 0, 255 }, #endif -#ifdef CONFIG_STM32MP1_DDR_TUNING - [DDR_CMD_TUNING] = { 0, 255 }, -#endif }; int i; @@ -111,7 +104,7 @@ static void stm32mp1_do_usage(void) "help displays help\n" "info displays DDR information\n" "info <param> <val> changes DDR information\n" - " with <param> = step, name, size, speed or cal\n" + " with <param> = step, name, size or speed\n" "freq displays the DDR PHY frequency in kHz\n" "freq <freq> changes the DDR PHY frequency\n" "param [type|reg] prints input parameters\n" @@ -126,13 +119,10 @@ static void stm32mp1_do_usage(void) #ifdef CONFIG_STM32MP1_DDR_TESTS "test [help] | <n> [...] lists (with help) or executes test <n>\n" #endif -#ifdef CONFIG_STM32MP1_DDR_TUNING - "tuning [help] | <n> [...] lists (with help) or execute tuning <n>\n" -#endif "\nwith for [type|reg]:\n" " all registers if absent\n" " <type> = ctl, phy\n" - " or one category (static, timing, map, perf, cal, dyn)\n" + " or one category (static, timing, map, perf, dyn)\n" " <reg> = name of the register\n" }; @@ -165,7 +155,6 @@ static void stm32mp1_do_info(struct ddr_info *priv, printf("name = %s\n", config->info.name); printf("size = 0x%x\n", config->info.size); printf("speed = %d kHz\n", config->info.speed); - printf("cal = %d\n", config->p_cal_present); return; } @@ -214,16 +203,6 @@ static void stm32mp1_do_info(struct ddr_info *priv, } return; } - if (!strcmp(argv[1], "cal")) { - if (strict_strtoul(argv[2], 10, &value) < 0 || - (value != 0 && value != 1)) { - printf("invalid value %s\n", argv[2]); - } else { - config->p_cal_present = value; - printf("cal = %d\n", config->p_cal_present); - } - return; - } printf("argument %s invalid\n", argv[1]); } @@ -322,7 +301,7 @@ end: return step; } -#if defined(CONFIG_STM32MP1_DDR_TESTS) || defined(CONFIG_STM32MP1_DDR_TUNING) +#if defined(CONFIG_STM32MP1_DDR_TESTS) static const char * const s_result[] = { [TEST_PASSED] = "Pass", [TEST_FAILED] = "Failed", @@ -479,16 +458,6 @@ bool stm32mp1_ddr_interactive(void *priv, stm32mp1_ddr_subcmd(priv, argc, argv, test, test_nb); break; #endif - -#ifdef CONFIG_STM32MP1_DDR_TUNING - case DDR_CMD_TUNING: - if (!stm32mp1_check_step(step, STEP_DDR_READY)) - continue; - stm32mp1_ddr_subcmd(priv, argc, argv, - tuning, tuning_nb); - break; -#endif - default: break; } diff --git a/drivers/ram/stm32mp1/stm32mp1_ram.c b/drivers/ram/stm32mp1/stm32mp1_ram.c index 98fa1f4f118..49b1262461b 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ram.c +++ b/drivers/ram/stm32mp1/stm32mp1_ram.c @@ -16,6 +16,12 @@ #include <asm/io.h> #include <dm/device_compat.h> #include "stm32mp1_ddr.h" +#include "stm32mp1_ddr_regs.h" + +/* DDR subsystem configuration */ +struct stm32mp1_ddr_cfg { + u8 nb_bytes; /* MEMC_DRAM_DATA_WIDTH */ +}; static const char *const clkname[] = { "ddrc1", @@ -82,7 +88,7 @@ static ofnode stm32mp1_ddr_get_ofnode(struct udevice *dev) return dev_ofnode(dev); } -static __maybe_unused int stm32mp1_ddr_setup(struct udevice *dev) +static int stm32mp1_ddr_setup(struct udevice *dev) { struct ddr_info *priv = dev_get_priv(dev); int ret; @@ -95,26 +101,22 @@ static __maybe_unused int stm32mp1_ddr_setup(struct udevice *dev) { .name = x, \ .offset = offsetof(struct stm32mp1_ddr_config, y), \ .size = sizeof(config.y) / sizeof(u32), \ - .present = z, \ } #define CTL_PARAM(x) PARAM("st,ctl-"#x, c_##x, NULL) #define PHY_PARAM(x) PARAM("st,phy-"#x, p_##x, NULL) -#define PHY_PARAM_OPT(x) PARAM("st,phy-"#x, p_##x, &config.p_##x##_present) const struct { const char *name; /* name in DT */ const u32 offset; /* offset in config struct */ const u32 size; /* size of parameters */ - bool * const present; /* presence indication for opt */ } param[] = { CTL_PARAM(reg), CTL_PARAM(timing), CTL_PARAM(map), CTL_PARAM(perf), PHY_PARAM(reg), - PHY_PARAM(timing), - PHY_PARAM_OPT(cal) + PHY_PARAM(timing) }; config.info.speed = ofnode_read_u32_default(node, "st,mem-speed", 0); @@ -133,25 +135,11 @@ static __maybe_unused int stm32mp1_ddr_setup(struct udevice *dev) param[idx].size); dev_dbg(dev, "%s: %s[0x%x] = %d\n", __func__, param[idx].name, param[idx].size, ret); - if (ret && - (ret != -FDT_ERR_NOTFOUND || !param[idx].present)) { + if (ret) { dev_err(dev, "Cannot read %s, error=%d\n", param[idx].name, ret); return -EINVAL; } - if (param[idx].present) { - /* save presence of optional parameters */ - *param[idx].present = true; - if (ret == -FDT_ERR_NOTFOUND) { - *param[idx].present = false; -#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE - /* reset values if used later */ - memset((void *)((u32)&config + - param[idx].offset), - 0, param[idx].size * sizeof(u32)); -#endif - } - } } ret = clk_get_by_name(dev, "axidcg", &axidcg); @@ -183,6 +171,183 @@ static __maybe_unused int stm32mp1_ddr_setup(struct udevice *dev) return 0; } +static u8 get_data_bus_width(struct stm32mp1_ddrctl *ctl) +{ + u32 reg = readl(&ctl->mstr) & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK; + u8 data_bus_width = reg >> DDRCTRL_MSTR_DATA_BUS_WIDTH_SHIFT; + + return data_bus_width; +} + +static u8 get_nb_bank(struct stm32mp1_ddrctl *ctl) +{ + /* Count bank address bits */ + u8 bits = 0; + u32 reg, val; + + reg = readl(&ctl->addrmap1); + /* addrmap1.addrmap_bank_b1 */ + val = (reg & GENMASK(5, 0)) >> 0; + if (val <= 31) + bits++; + /* addrmap1.addrmap_bank_b2 */ + val = (reg & GENMASK(13, 8)) >> 8; + if (val <= 31) + bits++; + /* addrmap1.addrmap_bank_b3 */ + val = (reg & GENMASK(21, 16)) >> 16; + if (val <= 31) + bits++; + + return bits; +} + +static u8 get_nb_col(struct stm32mp1_ddrctl *ctl, u8 data_bus_width) +{ + u8 bits; + u32 reg, val; + + /* Count column address bits, start at 2 for b0 and b1 (fixed) */ + bits = 2; + + reg = readl(&ctl->addrmap2); + /* addrmap2.addrmap_col_b2 */ + val = (reg & GENMASK(3, 0)) >> 0; + if (val <= 7) + bits++; + /* addrmap2.addrmap_col_b3 */ + val = (reg & GENMASK(11, 8)) >> 8; + if (val <= 7) + bits++; + /* addrmap2.addrmap_col_b4 */ + val = (reg & GENMASK(19, 16)) >> 16; + if (val <= 7) + bits++; + /* addrmap2.addrmap_col_b5 */ + val = (reg & GENMASK(27, 24)) >> 24; + if (val <= 7) + bits++; + + reg = readl(&ctl->addrmap3); + /* addrmap3.addrmap_col_b6 */ + val = (reg & GENMASK(3, 0)) >> 0; + if (val <= 7) + bits++; + /* addrmap3.addrmap_col_b7 */ + val = (reg & GENMASK(11, 8)) >> 8; + if (val <= 7) + bits++; + /* addrmap3.addrmap_col_b8 */ + val = (reg & GENMASK(19, 16)) >> 16; + if (val <= 7) + bits++; + /* addrmap3.addrmap_col_b9 */ + val = (reg & GENMASK(27, 24)) >> 24; + if (val <= 7) + bits++; + + reg = readl(&ctl->addrmap4); + /* addrmap4.addrmap_col_b10 */ + val = (reg & GENMASK(3, 0)) >> 0; + if (val <= 7) + bits++; + /* addrmap4.addrmap_col_b11 */ + val = (reg & GENMASK(11, 8)) >> 8; + if (val <= 7) + bits++; + + /* + * column bits shift up: + * 1 when half the data bus is used (data_bus_width = 1) + * 2 when a quarter the data bus is used (data_bus_width = 2) + * nothing to do for full data bus (data_bus_width = 0) + */ + bits += data_bus_width; + + return bits; +} + +static u8 get_nb_row(struct stm32mp1_ddrctl *ctl) +{ + /* Count row address bits */ + u8 bits = 0; + u32 reg, val; + + reg = readl(&ctl->addrmap5); + /* addrmap5.addrmap_row_b0 */ + val = (reg & GENMASK(3, 0)) >> 0; + if (val <= 11) + bits++; + /* addrmap5.addrmap_row_b1 */ + val = (reg & GENMASK(11, 8)) >> 8; + if (val <= 11) + bits++; + /* addrmap5.addrmap_row_b2_10 */ + val = (reg & GENMASK(19, 16)) >> 16; + if (val <= 11) + bits += 9; + else + printf("warning: addrmap5.addrmap_row_b2_10 not supported\n"); + /* addrmap5.addrmap_row_b11 */ + val = (reg & GENMASK(27, 24)) >> 24; + if (val <= 11) + bits++; + + reg = readl(&ctl->addrmap6); + /* addrmap6.addrmap_row_b12 */ + val = (reg & GENMASK(3, 0)) >> 0; + if (val <= 7) + bits++; + /* addrmap6.addrmap_row_b13 */ + val = (reg & GENMASK(11, 8)) >> 8; + if (val <= 7) + bits++; + /* addrmap6.addrmap_row_b14 */ + val = (reg & GENMASK(19, 16)) >> 16; + if (val <= 7) + bits++; + /* addrmap6.addrmap_row_b15 */ + val = (reg & GENMASK(27, 24)) >> 24; + if (val <= 7) + bits++; + + return bits; +} + +/* + * stm32mp1_ddr_size + * + * Get the current DRAM size from the DDR CTL registers + * + * @return: DRAM size + */ +u32 stm32mp1_ddr_size(struct udevice *dev) +{ + u8 nb_bit; + u32 ddr_size; + u8 data_bus_width; + struct ddr_info *priv = dev_get_priv(dev); + struct stm32mp1_ddrctl *ctl = priv->ctl; + struct stm32mp1_ddr_cfg *cfg = (struct stm32mp1_ddr_cfg *)dev_get_driver_data(dev); + const u8 nb_bytes = cfg->nb_bytes; + + data_bus_width = get_data_bus_width(ctl); + nb_bit = get_nb_bank(ctl) + get_nb_col(ctl, data_bus_width) + + get_nb_row(ctl); + if (nb_bit > 32) { + nb_bit = 32; + debug("invalid DDR configuration: %d bits\n", nb_bit); + } + + ddr_size = (nb_bytes >> data_bus_width) << nb_bit; + if (ddr_size > STM32_DDR_SIZE) { + ddr_size = STM32_DDR_SIZE; + debug("invalid DDR configuration: size = %x\n", ddr_size); + } + + return ddr_size; +} + static int stm32mp1_ddr_probe(struct udevice *dev) { struct ddr_info *priv = dev_get_priv(dev); @@ -209,8 +374,8 @@ static int stm32mp1_ddr_probe(struct udevice *dev) return log_ret(ret); } - ofnode node = stm32mp1_ddr_get_ofnode(dev); - priv->info.size = ofnode_read_u32_default(node, "st,mem-size", 0); + priv->info.size = stm32mp1_ddr_size(dev); + return 0; } @@ -227,8 +392,12 @@ static struct ram_ops stm32mp1_ddr_ops = { .get_info = stm32mp1_ddr_get_info, }; +static const struct stm32mp1_ddr_cfg stm32mp15x_ddr_cfg = { + .nb_bytes = 4, +}; + static const struct udevice_id stm32mp1_ddr_ids[] = { - { .compatible = "st,stm32mp1-ddr" }, + { .compatible = "st,stm32mp1-ddr", .data = (ulong)&stm32mp15x_ddr_cfg}, { } }; diff --git a/drivers/ram/stm32mp1/stm32mp1_tests.h b/drivers/ram/stm32mp1/stm32mp1_tests.h index 55f5d6d93bc..8436780790f 100644 --- a/drivers/ram/stm32mp1/stm32mp1_tests.h +++ b/drivers/ram/stm32mp1/stm32mp1_tests.h @@ -28,7 +28,4 @@ struct test_desc { extern const struct test_desc test[]; extern const int test_nb; -extern const struct test_desc tuning[]; -extern const int tuning_nb; - #endif diff --git a/drivers/ram/stm32mp1/stm32mp1_tuning.c b/drivers/ram/stm32mp1/stm32mp1_tuning.c deleted file mode 100644 index c8cd7c3ceaf..00000000000 --- a/drivers/ram/stm32mp1/stm32mp1_tuning.c +++ /dev/null @@ -1,1540 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause -/* - * Copyright (C) 2019, STMicroelectronics - All Rights Reserved - */ - -#define LOG_CATEGORY UCLASS_RAM - -#include <common.h> -#include <console.h> -#include <clk.h> -#include <log.h> -#include <ram.h> -#include <rand.h> -#include <reset.h> -#include <asm/io.h> -#include <linux/bitops.h> -#include <linux/delay.h> -#include <linux/iopoll.h> - -#include "stm32mp1_ddr_regs.h" -#include "stm32mp1_ddr.h" -#include "stm32mp1_tests.h" - -#define MAX_DQS_PHASE_IDX _144deg -#define MAX_DQS_UNIT_IDX 7 -#define MAX_GSL_IDX 5 -#define MAX_GPS_IDX 3 - -/* Number of bytes used in this SW. ( min 1--> max 4). */ -#define NUM_BYTES 4 - -enum dqs_phase_enum { - _36deg = 0, - _54deg = 1, - _72deg = 2, - _90deg = 3, - _108deg = 4, - _126deg = 5, - _144deg = 6 -}; - -/* BIST Result struct */ -struct BIST_result { - /* Overall test result: - * 0 Fail (any bit failed) , - * 1 Success (All bits success) - */ - bool test_result; - /* 1: true, all fail / 0: False, not all bits fail */ - bool all_bits_fail; - bool bit_i_test_result[8]; /* 0 fail / 1 success */ -}; - -/* a struct that defines tuning parameters of a byte. */ -struct tuning_position { - u8 phase; /* DQS phase */ - u8 unit; /* DQS unit delay */ - u32 bits_delay; /* Bits deskew in this byte */ -}; - -/* 36deg, 54deg, 72deg, 90deg, 108deg, 126deg, 144deg */ -const u8 dx_dll_phase[7] = {3, 2, 1, 0, 14, 13, 12}; - -static u8 BIST_error_max = 1; -static u32 BIST_seed = 0x1234ABCD; - -static u8 get_nb_bytes(struct stm32mp1_ddrctl *ctl) -{ - u32 data_bus = readl(&ctl->mstr) & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK; - u8 nb_bytes = NUM_BYTES; - - switch (data_bus) { - case DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF: - nb_bytes /= 2; - break; - case DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER: - nb_bytes /= 4; - break; - default: - break; - } - - return nb_bytes; -} - -static u8 get_nb_bank(struct stm32mp1_ddrctl *ctl) -{ - /* Count bank address bits */ - u8 bits = 0; - u32 reg, val; - - reg = readl(&ctl->addrmap1); - /* addrmap1.addrmap_bank_b1 */ - val = (reg & GENMASK(5, 0)) >> 0; - if (val <= 31) - bits++; - /* addrmap1.addrmap_bank_b2 */ - val = (reg & GENMASK(13, 8)) >> 8; - if (val <= 31) - bits++; - /* addrmap1.addrmap_bank_b3 */ - val = (reg & GENMASK(21, 16)) >> 16; - if (val <= 31) - bits++; - - return bits; -} - -static u8 get_nb_col(struct stm32mp1_ddrctl *ctl) -{ - u8 bits; - u32 reg, val; - - /* Count column address bits, start at 2 for b0 and b1 (fixed) */ - bits = 2; - - reg = readl(&ctl->addrmap2); - /* addrmap2.addrmap_col_b2 */ - val = (reg & GENMASK(3, 0)) >> 0; - if (val <= 7) - bits++; - /* addrmap2.addrmap_col_b3 */ - val = (reg & GENMASK(11, 8)) >> 8; - if (val <= 7) - bits++; - /* addrmap2.addrmap_col_b4 */ - val = (reg & GENMASK(19, 16)) >> 16; - if (val <= 7) - bits++; - /* addrmap2.addrmap_col_b5 */ - val = (reg & GENMASK(27, 24)) >> 24; - if (val <= 7) - bits++; - - reg = readl(&ctl->addrmap3); - /* addrmap3.addrmap_col_b6 */ - val = (reg & GENMASK(3, 0)) >> 0; - if (val <= 7) - bits++; - /* addrmap3.addrmap_col_b7 */ - val = (reg & GENMASK(11, 8)) >> 8; - if (val <= 7) - bits++; - /* addrmap3.addrmap_col_b8 */ - val = (reg & GENMASK(19, 16)) >> 16; - if (val <= 7) - bits++; - /* addrmap3.addrmap_col_b9 */ - val = (reg & GENMASK(27, 24)) >> 24; - if (val <= 7) - bits++; - - reg = readl(&ctl->addrmap4); - /* addrmap4.addrmap_col_b10 */ - val = (reg & GENMASK(3, 0)) >> 0; - if (val <= 7) - bits++; - /* addrmap4.addrmap_col_b11 */ - val = (reg & GENMASK(11, 8)) >> 8; - if (val <= 7) - bits++; - - return bits; -} - -static u8 get_nb_row(struct stm32mp1_ddrctl *ctl) -{ - /* Count row address bits */ - u8 bits = 0; - u32 reg, val; - - reg = readl(&ctl->addrmap5); - /* addrmap5.addrmap_row_b0 */ - val = (reg & GENMASK(3, 0)) >> 0; - if (val <= 11) - bits++; - /* addrmap5.addrmap_row_b1 */ - val = (reg & GENMASK(11, 8)) >> 8; - if (val <= 11) - bits++; - /* addrmap5.addrmap_row_b2_10 */ - val = (reg & GENMASK(19, 16)) >> 16; - if (val <= 11) - bits += 9; - else - printf("warning: addrmap5.addrmap_row_b2_10 not supported\n"); - /* addrmap5.addrmap_row_b11 */ - val = (reg & GENMASK(27, 24)) >> 24; - if (val <= 11) - bits++; - - reg = readl(&ctl->addrmap6); - /* addrmap6.addrmap_row_b12 */ - val = (reg & GENMASK(3, 0)) >> 0; - if (val <= 7) - bits++; - /* addrmap6.addrmap_row_b13 */ - val = (reg & GENMASK(11, 8)) >> 8; - if (val <= 7) - bits++; - /* addrmap6.addrmap_row_b14 */ - val = (reg & GENMASK(19, 16)) >> 16; - if (val <= 7) - bits++; - /* addrmap6.addrmap_row_b15 */ - val = (reg & GENMASK(27, 24)) >> 24; - if (val <= 7) - bits++; - - return bits; -} - -static void itm_soft_reset(struct stm32mp1_ddrphy *phy) -{ - stm32mp1_ddrphy_init(phy, DDRPHYC_PIR_ITMSRST); -} - -/* Read DQ unit delay register and provides the retrieved value for DQS - * We are assuming that we have the same delay when clocking - * by DQS and when clocking by DQSN - */ -static u8 DQ_unit_index(struct stm32mp1_ddrphy *phy, u8 byte, u8 bit) -{ - u32 index; - u32 addr = DXNDQTR(phy, byte); - - /* We are assuming that we have the same delay when clocking by DQS - * and when clocking by DQSN : use only the low bits - */ - index = (readl(addr) >> DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit)) - & DDRPHYC_DXNDQTR_DQDLY_LOW_MASK; - - log_debug("[%x]: %x => DQ unit index = %x\n", addr, readl(addr), index); - - return index; -} - -/* Sets the DQS phase delay for a byte lane. - *phase delay is specified by giving the index of the desired delay - * in the dx_dll_phase array. - */ -static void DQS_phase_delay(struct stm32mp1_ddrphy *phy, u8 byte, u8 phase_idx) -{ - u8 sdphase_val = 0; - - /* Write DXNDLLCR.SDPHASE = dx_dll_phase(phase_index); */ - sdphase_val = dx_dll_phase[phase_idx]; - clrsetbits_le32(DXNDLLCR(phy, byte), - DDRPHYC_DXNDLLCR_SDPHASE_MASK, - sdphase_val << DDRPHYC_DXNDLLCR_SDPHASE_SHIFT); -} - -/* Sets the DQS unit delay for a byte lane. - * unit delay is specified by giving the index of the desired delay - * for dgsdly and dqsndly (same value). - */ -static void DQS_unit_delay(struct stm32mp1_ddrphy *phy, - u8 byte, u8 unit_dly_idx) -{ - /* Write the same value in DXNDQSTR.DQSDLY and DXNDQSTR.DQSNDLY */ - clrsetbits_le32(DXNDQSTR(phy, byte), - DDRPHYC_DXNDQSTR_DQSDLY_MASK | - DDRPHYC_DXNDQSTR_DQSNDLY_MASK, - (unit_dly_idx << DDRPHYC_DXNDQSTR_DQSDLY_SHIFT) | - (unit_dly_idx << DDRPHYC_DXNDQSTR_DQSNDLY_SHIFT)); - - /* After changing this value, an ITM soft reset (PIR.ITMSRST=1, - * plus PIR.INIT=1) must be issued. - */ - stm32mp1_ddrphy_init(phy, DDRPHYC_PIR_ITMSRST); -} - -/* Sets the DQ unit delay for a bit line in particular byte lane. - * unit delay is specified by giving the desired delay - */ -static void set_DQ_unit_delay(struct stm32mp1_ddrphy *phy, - u8 byte, u8 bit, - u8 dq_delay_index) -{ - u8 dq_bit_delay_val = dq_delay_index | (dq_delay_index << 2); - - /* same value on delay for clock DQ an DQS_b */ - clrsetbits_le32(DXNDQTR(phy, byte), - DDRPHYC_DXNDQTR_DQDLY_MASK - << DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit), - dq_bit_delay_val << DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit)); -} - -static void set_r0dgsl_delay(struct stm32mp1_ddrphy *phy, - u8 byte, u8 r0dgsl_idx) -{ - clrsetbits_le32(DXNDQSTR(phy, byte), - DDRPHYC_DXNDQSTR_R0DGSL_MASK, - r0dgsl_idx << DDRPHYC_DXNDQSTR_R0DGSL_SHIFT); -} - -static void set_r0dgps_delay(struct stm32mp1_ddrphy *phy, - u8 byte, u8 r0dgps_idx) -{ - clrsetbits_le32(DXNDQSTR(phy, byte), - DDRPHYC_DXNDQSTR_R0DGPS_MASK, - r0dgps_idx << DDRPHYC_DXNDQSTR_R0DGPS_SHIFT); -} - -/* Basic BIST configuration for data lane tests. */ -static void config_BIST(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy) -{ - u8 nb_bank = get_nb_bank(ctl); - u8 nb_row = get_nb_row(ctl); - u8 nb_col = get_nb_col(ctl); - - /* Selects the SDRAM bank address to be used during BIST. */ - u32 bbank = 0; - /* Selects the SDRAM row address to be used during BIST. */ - u32 brow = 0; - /* Selects the SDRAM column address to be used during BIST. */ - u32 bcol = 0; - /* Selects the value by which the SDRAM address is incremented - * for each write/read access. - */ - u32 bainc = 0x00000008; - /* Specifies the maximum SDRAM rank to be used during BIST. - * The default value is set to maximum ranks minus 1. - * must be 0 with single rank - */ - u32 bmrank = 0; - /* Selects the SDRAM rank to be used during BIST. - * must be 0 with single rank - */ - u32 brank = 0; - - /* Specifies the maximum SDRAM bank address to be used during - * BIST before the address & increments to the next rank. - */ - u32 bmbank = (1 << nb_bank) - 1; - /* Specifies the maximum SDRAM row address to be used during - * BIST before the address & increments to the next bank. - */ - u32 bmrow = (1 << nb_row) - 1; - /* Specifies the maximum SDRAM column address to be used during - * BIST before the address & increments to the next row. - */ - u32 bmcol = (1 << nb_col) - 1; - - u32 bmode_conf = 0x00000001; /* DRam mode */ - u32 bdxen_conf = 0x00000001; /* BIST on Data byte */ - u32 bdpat_conf = 0x00000002; /* Select LFSR pattern */ - - /*Setup BIST for DRAM mode, and LFSR-random data pattern.*/ - /*Write BISTRR.BMODE = 1?b1;*/ - /*Write BISTRR.BDXEN = 1?b1;*/ - /*Write BISTRR.BDPAT = 2?b10;*/ - - /* reset BIST */ - writel(0x3, &phy->bistrr); - - writel((bmode_conf << 3) | (bdxen_conf << 14) | (bdpat_conf << 17), - &phy->bistrr); - - /*Setup BIST Word Count*/ - /*Write BISTWCR.BWCNT = 16?b0008;*/ - writel(0x00000200, &phy->bistwcr); /* A multiple of BL/2 */ - - writel(bcol | (brow << 12) | (bbank << 28), &phy->bistar0); - writel(brank | (bmrank << 2) | (bainc << 4), &phy->bistar1); - writel(bmcol | (bmrow << 12) | (bmbank << 28), &phy->bistar2); -} - -/* Select the Byte lane to be tested by BIST. */ -static void BIST_datx8_sel(struct stm32mp1_ddrphy *phy, u8 datx8) -{ - clrsetbits_le32(&phy->bistrr, - DDRPHYC_BISTRR_BDXSEL_MASK, - datx8 << DDRPHYC_BISTRR_BDXSEL_SHIFT); - - /*(For example, selecting Byte Lane 3, BISTRR.BDXSEL = 4?b0011)*/ - /* Write BISTRR.BDXSEL = datx8; */ -} - -/* Perform BIST Write_Read test on a byte lane and return test result. */ -static void BIST_test(struct stm32mp1_ddrphy *phy, u8 byte, - struct BIST_result *bist) -{ - bool result = true; /* BIST_SUCCESS */ - u32 cnt = 0; - u32 error = 0; - u32 val; - int ret; - - bist->test_result = true; - -run: - itm_soft_reset(phy); - - /*Perform BIST Reset*/ - /* Write BISTRR.BINST = 3?b011; */ - clrsetbits_le32(&phy->bistrr, - 0x00000007, - 0x00000003); - - /*Re-seed LFSR*/ - /* Write BISTLSR.SEED = 32'h1234ABCD; */ - if (BIST_seed) - writel(BIST_seed, &phy->bistlsr); - else - writel(rand(), &phy->bistlsr); - - /* some delay to reset BIST */ - udelay(10); - - /*Perform BIST Run*/ - clrsetbits_le32(&phy->bistrr, - 0x00000007, - 0x00000001); - /* Write BISTRR.BINST = 3?b001; */ - - /* poll on BISTGSR.BDONE and wait max 1000 us */ - ret = readl_poll_timeout(&phy->bistgsr, val, - val & DDRPHYC_BISTGSR_BDDONE, 1000); - - if (ret < 0) { - printf("warning: BIST timeout\n"); - result = false; /* BIST_FAIL; */ - /*Perform BIST Stop */ - clrsetbits_le32(&phy->bistrr, 0x00000007, 0x00000002); - } else { - /*Check if received correct number of words*/ - /* if (Read BISTWCSR.DXWCNT = Read BISTWCR.BWCNT) */ - if (((readl(&phy->bistwcsr)) >> DDRPHYC_BISTWCSR_DXWCNT_SHIFT) - == readl(&phy->bistwcr)) { - /*Determine if there is a data comparison error*/ - /* if (Read BISTGSR.BDXERR = 1?b0) */ - if (readl(&phy->bistgsr) & DDRPHYC_BISTGSR_BDXERR) - result = false; /* BIST_FAIL; */ - else - result = true; /* BIST_SUCCESS; */ - } else { - result = false; /* BIST_FAIL; */ - } - } - - /* loop while success */ - cnt++; - if (result && cnt != 1000) - goto run; - - if (!result) - error++; - - if (error < BIST_error_max) { - if (cnt != 1000) - goto run; - bist->test_result = true; - } else { - bist->test_result = false; - } -} - -/* After running the deskew algo, this function applies the new DQ delays - * by reading them from the array "deskew_delay"and writing in PHY registers. - * The bits that are not deskewed parfectly (too much skew on them, - * or data eye very wide) are marked in the array deskew_non_converge. - */ -static void apply_deskew_results(struct stm32mp1_ddrphy *phy, u8 byte, - u8 deskew_delay[NUM_BYTES][8], - u8 deskew_non_converge[NUM_BYTES][8]) -{ - u8 bit_i; - u8 index; - - for (bit_i = 0; bit_i < 8; bit_i++) { - set_DQ_unit_delay(phy, byte, bit_i, deskew_delay[byte][bit_i]); - index = DQ_unit_index(phy, byte, bit_i); - log_debug("Byte %d ; bit %d : The new DQ delay (%d) index=%d [delta=%d, 3 is the default]", - byte, bit_i, deskew_delay[byte][bit_i], - index, index - 3); - printf("Byte %d, bit %d, DQ delay = %d", - byte, bit_i, deskew_delay[byte][bit_i]); - if (deskew_non_converge[byte][bit_i] == 1) - log_debug(" - not converged : still more skew"); - printf("\n"); - } -} - -/* DQ Bit de-skew algorithm. - * Deskews data lines as much as possible. - * 1. Add delay to DQS line until finding the failure - * (normally a hold time violation) - * 2. Reduce DQS line by small steps until finding the very first time - * we go back to "Pass" condition. - * 3. For each DQ line, Reduce DQ delay until finding the very first failure - * (normally a hold time fail) - * 4. When all bits are at their first failure delay, we can consider them - * aligned. - * Handle conrer situation (Can't find Pass-fail, or fail-pass transitions - * at any step) - * TODO Provide a return Status. Improve doc - */ -static enum test_result bit_deskew(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy, char *string) -{ - /* New DQ delay value (index), set during Deskew algo */ - u8 deskew_delay[NUM_BYTES][8]; - /*If there is still skew on a bit, mark this bit. */ - u8 deskew_non_converge[NUM_BYTES][8]; - struct BIST_result result; - s8 dqs_unit_delay_index = 0; - u8 datx8 = 0; - u8 bit_i = 0; - s8 phase_idx = 0; - s8 bit_i_delay_index = 0; - u8 success = 0; - struct tuning_position last_right_ok; - u8 force_stop = 0; - u8 fail_found; - u8 error = 0; - u8 nb_bytes = get_nb_bytes(ctl); - /* u8 last_pass_dqs_unit = 0; */ - - memset(deskew_delay, 0, sizeof(deskew_delay)); - memset(deskew_non_converge, 0, sizeof(deskew_non_converge)); - - /*Disable DQS Drift Compensation*/ - clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); - /*Disable all bytes*/ - /* Disable automatic power down of DLL and IOs when disabling - * a byte (To avoid having to add programming and delay - * for a DLL re-lock when later re-enabling a disabled Byte Lane) - */ - clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX); - - /* Disable all data bytes */ - clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN); - - /* Config the BIST block */ - config_BIST(ctl, phy); - log_debug("BIST Config done.\n"); - - /* Train each byte */ - for (datx8 = 0; datx8 < nb_bytes; datx8++) { - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - datx8 + 1, nb_bytes, error); - return TEST_FAILED; - } - log_debug("\n======================\n"); - log_debug("Start deskew byte %d .\n", datx8); - log_debug("======================\n"); - /* Enable Byte (DXNGCR, bit DXEN) */ - setbits_le32(DXNGCR(phy, datx8), DDRPHYC_DXNGCR_DXEN); - - /* Select the byte lane for comparison of read data */ - BIST_datx8_sel(phy, datx8); - - /* Set all DQDLYn to maximum value. All bits within the byte - * will be delayed with DQSTR = 2 instead of max = 3 - * to avoid inter bits fail influence - */ - writel(0xAAAAAAAA, DXNDQTR(phy, datx8)); - - /* Set the DQS phase delay to 90 DEG (default). - * What is defined here is the index of the desired config - * in the PHASE array. - */ - phase_idx = _90deg; - - /* Set DQS unit delay to the max value. */ - dqs_unit_delay_index = MAX_DQS_UNIT_IDX; - DQS_unit_delay(phy, datx8, dqs_unit_delay_index); - DQS_phase_delay(phy, datx8, phase_idx); - - /* Issue a DLL soft reset */ - clrbits_le32(DXNDLLCR(phy, datx8), DDRPHYC_DXNDLLCR_DLLSRST); - setbits_le32(DXNDLLCR(phy, datx8), DDRPHYC_DXNDLLCR_DLLSRST); - - /* Test this typical init condition */ - BIST_test(phy, datx8, &result); - success = result.test_result; - - /* If the test pass in this typical condition, - * start the algo with it. - * Else, look for Pass init condition - */ - if (!success) { - log_debug("Fail at init condtion. Let's look for a good init condition.\n"); - success = 0; /* init */ - /* Make sure we start with a PASS condition before - * looking for a fail condition. - * Find the first PASS PHASE condition - */ - - /* escape if we find a PASS */ - log_debug("increase Phase idx\n"); - while (!success && (phase_idx <= MAX_DQS_PHASE_IDX)) { - DQS_phase_delay(phy, datx8, phase_idx); - BIST_test(phy, datx8, &result); - success = result.test_result; - phase_idx++; - } - /* if ended with success - * ==>> Restore the fist success condition - */ - if (success) - phase_idx--; /* because it ended with ++ */ - } - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - datx8 + 1, nb_bytes, error); - return TEST_FAILED; - } - /* We couldn't find a successful condition, its seems - * we have hold violation, lets try reduce DQS_unit Delay - */ - if (!success) { - /* We couldn't find a successful condition, its seems - * we have hold violation, lets try reduce DQS_unit - * Delay - */ - log_debug("Still fail. Try decrease DQS Unit delay\n"); - - phase_idx = 0; - dqs_unit_delay_index = 0; - DQS_phase_delay(phy, datx8, phase_idx); - - /* escape if we find a PASS */ - while (!success && - (dqs_unit_delay_index <= - MAX_DQS_UNIT_IDX)) { - DQS_unit_delay(phy, datx8, - dqs_unit_delay_index); - BIST_test(phy, datx8, &result); - success = result.test_result; - dqs_unit_delay_index++; - } - if (success) { - /* Restore the first success condition*/ - dqs_unit_delay_index--; - /* last_pass_dqs_unit = dqs_unit_delay_index;*/ - DQS_unit_delay(phy, datx8, - dqs_unit_delay_index); - } else { - /* No need to continue, - * there is no pass region. - */ - force_stop = 1; - } - } - - /* There is an initial PASS condition - * Look for the first failing condition by PHASE stepping. - * This part of the algo can finish without converging. - */ - if (force_stop) { - printf("Result: Failed "); - printf("[Cannot Deskew lines, "); - printf("there is no PASS region]\n"); - error++; - continue; - } - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - datx8 + 1, nb_bytes, error); - return TEST_FAILED; - } - - log_debug("there is a pass region for phase idx %d\n", - phase_idx); - log_debug("Step1: Find the first failing condition\n"); - /* Look for the first failing condition by PHASE stepping. - * This part of the algo can finish without converging. - */ - - /* escape if we find a fail (hold time violation) - * condition at any bit or if out of delay range. - */ - while (success && (phase_idx <= MAX_DQS_PHASE_IDX)) { - DQS_phase_delay(phy, datx8, phase_idx); - BIST_test(phy, datx8, &result); - success = result.test_result; - phase_idx++; - } - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - datx8 + 1, nb_bytes, error); - return TEST_FAILED; - } - - /* if the loop ended with a failing condition at any bit, - * lets look for the first previous success condition by unit - * stepping (minimal delay) - */ - if (!success) { - log_debug("Fail region (PHASE) found phase idx %d\n", - phase_idx); - log_debug("Let's look for first success by DQS Unit steps\n"); - /* This part, the algo always converge */ - phase_idx--; - - /* escape if we find a success condition - * or if out of delay range. - */ - while (!success && dqs_unit_delay_index >= 0) { - DQS_unit_delay(phy, datx8, - dqs_unit_delay_index); - BIST_test(phy, datx8, &result); - success = result.test_result; - dqs_unit_delay_index--; - } - /* if the loop ended with a success condition, - * the last delay Right OK (before hold violation) - * condition is then defined as following: - */ - if (success) { - /* Hold the dely parameters of the the last - * delay Right OK condition. - * -1 to get back to current condition - */ - last_right_ok.phase = phase_idx; - /*+1 to get back to current condition */ - last_right_ok.unit = dqs_unit_delay_index + 1; - last_right_ok.bits_delay = 0xFFFFFFFF; - log_debug("Found %d\n", dqs_unit_delay_index); - } else { - /* the last OK condition is then with the - * previous phase_idx. - * -2 instead of -1 because at the last - * iteration of the while(), - * we incremented phase_idx - */ - last_right_ok.phase = phase_idx - 1; - /* Nominal+1. Because we want the previous - * delay after reducing the phase delay. - */ - last_right_ok.unit = 1; - last_right_ok.bits_delay = 0xFFFFFFFF; - log_debug("Not Found : try previous phase %d\n", - phase_idx - 1); - - DQS_phase_delay(phy, datx8, phase_idx - 1); - dqs_unit_delay_index = 0; - success = true; - while (success && - (dqs_unit_delay_index < - MAX_DQS_UNIT_IDX)) { - DQS_unit_delay(phy, datx8, - dqs_unit_delay_index); - BIST_test(phy, datx8, &result); - success = result.test_result; - dqs_unit_delay_index++; - log_debug("dqs_unit_delay_index = %d, result = %d\n", - dqs_unit_delay_index, success); - } - - if (!success) { - last_right_ok.unit = - dqs_unit_delay_index - 1; - } else { - last_right_ok.unit = 0; - log_debug("ERROR: failed region not FOUND"); - } - } - } else { - /* we can't find a failing condition at all bits - * ==> Just hold the last test condition - * (the max DQS delay) - * which is the most likely, - * the closest to a hold violation - * If we can't find a Fail condition after - * the Pass region, stick at this position - * In order to have max chances to find a fail - * when reducing DQ delays. - */ - last_right_ok.phase = MAX_DQS_PHASE_IDX; - last_right_ok.unit = MAX_DQS_UNIT_IDX; - last_right_ok.bits_delay = 0xFFFFFFFF; - log_debug("Can't find the a fail condition\n"); - } - - /* step 2: - * if we arrive at this stage, it means that we found the last - * Right OK condition (by tweeking the DQS delay). Or we simply - * pushed DQS delay to the max - * This means that by reducing the delay on some DQ bits, - * we should find a failing condition. - */ - printf("Byte %d, DQS unit = %d, phase = %d\n", - datx8, last_right_ok.unit, last_right_ok.phase); - log_debug("Step2, unit = %d, phase = %d, bits delay=%x\n", - last_right_ok.unit, last_right_ok.phase, - last_right_ok.bits_delay); - - /* Restore the last_right_ok condtion. */ - DQS_unit_delay(phy, datx8, last_right_ok.unit); - DQS_phase_delay(phy, datx8, last_right_ok.phase); - writel(last_right_ok.bits_delay, DXNDQTR(phy, datx8)); - - /* train each bit - * reduce delay on each bit, and perform a write/read test - * and stop at the very first time it fails. - * the goal is the find the first failing condition - * for each bit. - * When we achieve this condition< for all the bits, - * we are sure they are aligned (+/- step resolution) - */ - fail_found = 0; - for (bit_i = 0; bit_i < 8; bit_i++) { - if (ctrlc()) { - sprintf(string, - "interrupted at byte %d/%d, error=%d", - datx8 + 1, nb_bytes, error); - return error; - } - log_debug("deskewing bit %d:\n", bit_i); - success = 1; /* init */ - /* Set all DQDLYn to maximum value. - * Only bit_i will be down-delayed - * ==> if we have a fail, it will be definitely - * from bit_i - */ - writel(0xFFFFFFFF, DXNDQTR(phy, datx8)); - /* Arriving at this stage, - * we have a success condition with delay = 3; - */ - bit_i_delay_index = 3; - - /* escape if bit delay is out of range or - * if a fatil occurs - */ - while ((bit_i_delay_index >= 0) && success) { - set_DQ_unit_delay(phy, datx8, - bit_i, - bit_i_delay_index); - BIST_test(phy, datx8, &result); - success = result.test_result; - bit_i_delay_index--; - } - - /* if escape with a fail condition - * ==> save this position for bit_i - */ - if (!success) { - /* save the delay position. - * Add 1 because the while loop ended with a --, - * and that we need to hold the last success - * delay - */ - deskew_delay[datx8][bit_i] = - bit_i_delay_index + 2; - if (deskew_delay[datx8][bit_i] > 3) - deskew_delay[datx8][bit_i] = 3; - - /* A flag that states we found at least a fail - * at one bit. - */ - fail_found = 1; - log_debug("Fail found on bit %d, for delay = %d => deskew[%d][%d] = %d\n", - bit_i, bit_i_delay_index + 1, - datx8, bit_i, - deskew_delay[datx8][bit_i]); - } else { - /* if we can find a success condition by - * back-delaying this bit, just set the delay - * to 0 (the best deskew - * possible) and mark the bit. - */ - deskew_delay[datx8][bit_i] = 0; - /* set a flag that will be used later - * in the report. - */ - deskew_non_converge[datx8][bit_i] = 1; - log_debug("Fail not found on bit %d => deskew[%d][%d] = %d\n", - bit_i, datx8, bit_i, - deskew_delay[datx8][bit_i]); - } - } - log_debug("**********byte %d tuning complete************\n", - datx8); - /* If we can't find any failure by back delaying DQ lines, - * hold the default values - */ - if (!fail_found) { - for (bit_i = 0; bit_i < 8; bit_i++) - deskew_delay[datx8][bit_i] = 0; - log_debug("The Deskew algorithm can't converge, there is too much margin in your design. Good job!\n"); - } - - apply_deskew_results(phy, datx8, deskew_delay, - deskew_non_converge); - /* Restore nominal value for DQS delay */ - DQS_phase_delay(phy, datx8, 3); - DQS_unit_delay(phy, datx8, 3); - /* disable byte after byte bits deskew */ - clrbits_le32(DXNGCR(phy, datx8), DDRPHYC_DXNGCR_DXEN); - } /* end of byte deskew */ - - /* re-enable all data bytes */ - setbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN); - setbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN); - setbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN); - setbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN); - - if (error) { - sprintf(string, "error = %d", error); - return TEST_FAILED; - } - - return TEST_PASSED; -} /* end function */ - -/* Trim DQS timings and set it in the centre of data eye. - * Look for a PPPPF region, then look for a FPPP region and finally select - * the mid of the FPPPPPF region - */ -static enum test_result eye_training(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy, char *string) -{ - /*Stores the DQS trim values (PHASE index, unit index) */ - u8 eye_training_val[NUM_BYTES][2]; - u8 byte = 0; - struct BIST_result result; - s8 dqs_unit_delay_index = 0; - s8 phase_idx = 0; - s8 dqs_unit_delay_index_pass = 0; - s8 phase_idx_pass = 0; - u8 success = 0; - u8 left_phase_bound_found, right_phase_bound_found; - u8 left_unit_bound_found, right_unit_bound_found; - u8 left_bound_found, right_bound_found; - struct tuning_position left_bound, right_bound; - u8 error = 0; - u8 nb_bytes = get_nb_bytes(ctl); - - /*Disable DQS Drift Compensation*/ - clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); - /*Disable all bytes*/ - /* Disable automatic power down of DLL and IOs when disabling a byte - * (To avoid having to add programming and delay - * for a DLL re-lock when later re-enabling a disabled Byte Lane) - */ - clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX); - - /*Disable all data bytes */ - clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN); - - /* Config the BIST block */ - config_BIST(ctl, phy); - - for (byte = 0; byte < nb_bytes; byte++) { - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - byte + 1, nb_bytes, error); - return TEST_FAILED; - } - right_bound.phase = 0; - right_bound.unit = 0; - - left_bound.phase = 0; - left_bound.unit = 0; - - left_phase_bound_found = 0; - right_phase_bound_found = 0; - - left_unit_bound_found = 0; - right_unit_bound_found = 0; - - left_bound_found = 0; - right_bound_found = 0; - - /* Enable Byte (DXNGCR, bit DXEN) */ - setbits_le32(DXNGCR(phy, byte), DDRPHYC_DXNGCR_DXEN); - - /* Select the byte lane for comparison of read data */ - BIST_datx8_sel(phy, byte); - - /* Set DQS phase delay to the nominal value. */ - phase_idx = _90deg; - phase_idx_pass = phase_idx; - - /* Set DQS unit delay to the nominal value. */ - dqs_unit_delay_index = 3; - dqs_unit_delay_index_pass = dqs_unit_delay_index; - success = 0; - - log_debug("STEP0: Find Init delay\n"); - /* STEP0: Find Init delay: a delay that put the system - * in a "Pass" condition then (TODO) update - * dqs_unit_delay_index_pass & phase_idx_pass - */ - DQS_unit_delay(phy, byte, dqs_unit_delay_index); - DQS_phase_delay(phy, byte, phase_idx); - BIST_test(phy, byte, &result); - success = result.test_result; - /* If we have a fail in the nominal condition */ - if (!success) { - /* Look at the left */ - while (phase_idx >= 0 && !success) { - phase_idx--; - DQS_phase_delay(phy, byte, phase_idx); - BIST_test(phy, byte, &result); - success = result.test_result; - } - } - if (!success) { - /* if we can't find pass condition, - * then look at the right - */ - phase_idx = _90deg; - while (phase_idx <= MAX_DQS_PHASE_IDX && - !success) { - phase_idx++; - DQS_phase_delay(phy, byte, - phase_idx); - BIST_test(phy, byte, &result); - success = result.test_result; - } - } - /* save the pass condition */ - if (success) { - phase_idx_pass = phase_idx; - } else { - printf("Result: Failed "); - printf("[Cannot DQS timings, "); - printf("there is no PASS region]\n"); - error++; - continue; - } - - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - byte + 1, nb_bytes, error); - return TEST_FAILED; - } - log_debug("STEP1: Find LEFT PHASE DQS Bound\n"); - /* STEP1: Find LEFT PHASE DQS Bound */ - while ((phase_idx >= 0) && - (phase_idx <= MAX_DQS_PHASE_IDX) && - !left_phase_bound_found) { - DQS_unit_delay(phy, byte, - dqs_unit_delay_index); - DQS_phase_delay(phy, byte, - phase_idx); - BIST_test(phy, byte, &result); - success = result.test_result; - - /*TODO: Manage the case were at the beginning - * there is already a fail - */ - if (!success) { - /* the last pass condition */ - left_bound.phase = ++phase_idx; - left_phase_bound_found = 1; - } else if (success) { - phase_idx--; - } - } - if (!left_phase_bound_found) { - left_bound.phase = 0; - phase_idx = 0; - } - /* If not found, lets take 0 */ - - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - byte + 1, nb_bytes, error); - return TEST_FAILED; - } - log_debug("STEP2: Find UNIT left bound\n"); - /* STEP2: Find UNIT left bound */ - while ((dqs_unit_delay_index >= 0) && - !left_unit_bound_found) { - DQS_unit_delay(phy, byte, - dqs_unit_delay_index); - DQS_phase_delay(phy, byte, phase_idx); - BIST_test(phy, byte, &result); - success = result.test_result; - if (!success) { - left_bound.unit = - ++dqs_unit_delay_index; - left_unit_bound_found = 1; - left_bound_found = 1; - } else if (success) { - dqs_unit_delay_index--; - } - } - - /* If not found, lets take 0 */ - if (!left_unit_bound_found) - left_bound.unit = 0; - - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - byte + 1, nb_bytes, error); - return TEST_FAILED; - } - log_debug("STEP3: Find PHase right bound\n"); - /* STEP3: Find PHase right bound, start with "pass" - * condition - */ - - /* Set DQS phase delay to the pass value. */ - phase_idx = phase_idx_pass; - - /* Set DQS unit delay to the pass value. */ - dqs_unit_delay_index = dqs_unit_delay_index_pass; - - while ((phase_idx <= MAX_DQS_PHASE_IDX) && - !right_phase_bound_found) { - DQS_unit_delay(phy, byte, - dqs_unit_delay_index); - DQS_phase_delay(phy, byte, phase_idx); - BIST_test(phy, byte, &result); - success = result.test_result; - if (!success) { - /* the last pass condition */ - right_bound.phase = --phase_idx; - right_phase_bound_found = 1; - } else if (success) { - phase_idx++; - } - } - - /* If not found, lets take the max value */ - if (!right_phase_bound_found) { - right_bound.phase = MAX_DQS_PHASE_IDX; - phase_idx = MAX_DQS_PHASE_IDX; - } - - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d, error=%d", - byte + 1, nb_bytes, error); - return TEST_FAILED; - } - log_debug("STEP4: Find UNIT right bound\n"); - /* STEP4: Find UNIT right bound */ - while ((dqs_unit_delay_index <= MAX_DQS_UNIT_IDX) && - !right_unit_bound_found) { - DQS_unit_delay(phy, byte, - dqs_unit_delay_index); - DQS_phase_delay(phy, byte, phase_idx); - BIST_test(phy, byte, &result); - success = result.test_result; - if (!success) { - right_bound.unit = - --dqs_unit_delay_index; - right_unit_bound_found = 1; - right_bound_found = 1; - } else if (success) { - dqs_unit_delay_index++; - } - } - /* If not found, lets take the max value */ - if (!right_unit_bound_found) - right_bound.unit = MAX_DQS_UNIT_IDX; - - /* If we found a regular FAil Pass FAil pattern - * FFPPPPPPFF - * OR PPPPPFF Or FFPPPPP - */ - - if (left_bound_found || right_bound_found) { - eye_training_val[byte][0] = (right_bound.phase + - left_bound.phase) / 2; - eye_training_val[byte][1] = (right_bound.unit + - left_bound.unit) / 2; - - /* If we already lost 1/2PHASE Tuning, - * let's try to recover by ++ on unit - */ - if (((right_bound.phase + left_bound.phase) % 2 == 1) && - eye_training_val[byte][1] != MAX_DQS_UNIT_IDX) - eye_training_val[byte][1]++; - log_debug("** found phase : %d - %d & unit %d - %d\n", - right_bound.phase, left_bound.phase, - right_bound.unit, left_bound.unit); - log_debug("** calculating mid region: phase: %d unit: %d (nominal is 3)\n", - eye_training_val[byte][0], - eye_training_val[byte][1]); - } else { - /* PPPPPPPPPP, we're already good. - * Set nominal values. - */ - eye_training_val[byte][0] = 3; - eye_training_val[byte][1] = 3; - } - DQS_phase_delay(phy, byte, eye_training_val[byte][0]); - DQS_unit_delay(phy, byte, eye_training_val[byte][1]); - - printf("Byte %d, DQS unit = %d, phase = %d\n", - byte, - eye_training_val[byte][1], - eye_training_val[byte][0]); - } - - if (error) { - sprintf(string, "error = %d", error); - return TEST_FAILED; - } - - return TEST_PASSED; -} - -static void display_reg_results(struct stm32mp1_ddrphy *phy, u8 byte) -{ - u8 i = 0; - - printf("Byte %d Dekew result, bit0 delay, bit1 delay...bit8 delay\n ", - byte); - - for (i = 0; i < 8; i++) - printf("%d ", DQ_unit_index(phy, byte, i)); - printf("\n"); - - printf("dxndllcr: [%08x] val:%08x\n", - DXNDLLCR(phy, byte), - readl(DXNDLLCR(phy, byte))); - printf("dxnqdstr: [%08x] val:%08x\n", - DXNDQSTR(phy, byte), - readl(DXNDQSTR(phy, byte))); - printf("dxndqtr: [%08x] val:%08x\n", - DXNDQTR(phy, byte), - readl(DXNDQTR(phy, byte))); -} - -/* analyse the dgs gating log table, and determine the midpoint.*/ -static u8 set_midpoint_read_dqs_gating(struct stm32mp1_ddrphy *phy, u8 byte, - u8 dqs_gating[NUM_BYTES] - [MAX_GSL_IDX + 1] - [MAX_GPS_IDX + 1]) -{ - /* stores the dqs gate values (gsl index, gps index) */ - u8 dqs_gate_values[NUM_BYTES][2]; - u8 gsl_idx, gps_idx = 0; - u8 left_bound_idx[2] = {0, 0}; - u8 right_bound_idx[2] = {0, 0}; - u8 left_bound_found = 0; - u8 right_bound_found = 0; - u8 intermittent = 0; - u8 value; - - for (gsl_idx = 0; gsl_idx <= MAX_GSL_IDX; gsl_idx++) { - for (gps_idx = 0; gps_idx <= MAX_GPS_IDX; gps_idx++) { - value = dqs_gating[byte][gsl_idx][gps_idx]; - if (value == 1 && left_bound_found == 0) { - left_bound_idx[0] = gsl_idx; - left_bound_idx[1] = gps_idx; - left_bound_found = 1; - } else if (value == 0 && - left_bound_found == 1 && - !right_bound_found) { - if (gps_idx == 0) { - right_bound_idx[0] = gsl_idx - 1; - right_bound_idx[1] = MAX_GPS_IDX; - } else { - right_bound_idx[0] = gsl_idx; - right_bound_idx[1] = gps_idx - 1; - } - right_bound_found = 1; - } else if (value == 1 && - right_bound_found == 1) { - intermittent = 1; - } - } - } - - /* if only ppppppp is found, there is no mid region. */ - if (left_bound_idx[0] == 0 && left_bound_idx[1] == 0 && - right_bound_idx[0] == 0 && right_bound_idx[1] == 0) - intermittent = 1; - - /*if we found a regular fail pass fail pattern ffppppppff - * or pppppff or ffppppp - */ - if (!intermittent) { - /*if we found a regular fail pass fail pattern ffppppppff - * or pppppff or ffppppp - */ - if (left_bound_found || right_bound_found) { - log_debug("idx0(%d): %d %d idx1(%d) : %d %d\n", - left_bound_found, - right_bound_idx[0], left_bound_idx[0], - right_bound_found, - right_bound_idx[1], left_bound_idx[1]); - dqs_gate_values[byte][0] = - (right_bound_idx[0] + left_bound_idx[0]) / 2; - dqs_gate_values[byte][1] = - (right_bound_idx[1] + left_bound_idx[1]) / 2; - /* if we already lost 1/2gsl tuning, - * let's try to recover by ++ on gps - */ - if (((right_bound_idx[0] + - left_bound_idx[0]) % 2 == 1) && - dqs_gate_values[byte][1] != MAX_GPS_IDX) - dqs_gate_values[byte][1]++; - /* if we already lost 1/2gsl tuning and gps is on max*/ - else if (((right_bound_idx[0] + - left_bound_idx[0]) % 2 == 1) && - dqs_gate_values[byte][1] == MAX_GPS_IDX) { - dqs_gate_values[byte][1] = 0; - dqs_gate_values[byte][0]++; - } - /* if we have gsl left and write limit too close - * (difference=1) - */ - if (((right_bound_idx[0] - left_bound_idx[0]) == 1)) { - dqs_gate_values[byte][1] = (left_bound_idx[1] + - right_bound_idx[1] + - 4) / 2; - if (dqs_gate_values[byte][1] >= 4) { - dqs_gate_values[byte][0] = - right_bound_idx[0]; - dqs_gate_values[byte][1] -= 4; - } else { - dqs_gate_values[byte][0] = - left_bound_idx[0]; - } - } - log_debug("*******calculating mid region: system latency: %d phase: %d********\n", - dqs_gate_values[byte][0], - dqs_gate_values[byte][1]); - log_debug("*******the nominal values were system latency: 0 phase: 2*******\n"); - } - } else { - /* if intermitant, restore defaut values */ - log_debug("dqs gating:no regular fail/pass/fail found. defaults values restored.\n"); - dqs_gate_values[byte][0] = 0; - dqs_gate_values[byte][1] = 2; - } - set_r0dgsl_delay(phy, byte, dqs_gate_values[byte][0]); - set_r0dgps_delay(phy, byte, dqs_gate_values[byte][1]); - printf("Byte %d, R0DGSL = %d, R0DGPS = %d\n", - byte, dqs_gate_values[byte][0], dqs_gate_values[byte][1]); - - /* return 0 if intermittent or if both left_bound - * and right_bound are not found - */ - return !(intermittent || (left_bound_found && right_bound_found)); -} - -static enum test_result read_dqs_gating(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy, - char *string) -{ - /* stores the log of pass/fail */ - u8 dqs_gating[NUM_BYTES][MAX_GSL_IDX + 1][MAX_GPS_IDX + 1]; - u8 byte, gsl_idx, gps_idx = 0; - struct BIST_result result; - u8 success = 0; - u8 nb_bytes = get_nb_bytes(ctl); - - memset(dqs_gating, 0x0, sizeof(dqs_gating)); - - /*disable dqs drift compensation*/ - clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); - /*disable all bytes*/ - /* disable automatic power down of dll and ios when disabling a byte - * (to avoid having to add programming and delay - * for a dll re-lock when later re-enabling a disabled byte lane) - */ - clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX); - - /* disable all data bytes */ - clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN); - clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN); - - /* config the bist block */ - config_BIST(ctl, phy); - - for (byte = 0; byte < nb_bytes; byte++) { - if (ctrlc()) { - sprintf(string, "interrupted at byte %d/%d", - byte + 1, nb_bytes); - return TEST_FAILED; - } - /* enable byte x (dxngcr, bit dxen) */ - setbits_le32(DXNGCR(phy, byte), DDRPHYC_DXNGCR_DXEN); - - /* select the byte lane for comparison of read data */ - BIST_datx8_sel(phy, byte); - for (gsl_idx = 0; gsl_idx <= MAX_GSL_IDX; gsl_idx++) { - for (gps_idx = 0; gps_idx <= MAX_GPS_IDX; gps_idx++) { - if (ctrlc()) { - sprintf(string, - "interrupted at byte %d/%d", - byte + 1, nb_bytes); - return TEST_FAILED; - } - /* write cfg to dxndqstr */ - set_r0dgsl_delay(phy, byte, gsl_idx); - set_r0dgps_delay(phy, byte, gps_idx); - - BIST_test(phy, byte, &result); - success = result.test_result; - if (success) - dqs_gating[byte][gsl_idx][gps_idx] = 1; - itm_soft_reset(phy); - } - } - set_midpoint_read_dqs_gating(phy, byte, dqs_gating); - /* dummy reads */ - readl(0xc0000000); - readl(0xc0000000); - } - - /* re-enable drift compensation */ - /* setbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); */ - return TEST_PASSED; -} - -/**************************************************************** - * TEST - **************************************************************** - */ -static enum test_result do_read_dqs_gating(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy, - char *string, int argc, - char *argv[]) -{ - u32 rfshctl3 = readl(&ctl->rfshctl3); - u32 pwrctl = readl(&ctl->pwrctl); - u32 derateen = readl(&ctl->derateen); - enum test_result res; - - writel(0x0, &ctl->derateen); - stm32mp1_refresh_disable(ctl); - - res = read_dqs_gating(ctl, phy, string); - - stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl); - writel(derateen, &ctl->derateen); - - return res; -} - -static enum test_result do_bit_deskew(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy, - char *string, int argc, char *argv[]) -{ - u32 rfshctl3 = readl(&ctl->rfshctl3); - u32 pwrctl = readl(&ctl->pwrctl); - u32 derateen = readl(&ctl->derateen); - enum test_result res; - - writel(0x0, &ctl->derateen); - stm32mp1_refresh_disable(ctl); - - res = bit_deskew(ctl, phy, string); - - stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl); - writel(derateen, &ctl->derateen); - - return res; -} - -static enum test_result do_eye_training(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy, - char *string, int argc, char *argv[]) -{ - u32 rfshctl3 = readl(&ctl->rfshctl3); - u32 pwrctl = readl(&ctl->pwrctl); - u32 derateen = readl(&ctl->derateen); - enum test_result res; - - writel(0x0, &ctl->derateen); - stm32mp1_refresh_disable(ctl); - - res = eye_training(ctl, phy, string); - - stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl); - writel(derateen, &ctl->derateen); - - return res; -} - -static enum test_result do_display(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy, - char *string, int argc, char *argv[]) -{ - int byte; - u8 nb_bytes = get_nb_bytes(ctl); - - for (byte = 0; byte < nb_bytes; byte++) - display_reg_results(phy, byte); - - return TEST_PASSED; -} - -static enum test_result do_bist_config(struct stm32mp1_ddrctl *ctl, - struct stm32mp1_ddrphy *phy, - char *string, int argc, char *argv[]) -{ - unsigned long value; - - if (argc > 0) { - if (strict_strtoul(argv[0], 0, &value) < 0) { - sprintf(string, "invalid nbErr %s", argv[0]); - return TEST_FAILED; - } - BIST_error_max = value; - } - if (argc > 1) { - if (strict_strtoul(argv[1], 0, &value) < 0) { - sprintf(string, "invalid Seed %s", argv[1]); - return TEST_FAILED; - } - BIST_seed = value; - } - printf("Bist.nbErr = %d\n", BIST_error_max); - if (BIST_seed) - printf("Bist.Seed = 0x%x\n", BIST_seed); - else - printf("Bist.Seed = random\n"); - - return TEST_PASSED; -} - -/**************************************************************** - * TEST Description - **************************************************************** - */ - -const struct test_desc tuning[] = { - {do_read_dqs_gating, "Read DQS gating", - "software read DQS Gating", "", 0 }, - {do_bit_deskew, "Bit de-skew", "", "", 0 }, - {do_eye_training, "Eye Training", "or DQS training", "", 0 }, - {do_display, "Display registers", "", "", 0 }, - {do_bist_config, "Bist config", "[nbErr] [seed]", - "configure Bist test", 2}, -}; - -const int tuning_nb = ARRAY_SIZE(tuning); diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c index 1bff8075ee3..ca0135a4203 100644 --- a/drivers/reset/reset-scmi.c +++ b/drivers/reset/reset-scmi.c @@ -2,6 +2,9 @@ /* * Copyright (C) 2019-2020 Linaro Limited */ + +#define LOG_CATEGORY UCLASS_RESET + #include <common.h> #include <dm.h> #include <errno.h> diff --git a/drivers/serial/arm_dcc.c b/drivers/serial/arm_dcc.c index dfcb6fd6981..a402a123b6d 100644 --- a/drivers/serial/arm_dcc.c +++ b/drivers/serial/arm_dcc.c @@ -51,7 +51,7 @@ #define status_dcc(x) \ __asm__ volatile ("mrc p14, 0, %0, c14, c0, 0\n" : "=r" (x)) -#elif defined(CONFIG_CPU_ARMV8) +#elif defined(CONFIG_ARM64) /* * ARMV8 */ diff --git a/drivers/serial/serial_efi.c b/drivers/serial/serial_efi.c index 33ddbd6080c..0067576389d 100644 --- a/drivers/serial/serial_efi.c +++ b/drivers/serial/serial_efi.c @@ -24,6 +24,9 @@ struct serial_efi_priv { bool have_key; }; +/* Convert a lower-case character to its ctrl-char equivalent */ +#define CTL_CH(c) ((c) - 'a' + 1) + int serial_efi_setbrg(struct udevice *dev, int baudrate) { return 0; @@ -49,6 +52,7 @@ static int serial_efi_get_key(struct serial_efi_priv *priv) static int serial_efi_getc(struct udevice *dev) { struct serial_efi_priv *priv = dev_get_priv(dev); + char conv_scan[10] = {0, 'p', 'n', 'f', 'b', 'a', 'e', 0, 8}; int ret, ch; ret = serial_efi_get_key(priv); @@ -63,8 +67,11 @@ static int serial_efi_getc(struct udevice *dev) * key scan code of 8. Handle this so that backspace works correctly * in the U-Boot command line. */ - if (!ch && priv->key.scan_code == 8) - ch = 8; + if (!ch && priv->key.scan_code < sizeof(conv_scan)) { + ch = conv_scan[priv->key.scan_code]; + if (ch >= 'a') + ch -= 'a' - 1; + } debug(" [%x %x %x] ", ch, priv->key.unicode_char, priv->key.scan_code); return ch; diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index 3c9a69598ad..ca49ef73723 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -5,6 +5,7 @@ */ #include <common.h> +#include <clock_legacy.h> #include <clk.h> #include <dm.h> #include <fsl_lpuart.h> @@ -102,13 +103,9 @@ static void lpuart_write32(u32 flags, u32 *addr, u32 val) } -#ifndef CONFIG_SYS_CLK_FREQ -#define CONFIG_SYS_CLK_FREQ 0 -#endif - u32 __weak get_lpuart_clk(void) { - return CONFIG_SYS_CLK_FREQ; + return get_board_sys_clk(); } #if CONFIG_IS_ENABLED(CLK) diff --git a/drivers/serial/serial_mvebu_a3700.c b/drivers/serial/serial_mvebu_a3700.c index 93e4d38d34c..3e673bde57b 100644 --- a/drivers/serial/serial_mvebu_a3700.c +++ b/drivers/serial/serial_mvebu_a3700.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2016 Stefan Roese <sr@denx.de> + * Copyright (C) 2021 Pali Rohár <pali@kernel.org> */ #include <common.h> diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index b1a3aa9a297..098298336da 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -17,6 +17,7 @@ #include <errno.h> #include <fdtdec.h> #include <dm/device_compat.h> +#include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/err.h> #include <linux/io.h> @@ -32,6 +33,7 @@ #define QSPI_RD 0x0008 /* Receive Data Register */ #define QSPI_TD 0x000c /* Transmit Data Register */ #define QSPI_SR 0x0010 /* Status Register */ +#define QSPI_SR2 0x0024 /* SAMA7G5 Status Register */ #define QSPI_IER 0x0014 /* Interrupt Enable Register */ #define QSPI_IDR 0x0018 /* Interrupt Disable Register */ #define QSPI_IMR 0x001c /* Interrupt Mask Register */ @@ -46,6 +48,13 @@ #define QSPI_SMR 0x0040 /* Scrambling Mode Register */ #define QSPI_SKR 0x0044 /* Scrambling Key Register */ +#define QSPI_REFRESH 0x0050 /* Refresh Register */ +#define QSPI_WRACNT 0x0054 /* Write Access Counter Register */ +#define QSPI_DLLCFG 0x0058 /* DLL Configuration Register */ +#define QSPI_PCALCFG 0x005C /* Pad Calibration Configuration Register */ +#define QSPI_PCALBP 0x0060 /* Pad Calibration Bypass Register */ +#define QSPI_TOUT 0x0064 /* Timeout Register */ + #define QSPI_WPMR 0x00E4 /* Write Protection Mode Register */ #define QSPI_WPSR 0x00E8 /* Write Protection Status Register */ @@ -54,7 +63,14 @@ /* Bitfields in QSPI_CR (Control Register) */ #define QSPI_CR_QSPIEN BIT(0) #define QSPI_CR_QSPIDIS BIT(1) +#define QSPI_CR_DLLON BIT(2) +#define QSPI_CR_DLLOFF BIT(3) +#define QSPI_CR_STPCAL BIT(4) +#define QSPI_CR_SRFRSH BIT(5) #define QSPI_CR_SWRST BIT(7) +#define QSPI_CR_UPDCFG BIT(8) +#define QSPI_CR_STTFR BIT(9) +#define QSPI_CR_RTOUT BIT(10) #define QSPI_CR_LASTXFER BIT(24) /* Bitfields in QSPI_MR (Mode Register) */ @@ -62,12 +78,15 @@ #define QSPI_MR_LLB BIT(1) #define QSPI_MR_WDRBT BIT(2) #define QSPI_MR_SMRM BIT(3) +#define QSPI_MR_DQSDLYEN BIT(3) + #define QSPI_MR_CSMODE_MASK GENMASK(5, 4) #define QSPI_MR_CSMODE_NOT_RELOADED (0 << 4) #define QSPI_MR_CSMODE_LASTXFER (1 << 4) #define QSPI_MR_CSMODE_SYSTEMATICALLY (2 << 4) #define QSPI_MR_NBBITS_MASK GENMASK(11, 8) #define QSPI_MR_NBBITS(n) ((((n) - 8) << 8) & QSPI_MR_NBBITS_MASK) +#define QSPI_MR_OENSD BIT(15) #define QSPI_MR_DLYBCT_MASK GENMASK(23, 16) #define QSPI_MR_DLYBCT(n) (((n) << 16) & QSPI_MR_DLYBCT_MASK) #define QSPI_MR_DLYCS_MASK GENMASK(31, 24) @@ -81,6 +100,13 @@ #define QSPI_SR_CSR BIT(8) #define QSPI_SR_CSS BIT(9) #define QSPI_SR_INSTRE BIT(10) +#define QSPI_SR_LWRA BIT(11) +#define QSPI_SR_QITF BIT(12) +#define QSPI_SR_QITR BIT(13) +#define QSPI_SR_CSFA BIT(14) +#define QSPI_SR_CSRA BIT(15) +#define QSPI_SR_RFRSHD BIT(16) +#define QSPI_SR_TOUT BIT(17) #define QSPI_SR_QSPIENS BIT(24) #define QSPI_SR_CMD_COMPLETED (QSPI_SR_INSTRE | QSPI_SR_CSR) @@ -93,9 +119,22 @@ #define QSPI_SCR_DLYBS_MASK GENMASK(23, 16) #define QSPI_SCR_DLYBS(n) (((n) << 16) & QSPI_SCR_DLYBS_MASK) +/* Bitfields in QSPI_SR2 (SAMA7G5 Status Register) */ +#define QSPI_SR2_SYNCBSY BIT(0) +#define QSPI_SR2_QSPIENS BIT(1) +#define QSPI_SR2_CSS BIT(2) +#define QSPI_SR2_RBUSY BIT(3) +#define QSPI_SR2_HIDLE BIT(4) +#define QSPI_SR2_DLOCK BIT(5) +#define QSPI_SR2_CALBSY BIT(6) + +/* Bitfields in QSPI_IAR (Instruction Address Register) */ +#define QSPI_IAR_ADDR GENMASK(31, 0) + /* Bitfields in QSPI_ICR (Read/Write Instruction Code Register) */ #define QSPI_ICR_INST_MASK GENMASK(7, 0) #define QSPI_ICR_INST(inst) (((inst) << 0) & QSPI_ICR_INST_MASK) +#define QSPI_ICR_INST_MASK_SAMA7G5 GENMASK(15, 0) #define QSPI_ICR_OPT_MASK GENMASK(23, 16) #define QSPI_ICR_OPT(opt) (((opt) << 16) & QSPI_ICR_OPT_MASK) @@ -108,6 +147,9 @@ #define QSPI_IFR_WIDTH_QUAD_IO (4 << 0) #define QSPI_IFR_WIDTH_DUAL_CMD (5 << 0) #define QSPI_IFR_WIDTH_QUAD_CMD (6 << 0) +#define QSPI_IFR_WIDTH_OCT_OUTPUT (7 << 0) +#define QSPI_IFR_WIDTH_OCT_IO (8 << 0) +#define QSPI_IFR_WIDTH_OCT_CMD (9 << 0) #define QSPI_IFR_INSTEN BIT(4) #define QSPI_IFR_ADDREN BIT(5) #define QSPI_IFR_OPTEN BIT(6) @@ -118,19 +160,60 @@ #define QSPI_IFR_OPTL_4BIT (2 << 8) #define QSPI_IFR_OPTL_8BIT (3 << 8) #define QSPI_IFR_ADDRL BIT(10) +#define QSPI_IFR_ADDRL_SAMA7G5 GENMASK(11, 10) #define QSPI_IFR_TFRTYP_MEM BIT(12) #define QSPI_IFR_SAMA5D2_WRITE_TRSFR BIT(13) #define QSPI_IFR_CRM BIT(14) +#define QSPI_IFR_DDREN BIT(15) #define QSPI_IFR_NBDUM_MASK GENMASK(20, 16) #define QSPI_IFR_NBDUM(n) (((n) << 16) & QSPI_IFR_NBDUM_MASK) +#define QSPI_IFR_END BIT(22) +#define QSPI_IFR_SMRM BIT(23) #define QSPI_IFR_APBTFRTYP_READ BIT(24) /* Defined in SAM9X60 */ +#define QSPI_IFR_DQSEN BIT(25) +#define QSPI_IFR_DDRCMDEN BIT(26) +#define QSPI_IFR_HFWBEN BIT(27) +#define QSPI_IFR_PROTTYP GENMASK(29, 28) +#define QSPI_IFR_PROTTYP_STD_SPI 0 +#define QSPI_IFR_PROTTYP_TWIN_QUAD 1 +#define QSPI_IFR_PROTTYP_OCTAFLASH 2 +#define QSPI_IFR_PROTTYP_HYPERFLASH 3 /* Bitfields in QSPI_SMR (Scrambling Mode Register) */ #define QSPI_SMR_SCREN BIT(0) #define QSPI_SMR_RVDIS BIT(1) +#define QSPI_SMR_SCRKL BIT(2) + +/* Bitfields in QSPI_REFRESH (Refresh Register) */ +#define QSPI_REFRESH_DELAY_COUNTER GENMASK(31, 0) + +/* Bitfields in QSPI_WRACNT (Write Access Counter Register) */ +#define QSPI_WRACNT_NBWRA GENMASK(31, 0) + +/* Bitfields in QSPI_DLLCFG (DLL Configuration Register) */ +#define QSPI_DLLCFG_RANGE BIT(0) + +/* Bitfields in QSPI_PCALCFG (DLL Pad Calibration Configuration Register) */ +#define QSPI_PCALCFG_AAON BIT(0) +#define QSPI_PCALCFG_DAPCAL BIT(1) +#define QSPI_PCALCFG_DIFFPM BIT(2) +#define QSPI_PCALCFG_CLKDIV GENMASK(6, 4) +#define QSPI_PCALCFG_CALCNT GENMASK(16, 8) +#define QSPI_PCALCFG_CALP GENMASK(27, 24) +#define QSPI_PCALCFG_CALN GENMASK(31, 28) + +/* Bitfields in QSPI_PCALBP (DLL Pad Calibration Bypass Register) */ +#define QSPI_PCALBP_BPEN BIT(0) +#define QSPI_PCALBP_CALPBP GENMASK(11, 8) +#define QSPI_PCALBP_CALNBP GENMASK(19, 16) + +/* Bitfields in QSPI_TOUT (Timeout Register) */ +#define QSPI_TOUT_TCNTM GENMASK(15, 0) /* Bitfields in QSPI_WPMR (Write Protection Mode Register) */ #define QSPI_WPMR_WPEN BIT(0) +#define QSPI_WPMR_WPITEN BIT(1) +#define QSPI_WPMR_WPCREN BIT(2) #define QSPI_WPMR_WPKEY_MASK GENMASK(31, 8) #define QSPI_WPMR_WPKEY(wpkey) (((wpkey) << 8) & QSPI_WPMR_WPKEY_MASK) @@ -139,21 +222,61 @@ #define QSPI_WPSR_WPVSRC_MASK GENMASK(15, 8) #define QSPI_WPSR_WPVSRC(src) (((src) << 8) & QSPI_WPSR_WPVSRC) +#define ATMEL_QSPI_TIMEOUT 1000000 /* us */ +#define ATMEL_QSPI_SYNC_TIMEOUT 300000 /* us */ +#define QSPI_DLLCFG_THRESHOLD_FREQ 90000000U +#define QSPI_TOUT_MAX 0xffff + +/** + * struct atmel_qspi_pcal - Pad Calibration Clock Division + * @pclk_rate: peripheral clock rate. + * @pclkdiv: calibration clock division. The clock applied to the calibration + * cell is divided by pclkdiv + 1. + */ +struct atmel_qspi_pcal { + u32 pclk_rate; + u8 pclk_div; +}; + +#define ATMEL_QSPI_PCAL_ARRAY_SIZE 8 +static const struct atmel_qspi_pcal pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE] = { + {25000000, 0}, + {50000000, 1}, + {75000000, 2}, + {100000000, 3}, + {125000000, 4}, + {150000000, 5}, + {175000000, 6}, + {200000000, 7}, +}; + struct atmel_qspi_caps { bool has_qspick; + bool has_gclk; bool has_ricr; + bool octal; }; +struct atmel_qspi_priv_ops; + struct atmel_qspi { void __iomem *regs; void __iomem *mem; resource_size_t mmap_size; const struct atmel_qspi_caps *caps; + const struct atmel_qspi_priv_ops *ops; struct udevice *dev; ulong bus_clk_rate; u32 mr; }; +struct atmel_qspi_priv_ops { + int (*set_cfg)(struct atmel_qspi *aq, const struct spi_mem_op *op, + u32 *offset); + int (*transfer)(struct atmel_qspi *aq, const struct spi_mem_op *op, + u32 offset); +}; + struct atmel_qspi_mode { u8 cmd_buswidth; u8 addr_buswidth; @@ -171,6 +294,19 @@ static const struct atmel_qspi_mode atmel_qspi_modes[] = { { 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD }, }; +static const struct atmel_qspi_mode atmel_qspi_sama7g5_modes[] = { + { 1, 1, 1, QSPI_IFR_WIDTH_SINGLE_BIT_SPI }, + { 1, 1, 2, QSPI_IFR_WIDTH_DUAL_OUTPUT }, + { 1, 1, 4, QSPI_IFR_WIDTH_QUAD_OUTPUT }, + { 1, 2, 2, QSPI_IFR_WIDTH_DUAL_IO }, + { 1, 4, 4, QSPI_IFR_WIDTH_QUAD_IO }, + { 2, 2, 2, QSPI_IFR_WIDTH_DUAL_CMD }, + { 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD }, + { 1, 1, 8, QSPI_IFR_WIDTH_OCT_OUTPUT }, + { 1, 8, 8, QSPI_IFR_WIDTH_OCT_IO }, + { 8, 8, 8, QSPI_IFR_WIDTH_OCT_CMD }, +}; + #ifdef VERBOSE_DEBUG static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz) { @@ -180,7 +316,7 @@ static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz) case QSPI_MR: return "MR"; case QSPI_RD: - return "MR"; + return "RD"; case QSPI_TD: return "TD"; case QSPI_SR: @@ -193,6 +329,8 @@ static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz) return "IMR"; case QSPI_SCR: return "SCR"; + case QSPI_SR2: + return "SR2"; case QSPI_IAR: return "IAR"; case QSPI_ICR: @@ -205,6 +343,18 @@ static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz) return "SMR"; case QSPI_SKR: return "SKR"; + case QSPI_REFRESH: + return "REFRESH"; + case QSPI_WRACNT: + return "WRACNT"; + case QSPI_DLLCFG: + return "DLLCFG"; + case QSPI_PCALCFG: + return "PCALCFG"; + case QSPI_PCALBP: + return "PCALBP"; + case QSPI_TOUT: + return "TOUT"; case QSPI_WPMR: return "WPMR"; case QSPI_WPSR: @@ -272,9 +422,29 @@ static int atmel_qspi_find_mode(const struct spi_mem_op *op) return -ENOTSUPP; } +static int atmel_qspi_sama7g5_find_mode(const struct spi_mem_op *op) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(atmel_qspi_sama7g5_modes); i++) + if (atmel_qspi_is_compatible(op, &atmel_qspi_sama7g5_modes[i])) + return i; + + return -EOPNOTSUPP; +} + static bool atmel_qspi_supports_op(struct spi_slave *slave, const struct spi_mem_op *op) { + struct atmel_qspi *aq = dev_get_priv(slave->dev->parent); + + if (aq->caps->octal) { + if (atmel_qspi_sama7g5_find_mode(op) < 0) + return false; + else + return true; + } + if (atmel_qspi_find_mode(op) < 0) return false; @@ -397,24 +567,10 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, return 0; } -static int atmel_qspi_exec_op(struct spi_slave *slave, - const struct spi_mem_op *op) +static int atmel_qspi_transfer(struct atmel_qspi *aq, + const struct spi_mem_op *op, u32 offset) { - struct atmel_qspi *aq = dev_get_priv(slave->dev->parent); - u32 sr, imr, offset; - int err; - - /* - * Check if the address exceeds the MMIO window size. An improvement - * would be to add support for regular SPI mode and fall back to it - * when the flash memories overrun the controller's memory space. - */ - if (op->addr.val + op->data.nbytes > aq->mmap_size) - return -ENOTSUPP; - - err = atmel_qspi_set_cfg(aq, op, &offset); - if (err) - return err; + u32 sr, imr; /* Skip to the final steps if there is no data */ if (op->data.nbytes) { @@ -436,7 +592,341 @@ static int atmel_qspi_exec_op(struct spi_slave *slave, /* Poll INSTruction End and Chip Select Rise flags. */ imr = QSPI_SR_INSTRE | QSPI_SR_CSR; return readl_poll_timeout(aq->regs + QSPI_SR, sr, (sr & imr) == imr, - 1000000); + ATMEL_QSPI_TIMEOUT); +} + +static int atmel_qspi_reg_sync(struct atmel_qspi *aq) +{ + u32 val; + + return readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_SYNCBSY), + ATMEL_QSPI_SYNC_TIMEOUT); +} + +static int atmel_qspi_update_config(struct atmel_qspi *aq) +{ + int ret; + + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR); + return atmel_qspi_reg_sync(aq); +} + +static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq, + const struct spi_mem_op *op, u32 *offset) +{ + u32 iar, icr, ifr; + int mode, ret; + + iar = 0; + icr = FIELD_PREP(QSPI_ICR_INST_MASK_SAMA7G5, op->cmd.opcode); + ifr = QSPI_IFR_INSTEN; + + mode = atmel_qspi_sama7g5_find_mode(op); + if (mode < 0) + return mode; + ifr |= atmel_qspi_sama7g5_modes[mode].config; + + if (op->dummy.buswidth && op->dummy.nbytes) { + if (op->addr.dtr && op->dummy.dtr && op->data.dtr) + ifr |= QSPI_IFR_NBDUM(op->dummy.nbytes * 8 / + (2 * op->dummy.buswidth)); + else + ifr |= QSPI_IFR_NBDUM(op->dummy.nbytes * 8 / + op->dummy.buswidth); + } + + if (op->addr.buswidth && op->addr.nbytes) { + ifr |= FIELD_PREP(QSPI_IFR_ADDRL_SAMA7G5, op->addr.nbytes - 1) | + QSPI_IFR_ADDREN; + iar = FIELD_PREP(QSPI_IAR_ADDR, op->addr.val); + } + + if (op->addr.dtr && op->dummy.dtr && op->data.dtr) { + ifr |= QSPI_IFR_DDREN; + if (op->cmd.dtr) + ifr |= QSPI_IFR_DDRCMDEN; + ifr |= QSPI_IFR_DQSEN; + } + + if (op->cmd.buswidth == 8 || op->addr.buswidth == 8 || + op->data.buswidth == 8) + ifr |= FIELD_PREP(QSPI_IFR_PROTTYP, QSPI_IFR_PROTTYP_OCTAFLASH); + + /* offset of the data access in the QSPI memory space */ + *offset = iar; + + /* Set data enable */ + if (op->data.nbytes) { + ifr |= QSPI_IFR_DATAEN; + if (op->addr.nbytes) + ifr |= QSPI_IFR_TFRTYP_MEM; + } + + /* + * If the QSPI controller is set in regular SPI mode, set it in + * Serial Memory Mode (SMM). + */ + if (aq->mr != QSPI_MR_SMM) { + atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR); + ret = atmel_qspi_update_config(aq); + if (ret) + return ret; + aq->mr = QSPI_MR_SMM; + } + + /* Clear pending interrupts */ + (void)atmel_qspi_read(aq, QSPI_SR); + + /* Set QSPI Instruction Frame registers */ + if (op->addr.nbytes && !op->data.nbytes) + atmel_qspi_write(iar, aq, QSPI_IAR); + + if (op->data.dir == SPI_MEM_DATA_IN) { + atmel_qspi_write(icr, aq, QSPI_RICR); + } else { + atmel_qspi_write(icr, aq, QSPI_WICR); + if (op->data.nbytes) + atmel_qspi_write(FIELD_PREP(QSPI_WRACNT_NBWRA, + op->data.nbytes), + aq, QSPI_WRACNT); + } + + atmel_qspi_write(ifr, aq, QSPI_IFR); + + return atmel_qspi_update_config(aq); +} + +static int atmel_qspi_sama7g5_transfer(struct atmel_qspi *aq, + const struct spi_mem_op *op, u32 offset) +{ + int err; + u32 val; + + if (!op->data.nbytes) { + /* Start the transfer. */ + err = atmel_qspi_reg_sync(aq); + if (err) + return err; + atmel_qspi_write(QSPI_CR_STTFR, aq, QSPI_CR); + + return readl_poll_timeout(aq->regs + QSPI_SR, val, + val & QSPI_SR_CSRA, + ATMEL_QSPI_TIMEOUT); + } + + /* Send/Receive data. */ + if (op->data.dir == SPI_MEM_DATA_IN) { + memcpy_fromio(op->data.buf.in, aq->mem + offset, + op->data.nbytes); + + if (op->addr.nbytes) { + err = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_RBUSY), + ATMEL_QSPI_SYNC_TIMEOUT); + if (err) + return err; + } + } else { + memcpy_toio(aq->mem + offset, op->data.buf.out, + op->data.nbytes); + + err = readl_poll_timeout(aq->regs + QSPI_SR, val, + val & QSPI_SR_LWRA, + ATMEL_QSPI_TIMEOUT); + if (err) + return err; + } + + /* Release the chip-select. */ + err = atmel_qspi_reg_sync(aq); + if (err) + return err; + atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR); + + return readl_poll_timeout(aq->regs + QSPI_SR, val, val & QSPI_SR_CSRA, + ATMEL_QSPI_TIMEOUT); +} + +static int atmel_qspi_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + struct atmel_qspi *aq = dev_get_priv(slave->dev->parent); + u32 offset; + int err; + + /* + * Check if the address exceeds the MMIO window size. An improvement + * would be to add support for regular SPI mode and fall back to it + * when the flash memories overrun the controller's memory space. + */ + if (op->addr.val + op->data.nbytes > aq->mmap_size) + return -ENOTSUPP; + + if (op->addr.nbytes > 4) + return -EOPNOTSUPP; + + err = aq->ops->set_cfg(aq, op, &offset); + if (err) + return err; + + return aq->ops->transfer(aq, op, offset); +} + +static int atmel_qspi_set_pad_calibration(struct udevice *bus, uint hz) +{ + struct atmel_qspi *aq = dev_get_priv(bus); + u32 status, val; + int i, ret; + u8 pclk_div = 0; + + for (i = 0; i < ATMEL_QSPI_PCAL_ARRAY_SIZE; i++) { + if (aq->bus_clk_rate <= pcal[i].pclk_rate) { + pclk_div = pcal[i].pclk_div; + break; + } + } + + /* + * Use the biggest divider in case the peripheral clock exceeds + * 200MHZ. + */ + if (aq->bus_clk_rate > pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE - 1].pclk_rate) + pclk_div = pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE - 1].pclk_div; + + /* Disable QSPI while configuring the pad calibration. */ + status = atmel_qspi_read(aq, QSPI_SR2); + if (status & QSPI_SR2_QSPIENS) { + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR); + } + + /* + * The analog circuitry is not shut down at the end of the calibration + * and the start-up time is only required for the first calibration + * sequence, thus increasing performance. Set the delay between the Pad + * calibration analog circuitry and the calibration request to 2us. + */ + atmel_qspi_write(QSPI_PCALCFG_AAON | + FIELD_PREP(QSPI_PCALCFG_CLKDIV, pclk_div) | + FIELD_PREP(QSPI_PCALCFG_CALCNT, + 2 * (aq->bus_clk_rate / 1000000)), + aq, QSPI_PCALCFG); + + /* DLL On + start calibration. */ + atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR); + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + (val & QSPI_SR2_DLOCK) && + !(val & QSPI_SR2_CALBSY), + ATMEL_QSPI_TIMEOUT); + + /* Refresh analogic blocks every 1 ms.*/ + atmel_qspi_write(FIELD_PREP(QSPI_REFRESH_DELAY_COUNTER, hz / 1000), + aq, QSPI_REFRESH); + + return ret; +} + +static int atmel_qspi_set_gclk(struct udevice *bus, uint hz) +{ + struct atmel_qspi *aq = dev_get_priv(bus); + struct clk gclk; + u32 status, val; + int ret; + + /* Disable DLL before setting GCLK */ + status = atmel_qspi_read(aq, QSPI_SR2); + if (status & QSPI_SR2_DLOCK) { + atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR); + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_DLOCK), + ATMEL_QSPI_TIMEOUT); + if (ret) + return ret; + } + + if (hz > QSPI_DLLCFG_THRESHOLD_FREQ) + atmel_qspi_write(QSPI_DLLCFG_RANGE, aq, QSPI_DLLCFG); + else + atmel_qspi_write(0, aq, QSPI_DLLCFG); + + ret = clk_get_by_name(bus, "gclk", &gclk); + if (ret) { + dev_err(bus, "Missing QSPI generic clock\n"); + return ret; + } + + ret = clk_disable(&gclk); + if (ret) + dev_err(bus, "Failed to disable QSPI generic clock\n"); + + ret = clk_set_rate(&gclk, hz); + if (ret < 0) { + dev_err(bus, "Failed to set generic clock rate.\n"); + return ret; + } + + ret = clk_enable(&gclk); + if (ret) + dev_err(bus, "Failed to enable QSPI generic clock\n"); + clk_free(&gclk); + + return ret; +} + +static int atmel_qspi_sama7g5_set_speed(struct udevice *bus, uint hz) +{ + struct atmel_qspi *aq = dev_get_priv(bus); + u32 val; + int ret; + + ret = atmel_qspi_set_gclk(bus, hz); + if (ret) + return ret; + + if (aq->caps->octal) { + ret = atmel_qspi_set_pad_calibration(bus, hz); + if (ret) + return ret; + } else { + atmel_qspi_write(QSPI_CR_DLLON, aq, QSPI_CR); + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + val & QSPI_SR2_DLOCK, + ATMEL_QSPI_TIMEOUT); + } + + /* Set the QSPI controller by default in Serial Memory Mode */ + atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR); + ret = atmel_qspi_update_config(aq); + if (ret) + return ret; + aq->mr = QSPI_MR_SMM; + + /* Enable the QSPI controller. */ + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + val & QSPI_SR2_QSPIENS, + ATMEL_QSPI_SYNC_TIMEOUT); + if (ret) + return ret; + + if (aq->caps->octal) + ret = readl_poll_timeout(aq->regs + QSPI_SR, val, + val & QSPI_SR_RFRSHD, + ATMEL_QSPI_TIMEOUT); + + atmel_qspi_write(FIELD_PREP(QSPI_TOUT_TCNTM, QSPI_TOUT_MAX), + aq, QSPI_TOUT); + + return ret; } static int atmel_qspi_set_speed(struct udevice *bus, uint hz) @@ -444,6 +934,9 @@ static int atmel_qspi_set_speed(struct udevice *bus, uint hz) struct atmel_qspi *aq = dev_get_priv(bus); u32 scr, scbr, mask, new_value; + if (aq->caps->has_gclk) + return atmel_qspi_sama7g5_set_speed(bus, hz); + /* Compute the QSPI baudrate */ scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz); if (scbr > 0) @@ -480,6 +973,8 @@ static int atmel_qspi_set_mode(struct udevice *bus, uint mode) scr = (scr & ~mask) | new_value; atmel_qspi_write(scr, aq, QSPI_SCR); + if (aq->caps->has_gclk) + return atmel_qspi_update_config(aq); return 0; } @@ -487,7 +982,7 @@ static int atmel_qspi_set_mode(struct udevice *bus, uint mode) static int atmel_qspi_enable_clk(struct udevice *dev) { struct atmel_qspi *aq = dev_get_priv(dev); - struct clk pclk, qspick; + struct clk pclk, qspick, gclk; int ret; ret = clk_get_by_name(dev, "pclk", &pclk); @@ -517,6 +1012,17 @@ static int atmel_qspi_enable_clk(struct udevice *dev) if (ret) dev_err(dev, "Failed to enable QSPI system clock\n"); clk_free(&qspick); + } else if (aq->caps->has_gclk) { + ret = clk_get_by_name(dev, "gclk", &gclk); + if (ret) { + dev_err(dev, "Missing QSPI generic clock\n"); + goto free_pclk; + } + + ret = clk_enable(&gclk); + if (ret) + dev_err(dev, "Failed to enable QSPI system clock\n"); + clk_free(&gclk); } aq->bus_clk_rate = clk_get_rate(&pclk); @@ -529,8 +1035,18 @@ free_pclk: return ret; } -static void atmel_qspi_init(struct atmel_qspi *aq) +static int atmel_qspi_init(struct atmel_qspi *aq) { + int ret; + + if (aq->caps->has_gclk) { + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR); + return 0; + } + /* Reset the QSPI controller */ atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR); @@ -540,8 +1056,20 @@ static void atmel_qspi_init(struct atmel_qspi *aq) /* Enable the QSPI controller */ atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); + + return 0; } +static const struct atmel_qspi_priv_ops atmel_qspi_priv_ops = { + .set_cfg = atmel_qspi_set_cfg, + .transfer = atmel_qspi_transfer, +}; + +static const struct atmel_qspi_priv_ops atmel_qspi_sama7g5_priv_ops = { + .set_cfg = atmel_qspi_sama7g5_set_cfg, + .transfer = atmel_qspi_sama7g5_transfer, +}; + static int atmel_qspi_probe(struct udevice *dev) { struct atmel_qspi *aq = dev_get_priv(dev); @@ -554,6 +1082,11 @@ static int atmel_qspi_probe(struct udevice *dev) return -EINVAL; }; + if (aq->caps->has_gclk) + aq->ops = &atmel_qspi_sama7g5_priv_ops; + else + aq->ops = &atmel_qspi_priv_ops; + /* Map the registers */ ret = dev_read_resource_byname(dev, "qspi_base", &res); if (ret) { @@ -583,10 +1116,7 @@ static int atmel_qspi_probe(struct udevice *dev) return ret; aq->dev = dev; - - atmel_qspi_init(aq); - - return 0; + return atmel_qspi_init(aq); } static const struct spi_controller_mem_ops atmel_qspi_mem_ops = { @@ -607,6 +1137,15 @@ static const struct atmel_qspi_caps atmel_sam9x60_qspi_caps = { .has_ricr = true, }; +static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = { + .has_gclk = true, + .octal = true, +}; + +static const struct atmel_qspi_caps atmel_sama7g5_qspi_caps = { + .has_gclk = true, +}; + static const struct udevice_id atmel_qspi_ids[] = { { .compatible = "atmel,sama5d2-qspi", @@ -616,6 +1155,14 @@ static const struct udevice_id atmel_qspi_ids[] = { .compatible = "microchip,sam9x60-qspi", .data = (ulong)&atmel_sam9x60_qspi_caps, }, + { + .compatible = "microchip,sama7g5-ospi", + .data = (ulong)&atmel_sama7g5_ospi_caps, + }, + { + .compatible = "microchip,sama7g5-qspi", + .data = (ulong)&atmel_sama7g5_qspi_caps, + }, { /* sentinel */ } }; diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 8d40ce60c2b..a8ef926a480 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -87,16 +87,6 @@ #define OPTEE_MSG_ATTR_CACHE_PREDEFINED 0 /* - * Same values as TEE_LOGIN_* from TEE Internal API - */ -#define OPTEE_MSG_LOGIN_PUBLIC 0x00000000 -#define OPTEE_MSG_LOGIN_USER 0x00000001 -#define OPTEE_MSG_LOGIN_GROUP 0x00000002 -#define OPTEE_MSG_LOGIN_APPLICATION 0x00000004 -#define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005 -#define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006 - -/* * Page size used in non-contiguous buffer entries */ #define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096 @@ -279,7 +269,7 @@ struct optee_msg_arg { * parameters to pass the following information: * param[0].u.value.a-b uuid of Trusted Application * param[1].u.value.a-b uuid of Client - * param[1].u.value.c Login class of client OPTEE_MSG_LOGIN_* + * param[1].u.value.c Login class of client TEE_LOGIN_* * * OPTEE_MSG_CMD_INVOKE_COMMAND invokes a command a previously opened * session to a Trusted Application. struct optee_msg_arg::func is Trusted diff --git a/drivers/timer/ostm_timer.c b/drivers/timer/ostm_timer.c index 24813de2653..3ec729d2c43 100644 --- a/drivers/timer/ostm_timer.c +++ b/drivers/timer/ostm_timer.c @@ -6,6 +6,7 @@ */ #include <common.h> +#include <clock_legacy.h> #include <malloc.h> #include <asm/global_data.h> #include <asm/io.h> @@ -51,7 +52,7 @@ static int ostm_probe(struct udevice *dev) clk_free(&clk); #else - uc_priv->clock_rate = CONFIG_SYS_CLK_FREQ / 2; + uc_priv->clock_rate = get_board_sys_clk() / 2; #endif readb(priv->regs + OSTM_CTL); diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index 43171678ee1..8958f0166bd 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -180,6 +180,7 @@ static int cdns_ti_remove(struct udevice *dev) static const struct udevice_id cdns_ti_of_match[] = { { .compatible = "ti,j721e-usb", }, + { .compatible = "ti,am64-usb", }, {}, }; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a58f87f479b..cfa08b501ba 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -14,6 +14,17 @@ config DM_VIDEO option compiles in the video uclass and routes all LCD/video access through this. +config VIDEO_LOGO + bool "Show the U-Boot logo on the display" + depends on DM_VIDEO + default y if !SPLASH_SCREEN + select VIDEO_BMP_RLE8 + help + This enables showing the U-Boot logo on the display when a video + device is probed. It appears at the top right. The logo itself is at + tools/logos/u-boot_logo.bmp and looks best when the display has a + black background. + config BACKLIGHT bool "Enable panel backlight uclass support" depends on DM_VIDEO diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 8956b5f9b00..4038395b128 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -17,6 +17,9 @@ obj-$(CONFIG_DM_VIDEO) += video_bmp.o obj-$(CONFIG_PANEL) += panel-uclass.o obj-$(CONFIG_DM_PANEL_HX8238D) += hx8238d.o obj-$(CONFIG_SIMPLE_PANEL) += simple_panel.o + +obj-$(CONFIG_VIDEO_LOGO) += u_boot_logo.o + endif obj-${CONFIG_EXYNOS_FB} += exynos/ diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 7df7d57e6ec..52b109f1551 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -42,11 +42,6 @@ * VIDEO_TSTC_FCT - keyboard_tstc function * VIDEO_GETC_FCT - keyboard_getc function * - * CONFIG_VIDEO_LOGO - display Linux Logo in upper left corner. - * Use CONFIG_SPLASH_SCREEN_ALIGN with - * environment variable "splashpos" to place - * the logo on other position. In this case - * no CONSOLE_EXTRA_INFO is possible. * CONFIG_VIDEO_BMP_LOGO - use bmp_logo instead of linux_logo * CONFIG_CONSOLE_EXTRA_INFO - display additional board information * strings that normaly goes to serial @@ -127,34 +122,6 @@ void console_cursor(int state); #define CURSOR_SET video_set_cursor() #endif /* CONFIG_VIDEO_SW_CURSOR */ -#ifdef CONFIG_VIDEO_LOGO -#ifdef CONFIG_VIDEO_BMP_LOGO -#include <bmp_logo.h> -#include <bmp_logo_data.h> -#define VIDEO_LOGO_WIDTH BMP_LOGO_WIDTH -#define VIDEO_LOGO_HEIGHT BMP_LOGO_HEIGHT -#define VIDEO_LOGO_LUT_OFFSET BMP_LOGO_OFFSET -#define VIDEO_LOGO_COLORS BMP_LOGO_COLORS - -#else /* CONFIG_VIDEO_BMP_LOGO */ -#define LINUX_LOGO_WIDTH 80 -#define LINUX_LOGO_HEIGHT 80 -#define LINUX_LOGO_COLORS 214 -#define LINUX_LOGO_LUT_OFFSET 0x20 -#define __initdata -#include <linux_logo.h> -#define VIDEO_LOGO_WIDTH LINUX_LOGO_WIDTH -#define VIDEO_LOGO_HEIGHT LINUX_LOGO_HEIGHT -#define VIDEO_LOGO_LUT_OFFSET LINUX_LOGO_LUT_OFFSET -#define VIDEO_LOGO_COLORS LINUX_LOGO_COLORS -#endif /* CONFIG_VIDEO_BMP_LOGO */ -#define VIDEO_INFO_X (VIDEO_LOGO_WIDTH) -#define VIDEO_INFO_Y (VIDEO_FONT_HEIGHT/2) -#else /* CONFIG_VIDEO_LOGO */ -#define VIDEO_LOGO_WIDTH 0 -#define VIDEO_LOGO_HEIGHT 0 -#endif /* CONFIG_VIDEO_LOGO */ - #define VIDEO_COLS VIDEO_VISIBLE_COLS #define VIDEO_ROWS VIDEO_VISIBLE_ROWS #ifndef VIDEO_LINE_LEN @@ -163,11 +130,7 @@ void console_cursor(int state); #define VIDEO_SIZE (VIDEO_ROWS * VIDEO_LINE_LEN) #define VIDEO_BURST_LEN (VIDEO_COLS/8) -#ifdef CONFIG_VIDEO_LOGO -#define CONSOLE_ROWS ((VIDEO_ROWS - video_logo_height) / VIDEO_FONT_HEIGHT) -#else #define CONSOLE_ROWS (VIDEO_ROWS / VIDEO_FONT_HEIGHT) -#endif #define CONSOLE_COLS (VIDEO_COLS / VIDEO_FONT_WIDTH) #define CONSOLE_ROW_SIZE (VIDEO_FONT_HEIGHT * VIDEO_LINE_LEN) @@ -214,7 +177,7 @@ static GraphicDevice *pGD; /* Pointer to Graphic array */ static void *video_fb_address; /* frame buffer address */ static void *video_console_address; /* console buffer start address */ -static int video_logo_height = VIDEO_LOGO_HEIGHT; +static int video_logo_height; /* not supported anymore */ static int __maybe_unused cursor_state; static int __maybe_unused old_col; @@ -1670,292 +1633,6 @@ int video_display_bitmap(ulong bmp_image, int x, int y) } #endif - -#ifdef CONFIG_VIDEO_LOGO -static int video_logo_xpos; -static int video_logo_ypos; - -static void plot_logo_or_black(void *screen, int x, int y, int black); - -static void logo_plot(void *screen, int x, int y) -{ - plot_logo_or_black(screen, x, y, 0); -} - -static void logo_black(void) -{ - plot_logo_or_black(video_fb_address, video_logo_xpos, video_logo_ypos, - 1); -} - -static int do_clrlogo(struct cmd_tbl *cmdtp, int flag, int argc, - char *const argv[]) -{ - if (argc != 1) - return cmd_usage(cmdtp); - - logo_black(); - return 0; -} - -U_BOOT_CMD( - clrlogo, 1, 0, do_clrlogo, - "fill the boot logo area with black", - " " - ); - -static void plot_logo_or_black(void *screen, int x, int y, int black) -{ - - int xcount, i; - int skip = VIDEO_LINE_LEN - VIDEO_LOGO_WIDTH * VIDEO_PIXEL_SIZE; - int ycount = video_logo_height; - unsigned char r, g, b, *logo_red, *logo_blue, *logo_green; - unsigned char *source; - unsigned char *dest; - -#ifdef CONFIG_SPLASH_SCREEN_ALIGN - if (x == BMP_ALIGN_CENTER) - x = max(0, (int)(VIDEO_VISIBLE_COLS - VIDEO_LOGO_WIDTH) / 2); - else if (x < 0) - x = max(0, (int)(VIDEO_VISIBLE_COLS - VIDEO_LOGO_WIDTH + x + 1)); - - if (y == BMP_ALIGN_CENTER) - y = max(0, (int)(VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT) / 2); - else if (y < 0) - y = max(0, (int)(VIDEO_VISIBLE_ROWS - VIDEO_LOGO_HEIGHT + y + 1)); -#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ - - dest = (unsigned char *)screen + y * VIDEO_LINE_LEN + x * VIDEO_PIXEL_SIZE; - -#ifdef CONFIG_VIDEO_BMP_LOGO - source = bmp_logo_bitmap; - - /* Allocate temporary space for computing colormap */ - logo_red = malloc(BMP_LOGO_COLORS); - logo_green = malloc(BMP_LOGO_COLORS); - logo_blue = malloc(BMP_LOGO_COLORS); - /* Compute color map */ - for (i = 0; i < VIDEO_LOGO_COLORS; i++) { - logo_red[i] = (bmp_logo_palette[i] & 0x0f00) >> 4; - logo_green[i] = (bmp_logo_palette[i] & 0x00f0); - logo_blue[i] = (bmp_logo_palette[i] & 0x000f) << 4; - } -#else - source = linux_logo; - logo_red = linux_logo_red; - logo_green = linux_logo_green; - logo_blue = linux_logo_blue; -#endif - - if (VIDEO_DATA_FORMAT == GDF__8BIT_INDEX) { - for (i = 0; i < VIDEO_LOGO_COLORS; i++) { - video_set_lut(i + VIDEO_LOGO_LUT_OFFSET, - logo_red[i], logo_green[i], - logo_blue[i]); - } - } - - while (ycount--) { -#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) - int xpos = x; -#endif - xcount = VIDEO_LOGO_WIDTH; - while (xcount--) { - if (black) { - r = 0x00; - g = 0x00; - b = 0x00; - } else { - r = logo_red[*source - VIDEO_LOGO_LUT_OFFSET]; - g = logo_green[*source - VIDEO_LOGO_LUT_OFFSET]; - b = logo_blue[*source - VIDEO_LOGO_LUT_OFFSET]; - } - - switch (VIDEO_DATA_FORMAT) { - case GDF__8BIT_INDEX: - *dest = *source; - break; - case GDF__8BIT_332RGB: - *dest = ((r >> 5) << 5) | - ((g >> 5) << 2) | - (b >> 6); - break; - case GDF_15BIT_555RGB: -#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) - fill_555rgb_pswap(dest, xpos++, r, g, b); -#else - *(unsigned short *) dest = - SWAP16((unsigned short) ( - ((r >> 3) << 10) | - ((g >> 3) << 5) | - (b >> 3))); -#endif - break; - case GDF_16BIT_565RGB: - *(unsigned short *) dest = - SWAP16((unsigned short) ( - ((r >> 3) << 11) | - ((g >> 2) << 5) | - (b >> 3))); - break; - case GDF_32BIT_X888RGB: - *(u32 *) dest = - SWAP32((u32) ( - (r << 16) | - (g << 8) | - b)); - break; - case GDF_24BIT_888RGB: -#ifdef VIDEO_FB_LITTLE_ENDIAN - dest[0] = b; - dest[1] = g; - dest[2] = r; -#else - dest[0] = r; - dest[1] = g; - dest[2] = b; -#endif - break; - } - source++; - dest += VIDEO_PIXEL_SIZE; - } - dest += skip; - } -#ifdef CONFIG_VIDEO_BMP_LOGO - free(logo_red); - free(logo_green); - free(logo_blue); -#endif -} - -static void *video_logo(void) -{ - char info[128]; - __maybe_unused int y_off = 0; - __maybe_unused ulong addr; - __maybe_unused char *s; - __maybe_unused int len, ret, space; - - splash_get_pos(&video_logo_xpos, &video_logo_ypos); - -#ifdef CONFIG_SPLASH_SCREEN - s = env_get("splashimage"); - if (s != NULL) { - ret = splash_screen_prepare(); - if (ret < 0) - return video_fb_address; - addr = hextoul(s, NULL); - - if (video_display_bitmap(addr, - video_logo_xpos, - video_logo_ypos) == 0) { - video_logo_height = 0; - return ((void *) (video_fb_address)); - } - } -#endif /* CONFIG_SPLASH_SCREEN */ - - logo_plot(video_fb_address, video_logo_xpos, video_logo_ypos); - -#ifdef CONFIG_SPLASH_SCREEN_ALIGN - /* - * when using splashpos for video_logo, skip any info - * output on video console if the logo is not at 0,0 - */ - if (video_logo_xpos || video_logo_ypos) { - /* - * video_logo_height is used in text and cursor offset - * calculations. Since the console is below the logo, - * we need to adjust the logo height - */ - if (video_logo_ypos == BMP_ALIGN_CENTER) - video_logo_height += max(0, (int)(VIDEO_VISIBLE_ROWS - - VIDEO_LOGO_HEIGHT) / 2); - else if (video_logo_ypos > 0) - video_logo_height += video_logo_ypos; - - return video_fb_address + video_logo_height * VIDEO_LINE_LEN; - } -#endif - if (board_cfb_skip()) - return 0; - - sprintf(info, " %s", version_string); - -#ifndef CONFIG_HIDE_LOGO_VERSION - space = (VIDEO_COLS - VIDEO_INFO_X) / VIDEO_FONT_WIDTH; - len = strlen(info); - - if (len > space) { - int xx = VIDEO_INFO_X, yy = VIDEO_INFO_Y; - uchar *p = (uchar *) info; - - while (len) { - if (len > space) { - video_drawchars(xx, yy, p, space); - len -= space; - - p = (uchar *)p + space; - - if (!y_off) { - xx += VIDEO_FONT_WIDTH; - space--; - } - yy += VIDEO_FONT_HEIGHT; - - y_off++; - } else { - video_drawchars(xx, yy, p, len); - len = 0; - } - } - } else - video_drawstring(VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *) info); - -#ifdef CONFIG_CONSOLE_EXTRA_INFO - { - int i, n = - ((video_logo_height - - VIDEO_FONT_HEIGHT) / VIDEO_FONT_HEIGHT); - - for (i = 1; i < n; i++) { - video_get_info_str(i, info); - if (!*info) - continue; - - len = strlen(info); - if (len > space) { - video_drawchars(VIDEO_INFO_X, - VIDEO_INFO_Y + - (i + y_off) * - VIDEO_FONT_HEIGHT, - (uchar *) info, space); - y_off++; - video_drawchars(VIDEO_INFO_X + - VIDEO_FONT_WIDTH, - VIDEO_INFO_Y + - (i + y_off) * - VIDEO_FONT_HEIGHT, - (uchar *) info + space, - len - space); - } else { - video_drawstring(VIDEO_INFO_X, - VIDEO_INFO_Y + - (i + y_off) * - VIDEO_FONT_HEIGHT, - (uchar *) info); - } - } - } -#endif -#endif - - return (video_fb_address + video_logo_height * VIDEO_LINE_LEN); -} -#endif - static int cfb_fb_is_in_dram(void) { struct bd_info *bd = gd->bd; diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 98427f4c618..de8b86bbacc 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -274,6 +274,27 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y, */ for (row = 0; row < height; row++) { switch (vid_priv->bpix) { + case VIDEO_BPP8: + if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { + u8 *dst = line + xoff; + int i; + + for (i = 0; i < width; i++) { + int val = *bits; + int out; + + if (vid_priv->colour_bg) + val = 255 - val; + out = val; + if (vid_priv->colour_fg) + *dst++ |= out; + else + *dst++ &= out; + bits++; + } + end = dst; + } + break; #ifdef CONFIG_VIDEO_BPP16 case VIDEO_BPP16: { uint16_t *dst = (uint16_t *)line + xoff; diff --git a/drivers/video/sandbox_sdl.c b/drivers/video/sandbox_sdl.c index 5956b59ce49..9081c7da62e 100644 --- a/drivers/video/sandbox_sdl.c +++ b/drivers/video/sandbox_sdl.c @@ -12,6 +12,7 @@ #include <asm/sdl.h> #include <asm/state.h> #include <asm/u-boot-sandbox.h> +#include <dm/device-internal.h> #include <dm/test.h> DECLARE_GLOBAL_DATA_PTR; @@ -43,27 +44,86 @@ static int sandbox_sdl_probe(struct udevice *dev) uc_priv->vidconsole_drv_name = plat->vidconsole_drv_name; uc_priv->font_size = plat->font_size; if (IS_ENABLED(CONFIG_VIDEO_COPY)) - uc_plat->copy_base = uc_plat->base - uc_plat->size / 2; + uc_plat->copy_base = uc_plat->base + uc_plat->size / 2; return 0; } -static int sandbox_sdl_bind(struct udevice *dev) +static void set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp) { struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev); struct sandbox_sdl_plat *plat = dev_get_plat(dev); - int ret = 0; - plat->xres = dev_read_u32_default(dev, "xres", LCD_MAX_WIDTH); - plat->yres = dev_read_u32_default(dev, "yres", LCD_MAX_HEIGHT); - plat->bpix = dev_read_u32_default(dev, "log2-depth", VIDEO_BPP16); - plat->rot = dev_read_u32_default(dev, "rotate", 0); - uc_plat->size = plat->xres * plat->yres * (1 << plat->bpix) / 8; + plat->bpix = l2bpp; + + uc_plat->size = plat->xres * plat->yres * VNBYTES(plat->bpix); + + /* + * Set up to the maximum size we'll ever need. This is a strange case. + * The video memory is allocated by video_post_bind() called from + * board_init_r(). If a test changes the reoslution so it needs more + * memory later (with sandbox_sdl_set_bpp()), it is too late to make + * the frame buffer larger. + * + * So use a maximum size here. + */ + uc_plat->size = max(uc_plat->size, 1920U * 1080 * VNBYTES(VIDEO_BPP32)); /* Allow space for two buffers, the lower one being the copy buffer */ log_debug("Frame buffer size %x\n", uc_plat->size); + + /* + * If a copy framebuffer is used, double the size and use the last half + * as the copy, with the first half as the normal frame buffer. + */ if (IS_ENABLED(CONFIG_VIDEO_COPY)) uc_plat->size *= 2; +} + +int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp) +{ + struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev); + int ret; + + if (device_active(dev)) + return -EINVAL; + sandbox_sdl_remove_display(); + + uc_plat->hide_logo = true; + set_bpp(dev, l2bpp); + + ret = device_probe(dev); + if (ret) + return ret; + + return 0; +} + +static int sandbox_sdl_remove(struct udevice *dev) +{ + /* + * Removing the display it a bit annoying when running unit tests, since + * they remove all devices. It is nice to be able to see what the test + * wrote onto the display. So this comment is just here to show how to + * do it, if we want to make it optional one day. + * + * sandbox_sdl_remove_display(); + */ + return 0; +} + +static int sandbox_sdl_bind(struct udevice *dev) +{ + struct sandbox_sdl_plat *plat = dev_get_plat(dev); + enum video_log2_bpp l2bpp; + int ret = 0; + + plat->xres = dev_read_u32_default(dev, "xres", LCD_MAX_WIDTH); + plat->yres = dev_read_u32_default(dev, "yres", LCD_MAX_HEIGHT); + l2bpp = dev_read_u32_default(dev, "log2-depth", VIDEO_BPP16); + plat->rot = dev_read_u32_default(dev, "rotate", 0); + + set_bpp(dev, l2bpp); return ret; } @@ -79,5 +139,6 @@ U_BOOT_DRIVER(sandbox_lcd_sdl) = { .of_match = sandbox_sdl_ids, .bind = sandbox_sdl_bind, .probe = sandbox_sdl_probe, + .remove = sandbox_sdl_remove, .plat_auto = sizeof(struct sandbox_sdl_plat), }; diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c index 65c882d9f12..87e5fd54d9a 100644 --- a/drivers/video/stm32/stm32_ltdc.c +++ b/drivers/video/stm32/stm32_ltdc.c @@ -459,7 +459,10 @@ static int stm32_ltdc_bind(struct udevice *dev) uc_plat->size = CONFIG_VIDEO_STM32_MAX_XRES * CONFIG_VIDEO_STM32_MAX_YRES * (CONFIG_VIDEO_STM32_MAX_BPP >> 3); - dev_dbg(dev, "frame buffer max size %d bytes\n", uc_plat->size); + /* align framebuffer on kernel MMU_SECTION_SIZE = max 2MB for LPAE */ + uc_plat->align = SZ_2M; + dev_dbg(dev, "frame buffer max size %d bytes align %x\n", + uc_plat->size, uc_plat->align); return 0; } diff --git a/drivers/video/u_boot_logo.bmp b/drivers/video/u_boot_logo.bmp Binary files differnew file mode 100644 index 00000000000..47f1e9b9978 --- /dev/null +++ b/drivers/video/u_boot_logo.bmp diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index 9f8cf6ef2a9..7d499bcec51 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -228,6 +228,20 @@ void video_sync_all(void) } } +bool video_is_active(void) +{ + struct udevice *dev; + + for (uclass_find_first_device(UCLASS_VIDEO, &dev); + dev; + uclass_find_next_device(&dev)) { + if (device_active(dev)) + return true; + } + + return false; +} + int video_get_xsize(struct udevice *dev) { struct video_priv *priv = dev_get_uclass_priv(dev); @@ -265,10 +279,10 @@ int video_sync_copy(struct udevice *dev, void *from, void *to) */ if (offset < -priv->fb_size || offset > 2 * priv->fb_size) { #ifdef DEBUG - char str[80]; + char str[120]; snprintf(str, sizeof(str), - "[sync_copy fb=%p, from=%p, to=%p, offset=%lx]", + "[** FAULT sync_copy fb=%p, from=%p, to=%p, offset=%lx]", priv->fb, from, to, offset); console_puts_select_stderr(true, str); #endif @@ -305,23 +319,20 @@ int video_sync_copy_all(struct udevice *dev) #endif -/* Set up the colour map */ -static int video_pre_probe(struct udevice *dev) -{ - struct video_priv *priv = dev_get_uclass_priv(dev); +#define SPLASH_DECL(_name) \ + extern u8 __splash_ ## _name ## _begin[]; \ + extern u8 __splash_ ## _name ## _end[] - priv->cmap = calloc(256, sizeof(ushort)); - if (!priv->cmap) - return -ENOMEM; +#define SPLASH_START(_name) __splash_ ## _name ## _begin - return 0; -} +SPLASH_DECL(u_boot_logo); -static int video_pre_remove(struct udevice *dev) +static int show_splash(struct udevice *dev) { - struct video_priv *priv = dev_get_uclass_priv(dev); + u8 *data = SPLASH_START(u_boot_logo); + int ret; - free(priv->cmap); + ret = video_bmp_display(dev, map_to_sysmem(data), -4, 4, true); return 0; } @@ -391,6 +402,14 @@ static int video_post_probe(struct udevice *dev) return ret; } + if (IS_ENABLED(CONFIG_VIDEO_LOGO) && !plat->hide_logo) { + ret = show_splash(dev); + if (ret) { + log_debug("Cannot show splash screen\n"); + return ret; + } + } + return 0; }; @@ -433,9 +452,7 @@ UCLASS_DRIVER(video) = { .name = "video", .flags = DM_UC_FLAG_SEQ_ALIAS, .post_bind = video_post_bind, - .pre_probe = video_pre_probe, .post_probe = video_post_probe, - .pre_remove = video_pre_remove, .priv_auto = sizeof(struct video_uc_priv), .per_device_auto = sizeof(struct video_priv), .per_device_plat_auto = sizeof(struct video_uc_plat), diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c index 1e6f07ff4b0..e8600b2def3 100644 --- a/drivers/video/video_bmp.c +++ b/drivers/video/video_bmp.c @@ -13,34 +13,90 @@ #include <watchdog.h> #include <asm/unaligned.h> -#ifdef CONFIG_VIDEO_BMP_RLE8 #define BMP_RLE8_ESCAPE 0 #define BMP_RLE8_EOL 0 #define BMP_RLE8_EOBMP 1 #define BMP_RLE8_DELTA 2 -static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap, +/** + * get_bmp_col_16bpp() - Convert a colour-table entry into a 16bpp pixel value + * + * @return value to write to the 16bpp frame buffer for this palette entry + */ +static uint get_bmp_col_16bpp(struct bmp_color_table_entry cte) +{ + return ((cte.red << 8) & 0xf800) | + ((cte.green << 3) & 0x07e0) | + ((cte.blue >> 3) & 0x001f); +} + +/** + * write_pix8() - Write a pixel from a BMP image into the framebuffer + * + * This handles frame buffers with 8, 16, 24 or 32 bits per pixel + * + * @fb: Place in frame buffer to update + * @bpix: Frame buffer bits-per-pixel, which controls how many bytes are written + * @palette: BMP palette table + * @bmap: Pointer to BMP bitmap position to write. This contains a single byte + * which is either written directly (bpix == 8) or used to look up the + * palette to get a colour to write + */ +static void write_pix8(u8 *fb, uint bpix, struct bmp_color_table_entry *palette, + u8 *bmap) +{ + if (bpix == 8) { + *fb++ = *bmap; + } else if (bpix == 16) { + *(u16 *)fb = get_bmp_col_16bpp(palette[*bmap]); + } else { + /* Only support big endian */ + struct bmp_color_table_entry *cte = &palette[*bmap]; + + if (bpix == 24) { + *fb++ = cte->red; + *fb++ = cte->green; + *fb++ = cte->blue; + } else { + *fb++ = cte->blue; + *fb++ = cte->green; + *fb++ = cte->red; + *fb++ = 0; + } + } +} + +static void draw_unencoded_bitmap(u8 **fbp, uint bpix, uchar *bmap, + struct bmp_color_table_entry *palette, int cnt) { + u8 *fb = *fbp; + while (cnt > 0) { - *(*fbp)++ = cmap[*bmap++]; + write_pix8(fb, bpix, palette, bmap++); + fb += bpix / 8; cnt--; } + *fbp = fb; } -static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt) +static void draw_encoded_bitmap(u8 **fbp, uint bpix, + struct bmp_color_table_entry *palette, u8 *bmap, + int cnt) { - ushort *fb = *fbp; + u8 *fb = *fbp; while (cnt > 0) { - *fb++ = col; + write_pix8(fb, bpix, palette, bmap); + fb += bpix / 8; cnt--; } *fbp = fb; } static void video_display_rle8_bitmap(struct udevice *dev, - struct bmp_image *bmp, ushort *cmap, + struct bmp_image *bmp, uint bpix, + struct bmp_color_table_entry *palette, uchar *fb, int x_off, int y_off, ulong width, ulong height) { @@ -49,6 +105,7 @@ static void video_display_rle8_bitmap(struct udevice *dev, ulong cnt, runlen; int x, y; int decode = 1; + uint bytes_per_pixel = bpix / 8; debug("%s\n", __func__); bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); @@ -64,8 +121,8 @@ static void video_display_rle8_bitmap(struct udevice *dev, bmap += 2; x = 0; y--; - /* 16bpix, 2-byte per pixel, width should *2 */ - fb -= (width * 2 + priv->line_length); + fb -= width * bytes_per_pixel + + priv->line_length; break; case BMP_RLE8_EOBMP: /* end of bitmap */ @@ -75,9 +132,9 @@ static void video_display_rle8_bitmap(struct udevice *dev, /* delta run */ x += bmap[2]; y -= bmap[3]; - /* 16bpix, 2-byte per pixel, x should *2 */ - fb = (uchar *)(priv->fb + (y + y_off - 1) - * priv->line_length + (x + x_off) * 2); + fb = (uchar *)(priv->fb + + (y + y_off - 1) * priv->line_length + + (x + x_off) * bytes_per_pixel); bmap += 4; break; default: @@ -91,8 +148,8 @@ static void video_display_rle8_bitmap(struct udevice *dev, else cnt = runlen; draw_unencoded_bitmap( - (ushort **)&fb, - bmap, cmap, cnt); + &fb, bpix, + bmap, palette, cnt); } x += runlen; } @@ -116,8 +173,8 @@ static void video_display_rle8_bitmap(struct udevice *dev, cnt = width - x; else cnt = runlen; - draw_encoded_bitmap((ushort **)&fb, - cmap[bmap[1]], cnt); + draw_encoded_bitmap(&fb, bpix, palette, + &bmap[1], cnt); } x += runlen; } @@ -125,20 +182,6 @@ static void video_display_rle8_bitmap(struct udevice *dev, } } } -#endif - -__weak void fb_put_byte(uchar **fb, uchar **from) -{ - *(*fb)++ = *(*from)++; -} - -#if defined(CONFIG_BMP_16BPP) -__weak void fb_put_word(uchar **fb, uchar **from) -{ - *(*fb)++ = *(*from)++; - *(*fb)++ = *(*from)++; -} -#endif /* CONFIG_BMP_16BPP */ /** * video_splash_align_axis() - Align a single coordinate @@ -169,28 +212,10 @@ static void video_splash_align_axis(int *axis, unsigned long panel_size, *axis = max(0, (int)axis_alignment); } -static void video_set_cmap(struct udevice *dev, - struct bmp_color_table_entry *cte, unsigned colours) -{ - struct video_priv *priv = dev_get_uclass_priv(dev); - int i; - ushort *cmap = priv->cmap; - - debug("%s: colours=%d\n", __func__, colours); - for (i = 0; i < colours; ++i) { - *cmap = ((cte->red << 8) & 0xf800) | - ((cte->green << 3) & 0x07e0) | - ((cte->blue >> 3) & 0x001f); - cmap++; - cte++; - } -} - int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, bool align) { struct video_priv *priv = dev_get_uclass_priv(dev); - ushort *cmap_base = NULL; int i, j; uchar *start, *fb; struct bmp_image *bmp = map_sysmem(bmp_image, 0); @@ -246,9 +271,6 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, debug("Display-bmp: %d x %d with %d colours, display %d\n", (int)width, (int)height, (int)colours, 1 << bpix); - if (bmp_bpix == 8) - video_set_cmap(dev, palette, colours); - padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width); if (align) { @@ -270,23 +292,19 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, switch (bmp_bpix) { case 1: - case 8: { - struct bmp_color_table_entry *cte; - cmap_base = priv->cmap; -#ifdef CONFIG_VIDEO_BMP_RLE8 - u32 compression = get_unaligned_le32(&bmp->header.compression); - debug("compressed %d %d\n", compression, BMP_BI_RLE8); - if (compression == BMP_BI_RLE8) { - if (bpix != 16) { - /* TODO implement render code for bpix != 16 */ - printf("Error: only support 16 bpix"); - return -EPROTONOSUPPORT; + case 8: + if (IS_ENABLED(CONFIG_VIDEO_BMP_RLE8)) { + u32 compression = get_unaligned_le32( + &bmp->header.compression); + debug("compressed %d %d\n", compression, BMP_BI_RLE8); + if (compression == BMP_BI_RLE8) { + video_display_rle8_bitmap(dev, bmp, bpix, palette, fb, + x, y, width, height); + break; } - video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x, - y, width, height); - break; } -#endif + + /* Not compressed */ byte_width = width * (bpix / 8); if (!byte_width) byte_width = width; @@ -294,81 +312,64 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, for (i = 0; i < height; ++i) { WATCHDOG_RESET(); for (j = 0; j < width; j++) { - if (bpix == 8) { - fb_put_byte(&fb, &bmap); - } else if (bpix == 16) { - *(uint16_t *)fb = cmap_base[*bmap]; - bmap++; - fb += sizeof(uint16_t) / sizeof(*fb); - } else { - /* Only support big endian */ - cte = &palette[*bmap]; - bmap++; - if (bpix == 24) { - *(fb++) = cte->red; - *(fb++) = cte->green; - *(fb++) = cte->blue; - } else { - *(fb++) = cte->blue; - *(fb++) = cte->green; - *(fb++) = cte->red; - *(fb++) = 0; - } - } + write_pix8(fb, bpix, palette, bmap); + bmap++; + fb += bpix / 8; } bmap += (padded_width - width); fb -= byte_width + priv->line_length; } break; - } -#if defined(CONFIG_BMP_16BPP) case 16: - for (i = 0; i < height; ++i) { - WATCHDOG_RESET(); - for (j = 0; j < width; j++) - fb_put_word(&fb, &bmap); - - bmap += (padded_width - width); - fb -= width * 2 + priv->line_length; + if (IS_ENABLED(CONFIG_BMP_16BPP)) { + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) { + *fb++ = *bmap++; + *fb++ = *bmap++; + } + bmap += (padded_width - width); + fb -= width * 2 + priv->line_length; + } } break; -#endif /* CONFIG_BMP_16BPP */ -#if defined(CONFIG_BMP_24BPP) case 24: - for (i = 0; i < height; ++i) { - for (j = 0; j < width; j++) { - if (bpix == 16) { - /* 16bit 555RGB format */ - *(u16 *)fb = ((bmap[2] >> 3) << 10) | - ((bmap[1] >> 3) << 5) | - (bmap[0] >> 3); - bmap += 3; - fb += 2; - } else { - *(fb++) = *(bmap++); - *(fb++) = *(bmap++); - *(fb++) = *(bmap++); - *(fb++) = 0; + if (IS_ENABLED(CONFIG_BMP_24BPP)) { + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + if (bpix == 16) { + /* 16bit 565RGB format */ + *(u16 *)fb = ((bmap[2] >> 3) + << 11) | + ((bmap[1] >> 2) << 5) | + (bmap[0] >> 3); + bmap += 3; + fb += 2; + } else { + *fb++ = *bmap++; + *fb++ = *bmap++; + *fb++ = *bmap++; + *fb++ = 0; + } } + fb -= priv->line_length + width * (bpix / 8); + bmap += (padded_width - width); } - fb -= priv->line_length + width * (bpix / 8); - bmap += (padded_width - width); } break; -#endif /* CONFIG_BMP_24BPP */ -#if defined(CONFIG_BMP_32BPP) case 32: - for (i = 0; i < height; ++i) { - for (j = 0; j < width; j++) { - *(fb++) = *(bmap++); - *(fb++) = *(bmap++); - *(fb++) = *(bmap++); - *(fb++) = *(bmap++); + if (IS_ENABLED(CONFIG_BMP_32BPP)) { + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *fb++ = *bmap++; + *fb++ = *bmap++; + *fb++ = *bmap++; + *fb++ = *bmap++; + } + fb -= priv->line_length + width * (bpix / 8); } - fb -= priv->line_length + width * (bpix / 8); } break; -#endif /* CONFIG_BMP_32BPP */ default: break; }; |