diff options
Diffstat (limited to 'drivers')
49 files changed, 1395 insertions, 134 deletions
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 094b1abf13c..d245b672fae 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -57,7 +57,7 @@ static int clk_of_xlate_default(struct clk *clk, debug("%s(clk=%p)\n", __func__, clk); if (args->args_count > 1) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/clk/clk_stm32f.c b/drivers/clk/clk_stm32f.c index e7c26db51c9..ed7660196ef 100644 --- a/drivers/clk/clk_stm32f.c +++ b/drivers/clk/clk_stm32f.c @@ -703,7 +703,7 @@ static int stm32_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) dev_dbg(clk->dev, "clk=%p\n", clk); if (args->args_count != 2) { - dev_dbg(clk->dev, "Invaild args_count: %d\n", args->args_count); + dev_dbg(clk->dev, "Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/clk/clk_stm32h7.c b/drivers/clk/clk_stm32h7.c index 20b36470994..d440c28eb48 100644 --- a/drivers/clk/clk_stm32h7.c +++ b/drivers/clk/clk_stm32h7.c @@ -835,7 +835,7 @@ static int stm32_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) { if (args->args_count != 1) { - dev_dbg(clk->dev, "Invaild args_count: %d\n", args->args_count); + dev_dbg(clk->dev, "Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/clk/clk_versaclock.c b/drivers/clk/clk_versaclock.c index 578668bcf83..89c8d02336a 100644 --- a/drivers/clk/clk_versaclock.c +++ b/drivers/clk/clk_versaclock.c @@ -627,7 +627,7 @@ static int vc5_clk_out_xlate(struct clk *hw, struct ofnode_phandle_args *args) unsigned int idx = args->args[0]; if (args->args_count != 1) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c index d2d0169dd87..3a68c5ad0eb 100644 --- a/drivers/clk/renesas/clk-rcar-gen2.c +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -256,7 +256,7 @@ static ulong gen2_clk_set_rate(struct clk *clk, ulong rate) static int gen2_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) { if (args->args_count != 2) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index 6cf07fb4187..bcf5865222f 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -365,7 +365,7 @@ static ulong gen3_clk_set_rate(struct clk *clk, ulong rate) static int gen3_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) { if (args->args_count != 2) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/clk/ti/clk-ctrl.c b/drivers/clk/ti/clk-ctrl.c index 8ac085ee4f3..6cc02d2eeaa 100644 --- a/drivers/clk/ti/clk-ctrl.c +++ b/drivers/clk/ti/clk-ctrl.c @@ -83,7 +83,7 @@ static int clk_ti_ctrl_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) { if (args->args_count != 2) { - dev_err(clk->dev, "invaild args_count: %d\n", args->args_count); + dev_err(clk->dev, "invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/core/device.c b/drivers/core/device.c index 4873c47d10b..d917d4e82da 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -518,6 +518,14 @@ int device_probe(struct udevice *dev) dev_or_flags(dev, DM_FLAG_ACTIVATED); + if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent && + (device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) && + !(drv->flags & DM_FLAG_DEFAULT_PD_CTRL_OFF)) { + ret = dev_power_domain_on(dev); + if (ret) + goto fail; + } + /* * Process pinctrl for everything except the root device, and * continue regardless of the result of pinctrl. Don't process pinctrl @@ -540,14 +548,6 @@ int device_probe(struct udevice *dev) dev->name, ret, errno_str(ret)); } - if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent && - (device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) && - !(drv->flags & DM_FLAG_DEFAULT_PD_CTRL_OFF)) { - ret = dev_power_domain_on(dev); - if (ret) - goto fail; - } - if (CONFIG_IS_ENABLED(IOMMU) && dev->parent && (device_get_uclass_id(dev) != UCLASS_IOMMU)) { ret = dev_iommu_enable(dev); diff --git a/drivers/core/of_addr.c b/drivers/core/of_addr.c index 3fbc0a7afa6..431dd4e565d 100644 --- a/drivers/core/of_addr.c +++ b/drivers/core/of_addr.c @@ -367,7 +367,7 @@ int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu, /* switch to that node */ parent = of_get_parent(dev); if (!parent) { - printf("Found dma-ranges in root node, shoudln't happen\n"); + printf("Found dma-ranges in root node, shouldn't happen\n"); ret = -EINVAL; goto out; } diff --git a/drivers/ddr/altera/sequencer.c b/drivers/ddr/altera/sequencer.c index 8a016f0628f..e402f2929ab 100644 --- a/drivers/ddr/altera/sequencer.c +++ b/drivers/ddr/altera/sequencer.c @@ -2770,7 +2770,7 @@ rw_mgr_mem_calibrate_dqs_enable_calibration(struct socfpga_sdrseq *seq, ret = rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase(seq, rw_group); debug_cond(DLEVEL >= 1, - "%s:%d: g=%u found=%u; Reseting delay chain to zero\n", + "%s:%d: g=%u found=%u; Resetting delay chain to zero\n", __func__, __LINE__, rw_group, !ret); for (r = 0; r < seq->rwcfg->mem_number_of_ranks; diff --git a/drivers/ddr/marvell/a38x/ddr3_training_centralization.c b/drivers/ddr/marvell/a38x/ddr3_training_centralization.c index 648b37ef6f7..42308b6965d 100644 --- a/drivers/ddr/marvell/a38x/ddr3_training_centralization.c +++ b/drivers/ddr/marvell/a38x/ddr3_training_centralization.c @@ -55,6 +55,7 @@ static int ddr3_tip_centralization(u32 dev_num, u32 mode) enum hws_training_ip_stat training_result[MAX_INTERFACE_NUM]; u32 if_id, pattern_id, bit_id; u8 bus_id; + u8 current_byte_status; u8 cur_start_win[BUS_WIDTH_IN_BITS]; u8 centralization_result[MAX_INTERFACE_NUM][BUS_WIDTH_IN_BITS]; u8 cur_end_win[BUS_WIDTH_IN_BITS]; @@ -166,6 +167,10 @@ static int ddr3_tip_centralization(u32 dev_num, u32 mode) result[search_dir_id][7])); } + current_byte_status = + mv_ddr_tip_sub_phy_byte_status_get(if_id, + bus_id); + for (bit_id = 0; bit_id < BUS_WIDTH_IN_BITS; bit_id++) { /* check if this code is valid for 2 edge, probably not :( */ @@ -174,11 +179,32 @@ static int ddr3_tip_centralization(u32 dev_num, u32 mode) [HWS_LOW2HIGH] [bit_id], EDGE_1); + if (current_byte_status & + BYTE_SPLIT_OUT_MIX) { + if (cur_start_win[bit_id] >= 64) + cur_start_win[bit_id] -= 64; + else + cur_start_win[bit_id] = 0; + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_INFO, + ("pattern %d IF %d pup %d bit %d subtract 64 adll from start\n", + pattern_id, if_id, bus_id, bit_id)); + } cur_end_win[bit_id] = GET_TAP_RESULT(result [HWS_HIGH2LOW] [bit_id], EDGE_1); + if (cur_end_win[bit_id] >= 64 && + (current_byte_status & + BYTE_SPLIT_OUT_MIX)) { + cur_end_win[bit_id] -= 64; + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_INFO, + ("pattern %d IF %d pup %d bit %d subtract 64 adll from end\n", + pattern_id, if_id, bus_id, bit_id)); + } + /* window length */ current_window[bit_id] = cur_end_win[bit_id] - diff --git a/drivers/ddr/marvell/a38x/mv_ddr_plat.c b/drivers/ddr/marvell/a38x/mv_ddr_plat.c index faafc86ea26..7c7bce73a35 100644 --- a/drivers/ddr/marvell/a38x/mv_ddr_plat.c +++ b/drivers/ddr/marvell/a38x/mv_ddr_plat.c @@ -167,8 +167,6 @@ static u16 a38x_vco_freq_per_sar_ref_clk_40_mhz[] = { }; -static u32 async_mode_at_tf; - static u32 dq_bit_map_2_phy_pin[] = { 1, 0, 2, 6, 9, 8, 3, 7, /* 0 */ 8, 9, 1, 7, 2, 6, 3, 0, /* 1 */ @@ -734,7 +732,8 @@ static int ddr3_tip_a38x_set_divider(u8 dev_num, u32 if_id, u32 divider = 0; u32 sar_val, ref_clk_satr; u32 async_val; - u32 freq = mv_ddr_freq_get(frequency); + u32 cpu_freq; + u32 ddr_freq = mv_ddr_freq_get(frequency); if (if_id != 0) { DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR, @@ -751,11 +750,14 @@ static int ddr3_tip_a38x_set_divider(u8 dev_num, u32 if_id, ref_clk_satr = reg_read(DEVICE_SAMPLE_AT_RESET2_REG); if (((ref_clk_satr >> DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_OFFSET) & 0x1) == DEVICE_SAMPLE_AT_RESET2_REG_REFCLK_25MHZ) - divider = a38x_vco_freq_per_sar_ref_clk_25_mhz[sar_val] / freq; + cpu_freq = a38x_vco_freq_per_sar_ref_clk_25_mhz[sar_val]; else - divider = a38x_vco_freq_per_sar_ref_clk_40_mhz[sar_val] / freq; + cpu_freq = a38x_vco_freq_per_sar_ref_clk_40_mhz[sar_val]; + + divider = cpu_freq / ddr_freq; - if ((async_mode_at_tf == 1) && (freq > 400)) { + if (((cpu_freq % ddr_freq != 0) || (divider != 2 && divider != 3)) && + (ddr_freq > 400)) { /* Set async mode */ dunit_write(0x20220, 0x1000, 0x1000); dunit_write(0xe42f4, 0x200, 0x200); @@ -869,8 +871,6 @@ int ddr3_tip_ext_write(u32 dev_num, u32 if_id, u32 reg_addr, int mv_ddr_early_init(void) { - struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get(); - /* FIXME: change this configuration per ddr type * configure a380 and a390 to work with receiver odt timing * the odt_config is defined: @@ -882,9 +882,6 @@ int mv_ddr_early_init(void) mv_ddr_sw_db_init(0, 0); - if (tm->interface_params[0].memory_freq != MV_DDR_FREQ_SAR) - async_mode_at_tf = 1; - return MV_OK; } diff --git a/drivers/dma/dma-uclass.c b/drivers/dma/dma-uclass.c index 652ddbb62b8..012609bb537 100644 --- a/drivers/dma/dma-uclass.c +++ b/drivers/dma/dma-uclass.c @@ -35,7 +35,7 @@ static int dma_of_xlate_default(struct dma *dma, debug("%s(dma=%p)\n", __func__, dma); if (args->args_count > 1) { - pr_err("Invaild args_count: %d\n", args->args_count); + pr_err("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index d5e4a02098a..b97c67bf609 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -21,6 +21,13 @@ config UDP_FUNCTION_FASTBOOT help This enables the fastboot protocol over UDP. +config UDP_FUNCTION_FASTBOOT_PORT + depends on UDP_FUNCTION_FASTBOOT + int "Define FASTBOOT UDP port" + default 5554 + help + The fastboot protocol requires a UDP port number. + if FASTBOOT config FASTBOOT_BUF_ADDR diff --git a/drivers/hwspinlock/hwspinlock-uclass.c b/drivers/hwspinlock/hwspinlock-uclass.c index cbe72360117..e012d5a4c93 100644 --- a/drivers/hwspinlock/hwspinlock-uclass.c +++ b/drivers/hwspinlock/hwspinlock-uclass.c @@ -25,7 +25,7 @@ static int hwspinlock_of_xlate_default(struct hwspinlock *hws, struct ofnode_phandle_args *args) { if (args->args_count > 1) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/mailbox/k3-sec-proxy.c b/drivers/mailbox/k3-sec-proxy.c index 20fdb09f314..a862e55bc39 100644 --- a/drivers/mailbox/k3-sec-proxy.c +++ b/drivers/mailbox/k3-sec-proxy.c @@ -116,7 +116,7 @@ static int k3_sec_proxy_of_xlate(struct mbox_chan *chan, debug("%s(chan=%p)\n", __func__, chan); if (args->args_count != 1) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } ind = args->args[0]; diff --git a/drivers/mailbox/mailbox-uclass.c b/drivers/mailbox/mailbox-uclass.c index 01c9e75fa5b..85ba8c5fd99 100644 --- a/drivers/mailbox/mailbox-uclass.c +++ b/drivers/mailbox/mailbox-uclass.c @@ -24,7 +24,7 @@ static int mbox_of_xlate_default(struct mbox_chan *chan, debug("%s(chan=%p)\n", __func__, chan); if (args->args_count != 1) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index 1d66d95fe48..08c51c40f14 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -77,7 +77,7 @@ static int tegra_hsp_of_xlate(struct mbox_chan *chan, debug("%s(chan=%p)\n", __func__, chan); if (args->args_count != 2) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c index eb9f3b902f4..7b79ed2df46 100644 --- a/drivers/misc/irq-uclass.c +++ b/drivers/misc/irq-uclass.c @@ -89,7 +89,7 @@ static int irq_of_xlate_default(struct irq *irq, log_debug("(irq=%p)\n", irq); if (args->args_count > 1) { - log_debug("Invaild args_count: %d\n", args->args_count); + log_debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index f8434ca88db..74c9348f7fc 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -1632,7 +1632,7 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, mtd->oobsize / trans, host->hwcfg.sector_size_1k); - if (!ret) { + if (ret != -EBADMSG) { *err_addr = brcmnand_read_reg(ctrl, BRCMNAND_UNCORR_ADDR) | ((u64)(brcmnand_read_reg(ctrl, diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c index 8ff58a70387..3a9c9ca508d 100644 --- a/drivers/mtd/nand/raw/pxa3xx_nand.c +++ b/drivers/mtd/nand/raw/pxa3xx_nand.c @@ -1862,10 +1862,10 @@ static int pxa3xx_nand_probe_dt(struct udevice *dev, struct pxa3xx_nand_info *in return -EINVAL; } - if (dev_read_bool(dev, "nand-enable-arbiter")) + if (dev_read_bool(dev, "marvell,nand-enable-arbiter")) pdata->enable_arbiter = 1; - if (dev_read_bool(dev, "nand-keep-config")) + if (dev_read_bool(dev, "marvell,nand-keep-config")) pdata->keep_config = 1; /* diff --git a/drivers/mtd/ubispl/ubispl.c b/drivers/mtd/ubispl/ubispl.c index 03b31f002b9..b58d8e8d565 100644 --- a/drivers/mtd/ubispl/ubispl.c +++ b/drivers/mtd/ubispl/ubispl.c @@ -953,7 +953,7 @@ retry: * Check, if the total number of blocks is correct */ if (be32_to_cpu(vh->used_ebs) != last) { - ubi_dbg("Block count missmatch."); + ubi_dbg("Block count mismatch."); ubi_dbg("vh->used_ebs: %d nrblocks: %d", be32_to_cpu(vh->used_ebs), last); generic_set_bit(pnum, ubi->corrupt); diff --git a/drivers/mux/mux-uclass.c b/drivers/mux/mux-uclass.c index 91842c5539f..8870305313a 100644 --- a/drivers/mux/mux-uclass.c +++ b/drivers/mux/mux-uclass.c @@ -130,7 +130,7 @@ static int mux_of_xlate_default(struct mux_chip *mux_chip, log_debug("%s(muxp=%p)\n", __func__, muxp); if (args->args_count > 1) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/net/eth-phy-uclass.c b/drivers/net/eth-phy-uclass.c index a9b358ee234..1f285f7afd2 100644 --- a/drivers/net/eth-phy-uclass.c +++ b/drivers/net/eth-phy-uclass.c @@ -103,7 +103,7 @@ struct mii_dev *eth_phy_get_mdio_bus(struct udevice *eth_dev) return uc_priv->mdio_bus; } } else { - log_notice("FEC: can't find phy-handle\n"); + log_debug("Can't find phy-handle for %s\n", eth_dev->name); } return NULL; diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 40a86a3e12f..811bc275c1c 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -1465,7 +1465,7 @@ static int fecmxc_probe(struct udevice *dev) start = get_timer(0); while (readl(&priv->eth->ecntrl) & FEC_ECNTRL_RESET) { if (get_timer(start) > (CONFIG_SYS_HZ * 5)) { - printf("FEC MXC: Timeout reseting chip\n"); + printf("FEC MXC: Timeout resetting chip\n"); goto err_timeout; } udelay(10); diff --git a/drivers/net/fm/memac_phy.c b/drivers/net/fm/memac_phy.c index 72b500a6d14..3ddae97e097 100644 --- a/drivers/net/fm/memac_phy.c +++ b/drivers/net/fm/memac_phy.c @@ -64,7 +64,7 @@ static int memac_wait_until_done(struct memac_mdio_controller *regs) { unsigned int timeout = MAX_NUM_RETRIES; - while ((memac_in_32(®s->mdio_data) & MDIO_DATA_BSY) && timeout--) + while ((memac_in_32(®s->mdio_stat) & MDIO_STAT_BSY) && timeout--) ; if (!timeout) { diff --git a/drivers/net/fsl_ls_mdio.c b/drivers/net/fsl_ls_mdio.c index 6d4e682fdfc..f213e0dd859 100644 --- a/drivers/net/fsl_ls_mdio.c +++ b/drivers/net/fsl_ls_mdio.c @@ -84,7 +84,7 @@ static int dm_fsl_ls_mdio_read(struct udevice *dev, int addr, memac_out_32(®s->mdio_ctl, mdio_ctl); /* Wait till the MDIO write is complete */ - while ((memac_in_32(®s->mdio_data)) & MDIO_DATA_BSY) + while ((memac_in_32(®s->mdio_stat)) & MDIO_STAT_BSY) ; /* Return all Fs if nothing was there */ @@ -107,7 +107,7 @@ static int dm_fsl_ls_mdio_write(struct udevice *dev, int addr, int devad, memac_out_32(®s->mdio_data, MDIO_DATA(val)); /* Wait till the MDIO write is complete */ - while ((memac_in_32(®s->mdio_data)) & MDIO_DATA_BSY) + while ((memac_in_32(®s->mdio_stat)) & MDIO_STAT_BSY) ; return 0; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index e69cd8a4b31..4f8d33ce8fd 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -280,6 +280,12 @@ config PHY_TI_DP83867 ---help--- Adds support for the TI DP83867 1Gbit PHY. +config PHY_TI_DP83869 + select PHY_TI + bool "Texas Instruments Ethernet DP83869 PHY support" + ---help--- + Adds support for the TI DP83869 1Gbit PHY. + config PHY_TI_GENERIC select PHY_TI bool "Texas Instruments Generic Ethernet PHYs support" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 218b8c7669b..77f7f606215 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_PHY_SMSC) += smsc.o obj-$(CONFIG_PHY_TERANETICS) += teranetics.o obj-$(CONFIG_PHY_TI) += ti_phy_init.o obj-$(CONFIG_PHY_TI_DP83867) += dp83867.o +obj-$(CONFIG_PHY_TI_DP83869) += dp83869.o obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o obj-$(CONFIG_PHY_VITESSE) += vitesse.o diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c new file mode 100644 index 00000000000..c9461185cfe --- /dev/null +++ b/drivers/net/phy/dp83869.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI PHY drivers + * + */ + +#include <common.h> +#include <phy.h> +#include <linux/compat.h> +#include <malloc.h> + +#include <dm.h> +#include <dt-bindings/net/ti-dp83869.h> + +/* TI DP83869 */ +#define DP83869_DEVADDR 0x1f + +#define MII_DP83869_PHYCTRL 0x10 +#define MII_DP83869_MICR 0x12 +#define MII_DP83869_CFG2 0x14 +#define MII_DP83869_BISCR 0x16 +#define DP83869_CTRL 0x1f +#define DP83869_CFG4 0x1e + +/* Extended Registers */ +#define DP83869_GEN_CFG3 0x0031 +#define DP83869_RGMIICTL 0x0032 +#define DP83869_STRAP_STS1 0x006E +#define DP83869_RGMIIDCTL 0x0086 +#define DP83869_IO_MUX_CFG 0x0170 +#define DP83869_OP_MODE 0x01df +#define DP83869_FX_CTRL 0x0c00 + +#define DP83869_SW_RESET BIT(15) +#define DP83869_SW_RESTART BIT(14) + +/* MICR Interrupt bits */ +#define MII_DP83869_MICR_AN_ERR_INT_EN BIT(15) +#define MII_DP83869_MICR_SPEED_CHNG_INT_EN BIT(14) +#define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN BIT(13) +#define MII_DP83869_MICR_PAGE_RXD_INT_EN BIT(12) +#define MII_DP83869_MICR_AUTONEG_COMP_INT_EN BIT(11) +#define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN BIT(10) +#define MII_DP83869_MICR_FALSE_CARRIER_INT_EN BIT(8) +#define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4) +#define MII_DP83869_MICR_WOL_INT_EN BIT(3) +#define MII_DP83869_MICR_XGMII_ERR_INT_EN BIT(2) +#define MII_DP83869_MICR_POL_CHNG_INT_EN BIT(1) +#define MII_DP83869_MICR_JABBER_INT_EN BIT(0) + +#define MII_DP83869_BMCR_DEFAULT (BMCR_ANENABLE | \ + BMCR_FULLDPLX | \ + BMCR_SPEED1000) + +/* This is the same bit mask as the BMCR so re-use the BMCR default */ +#define DP83869_FX_CTRL_DEFAULT MII_DP83869_BMCR_DEFAULT + +/* CFG1 bits */ +#define DP83869_CFG1_DEFAULT (ADVERTISE_1000HALF | \ + ADVERTISE_1000FULL | \ + CTL1000_AS_MASTER) + +/* RGMIICTL bits */ +#define DP83869_RGMII_TX_CLK_DELAY_EN BIT(1) +#define DP83869_RGMII_RX_CLK_DELAY_EN BIT(0) + +/* STRAP_STS1 bits */ +#define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0) +#define DP83869_STRAP_STS1_RESERVED BIT(11) +#define DP83869_STRAP_MIRROR_ENABLED BIT(12) + +/* PHY CTRL bits */ +#define DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT 12 +#define DP83869_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12) +#define DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT 14 +#define DP83869_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14) +#define DP83869_PHYCR_RESERVED_MASK BIT(11) +#define DP83869_PHYCR_MDI_CROSSOVER_SHIFT 5 +#define DP83869_PHYCR_MDI_CROSSOVER_MDIX 2 +#define DP83869_PHY_CTRL_DEFAULT 0x48 + +/* RGMIIDCTL bits */ +#define DP83869_RGMII_TX_CLK_DELAY_SHIFT 4 +#define DP83869_CLK_DELAY_DEF 7 + +/* CFG2 bits */ +#define MII_DP83869_CFG2_SPEEDOPT_10EN 0x0040 +#define MII_DP83869_CFG2_SGMII_AUTONEGEN 0x0080 +#define MII_DP83869_CFG2_SPEEDOPT_ENH 0x0100 +#define MII_DP83869_CFG2_SPEEDOPT_CNT 0x0800 +#define MII_DP83869_CFG2_SPEEDOPT_INTLOW 0x2000 +#define MII_DP83869_CFG2_MASK 0x003F + +/* User setting - can be taken from DTS */ +#define DEFAULT_FIFO_DEPTH DP83869_PHYCR_FIFO_DEPTH_4_B_NIB + +/* IO_MUX_CFG bits */ +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f + +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 +#define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f +#define DP83869_IO_MUX_CFG_CLK_O_DISABLE BIT(6) +#define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 +#define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK \ + GENMASK(0x1f, DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT) + +/* CFG3 bits */ +#define DP83869_CFG3_PORT_MIRROR_EN BIT(0) + +/* OP MODE bits */ +#define DP83869_OP_MODE_MII BIT(5) +#define DP83869_SGMII_RGMII_BRIDGE BIT(6) + +enum { + DP83869_PORT_MIRRORING_KEEP, + DP83869_PORT_MIRRORING_EN, + DP83869_PORT_MIRRORING_DIS, +}; + +struct dp83869_private { + int tx_fifo_depth; + int rx_fifo_depth; + s32 rx_int_delay; + s32 tx_int_delay; + int io_impedance; + int port_mirroring; + bool set_clk_output; + int clk_output_sel; + int mode; +}; + +static int dp83869_readext(struct phy_device *phydev, int addr, int devad, int reg) +{ + return phy_read_mmd(phydev, devad, reg); +} + +static int dp83869_writeext(struct phy_device *phydev, int addr, int devad, int reg, u16 val) +{ + return phy_write_mmd(phydev, devad, reg, val); +} + +static int dp83869_config_port_mirroring(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = + (struct dp83869_private *)phydev->priv; + u16 val; + + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_CFG4); + + if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN) + val |= DP83869_CFG3_PORT_MIRROR_EN; + else + val &= ~DP83869_CFG3_PORT_MIRROR_EN; + + phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_CFG4, val); + + return 0; +} + +#ifdef CONFIG_DM_ETH +static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500, + 1750, 2000, 2250, 2500, 2750, 3000, + 3250, 3500, 3750, 4000}; + +static int dp83869_set_strapped_mode(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + int val; + + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); + if (val < 0) + return val; + + dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK; + + return 0; +} + +/** + * dp83869_data_init - Convenience function for setting PHY specific data + * + * @phydev: the phy_device struct + */ +static int dp83869_of_init(struct phy_device *phydev) +{ + struct dp83869_private * const dp83869 = phydev->priv; + const int delay_entries = ARRAY_SIZE(dp83869_internal_delay); + int ret; + ofnode node; + + node = phy_get_ofnode(phydev); + if (!ofnode_valid(node)) + return -EINVAL; + + dp83869->io_impedance = -EINVAL; + + /* Optional configuration, set to default if required */ + dp83869->clk_output_sel = ofnode_read_u32_default(node, "ti,clk-output-sel", + DP83869_CLK_O_SEL_CHN_A_RCLK); + + if (dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK && + dp83869->clk_output_sel != DP83869_CLK_O_SEL_OFF) + dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK; + + /* If operation mode is not set use setting from straps */ + ret = ofnode_read_s32(node, "ti,op-mode", &dp83869->mode); + if (ret == 0) { + if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || + dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) + return -EINVAL; + } else { + ret = dp83869_set_strapped_mode(phydev); + if (ret) + return ret; + } + + if (ofnode_read_bool(node, "ti,max-output-impedance")) + dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX; + else if (ofnode_read_bool(node, "ti,min-output-impedance")) + dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN; + + if (ofnode_read_bool(node, "enet-phy-lane-swap")) { + dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; + } else { + ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1); + + if (ret < 0) + return ret; + + if (ret & DP83869_STRAP_MIRROR_ENABLED) + dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN; + else + dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS; + } + + dp83869->rx_fifo_depth = ofnode_read_s32_default(node, "rx-fifo-depth", + DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); + + dp83869->tx_fifo_depth = ofnode_read_s32_default(node, "tx-fifo-depth", + DP83869_PHYCR_FIFO_DEPTH_4_B_NIB); + + /* RX delay *must* be specified if internal delay of RX is used. */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + dp83869->rx_int_delay = ofnode_read_u32_default(node, "rx-internal-delay-ps", + DP83869_CLK_DELAY_DEF); + if (dp83869->rx_int_delay > delay_entries) { + dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF; + pr_debug("rx-internal-delay-ps not set/invalid, default to %ups\n", + dp83869_internal_delay[dp83869->rx_int_delay]); + } + + dp83869->rx_int_delay = dp83869_internal_delay[dp83869->rx_int_delay]; + } + + /* TX delay *must* be specified if internal delay of RX is used. */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + dp83869->tx_int_delay = ofnode_read_u32_default(node, "tx-internal-delay-ps", + DP83869_CLK_DELAY_DEF); + if (dp83869->tx_int_delay > delay_entries) { + dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF; + pr_debug("tx-internal-delay-ps not set/invalid, default to %ups\n", + dp83869_internal_delay[dp83869->tx_int_delay]); + } + + dp83869->tx_int_delay = dp83869_internal_delay[dp83869->tx_int_delay]; + } + + return 0; +} +#else +static int dp83869_of_init(struct phy_device *phydev) +{ + struct dp83869_private *dp83869 = phydev->priv; + + dp83869->rx_int_delay = DP83869_RGMIIDCTL_2_25_NS; + dp83869->tx_int_delay = DP83869_RGMIIDCTL_2_75_NS; + dp83869->fifo_depth = DEFAULT_FIFO_DEPTH; + dp83869->io_impedance = -EINVAL; + + return 0; +} +#endif /* CONFIG_OF_MDIO */ + +static int dp83869_configure_rgmii(struct phy_device *phydev, + struct dp83869_private *dp83869) +{ + int ret = 0, val; + + if (phy_interface_is_rgmii(phydev)) { + val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL); + if (val < 0) + return val; + + val &= ~(DP83869_PHYCR_TX_FIFO_DEPTH_MASK | DP83869_PHYCR_RX_FIFO_DEPTH_MASK); + val |= (dp83869->tx_fifo_depth << DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT); + val |= (dp83869->rx_fifo_depth << DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT); + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, val); + if (ret) + return ret; + } + + if (dp83869->io_impedance >= 0) { + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG); + + val &= ~DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL; + val |= dp83869->io_impedance & DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, val); + + if (ret) + return ret; + } + + return ret; +} + +static int dp83869_configure_mode(struct phy_device *phydev, + struct dp83869_private *dp83869) +{ + int phy_ctrl_val; + int ret, val; + + if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET || + dp83869->mode > DP83869_SGMII_COPPER_ETHERNET) + return -EINVAL; + + /* Below init sequence for each operational mode is defined in + * section 9.4.8 of the datasheet. + */ + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, + dp83869->mode); + if (ret) + return ret; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, MII_DP83869_BMCR_DEFAULT); + if (ret) + return ret; + + phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_PHYCR_RX_FIFO_DEPTH_SHIFT | + dp83869->tx_fifo_depth << DP83869_PHYCR_TX_FIFO_DEPTH_SHIFT | + DP83869_PHY_CTRL_DEFAULT); + + switch (dp83869->mode) { + case DP83869_RGMII_COPPER_ETHERNET: + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, + phy_ctrl_val); + if (ret) + return ret; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, DP83869_CFG1_DEFAULT); + if (ret) + return ret; + + ret = dp83869_configure_rgmii(phydev, dp83869); + if (ret) + return ret; + break; + case DP83869_RGMII_SGMII_BRIDGE: + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE); + + val |= DP83869_SGMII_RGMII_BRIDGE; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, val); + + if (ret) + return ret; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); + if (ret) + return ret; + + break; + case DP83869_1000M_MEDIA_CONVERT: + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, + phy_ctrl_val); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); + if (ret) + return ret; + break; + case DP83869_100M_MEDIA_CONVERT: + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, + phy_ctrl_val); + if (ret) + return ret; + break; + case DP83869_SGMII_COPPER_ETHERNET: + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83869_PHYCTRL, + phy_ctrl_val); + if (ret) + return ret; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, DP83869_CFG1_DEFAULT); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, + DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + return ret; +} + +static int dp83869_config(struct phy_device *phydev) +{ + struct dp83869_private *dp83869; + unsigned int val; + int ret; + + dp83869 = (struct dp83869_private *)phydev->priv; + + ret = dp83869_of_init(phydev); + if (ret) + return ret; + + ret = dp83869_configure_mode(phydev, dp83869); + if (ret) + return ret; + + if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP) + dp83869_config_port_mirroring(phydev); + + /* Clock output selection if muxing property is set */ + if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) { + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG); + + val &= ~DP83869_IO_MUX_CFG_CLK_O_SEL_MASK; + val |= dp83869->clk_output_sel << DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, val); + + if (ret) + return ret; + } + + if (phy_interface_is_rgmii(phydev)) { + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, + dp83869->rx_int_delay | + dp83869->tx_int_delay << DP83869_RGMII_TX_CLK_DELAY_SHIFT); + if (ret) + return ret; + + val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL); + val |= (DP83869_RGMII_TX_CLK_DELAY_EN | + DP83869_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN | + DP83869_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + val &= ~DP83869_RGMII_TX_CLK_DELAY_EN; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + val &= ~DP83869_RGMII_RX_CLK_DELAY_EN; + + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL, + val); + } + + genphy_config_aneg(phydev); + return 0; +} + +static int dp83869_probe(struct phy_device *phydev) +{ + struct dp83869_private *dp83869; + + dp83869 = kzalloc(sizeof(*dp83869), GFP_KERNEL); + if (!dp83869) + return -ENOMEM; + + phydev->priv = dp83869; + return 0; +} + +static struct phy_driver DP83869_driver = { + .name = "TI DP83869", + .uid = 0x2000a0f1, + .mask = 0xfffffff0, + .features = PHY_GBIT_FEATURES, + .probe = dp83869_probe, + .config = &dp83869_config, + .startup = &genphy_startup, + .shutdown = &genphy_shutdown, + .readext = dp83869_readext, + .writeext = dp83869_writeext +}; + +int phy_dp83869_init(void) +{ + phy_register(&DP83869_driver); + return 0; +} diff --git a/drivers/net/phy/ti_phy_init.c b/drivers/net/phy/ti_phy_init.c index 50eff77692f..075b19a39f0 100644 --- a/drivers/net/phy/ti_phy_init.c +++ b/drivers/net/phy/ti_phy_init.c @@ -88,6 +88,10 @@ int phy_ti_init(void) phy_dp83867_init(); #endif +#ifdef CONFIG_PHY_TI_DP83869 + phy_dp83869_init(); +#endif + #ifdef CONFIG_PHY_TI_GENERIC phy_register(&dp83822_driver); phy_register(&dp83825s_driver); diff --git a/drivers/net/phy/ti_phy_init.h b/drivers/net/phy/ti_phy_init.h index 6c7f6c640a7..bcb3d320d37 100644 --- a/drivers/net/phy/ti_phy_init.h +++ b/drivers/net/phy/ti_phy_init.h @@ -11,5 +11,6 @@ #define _TI_GEN_PHY_H int phy_dp83867_init(void); +int phy_dp83869_init(void); #endif /* _TI_GEN_PHY_H */ diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c index 17bab33eddb..4ca8709e347 100644 --- a/drivers/net/sja1105.c +++ b/drivers/net/sja1105.c @@ -3276,12 +3276,6 @@ static int sja1105_check_device_id(struct sja1105_private *priv) 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) diff --git a/drivers/nvme/nvme_show.c b/drivers/nvme/nvme_show.c index 15e459da1ac..72cbac82bcc 100644 --- a/drivers/nvme/nvme_show.c +++ b/drivers/nvme/nvme_show.c @@ -106,24 +106,41 @@ int nvme_print_info(struct udevice *udev) { struct nvme_ns *ns = dev_get_priv(udev); struct nvme_dev *dev = ns->dev; - ALLOC_CACHE_ALIGN_BUFFER(char, buf_ns, sizeof(struct nvme_id_ns)); - struct nvme_id_ns *id = (struct nvme_id_ns *)buf_ns; - ALLOC_CACHE_ALIGN_BUFFER(char, buf_ctrl, sizeof(struct nvme_id_ctrl)); - struct nvme_id_ctrl *ctrl = (struct nvme_id_ctrl *)buf_ctrl; + struct nvme_id_ctrl *ctrl; + struct nvme_id_ns *id; + int ret = 0; - if (nvme_identify(dev, 0, 1, (dma_addr_t)(long)ctrl)) - return -EIO; + ctrl = memalign(dev->page_size, sizeof(struct nvme_id_ctrl)); + if (!ctrl) + return -ENOMEM; + + if (nvme_identify(dev, 0, 1, (dma_addr_t)(long)ctrl)) { + ret = -EIO; + goto free_ctrl; + } print_optional_admin_cmd(le16_to_cpu(ctrl->oacs), ns->devnum); print_optional_nvm_cmd(le16_to_cpu(ctrl->oncs), ns->devnum); print_format_nvme_attributes(ctrl->fna, ns->devnum); - if (nvme_identify(dev, ns->ns_id, 0, (dma_addr_t)(long)id)) - return -EIO; + id = memalign(dev->page_size, sizeof(struct nvme_id_ns)); + if (!id) { + ret = -ENOMEM; + goto free_ctrl; + } + + if (nvme_identify(dev, ns->ns_id, 0, (dma_addr_t)(long)id)) { + ret = -EIO; + goto free_id; + } print_formats(id, ns); print_data_protect_cap(id->dpc, ns->devnum); print_metadata_cap(id->mc, ns->devnum); - return 0; +free_id: + free(id); +free_ctrl: + free(ctrl); + return ret; } diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 42f8cb6be0d..630d6e6cc5e 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -265,6 +265,7 @@ config PCI_MVEBU bool "Enable Armada XP/38x PCIe driver" depends on ARCH_MVEBU select MISC + select DM_RESET help Say Y here if you want to enable PCIe controller support on Armada XP/38x SoCs. diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index c0acf331398..c7968926a17 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -5,6 +5,7 @@ * Author: Matt Porter <mporter@mvista.com> * * Copyright 2000 MontaVista Software Inc. + * Copyright (c) 2021 Maciej W. Rozycki <macro@orcam.me.uk> */ #include <common.h> @@ -12,6 +13,7 @@ #include <errno.h> #include <log.h> #include <pci.h> +#include <time.h> #include "pci_internal.h" /* the user can define CONFIG_SYS_PCI_CACHE_LINE_SIZE to avoid problems */ @@ -180,6 +182,168 @@ static void dm_pciauto_setup_device(struct udevice *dev, dm_pci_write_config8(dev, PCI_LATENCY_TIMER, 0x80); } +/* + * Check if the link of a downstream PCIe port operates correctly. + * + * For that check if the optional Data Link Layer Link Active status gets + * on within a 200ms period or failing that wait until the completion of + * that period and check if link training has shown the completed status + * continuously throughout the second half of that period. + * + * Observation with the ASMedia ASM2824 Gen 3 switch indicates it takes + * 11-44ms to indicate the Data Link Layer Link Active status at 2.5GT/s, + * though it may take a couple of link training iterations. + */ +static bool dm_pciauto_exp_link_stable(struct udevice *dev, int pcie_off) +{ + u64 loops = 0, trcount = 0, ntrcount = 0, flips = 0; + bool dllla, lnktr, plnktr; + u16 exp_lnksta; + pci_dev_t bdf; + u64 end; + + dm_pci_read_config16(dev, pcie_off + PCI_EXP_LNKSTA, &exp_lnksta); + plnktr = !!(exp_lnksta & PCI_EXP_LNKSTA_LT); + + end = get_ticks() + usec_to_tick(200000); + do { + dm_pci_read_config16(dev, pcie_off + PCI_EXP_LNKSTA, + &exp_lnksta); + dllla = !!(exp_lnksta & PCI_EXP_LNKSTA_DLLLA); + lnktr = !!(exp_lnksta & PCI_EXP_LNKSTA_LT); + + flips += plnktr ^ lnktr; + if (lnktr) { + ntrcount = 0; + trcount++; + } else { + ntrcount++; + } + loops++; + + plnktr = lnktr; + } while (!dllla && get_ticks() < end); + + bdf = dm_pci_get_bdf(dev); + debug("PCI Autoconfig: %02x.%02x.%02x: Fixup link: DL active: %u; " + "%3llu flips, %6llu loops of which %6llu while training, " + "final %6llu stable\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), + (unsigned int)dllla, + (unsigned long long)flips, (unsigned long long)loops, + (unsigned long long)trcount, (unsigned long long)ntrcount); + + return dllla || ntrcount >= loops / 2; +} + +/* + * Retrain the link of a downstream PCIe port by hand if necessary. + * + * This is needed at least where a downstream port of the ASMedia ASM2824 + * Gen 3 switch is wired to the upstream port of the Pericom PI7C9X2G304 + * Gen 2 switch, and observed with the Delock Riser Card PCI Express x1 > + * 2 x PCIe x1 device, P/N 41433, plugged into the SiFive HiFive Unmatched + * board. + * + * In such a configuration the switches are supposed to negotiate the link + * speed of preferably 5.0GT/s, falling back to 2.5GT/s. However the link + * continues switching between the two speeds indefinitely and the data + * link layer never reaches the active state, with link training reported + * repeatedly active ~84% of the time. Forcing the target link speed to + * 2.5GT/s with the upstream ASM2824 device makes the two switches talk to + * each other correctly however. And more interestingly retraining with a + * higher target link speed afterwards lets the two successfully negotiate + * 5.0GT/s. + * + * As this can potentially happen with any device and is cheap in the case + * of correctly operating hardware, let's do it for all downstream ports, + * for root complexes, PCIe switches and PCI/PCI-X to PCIe bridges. + * + * First check if automatic link training may have failed to complete, as + * indicated by the optional Data Link Layer Link Active status being off + * and the Link Bandwidth Management Status indicating that hardware has + * changed the link speed or width in an attempt to correct unreliable + * link operation. If this is the case, then check if the link operates + * correctly by seeing whether it is being trained excessively. If it is, + * then conclude the link is broken. + * + * In that case restrict the speed to 2.5GT/s, observing that the Target + * Link Speed field is sticky and therefore the link will stay restricted + * even after a device reset is later made by an OS that is unaware of the + * problem. With the speed restricted request that the link be retrained + * and check again if the link operates correctly. If not, then set the + * Target Link Speed back to the original value. + * + * This requires the presence of the Link Control 2 register, so make sure + * the PCI Express Capability Version is at least 2. Also don't try, for + * obvious reasons, to limit the speed if 2.5GT/s is the only link speed + * supported. + */ +static void dm_pciauto_exp_fixup_link(struct udevice *dev, int pcie_off) +{ + u16 exp_lnksta, exp_lnkctl, exp_lnkctl2; + u16 exp_flags, exp_type, exp_version; + u32 exp_lnkcap; + pci_dev_t bdf; + + dm_pci_read_config16(dev, pcie_off + PCI_EXP_FLAGS, &exp_flags); + exp_version = exp_flags & PCI_EXP_FLAGS_VERS; + if (exp_version < 2) + return; + + exp_type = (exp_flags & PCI_EXP_FLAGS_TYPE) >> 4; + switch (exp_type) { + case PCI_EXP_TYPE_ROOT_PORT: + case PCI_EXP_TYPE_DOWNSTREAM: + case PCI_EXP_TYPE_PCIE_BRIDGE: + break; + default: + return; + } + + dm_pci_read_config32(dev, pcie_off + PCI_EXP_LNKCAP, &exp_lnkcap); + if ((exp_lnkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) + return; + + dm_pci_read_config16(dev, pcie_off + PCI_EXP_LNKSTA, &exp_lnksta); + if ((exp_lnksta & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_DLLLA)) != + PCI_EXP_LNKSTA_LBMS) + return; + + if (dm_pciauto_exp_link_stable(dev, pcie_off)) + return; + + bdf = dm_pci_get_bdf(dev); + printf("PCI Autoconfig: %02x.%02x.%02x: " + "Downstream link non-functional\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + printf("PCI Autoconfig: %02x.%02x.%02x: " + "Retrying with speed restricted to 2.5GT/s...\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + + dm_pci_read_config16(dev, pcie_off + PCI_EXP_LNKCTL, &exp_lnkctl); + dm_pci_read_config16(dev, pcie_off + PCI_EXP_LNKCTL2, &exp_lnkctl2); + + dm_pci_write_config16(dev, pcie_off + PCI_EXP_LNKCTL2, + (exp_lnkctl2 & ~PCI_EXP_LNKCTL2_TLS) | + PCI_EXP_LNKCTL2_TLS_2_5GT); + dm_pci_write_config16(dev, pcie_off + PCI_EXP_LNKCTL, + exp_lnkctl | PCI_EXP_LNKCTL_RL); + + if (dm_pciauto_exp_link_stable(dev, pcie_off)) { + printf("PCI Autoconfig: %02x.%02x.%02x: Succeeded!\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + } else { + printf("PCI Autoconfig: %02x.%02x.%02x: Failed!\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + + dm_pci_write_config16(dev, pcie_off + PCI_EXP_LNKCTL2, + exp_lnkctl2); + dm_pci_write_config16(dev, pcie_off + PCI_EXP_LNKCTL, + exp_lnkctl | PCI_EXP_LNKCTL_RL); + } +} + void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus) { struct pci_region *pci_mem; @@ -189,6 +353,7 @@ void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus) u8 io_32; struct udevice *ctlr = pci_get_controller(dev); struct pci_controller *ctlr_hose = dev_get_uclass_priv(ctlr); + int pcie_off; pci_mem = ctlr_hose->pci_mem; pci_prefetch = ctlr_hose->pci_prefetch; @@ -275,6 +440,11 @@ void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus) } } + /* For PCIe devices see if we need to retrain the link by hand */ + pcie_off = dm_pci_find_capability(dev, PCI_CAP_ID_EXP); + if (pcie_off) + dm_pciauto_exp_fixup_link(dev, pcie_off); + /* Enable memory and I/O accesses, enable bus master */ dm_pci_write_config16(dev, PCI_COMMAND, cmdstat | PCI_COMMAND_MASTER); } diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 18f79d249c7..b3ea034a284 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -18,13 +18,16 @@ #include <dm/lists.h> #include <dm/of_access.h> #include <pci.h> +#include <reset.h> #include <asm/io.h> #include <asm/arch/cpu.h> #include <asm/arch/soc.h> #include <linux/bitops.h> +#include <linux/delay.h> #include <linux/errno.h> #include <linux/ioport.h> #include <linux/mbus.h> +#include <linux/sizes.h> /* PCIe unit register offsets */ #define SELECT(x, n) ((x >> n) & 1UL) @@ -59,6 +62,9 @@ #define PCIE_DEBUG_CTRL 0x1a60 #define PCIE_DEBUG_SOFT_RESET BIT(20) +#define LINK_WAIT_RETRIES 100 +#define LINK_WAIT_TIMEOUT 1000 + struct mvebu_pcie { struct pci_controller hose; void __iomem *base; @@ -66,8 +72,10 @@ struct mvebu_pcie { struct resource mem; void __iomem *iobase; struct resource io; + u32 intregs; u32 port; u32 lane; + bool is_x4; int devfn; u32 lane_mask; int first_busno; @@ -80,14 +88,6 @@ struct mvebu_pcie { u32 cfgcache[(0x3c - 0x10) / 4]; }; -/* - * MVEBU PCIe controller needs MEMORY and I/O BARs to be mapped - * into SoCs address space. Each controller will map 128M of MEM - * and 64K of I/O space when registered. - */ -static void __iomem *mvebu_pcie_membase = (void __iomem *)MBUS_PCI_MEM_BASE; -static void __iomem *mvebu_pcie_iobase = (void __iomem *)MBUS_PCI_IO_BASE; - static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie) { u32 val; @@ -95,6 +95,23 @@ static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie) return !(val & PCIE_STAT_LINK_DOWN); } +static void mvebu_pcie_wait_for_link(struct mvebu_pcie *pcie) +{ + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_RETRIES; retries++) { + if (mvebu_pcie_link_up(pcie)) { + printf("%s: Link up\n", pcie->name); + return; + } + + udelay(LINK_WAIT_TIMEOUT); + } + + printf("%s: Link down\n", pcie->name); +} + static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie *pcie, int busno) { u32 stat; @@ -357,15 +374,43 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) 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(pcie->intregs, pcie->base + PCIE_BAR_LO_OFF(0)); writel(0, pcie->base + PCIE_BAR_HI_OFF(0)); } -static int mvebu_pcie_probe(struct udevice *dev) +/* Only enable PCIe link, do not setup it */ +static int mvebu_pcie_enable_link(struct mvebu_pcie *pcie, ofnode node) +{ + struct reset_ctl rst; + int ret; + + ret = reset_get_by_index_nodev(node, 0, &rst); + if (ret == -ENOENT) { + return 0; + } else if (ret < 0) { + printf("%s: cannot get reset controller: %d\n", pcie->name, ret); + return ret; + } + + ret = reset_request(&rst); + if (ret) { + printf("%s: cannot request reset controller: %d\n", pcie->name, ret); + return ret; + } + + ret = reset_deassert(&rst); + reset_free(&rst); + if (ret) { + printf("%s: cannot enable PCIe port: %d\n", pcie->name, ret); + return ret; + } + + return 0; +} + +/* Setup PCIe link but do not enable it */ +static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie) { - struct mvebu_pcie *pcie = dev_get_plat(dev); - struct udevice *ctlr = pci_get_controller(dev); - struct pci_controller *hose = dev_get_uclass_priv(ctlr); u32 reg; /* Setup PCIe controller to Root Complex mode */ @@ -374,6 +419,26 @@ static int mvebu_pcie_probe(struct udevice *dev) writel(reg, pcie->base + PCIE_CTRL_OFF); /* + * Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link + * Capability register. This register is defined by PCIe specification + * as read-only but this mvebu controller has it as read-write and must + * be set to number of SerDes PCIe lanes (1 or 4). If this register is + * not set correctly then link with endpoint card is not established. + */ + reg = readl(pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP); + reg &= ~PCI_EXP_LNKCAP_MLW; + reg |= (pcie->is_x4 ? 4 : 1) << 4; + writel(reg, pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP); +} + +static int mvebu_pcie_probe(struct udevice *dev) +{ + struct mvebu_pcie *pcie = dev_get_plat(dev); + struct udevice *ctlr = pci_get_controller(dev); + struct pci_controller *hose = dev_get_uclass_priv(ctlr); + u32 reg; + + /* * Change Class Code of PCI Bridge device to PCI Bridge (0x600400) * because default value is Memory controller (0x508000) which * U-Boot cannot recognize as P2P Bridge. @@ -433,26 +498,26 @@ static int mvebu_pcie_probe(struct udevice *dev) mvebu_pcie_set_local_bus_nr(pcie, 0); mvebu_pcie_set_local_dev_nr(pcie, 1); - pcie->mem.start = (u32)mvebu_pcie_membase; - pcie->mem.end = pcie->mem.start + MBUS_PCI_MEM_SIZE - 1; - mvebu_pcie_membase += MBUS_PCI_MEM_SIZE; - - if (mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr, + if (resource_size(&pcie->mem) && + mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr, (phys_addr_t)pcie->mem.start, resource_size(&pcie->mem))) { - printf("PCIe unable to add mbus window for mem at %08x+%08x\n", + printf("%s: unable to add mbus window for mem at %08x+%08x\n", + pcie->name, (u32)pcie->mem.start, (unsigned)resource_size(&pcie->mem)); + pcie->mem.start = 0; + pcie->mem.end = -1; } - pcie->io.start = (u32)mvebu_pcie_iobase; - pcie->io.end = pcie->io.start + MBUS_PCI_IO_SIZE - 1; - mvebu_pcie_iobase += MBUS_PCI_IO_SIZE; - - if (mvebu_mbus_add_window_by_id(pcie->io_target, pcie->io_attr, + if (resource_size(&pcie->io) && + mvebu_mbus_add_window_by_id(pcie->io_target, pcie->io_attr, (phys_addr_t)pcie->io.start, resource_size(&pcie->io))) { - printf("PCIe unable to add mbus window for IO at %08x+%08x\n", + printf("%s: unable to add mbus window for IO at %08x+%08x\n", + pcie->name, (u32)pcie->io.start, (unsigned)resource_size(&pcie->io)); + pcie->io.start = 0; + pcie->io.end = -1; } /* Setup windows and configure host bridge */ @@ -461,13 +526,23 @@ static int mvebu_pcie_probe(struct udevice *dev) /* PCI memory space */ pci_set_region(hose->regions + 0, pcie->mem.start, 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, resource_size(&pcie->io), PCI_REGION_IO); - hose->region_count = 3; + hose->region_count = 1; + + if (resource_size(&pcie->mem)) { + pci_set_region(hose->regions + hose->region_count, + pcie->mem.start, pcie->mem.start, + resource_size(&pcie->mem), + PCI_REGION_MEM); + hose->region_count++; + } + + if (resource_size(&pcie->io)) { + pci_set_region(hose->regions + hose->region_count, + pcie->io.start, pcie->io.start, + resource_size(&pcie->io), + PCI_REGION_IO); + hose->region_count++; + } /* PCI Bridge support 32-bit I/O and 64-bit prefetch mem addressing */ pcie->cfgcache[(PCI_IO_BASE - 0x10) / 4] = @@ -475,21 +550,7 @@ static int mvebu_pcie_probe(struct udevice *dev) pcie->cfgcache[(PCI_PREF_MEMORY_BASE - 0x10) / 4] = PCI_PREF_RANGE_TYPE_64 | (PCI_PREF_RANGE_TYPE_64 << 16); - return 0; -} - -static int mvebu_pcie_port_parse_dt(ofnode node, struct mvebu_pcie *pcie) -{ - const u32 *addr; - int len; - - addr = ofnode_get_property(node, "assigned-addresses", &len); - if (!addr) { - pr_err("property \"assigned-addresses\" not found"); - return -FDT_ERR_NOTFOUND; - } - - pcie->base = (void *)(fdt32_to_cpu(addr[2]) + SOC_REGS_PHY_BASE); + mvebu_pcie_wait_for_link(pcie); return 0; } @@ -554,31 +615,38 @@ static int mvebu_get_tgt_attr(ofnode node, int devfn, return -ENOENT; } -static int mvebu_pcie_of_to_plat(struct udevice *dev) +static int mvebu_pcie_port_parse_dt(ofnode node, ofnode parent, struct mvebu_pcie *pcie) { - struct mvebu_pcie *pcie = dev_get_plat(dev); + struct fdt_pci_addr pci_addr; + const u32 *addr; + u32 num_lanes; int ret = 0; + int len; /* Get port number, lane number and memory target / attr */ - if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-port", + if (ofnode_read_u32(node, "marvell,pcie-port", &pcie->port)) { ret = -ENODEV; goto err; } - if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-lane", &pcie->lane)) + if (ofnode_read_u32(node, "marvell,pcie-lane", &pcie->lane)) pcie->lane = 0; sprintf(pcie->name, "pcie%d.%d", pcie->port, pcie->lane); - /* pci_get_devfn() returns devfn in bits 15..8, see PCI_DEV usage */ - pcie->devfn = pci_get_devfn(dev); - if (pcie->devfn < 0) { - ret = -ENODEV; + if (!ofnode_read_u32(node, "num-lanes", &num_lanes) && num_lanes == 4) + pcie->is_x4 = true; + + /* devfn is in bits [15:8], see PCI_DEV usage */ + ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg", &pci_addr); + if (ret < 0) { + printf("%s: property \"reg\" is invalid\n", pcie->name); goto err; } + pcie->devfn = pci_addr.phys_hi & 0xff00; - ret = mvebu_get_tgt_attr(dev_ofnode(dev->parent), pcie->devfn, + ret = mvebu_get_tgt_attr(parent, pcie->devfn, IORESOURCE_MEM, &pcie->mem_target, &pcie->mem_attr); if (ret < 0) { @@ -586,7 +654,7 @@ static int mvebu_pcie_of_to_plat(struct udevice *dev) goto err; } - ret = mvebu_get_tgt_attr(dev_ofnode(dev->parent), pcie->devfn, + ret = mvebu_get_tgt_attr(parent, pcie->devfn, IORESOURCE_IO, &pcie->io_target, &pcie->io_attr); if (ret < 0) { @@ -595,9 +663,15 @@ static int mvebu_pcie_of_to_plat(struct udevice *dev) } /* Parse PCIe controller register base from DT */ - ret = mvebu_pcie_port_parse_dt(dev_ofnode(dev), pcie); - if (ret < 0) + addr = ofnode_get_property(node, "assigned-addresses", &len); + if (!addr) { + printf("%s: property \"assigned-addresses\" not found\n", pcie->name); + ret = -FDT_ERR_NOTFOUND; goto err; + } + + pcie->base = (void *)(u32)ofnode_translate_address(node, addr); + pcie->intregs = (u32)pcie->base - fdt32_to_cpu(addr[2]); return 0; @@ -615,7 +689,6 @@ static struct driver pcie_mvebu_drv = { .id = UCLASS_PCI, .ops = &mvebu_pcie_ops, .probe = mvebu_pcie_probe, - .of_to_plat = mvebu_pcie_of_to_plat, .plat_auto = sizeof(struct mvebu_pcie), }; @@ -625,9 +698,14 @@ static struct driver pcie_mvebu_drv = { */ static int mvebu_pcie_bind(struct udevice *parent) { + struct mvebu_pcie **ports_pcie; struct mvebu_pcie *pcie; struct uclass_driver *drv; struct udevice *dev; + struct resource mem; + struct resource io; + int ports_count, i; + ofnode *ports_nodes; ofnode subnode; /* Lookup pci driver */ @@ -637,19 +715,94 @@ static int mvebu_pcie_bind(struct udevice *parent) return -ENOENT; } + ports_count = ofnode_get_child_count(dev_ofnode(parent)); + ports_pcie = calloc(ports_count, sizeof(*ports_pcie)); + ports_nodes = calloc(ports_count, sizeof(*ports_nodes)); + if (!ports_pcie || !ports_nodes) { + free(ports_pcie); + free(ports_nodes); + return -ENOMEM; + } + ports_count = 0; + + mem.start = MBUS_PCI_MEM_BASE; + mem.end = MBUS_PCI_MEM_BASE + MBUS_PCI_MEM_SIZE - 1; + io.start = MBUS_PCI_IO_BASE; + io.end = MBUS_PCI_IO_BASE + MBUS_PCI_IO_SIZE - 1; + + /* First phase: Fill mvebu_pcie struct for each port */ ofnode_for_each_subnode(subnode, dev_ofnode(parent)) { if (!ofnode_is_available(subnode)) continue; pcie = calloc(1, sizeof(*pcie)); if (!pcie) - return -ENOMEM; + continue; + + if (mvebu_pcie_port_parse_dt(subnode, dev_ofnode(parent), pcie) < 0) { + free(pcie); + continue; + } + + /* + * MVEBU PCIe controller needs MEMORY and I/O BARs to be mapped + * into SoCs address space. Each controller will map 128M of MEM + * and 64K of I/O space when registered. + */ + + if (resource_size(&mem) >= SZ_128M) { + pcie->mem.start = mem.start; + pcie->mem.end = mem.start + SZ_128M - 1; + mem.start += SZ_128M; + } else { + printf("%s: unable to assign mbus window for mem\n", pcie->name); + pcie->mem.start = 0; + pcie->mem.end = -1; + } + + if (resource_size(&io) >= SZ_64K) { + pcie->io.start = io.start; + pcie->io.end = io.start + SZ_64K - 1; + io.start += SZ_64K; + } else { + printf("%s: unable to assign mbus window for io\n", pcie->name); + pcie->io.start = 0; + pcie->io.end = -1; + } + + ports_pcie[ports_count] = pcie; + ports_nodes[ports_count] = subnode; + ports_count++; + } + + /* Second phase: Setup all PCIe links (do not enable them yet) */ + for (i = 0; i < ports_count; i++) + mvebu_pcie_setup_link(ports_pcie[i]); + + /* Third phase: Enable all PCIe links and create for each UCLASS_PCI device */ + for (i = 0; i < ports_count; i++) { + pcie = ports_pcie[i]; + subnode = ports_nodes[i]; + + /* + * PCIe link can be enabled only after all PCIe links were + * properly configured. This is because more PCIe links shares + * one enable bit and some PCIe links cannot be enabled + * individually. + */ + if (mvebu_pcie_enable_link(pcie, subnode) < 0) { + free(pcie); + continue; + } /* Create child device UCLASS_PCI and bind it */ device_bind(parent, &pcie_mvebu_drv, pcie->name, pcie, subnode, &dev); } + free(ports_pcie); + free(ports_nodes); + return 0; } diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index ab2a5d17fcf..86c589a65fd 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -125,7 +125,6 @@ struct sun4i_usb_phy_info { struct sun4i_usb_phy_plat { void __iomem *pmu; - int power_on_count; int gpio_vbus; int gpio_vbus_det; int gpio_id_det; @@ -225,10 +224,6 @@ static int sun4i_usb_phy_power_on(struct phy *phy) initial_usb_scan_delay = 0; } - usb_phy->power_on_count++; - if (usb_phy->power_on_count != 1) - return 0; - if (usb_phy->gpio_vbus >= 0) gpio_set_value(usb_phy->gpio_vbus, SUNXI_GPIO_PULL_UP); @@ -240,10 +235,6 @@ static int sun4i_usb_phy_power_off(struct phy *phy) struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; - usb_phy->power_on_count--; - if (usb_phy->power_on_count != 0) - return 0; - if (usb_phy->gpio_vbus >= 0) gpio_set_value(usb_phy->gpio_vbus, SUNXI_GPIO_PULL_DISABLE); diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c index 59683a080cd..49e2ec25c28 100644 --- a/drivers/phy/phy-uclass.c +++ b/drivers/phy/phy-uclass.c @@ -11,19 +11,103 @@ #include <dm/device_compat.h> #include <dm/devres.h> #include <generic-phy.h> +#include <linux/list.h> + +/** + * struct phy_counts - Init and power-on counts of a single PHY port + * + * This structure is used to keep track of PHY initialization and power + * state change requests, so that we don't power off and deinitialize a + * PHY instance until all of its users want it done. Otherwise, multiple + * consumers using the same PHY port can cause problems (e.g. one might + * call power_off() after another's exit() and hang indefinitely). + * + * @id: The PHY ID within a PHY provider + * @power_on_count: Times generic_phy_power_on() was called for this ID + * without a matching generic_phy_power_off() afterwards + * @init_count: Times generic_phy_init() was called for this ID + * without a matching generic_phy_exit() afterwards + * @list: Handle for a linked list of these structures corresponding to + * ports of the same PHY provider + */ +struct phy_counts { + unsigned long id; + int power_on_count; + int init_count; + struct list_head list; +}; static inline struct phy_ops *phy_dev_ops(struct udevice *dev) { return (struct phy_ops *)dev->driver->ops; } +static struct phy_counts *phy_get_counts(struct phy *phy) +{ + struct list_head *uc_priv; + struct phy_counts *counts; + + if (!generic_phy_valid(phy)) + return NULL; + + uc_priv = dev_get_uclass_priv(phy->dev); + list_for_each_entry(counts, uc_priv, list) + if (counts->id == phy->id) + return counts; + + return NULL; +} + +static int phy_alloc_counts(struct phy *phy) +{ + struct list_head *uc_priv; + struct phy_counts *counts; + + if (!generic_phy_valid(phy)) + return 0; + if (phy_get_counts(phy)) + return 0; + + uc_priv = dev_get_uclass_priv(phy->dev); + counts = kzalloc(sizeof(*counts), GFP_KERNEL); + if (!counts) + return -ENOMEM; + + counts->id = phy->id; + counts->power_on_count = 0; + counts->init_count = 0; + list_add(&counts->list, uc_priv); + + return 0; +} + +static int phy_uclass_pre_probe(struct udevice *dev) +{ + struct list_head *uc_priv = dev_get_uclass_priv(dev); + + INIT_LIST_HEAD(uc_priv); + + return 0; +} + +static int phy_uclass_pre_remove(struct udevice *dev) +{ + struct list_head *uc_priv = dev_get_uclass_priv(dev); + struct phy_counts *counts, *next; + + list_for_each_entry_safe(counts, next, uc_priv, list) + kfree(counts); + + return 0; +} + static int generic_phy_xlate_offs_flags(struct phy *phy, struct ofnode_phandle_args *args) { debug("%s(phy=%p)\n", __func__, phy); if (args->args_count > 1) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } @@ -88,6 +172,12 @@ int generic_phy_get_by_index_nodev(ofnode node, int index, struct phy *phy) goto err; } + ret = phy_alloc_counts(phy); + if (ret) { + debug("phy_alloc_counts() failed: %d\n", ret); + goto err; + } + return 0; err: @@ -118,6 +208,7 @@ int generic_phy_get_by_name(struct udevice *dev, const char *phy_name, int generic_phy_init(struct phy *phy) { + struct phy_counts *counts; struct phy_ops const *ops; int ret; @@ -126,10 +217,19 @@ int generic_phy_init(struct phy *phy) ops = phy_dev_ops(phy->dev); if (!ops->init) return 0; + + counts = phy_get_counts(phy); + if (counts->init_count > 0) { + counts->init_count++; + return 0; + } + ret = ops->init(phy); if (ret) dev_err(phy->dev, "PHY: Failed to init %s: %d.\n", phy->dev->name, ret); + else + counts->init_count = 1; return ret; } @@ -154,6 +254,7 @@ int generic_phy_reset(struct phy *phy) int generic_phy_exit(struct phy *phy) { + struct phy_counts *counts; struct phy_ops const *ops; int ret; @@ -162,16 +263,28 @@ int generic_phy_exit(struct phy *phy) ops = phy_dev_ops(phy->dev); if (!ops->exit) return 0; + + counts = phy_get_counts(phy); + if (counts->init_count == 0) + return 0; + if (counts->init_count > 1) { + counts->init_count--; + return 0; + } + ret = ops->exit(phy); if (ret) dev_err(phy->dev, "PHY: Failed to exit %s: %d.\n", phy->dev->name, ret); + else + counts->init_count = 0; return ret; } int generic_phy_power_on(struct phy *phy) { + struct phy_counts *counts; struct phy_ops const *ops; int ret; @@ -180,16 +293,26 @@ int generic_phy_power_on(struct phy *phy) ops = phy_dev_ops(phy->dev); if (!ops->power_on) return 0; + + counts = phy_get_counts(phy); + if (counts->power_on_count > 0) { + counts->power_on_count++; + return 0; + } + ret = ops->power_on(phy); if (ret) dev_err(phy->dev, "PHY: Failed to power on %s: %d.\n", phy->dev->name, ret); + else + counts->power_on_count = 1; return ret; } int generic_phy_power_off(struct phy *phy) { + struct phy_counts *counts; struct phy_ops const *ops; int ret; @@ -198,10 +321,21 @@ int generic_phy_power_off(struct phy *phy) ops = phy_dev_ops(phy->dev); if (!ops->power_off) return 0; + + counts = phy_get_counts(phy); + if (counts->power_on_count == 0) + return 0; + if (counts->power_on_count > 1) { + counts->power_on_count--; + return 0; + } + ret = ops->power_off(phy); if (ret) dev_err(phy->dev, "PHY: Failed to power off %s: %d.\n", phy->dev->name, ret); + else + counts->power_on_count = 0; return ret; } @@ -316,4 +450,7 @@ int generic_phy_power_off_bulk(struct phy_bulk *bulk) UCLASS_DRIVER(phy) = { .id = UCLASS_PHY, .name = "phy", + .pre_probe = phy_uclass_pre_probe, + .pre_remove = phy_uclass_pre_remove, + .per_device_auto = sizeof(struct list_head), }; diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index 99b3f9ae71b..9aea5fcdf08 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -9,6 +9,15 @@ config POWER_DOMAIN domains). This may be used to save power. This API provides the means to control such power management hardware. +config APPLE_PMGR_POWER_DOMAIN + bool "Enable the Apple PMGR power domain driver" + depends on POWER_DOMAIN + default y if ARCH_APPLE + help + Enable support for manipulating power domains on Apple SoCs. + This driver is needed to power on parts of the SoC that have + not been powered on by previous boot stages. + config BCM6328_POWER_DOMAIN bool "Enable the BCM6328 power domain driver" depends on POWER_DOMAIN && ARCH_BMIPS diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 3d1e5f073cb..530ae35671a 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_$(SPL_)POWER_DOMAIN) += power-domain-uclass.o +obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o diff --git a/drivers/power/domain/apple-pmgr.c b/drivers/power/domain/apple-pmgr.c new file mode 100644 index 00000000000..d25f136b9dc --- /dev/null +++ b/drivers/power/domain/apple-pmgr.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org> + */ + +#include <common.h> +#include <asm/io.h> +#include <dm.h> +#include <linux/err.h> +#include <linux/bitfield.h> +#include <power-domain-uclass.h> +#include <regmap.h> +#include <syscon.h> + +#define APPLE_PMGR_PS_TARGET GENMASK(3, 0) +#define APPLE_PMGR_PS_ACTUAL GENMASK(7, 4) + +#define APPLE_PMGR_PS_ACTIVE 0xf +#define APPLE_PMGR_PS_PWRGATE 0x0 + +#define APPLE_PMGR_PS_SET_TIMEOUT_US 100 + +struct apple_pmgr_priv { + struct regmap *regmap; + u32 offset; /* offset within regmap for this domain */ +}; + +static int apple_pmgr_request(struct power_domain *power_domain) +{ + return 0; +} + +static int apple_pmgr_rfree(struct power_domain *power_domain) +{ + return 0; +} + +static int apple_pmgr_ps_set(struct power_domain *power_domain, u32 pstate) +{ + struct apple_pmgr_priv *priv = dev_get_priv(power_domain->dev); + uint reg; + + regmap_update_bits(priv->regmap, priv->offset, APPLE_PMGR_PS_TARGET, + FIELD_PREP(APPLE_PMGR_PS_TARGET, pstate)); + + return regmap_read_poll_timeout( + priv->regmap, priv->offset, reg, + (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate), 1, + APPLE_PMGR_PS_SET_TIMEOUT_US); +} + +static int apple_pmgr_on(struct power_domain *power_domain) +{ + return apple_pmgr_ps_set(power_domain, APPLE_PMGR_PS_ACTIVE); +} + +static int apple_pmgr_off(struct power_domain *power_domain) +{ + return 0; +} + +static int apple_pmgr_of_xlate(struct power_domain *power_domain, + struct ofnode_phandle_args *args) +{ + if (args->args_count != 0) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + return 0; +} + +static const struct udevice_id apple_pmgr_ids[] = { + { .compatible = "apple,pmgr-pwrstate" }, + { /* sentinel */ } +}; + +static int apple_pmgr_probe(struct udevice *dev) +{ + struct apple_pmgr_priv *priv = dev_get_priv(dev); + int ret; + + ret = dev_power_domain_on(dev); + if (ret) + return ret; + + priv->regmap = syscon_get_regmap(dev->parent); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + ret = dev_read_u32(dev, "reg", &priv->offset); + if (ret < 0) + return ret; + + return 0; +} + +struct power_domain_ops apple_pmgr_ops = { + .request = apple_pmgr_request, + .rfree = apple_pmgr_rfree, + .on = apple_pmgr_on, + .off = apple_pmgr_off, + .of_xlate = apple_pmgr_of_xlate, +}; + +U_BOOT_DRIVER(apple_pmgr) = { + .name = "apple_pmgr", + .id = UCLASS_POWER_DOMAIN, + .of_match = apple_pmgr_ids, + .ops = &apple_pmgr_ops, + .probe = apple_pmgr_probe, + .priv_auto = sizeof(struct apple_pmgr_priv), +}; diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c index c09c009130d..ca9f00a8f24 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -26,7 +26,7 @@ static int reset_of_xlate_default(struct reset_ctl *reset_ctl, debug("%s(reset_ctl=%p)\n", __func__, reset_ctl); if (args->args_count != 1) { - debug("Invaild args_count: %d\n", args->args_count); + debug("Invalid args_count: %d\n", args->args_count); return -EINVAL; } diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 30d44214d7d..96a1cb65ba2 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -104,7 +104,8 @@ static void serial_find_console_or_panic(void) } } } - if (!SPL_BUILD || !CONFIG_IS_ENABLED(OF_CONTROL) || !blob) { + if (!IS_ENABLED(CONFIG_SPL_BUILD) || !CONFIG_IS_ENABLED(OF_CONTROL) || + !blob) { /* * Try to use CONFIG_CONS_INDEX if available (it is numbered * from 1!). diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c index fb10884755b..2748270ad6a 100644 --- a/drivers/usb/gadget/dwc2_udc_otg.c +++ b/drivers/usb/gadget/dwc2_udc_otg.c @@ -461,7 +461,7 @@ static void reconfig_usbd(struct dwc2_udc *dev) u32 max_hw_ep; int pdata_hw_ep; - debug("Reseting OTG controller\n"); + debug("Resetting OTG controller\n"); dflt_gusbcfg = 0<<15 /* PHY Low Power Clock sel*/ diff --git a/drivers/usb/musb/musb_udc.c b/drivers/usb/musb/musb_udc.c index b9510e30459..2ffcb7caaad 100644 --- a/drivers/usb/musb/musb_udc.c +++ b/drivers/usb/musb/musb_udc.c @@ -269,7 +269,7 @@ static void musb_peri_ep0_set_address(void) __PRETTY_FUNCTION__, udc_device->address); } else { if (debug_level > 0) - serial_printf("ERROR : %s Address missmatch " + serial_printf("ERROR : %s Address mismatch " "sw %d vs hw %d\n", __PRETTY_FUNCTION__, udc_device->address, faddr); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1177f17fd8a..cabac290539 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -81,6 +81,15 @@ config WDT What exactly happens when the timer expires is up to a particular device/driver. +config WDT_APPLE + bool "Apple watchdog timer support" + depends on WDT + default y if ARCH_APPLE + help + Enable support for the watchdog timer on Apple SoCs. + The watchdog will perform a full SoC reset resulting in a + reboot of the entire system. + config WDT_ARMADA_37XX bool "Marvell Armada 37xx watchdog timer support" depends on WDT && ARMADA_3700 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index fa7ce583ce2..6d2b3822c06 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o obj-$(CONFIG_$(SPL_TPL_)WDT) += wdt-uclass.o obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o +obj-$(CONFIG_WDT_APPLE) += apple_wdt.o obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o obj-$(CONFIG_WDT_AST2600) += ast2600_wdt.o diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c new file mode 100644 index 00000000000..c7307f41cb7 --- /dev/null +++ b/drivers/watchdog/apple_wdt.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org> + */ + +#include <clk.h> +#include <dm.h> +#include <wdt.h> +#include <asm/io.h> +#include <linux/delay.h> + +#define APPLE_WDT_CUR_TIME 0x10 +#define APPLE_WDT_BARK_TIME 0x14 +#define APPLE_WDT_CTRL 0x1c +#define APPLE_WDT_CTRL_RESET_EN BIT(2) + +struct apple_wdt_priv { + void *base; + ulong clk_rate; +}; + +static int apple_wdt_reset(struct udevice *dev) +{ + struct apple_wdt_priv *priv = dev_get_priv(dev); + + writel(0, priv->base + APPLE_WDT_CUR_TIME); + + return 0; +} + +static int apple_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + struct apple_wdt_priv *priv = dev_get_priv(dev); + u64 timeout; + + timeout = (timeout_ms * priv->clk_rate) / 1000; + if (timeout > U32_MAX) + return -EINVAL; + + writel(0, priv->base + APPLE_WDT_CUR_TIME); + writel(timeout, priv->base + APPLE_WDT_BARK_TIME); + writel(APPLE_WDT_CTRL_RESET_EN, priv->base + APPLE_WDT_CTRL); + + return 0; +} + +static int apple_wdt_stop(struct udevice *dev) +{ + struct apple_wdt_priv *priv = dev_get_priv(dev); + + writel(0, priv->base + APPLE_WDT_CTRL); + + return 0; +} + +static int apple_wdt_expire_now(struct udevice *dev, ulong flags) +{ + int ret; + + ret = apple_wdt_start(dev, 0, flags); + if (ret) + return ret; + + /* + * It can take up to 25ms until the SoC actually resets, so + * wait 50ms just to be sure. + */ + mdelay(50); + + return 0; +} + +static const struct wdt_ops apple_wdt_ops = { + .reset = apple_wdt_reset, + .start = apple_wdt_start, + .stop = apple_wdt_stop, + .expire_now = apple_wdt_expire_now, +}; + +static const struct udevice_id apple_wdt_ids[] = { + { .compatible = "apple,wdt" }, + { /* sentinel */ } +}; + +static int apple_wdt_probe(struct udevice *dev) +{ + struct apple_wdt_priv *priv = dev_get_priv(dev); + struct clk clk; + int ret; + + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) + return -EINVAL; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return ret; + + ret = clk_enable(&clk); + if (ret) + return ret; + + priv->clk_rate = clk_get_rate(&clk); + + return 0; +} + +U_BOOT_DRIVER(apple_wdt) = { + .name = "apple_wdt", + .id = UCLASS_WDT, + .of_match = apple_wdt_ids, + .priv_auto = sizeof(struct apple_wdt_priv), + .probe = apple_wdt_probe, + .ops = &apple_wdt_ops, +}; |