Merge branch '2022-01-14-assorted-fixes'
- A number of fixes in various subsystems. This includes having the phy uclass track power-on and init counts as this should resolve some tricky functional problems on a number of platforms.
This commit is contained in:
@@ -102,7 +102,7 @@ config PSCI_RESET
|
||||
bool "Use PSCI for reset and shutdown"
|
||||
default y
|
||||
select ARM_SMCCC if OF_CONTROL
|
||||
depends on !ARCH_EXYNOS7 && !ARCH_BCM283X && \
|
||||
depends on !ARCH_APPLE && !ARCH_BCM283X && !ARCH_EXYNOS7 && \
|
||||
!TARGET_LS2080AQDS && \
|
||||
!TARGET_LS2080ARDB && !TARGET_LS2080A_EMU && \
|
||||
!TARGET_LS1088ARDB && !TARGET_LS1088AQDS && \
|
||||
|
||||
@@ -81,8 +81,8 @@ static int do_adc_single(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
ret = adc_channel_single_shot(argv[1], simple_strtol(argv[2], NULL, 0),
|
||||
&data);
|
||||
if (ret) {
|
||||
printf("Error getting single shot for device %s channel %s\n",
|
||||
argv[1], argv[2]);
|
||||
printf("Error getting single shot for device %s channel %s (ret=%d)\n",
|
||||
argv[1], argv[2], ret);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -11,12 +11,96 @@
|
||||
#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)
|
||||
{
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
8
env/fat.c
vendored
8
env/fat.c
vendored
@@ -77,7 +77,7 @@ static int env_fat_save(void)
|
||||
* This printf is embedded in the messages from env_save that
|
||||
* will calling it. The missing \n is intentional.
|
||||
*/
|
||||
printf("Unable to use %s %d:%d... ",
|
||||
printf("Unable to use %s %d:%d... \n",
|
||||
CONFIG_ENV_FAT_INTERFACE, dev, part);
|
||||
return 1;
|
||||
}
|
||||
@@ -93,7 +93,7 @@ static int env_fat_save(void)
|
||||
* This printf is embedded in the messages from env_save that
|
||||
* will calling it. The missing \n is intentional.
|
||||
*/
|
||||
printf("Unable to write \"%s\" from %s%d:%d... ",
|
||||
printf("Unable to write \"%s\" from %s%d:%d... \n",
|
||||
file, CONFIG_ENV_FAT_INTERFACE, dev, part);
|
||||
return 1;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ static int env_fat_load(void)
|
||||
* This printf is embedded in the messages from env_save that
|
||||
* will calling it. The missing \n is intentional.
|
||||
*/
|
||||
printf("Unable to use %s %d:%d... ",
|
||||
printf("Unable to use %s %d:%d... \n",
|
||||
CONFIG_ENV_FAT_INTERFACE, dev, part);
|
||||
goto err_env_relocate;
|
||||
}
|
||||
@@ -153,7 +153,7 @@ static int env_fat_load(void)
|
||||
* This printf is embedded in the messages from env_save that
|
||||
* will calling it. The missing \n is intentional.
|
||||
*/
|
||||
printf("Unable to read \"%s\" from %s%d:%d... ",
|
||||
printf("Unable to read \"%s\" from %s%d:%d... \n",
|
||||
CONFIG_ENV_FAT_FILE, CONFIG_ENV_FAT_INTERFACE, dev, part);
|
||||
goto err_env_relocate;
|
||||
}
|
||||
|
||||
@@ -21,11 +21,35 @@
|
||||
|
||||
/* Environment options */
|
||||
|
||||
#if CONFIG_IS_ENABLED(CMD_USB)
|
||||
# define BOOT_TARGET_USB(func) func(USB, usb, 0)
|
||||
#else
|
||||
# define BOOT_TARGET_USB(func)
|
||||
#endif
|
||||
|
||||
#if CONFIG_IS_ENABLED(CMD_SCSI)
|
||||
# define BOOT_TARGET_SCSI(func) func(SCSI, scsi, 0)
|
||||
#else
|
||||
# define BOOT_TARGET_SCSI(func)
|
||||
#endif
|
||||
|
||||
#if CONFIG_IS_ENABLED(CMD_VIRTIO)
|
||||
# define BOOT_TARGET_VIRTIO(func) func(VIRTIO, virtio, 0)
|
||||
#else
|
||||
# define BOOT_TARGET_VIRTIO(func)
|
||||
#endif
|
||||
|
||||
#if CONFIG_IS_ENABLED(CMD_DHCP)
|
||||
# define BOOT_TARGET_DHCP(func) func(DHCP, dhcp, na)
|
||||
#else
|
||||
# define BOOT_TARGET_DHCP(func)
|
||||
#endif
|
||||
|
||||
#define BOOT_TARGET_DEVICES(func) \
|
||||
func(USB, usb, 0) \
|
||||
func(SCSI, scsi, 0) \
|
||||
func(VIRTIO, virtio, 0) \
|
||||
func(DHCP, dhcp, na)
|
||||
BOOT_TARGET_USB(func) \
|
||||
BOOT_TARGET_SCSI(func) \
|
||||
BOOT_TARGET_VIRTIO(func) \
|
||||
BOOT_TARGET_DHCP(func)
|
||||
|
||||
#include <config_distro_bootcmd.h>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
* Copyright (c) 2021 Maciej W. Rozycki <macro@orcam.me.uk>
|
||||
*/
|
||||
|
||||
#ifndef _PCI_H
|
||||
@@ -475,16 +476,24 @@
|
||||
|
||||
/* PCI Express capabilities */
|
||||
#define PCI_EXP_FLAGS 2 /* Capabilities register */
|
||||
#define PCI_EXP_FLAGS_VERS 0x000f /* Capability Version */
|
||||
#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */
|
||||
#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */
|
||||
#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */
|
||||
#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */
|
||||
#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIe Bridge */
|
||||
#define PCI_EXP_DEVCAP 4 /* Device capabilities */
|
||||
#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */
|
||||
#define PCI_EXP_DEVCTL 8 /* Device Control */
|
||||
#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */
|
||||
#define PCI_EXP_LNKCAP 12 /* Link Capabilities */
|
||||
#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */
|
||||
#define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */
|
||||
#define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */
|
||||
#define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */
|
||||
#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */
|
||||
#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */
|
||||
#define PCI_EXP_LNKCTL 16 /* Link Control */
|
||||
#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */
|
||||
#define PCI_EXP_LNKSTA 18 /* Link Status */
|
||||
#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */
|
||||
#define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */
|
||||
@@ -492,7 +501,9 @@
|
||||
#define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */
|
||||
#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */
|
||||
#define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */
|
||||
#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */
|
||||
#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */
|
||||
#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */
|
||||
#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */
|
||||
#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */
|
||||
#define PCI_EXP_RTCTL 28 /* Root Control */
|
||||
@@ -503,8 +514,14 @@
|
||||
#define PCI_EXP_DEVCAP2_ARI 0x00000020 /* ARI Forwarding Supported */
|
||||
#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */
|
||||
#define PCI_EXP_DEVCTL2_ARI 0x0020 /* Alternative Routing-ID */
|
||||
|
||||
#define PCI_EXP_LNKCAP2 44 /* Link Capability 2 */
|
||||
#define PCI_EXP_LNKCAP2_SLS 0x000000fe /* Supported Link Speeds Vector */
|
||||
#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
|
||||
#define PCI_EXP_LNKCTL2_TLS 0x000f /* Target Link Speed */
|
||||
#define PCI_EXP_LNKCTL2_TLS_2_5GT 0x0001 /* Target Link Speed 2.5GT/s */
|
||||
#define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Target Link Speed 5.0GT/s */
|
||||
#define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Target Link Speed 8.0GT/s */
|
||||
|
||||
/* Single Root I/O Virtualization Registers */
|
||||
#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */
|
||||
#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */
|
||||
|
||||
@@ -307,6 +307,14 @@ char *strmhz(char *buf, unsigned long hz);
|
||||
*/
|
||||
void str_to_upper(const char *in, char *out, size_t len);
|
||||
|
||||
/**
|
||||
* vsscanf - Unformat a buffer into a list of arguments
|
||||
* @buf: input buffer
|
||||
* @fmt: format of buffer
|
||||
* @args: arguments
|
||||
*/
|
||||
int vsscanf(const char *inp, char const *fmt0, va_list ap);
|
||||
|
||||
/**
|
||||
* sscanf - Unformat a buffer into a list of arguments
|
||||
* @buf: input buffer
|
||||
|
||||
@@ -837,11 +837,11 @@ config LMB_RESERVED_REGIONS
|
||||
Define the number of supported reserved regions in the library logical
|
||||
memory blocks.
|
||||
|
||||
endmenu
|
||||
|
||||
config PHANDLE_CHECK_SEQ
|
||||
bool "Enable phandle check while getting sequence number"
|
||||
help
|
||||
When there are multiple device tree nodes with same name,
|
||||
enable this config option to distinguish them using
|
||||
phandles in fdtdec_get_alias_seq() function.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -46,9 +46,66 @@
|
||||
#include <asm/cache.h>
|
||||
|
||||
#include <linux/math64.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
static void default_log(const char *ignored, char *response) {}
|
||||
|
||||
static lbaint_t write_sparse_chunk_raw(struct sparse_storage *info,
|
||||
lbaint_t blk, lbaint_t blkcnt,
|
||||
void *data,
|
||||
char *response)
|
||||
{
|
||||
lbaint_t n = blkcnt, write_blks, blks = 0, aligned_buf_blks = 100;
|
||||
uint32_t *aligned_buf = NULL;
|
||||
|
||||
if (CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) {
|
||||
write_blks = info->write(info, blk, n, data);
|
||||
if (write_blks < n)
|
||||
goto write_fail;
|
||||
|
||||
return write_blks;
|
||||
}
|
||||
|
||||
aligned_buf = memalign(ARCH_DMA_MINALIGN, info->blksz * aligned_buf_blks);
|
||||
if (!aligned_buf) {
|
||||
info->mssg("Malloc failed for: CHUNK_TYPE_RAW", response);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
while (blkcnt > 0) {
|
||||
n = min(aligned_buf_blks, blkcnt);
|
||||
memcpy(aligned_buf, data, n * info->blksz);
|
||||
|
||||
/* write_blks might be > n due to NAND bad-blocks */
|
||||
write_blks = info->write(info, blk + blks, n, aligned_buf);
|
||||
if (write_blks < n) {
|
||||
free(aligned_buf);
|
||||
goto write_fail;
|
||||
}
|
||||
|
||||
blks += write_blks;
|
||||
data += n * info->blksz;
|
||||
blkcnt -= n;
|
||||
}
|
||||
|
||||
free(aligned_buf);
|
||||
return blks;
|
||||
|
||||
write_fail:
|
||||
if (IS_ERR_VALUE(write_blks)) {
|
||||
printf("%s: Write failed, block #" LBAFU " [" LBAFU "] (%lld)\n",
|
||||
__func__, blk + blks, n, (long long)write_blks);
|
||||
info->mssg("flash write failure", response);
|
||||
return write_blks;
|
||||
}
|
||||
|
||||
/* write_blks < n */
|
||||
printf("%s: Write failed, block #" LBAFU " [" LBAFU "]\n",
|
||||
__func__, blk + blks, n);
|
||||
info->mssg("flash write failure(incomplete)", response);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int write_sparse_image(struct sparse_storage *info,
|
||||
const char *part_name, void *data, char *response)
|
||||
{
|
||||
@@ -152,15 +209,11 @@ int write_sparse_image(struct sparse_storage *info,
|
||||
return -1;
|
||||
}
|
||||
|
||||
blks = info->write(info, blk, blkcnt, data);
|
||||
/* blks might be > blkcnt (eg. NAND bad-blocks) */
|
||||
if (blks < blkcnt) {
|
||||
printf("%s: %s" LBAFU " [" LBAFU "]\n",
|
||||
__func__, "Write failed, block #",
|
||||
blk, blks);
|
||||
info->mssg("flash write failure", response);
|
||||
blks = write_sparse_chunk_raw(info, blk, blkcnt,
|
||||
data, response);
|
||||
if (blks < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
blk += blks;
|
||||
bytes_written += ((u64)blkcnt) * info->blksz;
|
||||
total_blocks += chunk_header->chunk_sz;
|
||||
|
||||
@@ -2617,7 +2617,7 @@ sub u_boot_line {
|
||||
}
|
||||
|
||||
# Do not disable fdt / initrd relocation
|
||||
if ($rawline =~ /.*(fdt|initrd)_high=0xffffffff/) {
|
||||
if ($rawline =~ /^\+.*(fdt|initrd)_high=0xffffffff/) {
|
||||
ERROR("DISABLE_FDT_OR_INITRD_RELOC",
|
||||
"fdt or initrd relocation disabled at boot time\n" . $herecurr);
|
||||
}
|
||||
|
||||
@@ -79,12 +79,15 @@ static int dm_test_phy_ops(struct unit_test_state *uts)
|
||||
ut_assertok(generic_phy_power_off(&phy1));
|
||||
|
||||
/*
|
||||
* test operations after exit().
|
||||
* The sandbox phy driver does not allow it.
|
||||
* Test power_on() failure after exit().
|
||||
* The sandbox phy driver does not allow power-on/off after
|
||||
* exit, but the uclass counts power-on/init calls and skips
|
||||
* calling the driver's ops when e.g. powering off an already
|
||||
* powered-off phy.
|
||||
*/
|
||||
ut_assertok(generic_phy_exit(&phy1));
|
||||
ut_assert(generic_phy_power_on(&phy1) != 0);
|
||||
ut_assert(generic_phy_power_off(&phy1) != 0);
|
||||
ut_assertok(generic_phy_power_off(&phy1));
|
||||
|
||||
/*
|
||||
* test normal operations again (after re-init)
|
||||
@@ -99,6 +102,17 @@ static int dm_test_phy_ops(struct unit_test_state *uts)
|
||||
*/
|
||||
ut_assertok(generic_phy_reset(&phy1));
|
||||
|
||||
/*
|
||||
* Test power_off() failure after exit().
|
||||
* For this we need to call exit() while the phy is powered-on,
|
||||
* so that the uclass actually calls the driver's power-off()
|
||||
* and reports the resulting failure.
|
||||
*/
|
||||
ut_assertok(generic_phy_power_on(&phy1));
|
||||
ut_assertok(generic_phy_exit(&phy1));
|
||||
ut_assert(generic_phy_power_off(&phy1) != 0);
|
||||
ut_assertok(generic_phy_power_on(&phy1));
|
||||
|
||||
/* PHY2 has a known problem with power off */
|
||||
ut_assertok(generic_phy_init(&phy2));
|
||||
ut_assertok(generic_phy_power_on(&phy2));
|
||||
@@ -106,8 +120,8 @@ static int dm_test_phy_ops(struct unit_test_state *uts)
|
||||
|
||||
/* PHY3 has a known problem with power off and power on */
|
||||
ut_assertok(generic_phy_init(&phy3));
|
||||
ut_asserteq(-EIO, generic_phy_power_off(&phy3));
|
||||
ut_asserteq(-EIO, generic_phy_power_off(&phy3));
|
||||
ut_asserteq(-EIO, generic_phy_power_on(&phy3));
|
||||
ut_assertok(generic_phy_power_off(&phy3));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -145,3 +159,62 @@ static int dm_test_phy_bulk(struct unit_test_state *uts)
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_phy_bulk, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
static int dm_test_phy_multi_exit(struct unit_test_state *uts)
|
||||
{
|
||||
struct phy phy1_method1;
|
||||
struct phy phy1_method2;
|
||||
struct phy phy1_method3;
|
||||
struct udevice *parent;
|
||||
|
||||
/* Get the same phy instance in 3 different ways. */
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
|
||||
"gen_phy_user", &parent));
|
||||
ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method1));
|
||||
ut_asserteq(0, phy1_method1.id);
|
||||
ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method2));
|
||||
ut_asserteq(0, phy1_method2.id);
|
||||
ut_asserteq_ptr(phy1_method1.dev, phy1_method1.dev);
|
||||
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
|
||||
"gen_phy_user1", &parent));
|
||||
ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method3));
|
||||
ut_asserteq(0, phy1_method3.id);
|
||||
ut_asserteq_ptr(phy1_method1.dev, phy1_method3.dev);
|
||||
|
||||
/*
|
||||
* Test using the same PHY from different handles.
|
||||
* In non-test code these could be in different drivers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* These must only call the driver's ops at the first init()
|
||||
* and power_on().
|
||||
*/
|
||||
ut_assertok(generic_phy_init(&phy1_method1));
|
||||
ut_assertok(generic_phy_init(&phy1_method2));
|
||||
ut_assertok(generic_phy_power_on(&phy1_method1));
|
||||
ut_assertok(generic_phy_power_on(&phy1_method2));
|
||||
ut_assertok(generic_phy_init(&phy1_method3));
|
||||
ut_assertok(generic_phy_power_on(&phy1_method3));
|
||||
|
||||
/*
|
||||
* These must not call the driver's ops as other handles still
|
||||
* want the PHY powered-on and initialized.
|
||||
*/
|
||||
ut_assertok(generic_phy_power_off(&phy1_method3));
|
||||
ut_assertok(generic_phy_exit(&phy1_method3));
|
||||
|
||||
/*
|
||||
* We would get an error here if the generic_phy_exit() above
|
||||
* actually called the driver's exit(), as the sandbox driver
|
||||
* doesn't allow power-off() after exit().
|
||||
*/
|
||||
ut_assertok(generic_phy_power_off(&phy1_method1));
|
||||
ut_assertok(generic_phy_power_off(&phy1_method2));
|
||||
ut_assertok(generic_phy_exit(&phy1_method1));
|
||||
ut_assertok(generic_phy_exit(&phy1_method2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_phy_multi_exit, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.mark.boardspec('sandbox')
|
||||
@pytest.mark.buildconfigspec('blk')
|
||||
@pytest.mark.buildconfigspec('cmd_lsblk')
|
||||
def test_lsblk(u_boot_console):
|
||||
|
||||
Reference in New Issue
Block a user