[U-Boot] [PATCH 1/2] drivers: pci: add map_bar support for Enhanced Allocation

Makes dm_pci_map_bar function available for integrated PCI devices that support Enhanced Allocation instead of original PCI BAR mechanism.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- drivers/pci/pci-uclass.c | 47 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index cf1e7617ae..3204f156c3 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1341,11 +1341,58 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr, return bus_addr; }
+static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags) +{ + int ea_off, ea_cnt, i, entry_size = 0; + int bar_id = bar - PCI_BASE_ADDRESS_0; + u32 ea_entry; + u64 addr; + + /* handle PCI functions that use Enhanced Allocation */ + ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA); + + if (!ea_off) + return 0; + + /* EA capability structure header */ + dm_pci_read_config32(dev, ea_off, &ea_entry); + ea_cnt = (ea_entry >> 16) & 0x3f; + ea_off += 4; + + for (i = 0; i < ea_cnt; i++, ea_off += entry_size) { + /* Entry header */ + dm_pci_read_config32(dev, ea_off, &ea_entry); + entry_size = (ea_entry & 0x7) * 4; + + if (((ea_entry >> 4) & 0xf) != bar_id) + continue; + + /* Base address, 1st DW */ + dm_pci_read_config32(dev, ea_off + 4, &ea_entry); + addr = ea_entry & ~0x3; + if (ea_entry & 0x2) { + dm_pci_read_config32(dev, ea_off + 12, &ea_entry); + addr |= (u64)ea_entry << 32; + } + + /* size ignored for now */ + return map_physmem(addr, flags, 0); + } + return 0; +} + void *dm_pci_map_bar(struct udevice *dev, int bar, int flags) { pci_addr_t pci_bus_addr; u32 bar_response;
+ /* + * if the function supports Enhanced Allocation use that instead of + * BARs + */ + if (dm_pci_find_capability(dev, PCI_CAP_ID_EA)) + return dm_pci_map_ea_bar(dev, bar, flags); + /* read BAR address */ dm_pci_read_config32(dev, bar, &bar_response); pci_bus_addr = (pci_addr_t)(bar_response & ~0xf); diff --git a/include/pci.h b/include/pci.h index 508f7bca81..e1528bb257 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1314,7 +1314,7 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr, * @dev: Device to check * @bar: Bar number to read (numbered from 0) * @flags: Flags for the region type (PCI_REGION_...) - * @return: pointer to the virtual address to use + * @return: pointer to the virtual address to use or 0 on error */ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags);

Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- drivers/pci/pci-uclass.c | 25 +++++++++++++++++++++++++ include/pci.h | 11 +++++++++++ 2 files changed, 36 insertions(+)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 3204f156c3..12b171f9f2 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1495,6 +1495,31 @@ int dm_pci_find_ext_capability(struct udevice *dev, int cap) return dm_pci_find_next_ext_capability(dev, 0, cap); }
+int dm_pci_flr(struct udevice *dev) +{ + int pcie_off; + u32 cap; + u16 cmd; + + /* look for PCI Express Capability */ + pcie_off = dm_pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pcie_off) + return -ENOENT; + + /* check FLR capability */ + dm_pci_read_config32(dev, pcie_off + 4, &cap); + if (!(cap & PCI_X_CAP_FLR)) + return -ENOENT; + + dm_pci_read_config16(dev, pcie_off + 8, &cmd); + dm_pci_write_config16(dev, pcie_off + 8, cmd | PCI_X_CMD_FLR); + + /* wait 100ms, per PCI spec */ + mdelay(100); + + return 0; +} + UCLASS_DRIVER(pci) = { .id = UCLASS_PCI, .name = "pci", diff --git a/include/pci.h b/include/pci.h index e1528bb257..c93e06dfb1 100644 --- a/include/pci.h +++ b/include/pci.h @@ -384,11 +384,14 @@
/* PCI-X registers */
+#define PCI_X_CAP_FLR 0x10000000 /* Function Level Reset capability */ + #define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ #define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ #define PCI_X_CMD_MAX_READ 0x0000 /* Max Memory Read Byte Count */ #define PCI_X_CMD_MAX_SPLIT 0x0030 /* Max Outstanding Split Transactions */ #define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_CMD_FLR 0x8000 /* EP Function Level Reset */
/* Slot Identification */ @@ -1411,6 +1414,14 @@ int dm_pci_find_next_ext_capability(struct udevice *dev, int start, int cap); */ int dm_pci_find_ext_capability(struct udevice *dev, int cap);
+/** + * dm_pci_flr() - Perform FLR if the device suppoorts it + * + * @dev: PCI device to reset + * @return: 0 if OK, -ENOENT if FLR is not supported by dev + */ +int dm_pci_flr(struct udevice *dev); + #define dm_pci_virt_to_bus(dev, addr, flags) \ dm_pci_phys_to_bus(dev, (virt_to_phys(addr)), (flags)) #define dm_pci_bus_to_virt(dev, addr, flags, len, map_flags) \

Adds a driver for NXP ENETC ethernet controller currently integrated in LS1028a. ENETC is a fairly straight-forward BD ring device and interfaces are presented as PCI EPs on the SoC ECAM.
Signed-off-by: Catalin Horghidan catalin.horghidan@nxp.com Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- configs/ls1028aqds_tfa_defconfig | 1 + configs/ls1028ardb_tfa_defconfig | 1 + drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/fsl_enetc.c | 345 +++++++++++++++++++++++++++++++ drivers/net/fsl_enetc.h | 176 ++++++++++++++++ include/pci_ids.h | 1 + 7 files changed, 532 insertions(+) create mode 100644 drivers/net/fsl_enetc.c create mode 100644 drivers/net/fsl_enetc.h
diff --git a/configs/ls1028aqds_tfa_defconfig b/configs/ls1028aqds_tfa_defconfig index 7982ce4157..11fe344b04 100644 --- a/configs/ls1028aqds_tfa_defconfig +++ b/configs/ls1028aqds_tfa_defconfig @@ -45,6 +45,7 @@ CONFIG_PHY_ATHEROS=y CONFIG_DM_ETH=y CONFIG_PHY_GIGE=y CONFIG_E1000=y +CONFIG_FSL_ENETC=y CONFIG_PCI=y CONFIG_DM_PCI=y CONFIG_DM_PCI_COMPAT=y diff --git a/configs/ls1028ardb_tfa_defconfig b/configs/ls1028ardb_tfa_defconfig index c65e37df79..ab6f2a850c 100644 --- a/configs/ls1028ardb_tfa_defconfig +++ b/configs/ls1028ardb_tfa_defconfig @@ -45,6 +45,7 @@ CONFIG_PHY_ATHEROS=y CONFIG_DM_ETH=y CONFIG_PHY_GIGE=y CONFIG_E1000=y +CONFIG_FSL_ENETC=y CONFIG_PCI=y CONFIG_DM_PCI=y CONFIG_DM_PCI_COMPAT=y diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 6fba5a84dd..4aa82261f8 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -556,4 +556,11 @@ config HIGMACV300_ETH This driver supports HIGMACV300 Ethernet controller found on HiSilicon SoCs.
+config FSL_ENETC + bool "NXP ENETC Ethernet controller" + depends on DM_PCI && DM_ETH + help + This driver supports the NXP ENETC Ethernet controller found on some + of the NXP SoCs. + endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 8d02a37896..67b88ebae8 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -77,3 +77,4 @@ obj-y += ti/ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o +obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c new file mode 100644 index 0000000000..b253292bc0 --- /dev/null +++ b/drivers/net/fsl_enetc.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ENETC ethernet controller driver + * Copyright 2017-2019 NXP + */ + +#include "fsl_enetc.h" + +#include <dm.h> +#include <errno.h> +#include <memalign.h> +#include <asm/io.h> +#include <pci.h> + +static int enetc_bind(struct udevice *dev) +{ + static int eth_num_devices; + char name[16]; + + /* prefer using PCI function numbers, if not available use #idx */ + if (ofnode_valid(dev->node)) + sprintf(name, "enetc%u", PCI_FUNC(pci_get_devfn(dev))); + else + sprintf(name, "enetc#%u", eth_num_devices++); + + device_set_name(dev, name); + + return 0; +} + +/* + * Probe ENETC driver: + * - initialize port and station interface BARs + */ +static int enetc_probe(struct udevice *dev) +{ + struct enetc_devfn *hw = dev_get_priv(dev); + int err = 0; + + if (ofnode_valid(dev->node) && !ofnode_is_available(dev->node)) { + ENETC_DBG(dev, "interface disabled\n"); + return -ENODEV; + } + + /* initialize register */ + hw->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); + if (!hw->regs_base) { + ENETC_DBG(dev, "failed to map BAR0\n"); + return -EINVAL; + } + hw->port_regs = hw->regs_base + 0x10000; + + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); + + return err; +} + +/* ENETC Port MAC address registers accept big-endian format */ +static void enetc_set_primary_mac_addr(struct enetc_devfn *hw, const u8 *addr) +{ + u16 lower = *(const u16 *)(addr + 4); + u32 upper = *(const u32 *)addr; + + enetc_write_port(hw, ENETC_PSIPMAR0, upper); + enetc_write_port(hw, ENETC_PSIPMAR1, lower); +} + +int enetc_enable_si_port(struct enetc_devfn *hw) +{ + u32 val; + + /* set Rx/Tx BDR count */ + val = ENETC_PSICFGR_SET_TXBDR(ENETC_TX_BDR_CNT); + val |= ENETC_PSICFGR_SET_RXBDR(ENETC_RX_BDR_CNT); + enetc_write_port(hw, ENETC_PSICFGR(0), val); + /* set Rx max frame size */ + enetc_write_port(hw, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE); + /* enable MAC port */ + enetc_write_port(hw, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN); + /* enable port */ + enetc_write_port(hw, ENETC_PMR, ENETC_PMR_SI0_EN); + /* set SI cache policy */ + enetc_write(hw, ENETC_SICAR0, ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG); + /* enable SI */ + enetc_write(hw, ENETC_SIMR, ENETC_SIMR_EN); + + return 0; +} + +/* Use a single set of BDs and buffers. It's functionally OK as u-boot doesn't + * use multiple interfaces at once. + */ +DEFINE_ALIGN_BUFFER(struct enetc_tx_bd, enetc_txbd, ENETC_BD_CNT, ENETC_ALIGN); +DEFINE_ALIGN_BUFFER(union enetc_rx_bd, enetc_rxbd, ENETC_BD_CNT, ENETC_ALIGN); +DEFINE_ALIGN_BUFFER(u8, enetc_rx_buff, ENETC_RX_MBUFF_SIZE, ENETC_ALIGN); + +static inline u64 enetc_rxb_address(struct udevice *dev, int i) +{ + int off = i * ENETC_RX_MAXFRM_SIZE; + + return cpu_to_le64(dm_pci_virt_to_mem(dev, enetc_rx_buff + off)); +} + +/** + * Setup a single Tx BD Ring (ID = 0): + * - set Tx buffer descriptor address + * - set the BD count + * - initialize the producer and consumer index + */ +static void enetc_setup_tx_bdr(struct enetc_devfn *hw) +{ + struct bd_ring *tx_bdr = &hw->tx_bdr; + u64 tx_bd_add = (u64)enetc_txbd; + + /* used later to advance to the next Tx BD */ + tx_bdr->bd_count = ENETC_BD_CNT; + tx_bdr->next_prod_idx = 0; + tx_bdr->next_cons_idx = 0; + tx_bdr->cons_idx = hw->regs_base + + ENETC_BDR(TX, ENETC_TX_BDR_ID, ENETC_TBCIR); + tx_bdr->prod_idx = hw->regs_base + + ENETC_BDR(TX, ENETC_TX_BDR_ID, ENETC_TBPIR); + + /* set Tx BD address */ + enetc_bdr_write(hw, TX, ENETC_TX_BDR_ID, ENETC_TBBAR0, + lower_32_bits(tx_bd_add)); + enetc_bdr_write(hw, TX, ENETC_TX_BDR_ID, ENETC_TBBAR1, + upper_32_bits(tx_bd_add)); + /* set Tx 8 BD count */ + enetc_bdr_write(hw, TX, ENETC_TX_BDR_ID, ENETC_TBLENR, + tx_bdr->bd_count); + + /* reset both producer/consumer indexes */ + enetc_write_reg(tx_bdr->cons_idx, tx_bdr->next_cons_idx); + enetc_write_reg(tx_bdr->prod_idx, tx_bdr->next_prod_idx); + + /* enable TX ring */ + enetc_bdr_write(hw, TX, ENETC_TX_BDR_ID, ENETC_TBMR, ENETC_TBMR_EN); +} + +/** + * Setup a single Rx BD Ring (ID = 0): + * - set Rx buffer descriptors address (one descriptor per buffer) + * - set buffer size as max frame size + * - enable Rx ring + * - reset consumer and producer indexes + * - set buffer for each descriptor + */ +static void enetc_setup_rx_bdr(struct udevice *dev, struct enetc_devfn *hw) +{ + struct bd_ring *rx_bdr = &hw->rx_bdr; + u64 rx_bd_add = (u64)enetc_rxbd; + int i; + + /* used later to advance to the next BD produced by ENETC HW */ + rx_bdr->bd_count = ENETC_BD_CNT; + rx_bdr->next_prod_idx = 0; + rx_bdr->next_cons_idx = ENETC_RBCI_INIT; + rx_bdr->cons_idx = hw->regs_base + + ENETC_BDR(RX, ENETC_RX_BDR_ID, ENETC_RBCIR); + rx_bdr->prod_idx = hw->regs_base + + ENETC_BDR(RX, ENETC_RX_BDR_ID, ENETC_RBPIR); + + /* set Rx BD address */ + enetc_bdr_write(hw, RX, ENETC_RX_BDR_ID, ENETC_RBBAR0, + lower_32_bits(rx_bd_add)); + enetc_bdr_write(hw, RX, ENETC_RX_BDR_ID, ENETC_RBBAR1, + upper_32_bits(rx_bd_add)); + /* set Rx BD count (multiple of 8) */ + enetc_bdr_write(hw, RX, ENETC_RX_BDR_ID, ENETC_RBLENR, + rx_bdr->bd_count); + /* set Rx buffer size */ + enetc_bdr_write(hw, RX, ENETC_RX_BDR_ID, ENETC_RBBSR, ENETC_BUFF_SIZE); + + /* reset producer (ENETC owned) and consumer (SW owned) index */ + enetc_write_reg(rx_bdr->cons_idx, rx_bdr->next_cons_idx); + enetc_write_reg(rx_bdr->prod_idx, rx_bdr->next_prod_idx); + + /* fill Rx BD */ + memset(enetc_rxbd, 0, rx_bdr->bd_count * sizeof(union enetc_rx_bd)); + for (i = 0; i < rx_bdr->bd_count; i++) { + enetc_rxbd[i].w.addr = enetc_rxb_address(dev, i); + /* each RX buffer must be aligned to 64B */ + WARN_ON(enetc_rxbd[i].w.addr & (ENETC_ALIGN - 1)); + } + /* enable Rx ring */ + enetc_bdr_write(hw, RX, ENETC_RX_BDR_ID, ENETC_RBMR, ENETC_RBMR_EN); +} + +/** + * Start ENETC interface: + * - perform FLR + * - enable access to port and SI registers + * - set mac address + * - setup TX/RX buffer descriptors + * - enable Tx/Rx rings + */ +static int enetc_start(struct udevice *dev) +{ + struct eth_pdata *plat = dev_get_platdata(dev); + struct enetc_devfn *hw = dev_get_priv(dev); + u32 if_mode; + + /* reset and enable the PCI device */ + dm_pci_flr(dev); + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + if_mode = enetc_read_port(hw, ENETC_PM_IF_MODE); + if (if_mode & ENETC_PM_IF_MODE_RG) + if_mode |= ENETC_PM_IF_MODE_AN_ENA; + enetc_write_port(hw, ENETC_PM_IF_MODE, if_mode); + + if (!is_valid_ethaddr(plat->enetaddr)) { + ENETC_DBG(dev, "invalid MAC address, generate random ...\n"); + net_random_ethaddr(plat->enetaddr); + } + enetc_set_primary_mac_addr(hw, plat->enetaddr); + + enetc_enable_si_port(hw); + + /* setup Tx/Rx buffer descriptors */ + enetc_setup_tx_bdr(hw); + enetc_setup_rx_bdr(dev, hw); + + return 0; +} + +/* FLR is sufficient to quiesce the device */ +static void enetc_stop(struct udevice *dev) +{ + /* reset device */ + dm_pci_flr(dev); +} + +/** + * ENETC transmit packet: + * - check if Tx BD ring is full + * - set buffer/packet address (dma address) + * - set final fragment flag + * - try while producer index equals consumer index or timeout + */ +static int enetc_send(struct udevice *dev, void *packet, int length) +{ + struct enetc_devfn *hw = dev_get_priv(dev); + struct bd_ring *txr = &hw->tx_bdr; + void *nv_packet = (void *)packet; + int tries = ENETC_POLL_TRIES; + u32 pi, ci; + + pi = txr->next_prod_idx; + ci = enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK; + /* Tx ring is full when */ + if (((pi + 1) % txr->bd_count) == ci) { + ENETC_DBG(dev, "Tx BDR full\n"); + return -ETIMEDOUT; + } + ENETC_DBG(dev, "TxBD[%d]send: pkt_len=%d, buff @0x%x%08x\n", pi, length, + upper_32_bits((u64)nv_packet), lower_32_bits((u64)nv_packet)); + + /* prepare Tx BD */ + memset(&enetc_txbd[pi], 0x0, sizeof(struct enetc_tx_bd)); + enetc_txbd[pi].addr = cpu_to_le64(dm_pci_virt_to_mem(dev, nv_packet)); + enetc_txbd[pi].buf_len = cpu_to_le16(length); + enetc_txbd[pi].frm_len = cpu_to_le16(length); + enetc_txbd[pi].flags = cpu_to_le16(ENETC_TXBD_FLAGS_F); + dmb(); + /* send frame: increment producer index */ + pi = (pi + 1) % txr->bd_count; + txr->next_prod_idx = pi; + enetc_write_reg(txr->prod_idx, pi); + while ((--tries >= 0) && + (pi != (enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK))) + udelay(10); + + return tries > 0 ? 0 : -ETIMEDOUT; +} + +/** + * Handles frame receive and cleans up the BD slot on the Rx ring. + */ +static int enetc_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct enetc_devfn *hw = dev_get_priv(dev); + struct bd_ring *rxr = &hw->rx_bdr; + int tries = ENETC_POLL_TRIES; + int pi = rxr->next_prod_idx; + int ci = rxr->next_cons_idx; + u32 status; + int len; + u8 rdy; + + do { + dmb(); + status = le32_to_cpu(enetc_rxbd[pi].r.lstatus); + /* check if current BD is ready to be consumed */ + rdy = ENETC_RXBD_STATUS_R(status); + } while (--tries >= 0 && !rdy); + + if (!rdy) + return -EAGAIN; + + dmb(); + len = le16_to_cpu(enetc_rxbd[pi].r.buf_len); + *packetp = (uchar *)enetc_rxb_address(dev, pi); + ENETC_DBG(dev, "RxBD[%d]: len=%d err=%d pkt=0x%x%08x\n", pi, len, + ENETC_RXBD_STATUS_ERRORS(status), + upper_32_bits((u64)*packetp), lower_32_bits((u64)*packetp)); + + /* BD clean up and advance to next in ring */ + memset(&enetc_rxbd[pi], 0, sizeof(union enetc_rx_bd)); + enetc_rxbd[pi].w.addr = enetc_rxb_address(dev, pi); + rxr->next_prod_idx = (pi + 1) % rxr->bd_count; + ci = (ci + 1) % rxr->bd_count; + rxr->next_cons_idx = ci; + dmb(); + /* free up the slot in the ring for HW */ + enetc_write_reg(rxr->cons_idx, ci); + + return len; +} + +static const struct eth_ops enetc_ops = { + .start = enetc_start, + .send = enetc_send, + .recv = enetc_recv, + .stop = enetc_stop, +}; + +U_BOOT_DRIVER(eth_enetc) = { + .name = "enetc_eth", + .id = UCLASS_ETH, + .bind = enetc_bind, + .probe = enetc_probe, + .ops = &enetc_ops, + .priv_auto_alloc_size = sizeof(struct enetc_devfn), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static struct pci_device_id enetc_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_PF_V1) }, + {} +}; + +U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids); diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h new file mode 100644 index 0000000000..4ebf03a61a --- /dev/null +++ b/drivers/net/fsl_enetc.h @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ENETC ethernet controller driver + * Copyright 2017-2019 NXP + */ + +#ifndef _ENETC_H +#define _ENETC_H + +#include <common.h> + +#ifdef ENETC_DEBUG +#define ENETC_DBG(udev, fmt, args...) \ + printf("enetc[%s]: " fmt, udev->name, ##args) +#else +#define ENETC_DBG(devno, args...) do { } while (0) +#endif +/* ENETC controller registers */ + +/* Station interface register offsets */ +#define ENETC_SIMR 0 +#define ENETC_SIMR_EN BIT(31) +#define ENETC_SICAR0 0x40 +/* write cache cfg: snoop, no allocate, update full (data), partial (BD) */ +#define ENETC_SICAR_WR_CFG 0x6767 +/* read cache cfg: coherent copy, look up, no allocate */ +#define ENETC_SICAR_RD_CFG 0x27270000 + +#define ENETC_SIROCT 0x300 +#define ENETC_SIRFRM 0x308 +#define ENETC_SITOCT 0x320 +#define ENETC_SITFRM 0x328 + +/* Station Interface Rx/Tx Buffer Descriptor Ring registers */ +enum enetc_bdr_type {TX, RX}; +#define ENETC_BDR(type, n, off) (0x8000 + (type) * 0x100 + (n) * 0x200 + (off)) +#define ENETC_BDR_IDX_MASK 0xffff + +/* Rx BDR reg offsets */ +#define ENETC_RBMR 0 +#define ENETC_RBMR_EN BIT(31) +#define ENETC_RBBSR 0x8 +#define ENETC_RBCIR 0xc +/* initial consumer index for Rx BDR */ +#define ENETC_RBCI_INIT 4 +#define ENETC_RBBAR0 0x10 +#define ENETC_RBBAR1 0x14 +#define ENETC_RBPIR 0x18 +#define ENETC_RBLENR 0x20 + +/* Tx BDR reg offsets */ +#define ENETC_TBMR 0 +#define ENETC_TBMR_EN BIT(31) +#define ENETC_TBBAR0 0x10 +#define ENETC_TBBAR1 0x14 +#define ENETC_TBPIR 0x18 +#define ENETC_TBCIR 0x1c +#define ENETC_TBLENR 0x20 + +/* Port registers offset */ +#define ENETC_PORT_REGS_OFF 0x10000 +#define ENETC_PMR 0x00000 + +#define ENETC_PMR_SI0_EN BIT(16) +#define ENETC_PSIPMMR 0x18 +#define ENETC_PSIPMAR0 0x00100 +#define ENETC_PSIPMAR1 0x00104 +#define ENETC_PSICFGR(n) (0x00940 + (n) * 0x10) +#define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff) +#define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16) +#define ENETC_EMDIO_CFG 0x1c00 +#define ENETC_PM_CC 0x8008 +/* Port config: enable MAC Tx/Rx, Tx padding, MAC promisc */ +#define ENETC_PM_CC_DEFAULT 0x810 +#define ENETC_PM_CC_RX_TX_EN 0x8813 +#define ENETC_PM_MAXFRM 0x8014 +#define ENETC_RX_MAXFRM_SIZE PKTSIZE_ALIGN +#define ENETC_BUFF_SIZE PKTSIZE_ALIGN +#define ENETC_PM_IF_MODE 0x8300 +#define ENETC_PM_IF_MODE_RG 0x00000004 +#define ENETC_PM_IF_MODE_AN_ENA 0x00008000 +#define ENETC_PM_IF_STATUS 0x8304 +#define ENETC_PM_IF_STATUS_RGL 0x00008000 +#define ENETC_PM_IF_STATUS_RGFD 0x00001000 +/* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */ +#define ENETC_BD_CNT 16 +#define ENETC_ALIGN 128 +#define ENETC_RX_MBUFF_SIZE (ENETC_BD_CNT * ENETC_RX_MAXFRM_SIZE) + +/* single pair of Rx/Tx rings */ +#define ENETC_RX_BDR_CNT 1 +#define ENETC_TX_BDR_CNT 1 +#define ENETC_RX_BDR_ID 0 +#define ENETC_TX_BDR_ID 0 + +/* Tx buffer descriptor */ +struct enetc_tx_bd { + __le64 addr; + __le16 buf_len; + __le16 frm_len; + __le16 err_csum; + __le16 flags; +}; + +#define ENETC_TXBD_FLAGS_F BIT(15) +#define ENETC_POLL_TRIES 0x8000 + +/* Rx buffer descriptor */ +union enetc_rx_bd { + /* SW provided BD format */ + struct { + __le64 addr; + u8 reserved[8]; + } w; + + /* ENETC returned BD format */ + struct { + __le16 inet_csum; + __le16 parse_summary; + __le32 rss_hash; + __le16 buf_len; + __le16 vlan_opt; + union { + struct { + __le16 flags; + __le16 error; + }; + __le32 lstatus; + }; + } r; +}; + +#define ENETC_RXBD_STATUS_R(status) (((status) >> 30) & 0x1) +#define ENETC_RXBD_STATUS_F(status) (((status) >> 31) & 0x1) +#define ENETC_RXBD_STATUS_ERRORS(status) (((status) >> 16) & 0xff) +#define ENETC_RXBD_STATUS(flags) ((flags) << 16) + +/* Tx/Rx ring info */ +struct bd_ring { + void *cons_idx; + void *prod_idx; + /* next BD index to use */ + int next_prod_idx; + int next_cons_idx; + int bd_count; +}; + +/* ENETC HW access info */ +struct enetc_devfn { + void *regs_base; /* base ENETC registers */ + void *port_regs; /* base ENETC port registers */ + + /* Rx/Tx buffer descriptor rings info */ + struct bd_ring tx_bdr; + struct bd_ring rx_bdr; +}; + +/* register accessors */ +#define enetc_read_reg(x) readl((x)) +#define enetc_write_reg(x, val) writel((val), (x)) +#define enetc_read(hw, off) enetc_read_reg((hw)->regs_base + (off)) +#define enetc_write(hw, off, v) enetc_write_reg((hw)->regs_base + (off), v) + +/* port register accessors */ +#define enetc_port_regs(hw, off) ((hw)->port_regs + (off)) +#define enetc_read_port(hw, off) enetc_read_reg(enetc_port_regs((hw), (off))) +#define enetc_write_port(hw, off, v) \ + enetc_write_reg(enetc_port_regs((hw), (off)), v) + +/* BDR register accessors, see ENETC_BDR() */ +#define enetc_bdr_read(hw, t, n, off) \ + enetc_read(hw, ENETC_BDR(t, n, off)) +#define enetc_bdr_write(hw, t, n, off, val) \ + enetc_write(hw, ENETC_BDR(t, n, off), val) + +#endif /* _ENETC_H */ diff --git a/include/pci_ids.h b/include/pci_ids.h index bd59578ccb..06e1319366 100644 --- a/include/pci_ids.h +++ b/include/pci_ids.h @@ -2483,6 +2483,7 @@ #define PCI_DEVICE_ID_MPC8641 0x7010 #define PCI_DEVICE_ID_MPC8641D 0x7011 #define PCI_DEVICE_ID_MPC8610 0x7018 +#define PCI_DEVICE_ID_ENETC_PF_V1 0xE100
#define PCI_VENDOR_ID_PASEMI 0x1959

Adds a driver for the MDIO interface currently integrated in LS1028a SoC. This MDIO interface is shared by multiple ethernet interfaces and is presented as a stand-alone PCI function on the SoC ECAM.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- configs/ls1028aqds_tfa_defconfig | 1 + configs/ls1028ardb_tfa_defconfig | 1 + drivers/net/fsl_enetc.c | 169 +++++++++++++++++++++++++++++++ drivers/net/fsl_enetc.h | 13 +++ include/pci_ids.h | 1 + 5 files changed, 185 insertions(+)
diff --git a/configs/ls1028aqds_tfa_defconfig b/configs/ls1028aqds_tfa_defconfig index 11fe344b04..84a1bf90bf 100644 --- a/configs/ls1028aqds_tfa_defconfig +++ b/configs/ls1028aqds_tfa_defconfig @@ -43,6 +43,7 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_PHYLIB=y CONFIG_PHY_ATHEROS=y CONFIG_DM_ETH=y +CONFIG_DM_MDIO=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_FSL_ENETC=y diff --git a/configs/ls1028ardb_tfa_defconfig b/configs/ls1028ardb_tfa_defconfig index ab6f2a850c..3f5bc2e139 100644 --- a/configs/ls1028ardb_tfa_defconfig +++ b/configs/ls1028ardb_tfa_defconfig @@ -43,6 +43,7 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_PHYLIB=y CONFIG_PHY_ATHEROS=y CONFIG_DM_ETH=y +CONFIG_DM_MDIO=y CONFIG_PHY_GIGE=y CONFIG_E1000=y CONFIG_FSL_ENETC=y diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index b253292bc0..6be01e7d7f 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -11,6 +11,7 @@ #include <memalign.h> #include <asm/io.h> #include <pci.h> +#include <miiphy.h>
static int enetc_bind(struct udevice *dev) { @@ -28,6 +29,61 @@ static int enetc_bind(struct udevice *dev) return 0; }
+static void enetc_start_phy(struct udevice *dev) +{ +#ifdef CONFIG_DM_MDIO + int supported, if_type = PHY_INTERFACE_MODE_NONE; + struct udevice *miidev; + struct phy_device *phy; + u32 phandle, phy_id; + const char *if_str; + ofnode phy_node; + + if (!ofnode_valid(dev->node)) { + ENETC_DBG(dev, "no enetc ofnode found, skipping PHY set-up\n"); + return; + } + + if (ofnode_read_u32(dev->node, "phy-handle", &phandle)) { + ENETC_DBG(dev, "phy-handle not found, skipping PHY set-up\n"); + return; + } + + phy_node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(phy_node)) { + ENETC_DBG(dev, "invalid phy node, skipping PHY set-up\n"); + return; + } + ENETC_DBG(dev, "phy node: %s\n", ofnode_get_name(phy_node)); + + if (ofnode_read_u32(phy_node, "reg", &phy_id)) { + ENETC_DBG(dev, + "missing reg in PHY node, skipping PHY set-up\n"); + return; + } + + if_str = ofnode_read_string(phy_node, "phy-mode"); + if (if_str) + if_type = phy_get_interface_by_name(if_str); + if (if_type < 0) + if_type = PHY_INTERFACE_MODE_NONE; + + if (uclass_get_device_by_ofnode(UCLASS_MDIO, + ofnode_get_parent(phy_node), + &miidev)) + return; + + phy = dm_mdio_phy_connect(miidev, phy_id, dev, if_type); + if (!phy) + return; + + supported = GENMASK(6, 0); /* speeds up to 1G & AN */ + phy->advertising = phy->supported & supported; + phy_config(phy); + phy_startup(phy); +#endif +} + /* * Probe ENETC driver: * - initialize port and station interface BARs @@ -223,6 +279,8 @@ static int enetc_start(struct udevice *dev) enetc_setup_tx_bdr(hw); enetc_setup_rx_bdr(dev, hw);
+ enetc_start_phy(dev); + return 0; }
@@ -343,3 +401,114 @@ static struct pci_device_id enetc_ids[] = { };
U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids); + +#ifdef CONFIG_DM_MDIO + +static int enetc_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct enetc_devfn *hw = dev_get_priv(dev); + + if (devad == MDIO_DEVAD_NONE) + enetc_write(hw, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(hw, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + + while (enetc_read(hw, ENETC_MDIO_CFG) & 1) + ; + if (devad == MDIO_DEVAD_NONE) { + enetc_write(hw, ENETC_MDIO_CTL, (addr << 5) + reg + 0x8000); + } else { + enetc_write(hw, ENETC_MDIO_CTL, (addr << 5) + devad); + while (enetc_read(hw, ENETC_MDIO_CFG) & 1) + ; + enetc_write(hw, ENETC_MDIO_STAT, reg); + while (enetc_read(hw, ENETC_MDIO_CFG) & 1) + ; + enetc_write(hw, ENETC_MDIO_CTL, (addr << 5) + devad + 0x8000); + } + + while (enetc_read(hw, ENETC_MDIO_CFG) & 1) + ; + if (enetc_read(hw, ENETC_MDIO_CFG) & 2) + return ENETC_MDIO_READ_ERR; + + return enetc_read(hw, ENETC_MDIO_DATA); +} + +static int enetc_mdio_write(struct udevice *dev, int addr, int devad, int reg, + u16 val) +{ + struct enetc_devfn *hw = dev_get_priv(dev); + + if (devad == MDIO_DEVAD_NONE) + enetc_write(hw, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(hw, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + + while (enetc_read(hw, ENETC_MDIO_CFG) & 1) + ; + if (devad != MDIO_DEVAD_NONE) { + enetc_write(hw, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_write(hw, ENETC_MDIO_STAT, reg); + } else { + enetc_write(hw, ENETC_MDIO_CTL, (addr << 5) + reg); + } + while (enetc_read(hw, ENETC_MDIO_CFG) & 1) + ; + enetc_write(hw, ENETC_MDIO_DATA, val); + while (enetc_read(hw, ENETC_MDIO_CFG) & 1) + ; + return 0; +} + +static const struct mdio_ops enetc_mdio_ops = { + .read = enetc_mdio_read, + .write = enetc_mdio_write, +}; + +static int enetc_mdio_bind(struct udevice *dev) +{ + static int mdio_num_devices; + char name[16]; + + if (ofnode_valid(dev->node)) + sprintf(name, "emdio%u", PCI_FUNC(pci_get_devfn(dev))); + else + sprintf(name, "emdio#%u", mdio_num_devices++); + + device_set_name(dev, name); + + return 0; +} + +static int enetc_mdio_probe(struct udevice *dev) +{ + struct enetc_devfn *hw = dev_get_priv(dev); + + hw->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); + if (!hw->regs_base) { + ENETC_DBG(dev, "failed to map BAR0\n"); + return -EINVAL; + } + + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); + + return 0; +} + +U_BOOT_DRIVER(enetc_mdio) = { + .name = "enetc_mdio", + .id = UCLASS_MDIO, + .bind = enetc_mdio_bind, + .probe = enetc_mdio_probe, + .ops = &enetc_mdio_ops, + .priv_auto_alloc_size = sizeof(struct enetc_mdio_devfn), +}; + +static struct pci_device_id enetc_mdio_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_MDIO_V1) }, +}; + +U_BOOT_PCI_DEVICE(enetc_mdio, enetc_mdio_ids); + +#endif /* CONFIG_DM_MDIO */ diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index 4ebf03a61a..3636ce1d93 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -173,4 +173,17 @@ struct enetc_devfn { #define enetc_bdr_write(hw, t, n, off, val) \ enetc_write(hw, ENETC_BDR(t, n, off), val)
+#define ENETC_MDIO_CFG 0x1c00 +#define ENETC_EMDIO_CFG_C22 0x00009508 +#define ENETC_EMDIO_CFG_C45 0x00009548 +#define ENETC_MDIO_CTL 0x1c04 +#define ENETC_MDIO_DATA 0x1c08 +#define ENETC_MDIO_STAT 0x1c0c + +#define ENETC_MDIO_READ_ERR 0xffff + +struct enetc_mdio_devfn { + void *regs_base; +}; + #endif /* _ENETC_H */ diff --git a/include/pci_ids.h b/include/pci_ids.h index 06e1319366..c4b424c6a9 100644 --- a/include/pci_ids.h +++ b/include/pci_ids.h @@ -2484,6 +2484,7 @@ #define PCI_DEVICE_ID_MPC8641D 0x7011 #define PCI_DEVICE_ID_MPC8610 0x7018 #define PCI_DEVICE_ID_ENETC_PF_V1 0xE100 +#define PCI_DEVICE_ID_MDIO_V1 0xEE01
#define PCI_VENDOR_ID_PASEMI 0x1959

+Simon
Hi Alex,
On Sat, Jun 1, 2019 at 12:27 AM Alex Marginean alexm.osslist@gmail.com wrote:
Please add a commit message to explain the changes.
Also a nits in the commit tile: please remove the ,
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
drivers/pci/pci-uclass.c | 25 +++++++++++++++++++++++++ include/pci.h | 11 +++++++++++ 2 files changed, 36 insertions(+)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 3204f156c3..12b171f9f2 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1495,6 +1495,31 @@ int dm_pci_find_ext_capability(struct udevice *dev, int cap) return dm_pci_find_next_ext_capability(dev, 0, cap); }
+int dm_pci_flr(struct udevice *dev) +{
int pcie_off;
u32 cap;
u16 cmd;
/* look for PCI Express Capability */
pcie_off = dm_pci_find_capability(dev, PCI_CAP_ID_EXP);
if (!pcie_off)
return -ENOENT;
/* check FLR capability */
dm_pci_read_config32(dev, pcie_off + 4, &cap);
Please use macros for offset 4
if (!(cap & PCI_X_CAP_FLR))
return -ENOENT;
dm_pci_read_config16(dev, pcie_off + 8, &cmd);
ditto
dm_pci_write_config16(dev, pcie_off + 8, cmd | PCI_X_CMD_FLR);
/* wait 100ms, per PCI spec */
mdelay(100);
return 0;
+}
UCLASS_DRIVER(pci) = { .id = UCLASS_PCI, .name = "pci", diff --git a/include/pci.h b/include/pci.h index e1528bb257..c93e06dfb1 100644 --- a/include/pci.h +++ b/include/pci.h @@ -384,11 +384,14 @@
/* PCI-X registers */
+#define PCI_X_CAP_FLR 0x10000000 /* Function Level Reset capability */
This is not PCI_X, but PCI express.
#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ #define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ #define PCI_X_CMD_MAX_READ 0x0000 /* Max Memory Read Byte Count */ #define PCI_X_CMD_MAX_SPLIT 0x0030 /* Max Outstanding Split Transactions */ #define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_CMD_FLR 0x8000 /* EP Function Level Reset */
Ditto. Please import appropriate macros from Linux for above magic numbers too.
/* Slot Identification */ @@ -1411,6 +1414,14 @@ int dm_pci_find_next_ext_capability(struct udevice *dev, int start, int cap); */ int dm_pci_find_ext_capability(struct udevice *dev, int cap);
+/**
- dm_pci_flr() - Perform FLR if the device suppoorts it
- @dev: PCI device to reset
- @return: 0 if OK, -ENOENT if FLR is not supported by dev
- */
+int dm_pci_flr(struct udevice *dev);
#define dm_pci_virt_to_bus(dev, addr, flags) \ dm_pci_phys_to_bus(dev, (virt_to_phys(addr)), (flags))
#define dm_pci_bus_to_virt(dev, addr, flags, len, map_flags) \
Regards, Bin

+Simon,
Hi Alex,
On Sat, Jun 1, 2019 at 12:26 AM Alex Marginean alexm.osslist@gmail.com wrote:
Makes dm_pci_map_bar function available for integrated PCI devices that support Enhanced Allocation instead of original PCI BAR mechanism.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
drivers/pci/pci-uclass.c | 47 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index cf1e7617ae..3204f156c3 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1341,11 +1341,58 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr, return bus_addr; }
+static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags) +{
int ea_off, ea_cnt, i, entry_size = 0;
nits: no need to initialize entry_size here.
int bar_id = bar - PCI_BASE_ADDRESS_0;
This does not work for anything other than BAR0. It should be (bar - PCI_BASE_ADDRESS_0) >> 2;
u32 ea_entry;
u64 addr;
This should be: pci_addr_t addr
/* handle PCI functions that use Enhanced Allocation */
ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA);
if (!ea_off)
return 0;
Above codes are not necessary. EA offset is already known when calling dm_pci_map_ea_bar() from dm_pci_map_bar(). We can pass the offset to this function.
/* EA capability structure header */
dm_pci_read_config32(dev, ea_off, &ea_entry);
ea_cnt = (ea_entry >> 16) & 0x3f;
Avoid using magic numbers, instead use a macro PCI_EA_NUM_ENT_MASK. In fact, Linux has several macros for EA capability (include/uapi/linux/pci_regs.h) and we can just import these macros in U-Boot too.
ea_off += 4;
for (i = 0; i < ea_cnt; i++, ea_off += entry_size) {
nits: two spaces before entry_size
/* Entry header */
dm_pci_read_config32(dev, ea_off, &ea_entry);
entry_size = (ea_entry & 0x7) * 4;
Per the spec, entry size is number of DW following the initial DW in this entry. So it should really be: ((ea_entry & 0x7) + 1) * 4. Again like the bar_id comments above, we can use << 2 here instead of * 4.
if (((ea_entry >> 4) & 0xf) != bar_id)
continue;
/* Base address, 1st DW */
dm_pci_read_config32(dev, ea_off + 4, &ea_entry);
addr = ea_entry & ~0x3;
if (ea_entry & 0x2) {
dm_pci_read_config32(dev, ea_off + 12, &ea_entry);
addr |= (u64)ea_entry << 32;
}
/* size ignored for now */
return map_physmem(addr, flags, 0);
}
nits: should have one blank line here
return 0;
+}
void *dm_pci_map_bar(struct udevice *dev, int bar, int flags) { pci_addr_t pci_bus_addr; u32 bar_response;
/*
* if the function supports Enhanced Allocation use that instead of
* BARs
*/
if (dm_pci_find_capability(dev, PCI_CAP_ID_EA))
return dm_pci_map_ea_bar(dev, bar, flags);
/* read BAR address */ dm_pci_read_config32(dev, bar, &bar_response); pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
diff --git a/include/pci.h b/include/pci.h index 508f7bca81..e1528bb257 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1314,7 +1314,7 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr,
- @dev: Device to check
- @bar: Bar number to read (numbered from 0)
This one is confusing. It it not bar number (0/1/...), but bar register offset. Suggest a separate patch to correct it. And this function seems to only handle BAR0-BAR0 for header type 0. Please also comment that.
- @flags: Flags for the region type (PCI_REGION_...)
- @return: pointer to the virtual address to use
- @return: pointer to the virtual address to use or 0 on error
This should be separate patch to correct the comments. Together with the bar comments above.
*/ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags);
Please create test cases in test/dm/pci.c to cover the EA capability test. Especially since there are some bugs in the for loop in dm_pci_map_ea_bar(), we should create case to get something like BAR3 instead of BAR0. I suspect why you did not see the issue was because you only covered the BAR0 hence only one iteration of the for loop was executed.
Regards, Bin

Hi Bin,
On 6/2/2019 4:15 PM, Bin Meng wrote:
+Simon,
Hi Alex,
On Sat, Jun 1, 2019 at 12:26 AM Alex Marginean alexm.osslist@gmail.com wrote:
Makes dm_pci_map_bar function available for integrated PCI devices that support Enhanced Allocation instead of original PCI BAR mechanism.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
drivers/pci/pci-uclass.c | 47 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index cf1e7617ae..3204f156c3 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1341,11 +1341,58 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr, return bus_addr; }
+static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags) +{
int ea_off, ea_cnt, i, entry_size = 0;
nits: no need to initialize entry_size here.
int bar_id = bar - PCI_BASE_ADDRESS_0;
This does not work for anything other than BAR0. It should be (bar - PCI_BASE_ADDRESS_0) >> 2;
Good find, you're right, I did use it only for BAR0 and missed this.
u32 ea_entry;
u64 addr;
This should be: pci_addr_t addr
I think maybe phys_addr_t is more appropriate, EA functions are supposed to be integrated and their BAR equivalent addresses map into system address space. In the end this goes to map_physmem, which takes a phys_addr_t.
/* handle PCI functions that use Enhanced Allocation */
ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA);
if (!ea_off)
return 0;
Above codes are not necessary. EA offset is already known when calling dm_pci_map_ea_bar() from dm_pci_map_bar(). We can pass the offset to this function.
/* EA capability structure header */
dm_pci_read_config32(dev, ea_off, &ea_entry);
ea_cnt = (ea_entry >> 16) & 0x3f;
Avoid using magic numbers, instead use a macro PCI_EA_NUM_ENT_MASK. In fact, Linux has several macros for EA capability (include/uapi/linux/pci_regs.h) and we can just import these macros in U-Boot too.
That's a good suggestion, I will do that.
ea_off += 4;
for (i = 0; i < ea_cnt; i++, ea_off += entry_size) {
nits: two spaces before entry_size
/* Entry header */
dm_pci_read_config32(dev, ea_off, &ea_entry);
entry_size = (ea_entry & 0x7) * 4;
Per the spec, entry size is number of DW following the initial DW in this entry. So it should really be: ((ea_entry & 0x7) + 1) * 4. Again like the bar_id comments above, we can use << 2 here instead of * 4.
if (((ea_entry >> 4) & 0xf) != bar_id)
continue;
/* Base address, 1st DW */
dm_pci_read_config32(dev, ea_off + 4, &ea_entry);
addr = ea_entry & ~0x3;
if (ea_entry & 0x2) {
dm_pci_read_config32(dev, ea_off + 12, &ea_entry);
addr |= (u64)ea_entry << 32;
}
/* size ignored for now */
return map_physmem(addr, flags, 0);
}
nits: should have one blank line here
return 0;
+}
void *dm_pci_map_bar(struct udevice *dev, int bar, int flags) { pci_addr_t pci_bus_addr; u32 bar_response;
/*
* if the function supports Enhanced Allocation use that instead of
* BARs
*/
if (dm_pci_find_capability(dev, PCI_CAP_ID_EA))
return dm_pci_map_ea_bar(dev, bar, flags);
/* read BAR address */ dm_pci_read_config32(dev, bar, &bar_response); pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
diff --git a/include/pci.h b/include/pci.h index 508f7bca81..e1528bb257 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1314,7 +1314,7 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr,
- @dev: Device to check
- @bar: Bar number to read (numbered from 0)
This one is confusing. It it not bar number (0/1/...), but bar register offset. Suggest a separate patch to correct it. And this function seems to only handle BAR0-BAR0 for header type 0. Please also comment that.
I suppose it works for BARs0-5 on type 0. I'm not clear if it also works for type 1 though, type 1 defines a couple of BARs at offset 0x10 too.
- @flags: Flags for the region type (PCI_REGION_...)
- @return: pointer to the virtual address to use
- @return: pointer to the virtual address to use or 0 on error
This should be separate patch to correct the comments. Together with the bar comments above.
*/ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags);
Please create test cases in test/dm/pci.c to cover the EA capability test. Especially since there are some bugs in the for loop in dm_pci_map_ea_bar(), we should create case to get something like BAR3 instead of BAR0. I suspect why you did not see the issue was because you only covered the BAR0 hence only one iteration of the for loop was executed.
Yes, that's precisely what I did..
Regards, Bin
Thank you! Alex

Hi Alex,
On Mon, Jun 3, 2019 at 8:49 PM Alex Marginean alexm.osslist@gmail.com wrote:
Hi Bin,
On 6/2/2019 4:15 PM, Bin Meng wrote:
+Simon,
Hi Alex,
On Sat, Jun 1, 2019 at 12:26 AM Alex Marginean alexm.osslist@gmail.com wrote:
Makes dm_pci_map_bar function available for integrated PCI devices that support Enhanced Allocation instead of original PCI BAR mechanism.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
drivers/pci/pci-uclass.c | 47 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index cf1e7617ae..3204f156c3 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1341,11 +1341,58 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr, return bus_addr; }
+static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags) +{
int ea_off, ea_cnt, i, entry_size = 0;
nits: no need to initialize entry_size here.
int bar_id = bar - PCI_BASE_ADDRESS_0;
This does not work for anything other than BAR0. It should be (bar - PCI_BASE_ADDRESS_0) >> 2;
Good find, you're right, I did use it only for BAR0 and missed this.
u32 ea_entry;
u64 addr;
This should be: pci_addr_t addr
I think maybe phys_addr_t is more appropriate, EA functions are supposed to be integrated and their BAR equivalent addresses map into system address space. In the end this goes to map_physmem, which takes a phys_addr_t.
Makes sense.
/* handle PCI functions that use Enhanced Allocation */
ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA);
if (!ea_off)
return 0;
Above codes are not necessary. EA offset is already known when calling dm_pci_map_ea_bar() from dm_pci_map_bar(). We can pass the offset to this function.
/* EA capability structure header */
dm_pci_read_config32(dev, ea_off, &ea_entry);
ea_cnt = (ea_entry >> 16) & 0x3f;
Avoid using magic numbers, instead use a macro PCI_EA_NUM_ENT_MASK. In fact, Linux has several macros for EA capability (include/uapi/linux/pci_regs.h) and we can just import these macros in U-Boot too.
That's a good suggestion, I will do that.
ea_off += 4;
for (i = 0; i < ea_cnt; i++, ea_off += entry_size) {
nits: two spaces before entry_size
/* Entry header */
dm_pci_read_config32(dev, ea_off, &ea_entry);
entry_size = (ea_entry & 0x7) * 4;
Per the spec, entry size is number of DW following the initial DW in this entry. So it should really be: ((ea_entry & 0x7) + 1) * 4. Again like the bar_id comments above, we can use << 2 here instead of * 4.
if (((ea_entry >> 4) & 0xf) != bar_id)
continue;
/* Base address, 1st DW */
dm_pci_read_config32(dev, ea_off + 4, &ea_entry);
addr = ea_entry & ~0x3;
if (ea_entry & 0x2) {
dm_pci_read_config32(dev, ea_off + 12, &ea_entry);
addr |= (u64)ea_entry << 32;
}
/* size ignored for now */
return map_physmem(addr, flags, 0);
}
nits: should have one blank line here
return 0;
+}
void *dm_pci_map_bar(struct udevice *dev, int bar, int flags) { pci_addr_t pci_bus_addr; u32 bar_response;
/*
* if the function supports Enhanced Allocation use that instead of
* BARs
*/
if (dm_pci_find_capability(dev, PCI_CAP_ID_EA))
return dm_pci_map_ea_bar(dev, bar, flags);
/* read BAR address */ dm_pci_read_config32(dev, bar, &bar_response); pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
diff --git a/include/pci.h b/include/pci.h index 508f7bca81..e1528bb257 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1314,7 +1314,7 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr,
- @dev: Device to check
- @bar: Bar number to read (numbered from 0)
This one is confusing. It it not bar number (0/1/...), but bar register offset. Suggest a separate patch to correct it. And this function seems to only handle BAR0-BAR0 for header type 0. Please also comment that.
I suppose it works for BARs0-5 on type 0. I'm not clear if it also works for type 1 though, type 1 defines a couple of BARs at offset 0x10 too.
Yes, I think type 1's BAR0/BAR1 are also supported.
- @flags: Flags for the region type (PCI_REGION_...)
- @return: pointer to the virtual address to use
- @return: pointer to the virtual address to use or 0 on error
This should be separate patch to correct the comments. Together with the bar comments above.
*/ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags);
Please create test cases in test/dm/pci.c to cover the EA capability test. Especially since there are some bugs in the for loop in dm_pci_map_ea_bar(), we should create case to get something like BAR3 instead of BAR0. I suspect why you did not see the issue was because you only covered the BAR0 hence only one iteration of the for loop was executed.
Yes, that's precisely what I did..
Regards, Bin

The comment now indicates that the input argument bar is a register offset, not a BAR index. It also mentions which BARs are supported for type 0/1 and that the function can return 0 on error.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - new patch
include/pci.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/include/pci.h b/include/pci.h index 508f7bca81..40c7751acf 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1309,12 +1309,14 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr, * dm_pci_map_bar() - get a virtual address associated with a BAR region * * Looks up a base address register and finds the physical memory address - * that corresponds to it + * that corresponds to it. + * Can be used for 32b BARs 0-5 on type 0 functions and for 32b BARs 0-1 on + * type 1 functions. * * @dev: Device to check - * @bar: Bar number to read (numbered from 0) + * @bar: Bar register offset (PCI_BASE_ADDRESS_...) * @flags: Flags for the region type (PCI_REGION_...) - * @return: pointer to the virtual address to use + * @return: pointer to the virtual address to use or 0 on error */ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags);

Makes dm_pci_map_bar API available for integrated PCI devices that support Enhanced Allocation instead of the original PCI BAR mechanism.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - fixed parsing for BAR1+ - fixed an issue with EA entry size - don't look up EA capability structure twice - use phys_addr_t for EA addresses - use kernel MACROS for EA registers
drivers/pci/pci-uclass.c | 46 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 13 ++++++++++++ 2 files changed, 59 insertions(+)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index cf1e7617ae..389aec15ce 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1341,10 +1341,56 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr, return bus_addr; }
+static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags, + int ea_off) +{ + int ea_cnt, i, entry_size; + int bar_id = (bar - PCI_BASE_ADDRESS_0) >> 2; + u32 ea_entry; + phys_addr_t addr; + + /* EA capability structure header */ + dm_pci_read_config32(dev, ea_off, &ea_entry); + ea_cnt = (ea_entry >> 16) & PCI_EA_NUM_ENT_MASK; + ea_off += PCI_EA_FIRST_ENT; + + for (i = 0; i < ea_cnt; i++, ea_off += entry_size) { + /* Entry header */ + dm_pci_read_config32(dev, ea_off, &ea_entry); + entry_size = ((ea_entry & PCI_EA_ES) + 1) << 2; + + if (((ea_entry & PCI_EA_BEI) >> 4) != bar_id) + continue; + + /* Base address, 1st DW */ + dm_pci_read_config32(dev, ea_off + 4, &ea_entry); + addr = ea_entry & PCI_EA_FIELD_MASK; + if (ea_entry & PCI_EA_IS_64) { + /* Base address, 2nd DW, skip over 4B MaxOffset */ + dm_pci_read_config32(dev, ea_off + 12, &ea_entry); + addr |= ((u64)ea_entry) << 32; + } + + /* size ignored for now */ + return map_physmem(addr, flags, 0); + } + + return 0; +} + void *dm_pci_map_bar(struct udevice *dev, int bar, int flags) { pci_addr_t pci_bus_addr; u32 bar_response; + int ea_off; + + /* + * if the function supports Enhanced Allocation use that instead of + * BARs + */ + ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA); + if (ea_off) + return dm_pci_map_ea_bar(dev, bar, flags, ea_off);
/* read BAR address */ dm_pci_read_config32(dev, bar, &bar_response); diff --git a/include/pci.h b/include/pci.h index 40c7751acf..0aab438159 100644 --- a/include/pci.h +++ b/include/pci.h @@ -455,6 +455,17 @@ #define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */ #define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
+/* Enhanced Allocation Registers */ +#define PCI_EA_NUM_ENT 2 /* Number of Capability Entries */ +#define PCI_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */ +#define PCI_EA_FIRST_ENT 4 /* First EA Entry in List */ +#define PCI_EA_ES 0x00000007 /* Entry Size */ +#define PCI_EA_BEI 0x000000f0 /* BAR Equivalent Indicator */ +/* Base, MaxOffset registers */ +/* bit 0 is reserved */ +#define PCI_EA_IS_64 0x00000002 /* 64-bit field flag */ +#define PCI_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */ + /* Include the ID list */
#include <pci_ids.h> @@ -1312,6 +1323,8 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr, * that corresponds to it. * Can be used for 32b BARs 0-5 on type 0 functions and for 32b BARs 0-1 on * type 1 functions. + * Can also be used on type 0 functions that support Enhanced Allocation for + * 32b/64b BARs. Note that duplicate BEI entries are not supported. * * @dev: Device to check * @bar: Bar register offset (PCI_BASE_ADDRESS_...)

On Tue, Jun 4, 2019 at 8:46 PM Alex Marginean alexm.osslist@gmail.com wrote:
Makes dm_pci_map_bar API available for integrated PCI devices that support Enhanced Allocation instead of the original PCI BAR mechanism.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - fixed parsing for BAR1+ - fixed an issue with EA entry size - don't look up EA capability structure twice - use phys_addr_t for EA addresses - use kernel MACROS for EA registers
drivers/pci/pci-uclass.c | 46 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 13 ++++++++++++ 2 files changed, 59 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com

This test is built on top of the existing swap_case driver. It adds EA capability structure support to swap_case and uses that to map BARs. BAR1 works as it used to, swapping upper/lower case. BARs 2,4 map to a couple of magic values.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - new patch, v1 didn't have a test
arch/sandbox/dts/test.dts | 8 +++ arch/sandbox/include/asm/test.h | 13 ++++ drivers/misc/swap_case.c | 102 +++++++++++++++++++++++++++++++- test/dm/pci.c | 50 ++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 46d8a56d0f..dd50a951a8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -434,6 +434,14 @@ compatible = "sandbox,swap-case"; }; }; + pci@1,0 { + compatible = "pci-generic"; + reg = <0x0800 0 0 0 0>; + emul@0,0 { + compatible = "sandbox,swap-case"; + use-ea; + }; + }; pci@1f,0 { compatible = "pci-generic"; reg = <0xf800 0 0 0 0>; diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index e956a05262..32125f3037 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -19,6 +19,7 @@ #define PCI_CAP_ID_PM_OFFSET 0x50 #define PCI_CAP_ID_EXP_OFFSET 0x60 #define PCI_CAP_ID_MSIX_OFFSET 0x70 +#define PCI_CAP_ID_EA_OFFSET 0x80
#define PCI_EXT_CAP_ID_ERR_OFFSET 0x100 #define PCI_EXT_CAP_ID_VC_OFFSET 0x200 @@ -30,6 +31,18 @@
#define SANDBOX_CLK_RATE 32768
+/* Macros used to test PCI EA capability structure */ +#define PCI_CAP_EA_BASE_LO0 0x00100000 +#define PCI_CAP_EA_BASE_LO1 0x00110000 +#define PCI_CAP_EA_BASE_LO2 0x00120000 +#define PCI_CAP_EA_BASE_LO4 0x00140000 +#define PCI_CAP_EA_BASE_HI2 0x00020000ULL +#define PCI_CAP_EA_BASE_HI4 0x00040000ULL +#define PCI_CAP_EA_SIZE_LO 0x0000ffff +#define PCI_CAP_EA_SIZE_HI 0x00000010ULL +#define PCI_EA_BAR2_MAGIC 0x72727272 +#define PCI_EA_BAR4_MAGIC 0x74747474 + /* System controller driver data */ enum { SYSCON0 = 32, diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c index fa608cec1b..949ef0fdd7 100644 --- a/drivers/misc/swap_case.c +++ b/drivers/misc/swap_case.c @@ -61,11 +61,63 @@ static int sandbox_swap_case_get_devfn(struct udevice *dev) return plat->devfn; }
+static int sandbox_swap_use_ea(struct udevice *dev) +{ + return !!ofnode_get_property(dev->node, "use-ea", NULL); +} + +/* Please keep these macros in sync with ea_regs below */ +#define PCI_CAP_ID_EA_SIZE (sizeof(ea_regs) + 4) +#define PCI_CAP_ID_EA_ENTRY_CNT 4 +/* Hardcoded EA structure, excluding 1st DW. */ +static const u32 ea_regs[] = { + /* BEI=0, ES=2, BAR0 32b Base + 32b MaxOffset, I/O space */ + (2 << 8) | 2, + PCI_CAP_EA_BASE_LO0, + 0, + /* BEI=1, ES=2, BAR1 32b Base + 32b MaxOffset */ + (1 << 4) | 2, + PCI_CAP_EA_BASE_LO1, + MEM_TEXT_SIZE - 1, + /* BEI=2, ES=3, BAR2 64b Base + 32b MaxOffset */ + (2 << 4) | 3, + PCI_CAP_EA_BASE_LO2 | PCI_EA_IS_64, + PCI_CAP_EA_SIZE_LO, + PCI_CAP_EA_BASE_HI2, + /* BEI=4, ES=4, BAR4 63b Base + 64b MaxOffset */ + (4 << 4) | 4, + PCI_CAP_EA_BASE_LO4 | PCI_EA_IS_64, + PCI_CAP_EA_SIZE_LO | PCI_EA_IS_64, + PCI_CAP_EA_BASE_HI4, + PCI_CAP_EA_SIZE_HI, +}; + +static int sandbox_swap_case_read_ea(struct udevice *emul, uint offset, + ulong *valuep, enum pci_size_t size) +{ + u32 reg; + + offset = offset - PCI_CAP_ID_EA_OFFSET - 4; + reg = ea_regs[offset >> 2]; + reg >>= (offset % 4) * 8; + + *valuep = reg; + return 0; +} + static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, ulong *valuep, enum pci_size_t size) { struct swap_case_platdata *plat = dev_get_platdata(emul);
+ /* + * The content of the EA capability structure is handled elseware to + * keep the switch/case below sane + */ + if (offset > PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT && + offset < PCI_CAP_ID_EA_OFFSET + PCI_CAP_ID_EA_SIZE) + return sandbox_swap_case_read_ea(emul, offset, valuep, size); + switch (offset) { case PCI_COMMAND: *valuep = plat->command; @@ -134,9 +186,21 @@ static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, *valuep = PCI_CAP_ID_MSIX_OFFSET; break; case PCI_CAP_ID_MSIX_OFFSET: - *valuep = PCI_CAP_ID_MSIX; + if (sandbox_swap_use_ea(emul)) + *valuep = (PCI_CAP_ID_EA_OFFSET << 8) | PCI_CAP_ID_MSIX; + else + *valuep = PCI_CAP_ID_MSIX; break; case PCI_CAP_ID_MSIX_OFFSET + PCI_CAP_LIST_NEXT: + if (sandbox_swap_use_ea(emul)) + *valuep = PCI_CAP_ID_EA_OFFSET; + else + *valuep = 0; + break; + case PCI_CAP_ID_EA_OFFSET: + *valuep = (PCI_CAP_ID_EA_ENTRY_CNT << 16) | PCI_CAP_ID_EA; + break; + case PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT: *valuep = 0; break; case PCI_EXT_CAP_ID_ERR_OFFSET: @@ -257,6 +321,9 @@ int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr, return 0; }
+static int pci_ea_bar2_magic = PCI_EA_BAR2_MAGIC; +static int pci_ea_bar4_magic = PCI_EA_BAR4_MAGIC; + static int sandbox_swap_case_map_physmem(struct udevice *dev, phys_addr_t addr, unsigned long *lenp, void **ptrp) { @@ -265,9 +332,42 @@ static int sandbox_swap_case_map_physmem(struct udevice *dev, int barnum; int ret;
+ if (sandbox_swap_use_ea(dev)) { + /* + * only support mapping base address in EA test for now, we + * don't handle mapping an offset inside a BAR. Seems good + * enough for the current test. + */ + switch (addr) { + case (phys_addr_t)PCI_CAP_EA_BASE_LO0: + *ptrp = &priv->op; + *lenp = 4; + break; + case (phys_addr_t)PCI_CAP_EA_BASE_LO1: + *ptrp = priv->mem_text; + *lenp = barinfo[1].size - 1; + break; + case (phys_addr_t)((PCI_CAP_EA_BASE_HI2 << 32) | + PCI_CAP_EA_BASE_LO2): + *ptrp = &pci_ea_bar2_magic; + *lenp = PCI_CAP_EA_SIZE_LO; + break; + case (phys_addr_t)((PCI_CAP_EA_BASE_HI4 << 32) | + PCI_CAP_EA_BASE_LO4): + *ptrp = &pci_ea_bar4_magic; + *lenp = (PCI_CAP_EA_SIZE_HI << 32) | + PCI_CAP_EA_SIZE_LO; + break; + default: + return -ENOENT; + } + return 0; + } + ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); if (ret) return ret; + if (barnum == 1) { *ptrp = priv->mem_text + offset; avail = barinfo[1].size - offset; diff --git a/test/dm/pci.c b/test/dm/pci.c index a1febd54b7..4657f5d68d 100644 --- a/test/dm/pci.c +++ b/test/dm/pci.c @@ -245,3 +245,53 @@ static int dm_test_pci_cap(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_pci_cap, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test looking up BARs in EA capability structure */ +static int dm_test_pci_ea(struct unit_test_state *uts) +{ + struct udevice *bus, *swap; + void *bar; + int cap; + + /* + * use emulated device mapping function, we're not using real physical + * addresses in this test + */ + sandbox_set_enable_pci_map(true); + + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus)); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x01, 0), &swap)); + + /* look up PCI_CAP_ID_EA */ + cap = dm_pci_find_capability(swap, PCI_CAP_ID_EA); + ut_asserteq(PCI_CAP_ID_EA_OFFSET, cap); + + /* test swap case in BAR 1 */ + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_0, 0); + ut_assertnonnull(bar); + *(int *)bar = 2; /* swap upper/lower */ + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0); + ut_assertnonnull(bar); + strcpy(bar, "ea TEST"); + unmap_sysmem(bar); + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0); + ut_assertnonnull(bar); + ut_asserteq_str("EA test", bar); + + /* test magic values in BARs2, 4; BAR 3 is n/a */ + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_2, 0); + ut_assertnonnull(bar); + ut_asserteq(PCI_EA_BAR2_MAGIC, *(u32 *)bar); + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_3, 0); + ut_assertnull(bar); + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_4, 0); + ut_assertnonnull(bar); + ut_asserteq(PCI_EA_BAR4_MAGIC, *(u32 *)bar); + + return 0; +} + +DM_TEST(dm_test_pci_ea, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

Hi Alex,
On Tue, Jun 4, 2019 at 8:46 PM Alex Marginean alexm.osslist@gmail.com wrote:
This test is built on top of the existing swap_case driver. It adds EA capability structure support to swap_case and uses that to map BARs. BAR1 works as it used to, swapping upper/lower case. BARs 2,4 map to a couple of magic values.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - new patch, v1 didn't have a test
arch/sandbox/dts/test.dts | 8 +++ arch/sandbox/include/asm/test.h | 13 ++++ drivers/misc/swap_case.c | 102 +++++++++++++++++++++++++++++++- test/dm/pci.c | 50 ++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-)
Well done!
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
But please see some nits below:
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 46d8a56d0f..dd50a951a8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -434,6 +434,14 @@ compatible = "sandbox,swap-case"; }; };
pci@1,0 {
compatible = "pci-generic";
reg = <0x0800 0 0 0 0>;
emul@0,0 {
compatible = "sandbox,swap-case";
use-ea;
};
}; pci@1f,0 { compatible = "pci-generic"; reg = <0xf800 0 0 0 0>;
diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index e956a05262..32125f3037 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -19,6 +19,7 @@ #define PCI_CAP_ID_PM_OFFSET 0x50 #define PCI_CAP_ID_EXP_OFFSET 0x60 #define PCI_CAP_ID_MSIX_OFFSET 0x70 +#define PCI_CAP_ID_EA_OFFSET 0x80
#define PCI_EXT_CAP_ID_ERR_OFFSET 0x100 #define PCI_EXT_CAP_ID_VC_OFFSET 0x200 @@ -30,6 +31,18 @@
#define SANDBOX_CLK_RATE 32768
+/* Macros used to test PCI EA capability structure */ +#define PCI_CAP_EA_BASE_LO0 0x00100000 +#define PCI_CAP_EA_BASE_LO1 0x00110000 +#define PCI_CAP_EA_BASE_LO2 0x00120000 +#define PCI_CAP_EA_BASE_LO4 0x00140000 +#define PCI_CAP_EA_BASE_HI2 0x00020000ULL +#define PCI_CAP_EA_BASE_HI4 0x00040000ULL +#define PCI_CAP_EA_SIZE_LO 0x0000ffff +#define PCI_CAP_EA_SIZE_HI 0x00000010ULL +#define PCI_EA_BAR2_MAGIC 0x72727272 +#define PCI_EA_BAR4_MAGIC 0x74747474
/* System controller driver data */ enum { SYSCON0 = 32, diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c index fa608cec1b..949ef0fdd7 100644 --- a/drivers/misc/swap_case.c +++ b/drivers/misc/swap_case.c @@ -61,11 +61,63 @@ static int sandbox_swap_case_get_devfn(struct udevice *dev) return plat->devfn; }
+static int sandbox_swap_use_ea(struct udevice *dev)
nits: for consistency, name it as "sandbox_swap_case_use_ea"
+{
return !!ofnode_get_property(dev->node, "use-ea", NULL);
+}
+/* Please keep these macros in sync with ea_regs below */ +#define PCI_CAP_ID_EA_SIZE (sizeof(ea_regs) + 4) +#define PCI_CAP_ID_EA_ENTRY_CNT 4 +/* Hardcoded EA structure, excluding 1st DW. */ +static const u32 ea_regs[] = {
/* BEI=0, ES=2, BAR0 32b Base + 32b MaxOffset, I/O space */
(2 << 8) | 2,
PCI_CAP_EA_BASE_LO0,
0,
/* BEI=1, ES=2, BAR1 32b Base + 32b MaxOffset */
(1 << 4) | 2,
PCI_CAP_EA_BASE_LO1,
MEM_TEXT_SIZE - 1,
/* BEI=2, ES=3, BAR2 64b Base + 32b MaxOffset */
(2 << 4) | 3,
PCI_CAP_EA_BASE_LO2 | PCI_EA_IS_64,
PCI_CAP_EA_SIZE_LO,
PCI_CAP_EA_BASE_HI2,
/* BEI=4, ES=4, BAR4 63b Base + 64b MaxOffset */
nits: typo of '63b'
(4 << 4) | 4,
PCI_CAP_EA_BASE_LO4 | PCI_EA_IS_64,
PCI_CAP_EA_SIZE_LO | PCI_EA_IS_64,
PCI_CAP_EA_BASE_HI4,
PCI_CAP_EA_SIZE_HI,
+};
+static int sandbox_swap_case_read_ea(struct udevice *emul, uint offset,
ulong *valuep, enum pci_size_t size)
+{
u32 reg;
offset = offset - PCI_CAP_ID_EA_OFFSET - 4;
reg = ea_regs[offset >> 2];
reg >>= (offset % 4) * 8;
*valuep = reg;
return 0;
+}
static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, ulong *valuep, enum pci_size_t size) { struct swap_case_platdata *plat = dev_get_platdata(emul);
/*
* The content of the EA capability structure is handled elseware to
nits: elsewhere
* keep the switch/case below sane
*/
if (offset > PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT &&
offset < PCI_CAP_ID_EA_OFFSET + PCI_CAP_ID_EA_SIZE)
return sandbox_swap_case_read_ea(emul, offset, valuep, size);
switch (offset) { case PCI_COMMAND: *valuep = plat->command;
@@ -134,9 +186,21 @@ static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, *valuep = PCI_CAP_ID_MSIX_OFFSET; break; case PCI_CAP_ID_MSIX_OFFSET:
*valuep = PCI_CAP_ID_MSIX;
if (sandbox_swap_use_ea(emul))
*valuep = (PCI_CAP_ID_EA_OFFSET << 8) | PCI_CAP_ID_MSIX;
else
*valuep = PCI_CAP_ID_MSIX; break; case PCI_CAP_ID_MSIX_OFFSET + PCI_CAP_LIST_NEXT:
if (sandbox_swap_use_ea(emul))
*valuep = PCI_CAP_ID_EA_OFFSET;
else
*valuep = 0;
break;
case PCI_CAP_ID_EA_OFFSET:
*valuep = (PCI_CAP_ID_EA_ENTRY_CNT << 16) | PCI_CAP_ID_EA;
break;
case PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT: *valuep = 0; break; case PCI_EXT_CAP_ID_ERR_OFFSET:
@@ -257,6 +321,9 @@ int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr, return 0; }
+static int pci_ea_bar2_magic = PCI_EA_BAR2_MAGIC; +static int pci_ea_bar4_magic = PCI_EA_BAR4_MAGIC;
static int sandbox_swap_case_map_physmem(struct udevice *dev, phys_addr_t addr, unsigned long *lenp, void **ptrp) { @@ -265,9 +332,42 @@ static int sandbox_swap_case_map_physmem(struct udevice *dev, int barnum; int ret;
if (sandbox_swap_use_ea(dev)) {
/*
* only support mapping base address in EA test for now, we
* don't handle mapping an offset inside a BAR. Seems good
* enough for the current test.
*/
switch (addr) {
case (phys_addr_t)PCI_CAP_EA_BASE_LO0:
*ptrp = &priv->op;
*lenp = 4;
break;
case (phys_addr_t)PCI_CAP_EA_BASE_LO1:
*ptrp = priv->mem_text;
*lenp = barinfo[1].size - 1;
break;
case (phys_addr_t)((PCI_CAP_EA_BASE_HI2 << 32) |
PCI_CAP_EA_BASE_LO2):
*ptrp = &pci_ea_bar2_magic;
*lenp = PCI_CAP_EA_SIZE_LO;
break;
case (phys_addr_t)((PCI_CAP_EA_BASE_HI4 << 32) |
PCI_CAP_EA_BASE_LO4):
*ptrp = &pci_ea_bar4_magic;
*lenp = (PCI_CAP_EA_SIZE_HI << 32) |
PCI_CAP_EA_SIZE_LO;
break;
default:
return -ENOENT;
}
return 0;
}
ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); if (ret) return ret;
if (barnum == 1) { *ptrp = priv->mem_text + offset; avail = barinfo[1].size - offset;
diff --git a/test/dm/pci.c b/test/dm/pci.c index a1febd54b7..4657f5d68d 100644 --- a/test/dm/pci.c +++ b/test/dm/pci.c @@ -245,3 +245,53 @@ static int dm_test_pci_cap(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_pci_cap, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+/* Test looking up BARs in EA capability structure */ +static int dm_test_pci_ea(struct unit_test_state *uts) +{
struct udevice *bus, *swap;
void *bar;
int cap;
/*
* use emulated device mapping function, we're not using real physical
* addresses in this test
*/
sandbox_set_enable_pci_map(true);
ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus));
ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x01, 0), &swap));
/* look up PCI_CAP_ID_EA */
cap = dm_pci_find_capability(swap, PCI_CAP_ID_EA);
ut_asserteq(PCI_CAP_ID_EA_OFFSET, cap);
/* test swap case in BAR 1 */
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_0, 0);
ut_assertnonnull(bar);
*(int *)bar = 2; /* swap upper/lower */
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0);
ut_assertnonnull(bar);
strcpy(bar, "ea TEST");
unmap_sysmem(bar);
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0);
ut_assertnonnull(bar);
ut_asserteq_str("EA test", bar);
/* test magic values in BARs2, 4; BAR 3 is n/a */
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_2, 0);
ut_assertnonnull(bar);
ut_asserteq(PCI_EA_BAR2_MAGIC, *(u32 *)bar);
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_3, 0);
ut_assertnull(bar);
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_4, 0);
ut_assertnonnull(bar);
ut_asserteq(PCI_EA_BAR4_MAGIC, *(u32 *)bar);
return 0;
+}
nits: remove this blank line
+DM_TEST(dm_test_pci_ea, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
Regards, Bin

Hi Bin,
On 6/5/2019 1:05 PM, Bin Meng wrote:
Hi Alex,
On Tue, Jun 4, 2019 at 8:46 PM Alex Marginean alexm.osslist@gmail.com wrote:
This test is built on top of the existing swap_case driver. It adds EA capability structure support to swap_case and uses that to map BARs. BAR1 works as it used to, swapping upper/lower case. BARs 2,4 map to a couple of magic values.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - new patch, v1 didn't have a test
arch/sandbox/dts/test.dts | 8 +++ arch/sandbox/include/asm/test.h | 13 ++++ drivers/misc/swap_case.c | 102 +++++++++++++++++++++++++++++++- test/dm/pci.c | 50 ++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-)
Well done!
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
But please see some nits below:
I'm replying from the nxp account, apparently google decided this is just spam and it's not worth sending out through gmail.
I'll send a v3 with fixes for you comments, should I keep either of your two tags on this patch?
Thank you! Alex
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 46d8a56d0f..dd50a951a8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -434,6 +434,14 @@ compatible = "sandbox,swap-case"; }; };
pci@1,0 {
compatible = "pci-generic";
reg = <0x0800 0 0 0 0>;
emul@0,0 {
compatible = "sandbox,swap-case";
use-ea;
};
}; pci@1f,0 { compatible = "pci-generic"; reg = <0xf800 0 0 0 0>;
diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index e956a05262..32125f3037 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -19,6 +19,7 @@ #define PCI_CAP_ID_PM_OFFSET 0x50 #define PCI_CAP_ID_EXP_OFFSET 0x60 #define PCI_CAP_ID_MSIX_OFFSET 0x70 +#define PCI_CAP_ID_EA_OFFSET 0x80
#define PCI_EXT_CAP_ID_ERR_OFFSET 0x100 #define PCI_EXT_CAP_ID_VC_OFFSET 0x200 @@ -30,6 +31,18 @@
#define SANDBOX_CLK_RATE 32768
+/* Macros used to test PCI EA capability structure */ +#define PCI_CAP_EA_BASE_LO0 0x00100000 +#define PCI_CAP_EA_BASE_LO1 0x00110000 +#define PCI_CAP_EA_BASE_LO2 0x00120000 +#define PCI_CAP_EA_BASE_LO4 0x00140000 +#define PCI_CAP_EA_BASE_HI2 0x00020000ULL +#define PCI_CAP_EA_BASE_HI4 0x00040000ULL +#define PCI_CAP_EA_SIZE_LO 0x0000ffff +#define PCI_CAP_EA_SIZE_HI 0x00000010ULL +#define PCI_EA_BAR2_MAGIC 0x72727272 +#define PCI_EA_BAR4_MAGIC 0x74747474
- /* System controller driver data */ enum { SYSCON0 = 32,
diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c index fa608cec1b..949ef0fdd7 100644 --- a/drivers/misc/swap_case.c +++ b/drivers/misc/swap_case.c @@ -61,11 +61,63 @@ static int sandbox_swap_case_get_devfn(struct udevice *dev) return plat->devfn; }
+static int sandbox_swap_use_ea(struct udevice *dev)
nits: for consistency, name it as "sandbox_swap_case_use_ea"
+{
return !!ofnode_get_property(dev->node, "use-ea", NULL);
+}
+/* Please keep these macros in sync with ea_regs below */ +#define PCI_CAP_ID_EA_SIZE (sizeof(ea_regs) + 4) +#define PCI_CAP_ID_EA_ENTRY_CNT 4 +/* Hardcoded EA structure, excluding 1st DW. */ +static const u32 ea_regs[] = {
/* BEI=0, ES=2, BAR0 32b Base + 32b MaxOffset, I/O space */
(2 << 8) | 2,
PCI_CAP_EA_BASE_LO0,
0,
/* BEI=1, ES=2, BAR1 32b Base + 32b MaxOffset */
(1 << 4) | 2,
PCI_CAP_EA_BASE_LO1,
MEM_TEXT_SIZE - 1,
/* BEI=2, ES=3, BAR2 64b Base + 32b MaxOffset */
(2 << 4) | 3,
PCI_CAP_EA_BASE_LO2 | PCI_EA_IS_64,
PCI_CAP_EA_SIZE_LO,
PCI_CAP_EA_BASE_HI2,
/* BEI=4, ES=4, BAR4 63b Base + 64b MaxOffset */
nits: typo of '63b'
(4 << 4) | 4,
PCI_CAP_EA_BASE_LO4 | PCI_EA_IS_64,
PCI_CAP_EA_SIZE_LO | PCI_EA_IS_64,
PCI_CAP_EA_BASE_HI4,
PCI_CAP_EA_SIZE_HI,
+};
+static int sandbox_swap_case_read_ea(struct udevice *emul, uint offset,
ulong *valuep, enum pci_size_t size)
+{
u32 reg;
offset = offset - PCI_CAP_ID_EA_OFFSET - 4;
reg = ea_regs[offset >> 2];
reg >>= (offset % 4) * 8;
*valuep = reg;
return 0;
+}
static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, ulong *valuep, enum pci_size_t size) { struct swap_case_platdata *plat = dev_get_platdata(emul);
/*
* The content of the EA capability structure is handled elseware to
nits: elsewhere
* keep the switch/case below sane
*/
if (offset > PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT &&
offset < PCI_CAP_ID_EA_OFFSET + PCI_CAP_ID_EA_SIZE)
return sandbox_swap_case_read_ea(emul, offset, valuep, size);
switch (offset) { case PCI_COMMAND: *valuep = plat->command;
@@ -134,9 +186,21 @@ static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, *valuep = PCI_CAP_ID_MSIX_OFFSET; break; case PCI_CAP_ID_MSIX_OFFSET:
*valuep = PCI_CAP_ID_MSIX;
if (sandbox_swap_use_ea(emul))
*valuep = (PCI_CAP_ID_EA_OFFSET << 8) | PCI_CAP_ID_MSIX;
else
*valuep = PCI_CAP_ID_MSIX; break; case PCI_CAP_ID_MSIX_OFFSET + PCI_CAP_LIST_NEXT:
if (sandbox_swap_use_ea(emul))
*valuep = PCI_CAP_ID_EA_OFFSET;
else
*valuep = 0;
break;
case PCI_CAP_ID_EA_OFFSET:
*valuep = (PCI_CAP_ID_EA_ENTRY_CNT << 16) | PCI_CAP_ID_EA;
break;
case PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT: *valuep = 0; break; case PCI_EXT_CAP_ID_ERR_OFFSET:
@@ -257,6 +321,9 @@ int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr, return 0; }
+static int pci_ea_bar2_magic = PCI_EA_BAR2_MAGIC; +static int pci_ea_bar4_magic = PCI_EA_BAR4_MAGIC;
- static int sandbox_swap_case_map_physmem(struct udevice *dev, phys_addr_t addr, unsigned long *lenp, void **ptrp) {
@@ -265,9 +332,42 @@ static int sandbox_swap_case_map_physmem(struct udevice *dev, int barnum; int ret;
if (sandbox_swap_use_ea(dev)) {
/*
* only support mapping base address in EA test for now, we
* don't handle mapping an offset inside a BAR. Seems good
* enough for the current test.
*/
switch (addr) {
case (phys_addr_t)PCI_CAP_EA_BASE_LO0:
*ptrp = &priv->op;
*lenp = 4;
break;
case (phys_addr_t)PCI_CAP_EA_BASE_LO1:
*ptrp = priv->mem_text;
*lenp = barinfo[1].size - 1;
break;
case (phys_addr_t)((PCI_CAP_EA_BASE_HI2 << 32) |
PCI_CAP_EA_BASE_LO2):
*ptrp = &pci_ea_bar2_magic;
*lenp = PCI_CAP_EA_SIZE_LO;
break;
case (phys_addr_t)((PCI_CAP_EA_BASE_HI4 << 32) |
PCI_CAP_EA_BASE_LO4):
*ptrp = &pci_ea_bar4_magic;
*lenp = (PCI_CAP_EA_SIZE_HI << 32) |
PCI_CAP_EA_SIZE_LO;
break;
default:
return -ENOENT;
}
return 0;
}
ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); if (ret) return ret;
if (barnum == 1) { *ptrp = priv->mem_text + offset; avail = barinfo[1].size - offset;
diff --git a/test/dm/pci.c b/test/dm/pci.c index a1febd54b7..4657f5d68d 100644 --- a/test/dm/pci.c +++ b/test/dm/pci.c @@ -245,3 +245,53 @@ static int dm_test_pci_cap(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_pci_cap, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+/* Test looking up BARs in EA capability structure */ +static int dm_test_pci_ea(struct unit_test_state *uts) +{
struct udevice *bus, *swap;
void *bar;
int cap;
/*
* use emulated device mapping function, we're not using real physical
* addresses in this test
*/
sandbox_set_enable_pci_map(true);
ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus));
ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x01, 0), &swap));
/* look up PCI_CAP_ID_EA */
cap = dm_pci_find_capability(swap, PCI_CAP_ID_EA);
ut_asserteq(PCI_CAP_ID_EA_OFFSET, cap);
/* test swap case in BAR 1 */
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_0, 0);
ut_assertnonnull(bar);
*(int *)bar = 2; /* swap upper/lower */
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0);
ut_assertnonnull(bar);
strcpy(bar, "ea TEST");
unmap_sysmem(bar);
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0);
ut_assertnonnull(bar);
ut_asserteq_str("EA test", bar);
/* test magic values in BARs2, 4; BAR 3 is n/a */
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_2, 0);
ut_assertnonnull(bar);
ut_asserteq(PCI_EA_BAR2_MAGIC, *(u32 *)bar);
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_3, 0);
ut_assertnull(bar);
bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_4, 0);
ut_assertnonnull(bar);
ut_asserteq(PCI_EA_BAR4_MAGIC, *(u32 *)bar);
return 0;
+}
nits: remove this blank line
+DM_TEST(dm_test_pci_ea, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
Regards, Bin

Hi Alex,
On Thu, Jun 6, 2019 at 3:38 PM Alexandru Marginean alexandru.marginean@nxp.com wrote:
Hi Bin,
On 6/5/2019 1:05 PM, Bin Meng wrote:
Hi Alex,
On Tue, Jun 4, 2019 at 8:46 PM Alex Marginean alexm.osslist@gmail.com wrote:
This test is built on top of the existing swap_case driver. It adds EA capability structure support to swap_case and uses that to map BARs. BAR1 works as it used to, swapping upper/lower case. BARs 2,4 map to a couple of magic values.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - new patch, v1 didn't have a test
arch/sandbox/dts/test.dts | 8 +++ arch/sandbox/include/asm/test.h | 13 ++++ drivers/misc/swap_case.c | 102 +++++++++++++++++++++++++++++++- test/dm/pci.c | 50 ++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-)
Well done!
Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
But please see some nits below:
I'm replying from the nxp account, apparently google decided this is just spam and it's not worth sending out through gmail.
I'll send a v3 with fixes for you comments, should I keep either of your two tags on this patch?
Yes, please keep the two tags in the v3. thanks!
Regards, Bin

The comment now indicates that the input argument bar is a register offset, not a BAR index. It also mentions which BARs are supported for type 0/1 and that the function can return 0 on error.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com Reviewed-by: Bin Meng bmeng.cn@gmail.com ---
Changes in v2: - new patch Changes in v3: - none
include/pci.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/include/pci.h b/include/pci.h index 508f7bca81..40c7751acf 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1309,12 +1309,14 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr, * dm_pci_map_bar() - get a virtual address associated with a BAR region * * Looks up a base address register and finds the physical memory address - * that corresponds to it + * that corresponds to it. + * Can be used for 32b BARs 0-5 on type 0 functions and for 32b BARs 0-1 on + * type 1 functions. * * @dev: Device to check - * @bar: Bar number to read (numbered from 0) + * @bar: Bar register offset (PCI_BASE_ADDRESS_...) * @flags: Flags for the region type (PCI_REGION_...) - * @return: pointer to the virtual address to use + * @return: pointer to the virtual address to use or 0 on error */ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags);

Makes dm_pci_map_bar API available for integrated PCI devices that support Enhanced Allocation instead of the original PCI BAR mechanism.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com Reviewed-by: Bin Meng bmeng.cn@gmail.com ---
Changes in v2: - fixed parsing for BAR1+ - fixed an issue with EA entry size - don't look up EA capability structure twice - use phys_addr_t for EA addresses - use kernel MACROS for EA registers Changes in v3: - none
drivers/pci/pci-uclass.c | 46 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 13 ++++++++++++ 2 files changed, 59 insertions(+)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index cf1e7617ae..389aec15ce 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1341,10 +1341,56 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr, return bus_addr; }
+static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags, + int ea_off) +{ + int ea_cnt, i, entry_size; + int bar_id = (bar - PCI_BASE_ADDRESS_0) >> 2; + u32 ea_entry; + phys_addr_t addr; + + /* EA capability structure header */ + dm_pci_read_config32(dev, ea_off, &ea_entry); + ea_cnt = (ea_entry >> 16) & PCI_EA_NUM_ENT_MASK; + ea_off += PCI_EA_FIRST_ENT; + + for (i = 0; i < ea_cnt; i++, ea_off += entry_size) { + /* Entry header */ + dm_pci_read_config32(dev, ea_off, &ea_entry); + entry_size = ((ea_entry & PCI_EA_ES) + 1) << 2; + + if (((ea_entry & PCI_EA_BEI) >> 4) != bar_id) + continue; + + /* Base address, 1st DW */ + dm_pci_read_config32(dev, ea_off + 4, &ea_entry); + addr = ea_entry & PCI_EA_FIELD_MASK; + if (ea_entry & PCI_EA_IS_64) { + /* Base address, 2nd DW, skip over 4B MaxOffset */ + dm_pci_read_config32(dev, ea_off + 12, &ea_entry); + addr |= ((u64)ea_entry) << 32; + } + + /* size ignored for now */ + return map_physmem(addr, flags, 0); + } + + return 0; +} + void *dm_pci_map_bar(struct udevice *dev, int bar, int flags) { pci_addr_t pci_bus_addr; u32 bar_response; + int ea_off; + + /* + * if the function supports Enhanced Allocation use that instead of + * BARs + */ + ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA); + if (ea_off) + return dm_pci_map_ea_bar(dev, bar, flags, ea_off);
/* read BAR address */ dm_pci_read_config32(dev, bar, &bar_response); diff --git a/include/pci.h b/include/pci.h index 40c7751acf..0aab438159 100644 --- a/include/pci.h +++ b/include/pci.h @@ -455,6 +455,17 @@ #define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */ #define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM
+/* Enhanced Allocation Registers */ +#define PCI_EA_NUM_ENT 2 /* Number of Capability Entries */ +#define PCI_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */ +#define PCI_EA_FIRST_ENT 4 /* First EA Entry in List */ +#define PCI_EA_ES 0x00000007 /* Entry Size */ +#define PCI_EA_BEI 0x000000f0 /* BAR Equivalent Indicator */ +/* Base, MaxOffset registers */ +/* bit 0 is reserved */ +#define PCI_EA_IS_64 0x00000002 /* 64-bit field flag */ +#define PCI_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */ + /* Include the ID list */
#include <pci_ids.h> @@ -1312,6 +1323,8 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t addr, * that corresponds to it. * Can be used for 32b BARs 0-5 on type 0 functions and for 32b BARs 0-1 on * type 1 functions. + * Can also be used on type 0 functions that support Enhanced Allocation for + * 32b/64b BARs. Note that duplicate BEI entries are not supported. * * @dev: Device to check * @bar: Bar register offset (PCI_BASE_ADDRESS_...)

On Fri, 7 Jun 2019 at 02:24, Alex Marginean alexm.osslist@gmail.com wrote:
Makes dm_pci_map_bar API available for integrated PCI devices that support Enhanced Allocation instead of the original PCI BAR mechanism.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com Reviewed-by: Bin Meng bmeng.cn@gmail.com
Changes in v2: - fixed parsing for BAR1+ - fixed an issue with EA entry size - don't look up EA capability structure twice - use phys_addr_t for EA addresses - use kernel MACROS for EA registers Changes in v3: - none
drivers/pci/pci-uclass.c | 46 ++++++++++++++++++++++++++++++++++++++++ include/pci.h | 13 ++++++++++++ 2 files changed, 59 insertions(+)
Applied to u-boot-dm/next, thanks!

This test is built on top of the existing swap_case driver. It adds EA capability structure support to swap_case and uses that to map BARs. BAR1 works as it used to, swapping upper/lower case. BARs 2,4 map to a couple of magic values.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com ---
Changes in v2: - new patch, v1 didn't have a test Changes in v3: - renamed sandbox_swap_use_ea function for consistency - fixed several typos and styling issues
arch/sandbox/dts/test.dts | 8 +++ arch/sandbox/include/asm/test.h | 13 ++++ drivers/misc/swap_case.c | 102 +++++++++++++++++++++++++++++++- test/dm/pci.c | 49 +++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 46d8a56d0f..dd50a951a8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -434,6 +434,14 @@ compatible = "sandbox,swap-case"; }; }; + pci@1,0 { + compatible = "pci-generic"; + reg = <0x0800 0 0 0 0>; + emul@0,0 { + compatible = "sandbox,swap-case"; + use-ea; + }; + }; pci@1f,0 { compatible = "pci-generic"; reg = <0xf800 0 0 0 0>; diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index e956a05262..32125f3037 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -19,6 +19,7 @@ #define PCI_CAP_ID_PM_OFFSET 0x50 #define PCI_CAP_ID_EXP_OFFSET 0x60 #define PCI_CAP_ID_MSIX_OFFSET 0x70 +#define PCI_CAP_ID_EA_OFFSET 0x80
#define PCI_EXT_CAP_ID_ERR_OFFSET 0x100 #define PCI_EXT_CAP_ID_VC_OFFSET 0x200 @@ -30,6 +31,18 @@
#define SANDBOX_CLK_RATE 32768
+/* Macros used to test PCI EA capability structure */ +#define PCI_CAP_EA_BASE_LO0 0x00100000 +#define PCI_CAP_EA_BASE_LO1 0x00110000 +#define PCI_CAP_EA_BASE_LO2 0x00120000 +#define PCI_CAP_EA_BASE_LO4 0x00140000 +#define PCI_CAP_EA_BASE_HI2 0x00020000ULL +#define PCI_CAP_EA_BASE_HI4 0x00040000ULL +#define PCI_CAP_EA_SIZE_LO 0x0000ffff +#define PCI_CAP_EA_SIZE_HI 0x00000010ULL +#define PCI_EA_BAR2_MAGIC 0x72727272 +#define PCI_EA_BAR4_MAGIC 0x74747474 + /* System controller driver data */ enum { SYSCON0 = 32, diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c index fa608cec1b..6afc6d9466 100644 --- a/drivers/misc/swap_case.c +++ b/drivers/misc/swap_case.c @@ -61,11 +61,63 @@ static int sandbox_swap_case_get_devfn(struct udevice *dev) return plat->devfn; }
+static int sandbox_swap_case_use_ea(struct udevice *dev) +{ + return !!ofnode_get_property(dev->node, "use-ea", NULL); +} + +/* Please keep these macros in sync with ea_regs below */ +#define PCI_CAP_ID_EA_SIZE (sizeof(ea_regs) + 4) +#define PCI_CAP_ID_EA_ENTRY_CNT 4 +/* Hardcoded EA structure, excluding 1st DW. */ +static const u32 ea_regs[] = { + /* BEI=0, ES=2, BAR0 32b Base + 32b MaxOffset, I/O space */ + (2 << 8) | 2, + PCI_CAP_EA_BASE_LO0, + 0, + /* BEI=1, ES=2, BAR1 32b Base + 32b MaxOffset */ + (1 << 4) | 2, + PCI_CAP_EA_BASE_LO1, + MEM_TEXT_SIZE - 1, + /* BEI=2, ES=3, BAR2 64b Base + 32b MaxOffset */ + (2 << 4) | 3, + PCI_CAP_EA_BASE_LO2 | PCI_EA_IS_64, + PCI_CAP_EA_SIZE_LO, + PCI_CAP_EA_BASE_HI2, + /* BEI=4, ES=4, BAR4 64b Base + 64b MaxOffset */ + (4 << 4) | 4, + PCI_CAP_EA_BASE_LO4 | PCI_EA_IS_64, + PCI_CAP_EA_SIZE_LO | PCI_EA_IS_64, + PCI_CAP_EA_BASE_HI4, + PCI_CAP_EA_SIZE_HI, +}; + +static int sandbox_swap_case_read_ea(struct udevice *emul, uint offset, + ulong *valuep, enum pci_size_t size) +{ + u32 reg; + + offset = offset - PCI_CAP_ID_EA_OFFSET - 4; + reg = ea_regs[offset >> 2]; + reg >>= (offset % 4) * 8; + + *valuep = reg; + return 0; +} + static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, ulong *valuep, enum pci_size_t size) { struct swap_case_platdata *plat = dev_get_platdata(emul);
+ /* + * The content of the EA capability structure is handled elsewhere to + * keep the switch/case below sane + */ + if (offset > PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT && + offset < PCI_CAP_ID_EA_OFFSET + PCI_CAP_ID_EA_SIZE) + return sandbox_swap_case_read_ea(emul, offset, valuep, size); + switch (offset) { case PCI_COMMAND: *valuep = plat->command; @@ -134,9 +186,21 @@ static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, *valuep = PCI_CAP_ID_MSIX_OFFSET; break; case PCI_CAP_ID_MSIX_OFFSET: - *valuep = PCI_CAP_ID_MSIX; + if (sandbox_swap_case_use_ea(emul)) + *valuep = (PCI_CAP_ID_EA_OFFSET << 8) | PCI_CAP_ID_MSIX; + else + *valuep = PCI_CAP_ID_MSIX; break; case PCI_CAP_ID_MSIX_OFFSET + PCI_CAP_LIST_NEXT: + if (sandbox_swap_case_use_ea(emul)) + *valuep = PCI_CAP_ID_EA_OFFSET; + else + *valuep = 0; + break; + case PCI_CAP_ID_EA_OFFSET: + *valuep = (PCI_CAP_ID_EA_ENTRY_CNT << 16) | PCI_CAP_ID_EA; + break; + case PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT: *valuep = 0; break; case PCI_EXT_CAP_ID_ERR_OFFSET: @@ -257,6 +321,9 @@ int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr, return 0; }
+static int pci_ea_bar2_magic = PCI_EA_BAR2_MAGIC; +static int pci_ea_bar4_magic = PCI_EA_BAR4_MAGIC; + static int sandbox_swap_case_map_physmem(struct udevice *dev, phys_addr_t addr, unsigned long *lenp, void **ptrp) { @@ -265,9 +332,42 @@ static int sandbox_swap_case_map_physmem(struct udevice *dev, int barnum; int ret;
+ if (sandbox_swap_case_use_ea(dev)) { + /* + * only support mapping base address in EA test for now, we + * don't handle mapping an offset inside a BAR. Seems good + * enough for the current test. + */ + switch (addr) { + case (phys_addr_t)PCI_CAP_EA_BASE_LO0: + *ptrp = &priv->op; + *lenp = 4; + break; + case (phys_addr_t)PCI_CAP_EA_BASE_LO1: + *ptrp = priv->mem_text; + *lenp = barinfo[1].size - 1; + break; + case (phys_addr_t)((PCI_CAP_EA_BASE_HI2 << 32) | + PCI_CAP_EA_BASE_LO2): + *ptrp = &pci_ea_bar2_magic; + *lenp = PCI_CAP_EA_SIZE_LO; + break; + case (phys_addr_t)((PCI_CAP_EA_BASE_HI4 << 32) | + PCI_CAP_EA_BASE_LO4): + *ptrp = &pci_ea_bar4_magic; + *lenp = (PCI_CAP_EA_SIZE_HI << 32) | + PCI_CAP_EA_SIZE_LO; + break; + default: + return -ENOENT; + } + return 0; + } + ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); if (ret) return ret; + if (barnum == 1) { *ptrp = priv->mem_text + offset; avail = barinfo[1].size - offset; diff --git a/test/dm/pci.c b/test/dm/pci.c index a1febd54b7..c325f6600e 100644 --- a/test/dm/pci.c +++ b/test/dm/pci.c @@ -245,3 +245,52 @@ static int dm_test_pci_cap(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_pci_cap, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test looking up BARs in EA capability structure */ +static int dm_test_pci_ea(struct unit_test_state *uts) +{ + struct udevice *bus, *swap; + void *bar; + int cap; + + /* + * use emulated device mapping function, we're not using real physical + * addresses in this test + */ + sandbox_set_enable_pci_map(true); + + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus)); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x01, 0), &swap)); + + /* look up PCI_CAP_ID_EA */ + cap = dm_pci_find_capability(swap, PCI_CAP_ID_EA); + ut_asserteq(PCI_CAP_ID_EA_OFFSET, cap); + + /* test swap case in BAR 1 */ + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_0, 0); + ut_assertnonnull(bar); + *(int *)bar = 2; /* swap upper/lower */ + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0); + ut_assertnonnull(bar); + strcpy(bar, "ea TEST"); + unmap_sysmem(bar); + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0); + ut_assertnonnull(bar); + ut_asserteq_str("EA test", bar); + + /* test magic values in BARs2, 4; BAR 3 is n/a */ + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_2, 0); + ut_assertnonnull(bar); + ut_asserteq(PCI_EA_BAR2_MAGIC, *(u32 *)bar); + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_3, 0); + ut_assertnull(bar); + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_4, 0); + ut_assertnonnull(bar); + ut_asserteq(PCI_EA_BAR4_MAGIC, *(u32 *)bar); + + return 0; +} +DM_TEST(dm_test_pci_ea, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

On Fri, 7 Jun 2019 at 02:24, Alex Marginean alexm.osslist@gmail.com wrote:
This test is built on top of the existing swap_case driver. It adds EA capability structure support to swap_case and uses that to map BARs. BAR1 works as it used to, swapping upper/lower case. BARs 2,4 map to a couple of magic values.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com Reviewed-by: Bin Meng bmeng.cn@gmail.com Tested-by: Bin Meng bmeng.cn@gmail.com
Changes in v2: - new patch, v1 didn't have a test Changes in v3: - renamed sandbox_swap_use_ea function for consistency - fixed several typos and styling issues
arch/sandbox/dts/test.dts | 8 +++ arch/sandbox/include/asm/test.h | 13 ++++ drivers/misc/swap_case.c | 102 +++++++++++++++++++++++++++++++- test/dm/pci.c | 49 +++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-)
Applied to u-boot-dm/next, thanks!

Adds dm_pci_flr API that issues a Function Level reset on a PCI-e function, if FLR is supported.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com Reviewed-by: Bin Meng bmeng.cn@gmail.com ---
Changes in v2: - Use kernel PCI_EXP macros for register offsets Changes in v3: - none
drivers/pci/pci-uclass.c | 24 ++++++++++++++++++++++++ include/pci.h | 14 ++++++++++++++ 2 files changed, 38 insertions(+)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 389aec15ce..c74ebf6a76 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1494,6 +1494,30 @@ int dm_pci_find_ext_capability(struct udevice *dev, int cap) return dm_pci_find_next_ext_capability(dev, 0, cap); }
+int dm_pci_flr(struct udevice *dev) +{ + int pcie_off; + u32 cap; + + /* look for PCI Express Capability */ + pcie_off = dm_pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pcie_off) + return -ENOENT; + + /* check FLR capability */ + dm_pci_read_config32(dev, pcie_off + PCI_EXP_DEVCAP, &cap); + if (!(cap & PCI_EXP_DEVCAP_FLR)) + return -ENOENT; + + dm_pci_clrset_config16(dev, pcie_off + PCI_EXP_DEVCTL, 0, + PCI_EXP_DEVCTL_BCR_FLR); + + /* wait 100ms, per PCI spec */ + mdelay(100); + + return 0; +} + UCLASS_DRIVER(pci) = { .id = UCLASS_PCI, .name = "pci", diff --git a/include/pci.h b/include/pci.h index 0aab438159..298d0d4355 100644 --- a/include/pci.h +++ b/include/pci.h @@ -466,6 +466,12 @@ #define PCI_EA_IS_64 0x00000002 /* 64-bit field flag */ #define PCI_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */
+/* PCI Express capabilities */ +#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 */ + /* Include the ID list */
#include <pci_ids.h> @@ -1426,6 +1432,14 @@ int dm_pci_find_next_ext_capability(struct udevice *dev, int start, int cap); */ int dm_pci_find_ext_capability(struct udevice *dev, int cap);
+/** + * dm_pci_flr() - Perform FLR if the device suppoorts it + * + * @dev: PCI device to reset + * @return: 0 if OK, -ENOENT if FLR is not supported by dev + */ +int dm_pci_flr(struct udevice *dev); + #define dm_pci_virt_to_bus(dev, addr, flags) \ dm_pci_phys_to_bus(dev, (virt_to_phys(addr)), (flags)) #define dm_pci_bus_to_virt(dev, addr, flags, len, map_flags) \

On Fri, 7 Jun 2019 at 02:24, Alex Marginean alexm.osslist@gmail.com wrote:
Adds dm_pci_flr API that issues a Function Level reset on a PCI-e function, if FLR is supported.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com Reviewed-by: Bin Meng bmeng.cn@gmail.com
Changes in v2: - Use kernel PCI_EXP macros for register offsets Changes in v3: - none
drivers/pci/pci-uclass.c | 24 ++++++++++++++++++++++++ include/pci.h | 14 ++++++++++++++ 2 files changed, 38 insertions(+)
Applied to u-boot-dm/next, thanks!

Adds dm_pci_flr API that issues a Function Level reset on a PCI-e function, if FLR is supported.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com ---
Changes in v2: - Use kernel PCI_EXP macros for register offsets
drivers/pci/pci-uclass.c | 24 ++++++++++++++++++++++++ include/pci.h | 14 ++++++++++++++ 2 files changed, 38 insertions(+)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 389aec15ce..c74ebf6a76 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1494,6 +1494,30 @@ int dm_pci_find_ext_capability(struct udevice *dev, int cap) return dm_pci_find_next_ext_capability(dev, 0, cap); }
+int dm_pci_flr(struct udevice *dev) +{ + int pcie_off; + u32 cap; + + /* look for PCI Express Capability */ + pcie_off = dm_pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pcie_off) + return -ENOENT; + + /* check FLR capability */ + dm_pci_read_config32(dev, pcie_off + PCI_EXP_DEVCAP, &cap); + if (!(cap & PCI_EXP_DEVCAP_FLR)) + return -ENOENT; + + dm_pci_clrset_config16(dev, pcie_off + PCI_EXP_DEVCTL, 0, + PCI_EXP_DEVCTL_BCR_FLR); + + /* wait 100ms, per PCI spec */ + mdelay(100); + + return 0; +} + UCLASS_DRIVER(pci) = { .id = UCLASS_PCI, .name = "pci", diff --git a/include/pci.h b/include/pci.h index 0aab438159..298d0d4355 100644 --- a/include/pci.h +++ b/include/pci.h @@ -466,6 +466,12 @@ #define PCI_EA_IS_64 0x00000002 /* 64-bit field flag */ #define PCI_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */
+/* PCI Express capabilities */ +#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 */ + /* Include the ID list */
#include <pci_ids.h> @@ -1426,6 +1432,14 @@ int dm_pci_find_next_ext_capability(struct udevice *dev, int start, int cap); */ int dm_pci_find_ext_capability(struct udevice *dev, int cap);
+/** + * dm_pci_flr() - Perform FLR if the device suppoorts it + * + * @dev: PCI device to reset + * @return: 0 if OK, -ENOENT if FLR is not supported by dev + */ +int dm_pci_flr(struct udevice *dev); + #define dm_pci_virt_to_bus(dev, addr, flags) \ dm_pci_phys_to_bus(dev, (virt_to_phys(addr)), (flags)) #define dm_pci_bus_to_virt(dev, addr, flags, len, map_flags) \

On Tue, Jun 4, 2019 at 8:46 PM Alex Marginean alexm.osslist@gmail.com wrote:
Adds dm_pci_flr API that issues a Function Level reset on a PCI-e function, if FLR is supported.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - Use kernel PCI_EXP macros for register offsets
drivers/pci/pci-uclass.c | 24 ++++++++++++++++++++++++ include/pci.h | 14 ++++++++++++++ 2 files changed, 38 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com

On Tue, Jun 4, 2019 at 8:46 PM Alex Marginean alexm.osslist@gmail.com wrote:
The comment now indicates that the input argument bar is a register offset, not a BAR index. It also mentions which BARs are supported for type 0/1 and that the function can return 0 on error.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - new patch
include/pci.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
Reviewed-by: Bin Meng bmeng.cn@gmail.com

On Wed, 5 Jun 2019 at 04:05, Bin Meng bmeng.cn@gmail.com wrote:
On Tue, Jun 4, 2019 at 8:46 PM Alex Marginean alexm.osslist@gmail.com wrote:
The comment now indicates that the input argument bar is a register offset, not a BAR index. It also mentions which BARs are supported for type 0/1 and that the function can return 0 on error.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
Changes in v2: - new patch
include/pci.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
Reviewed-by: Bin Meng bmeng.cn@gmail.com
Applied to u-boot-dm/next, thanks!
participants (4)
-
Alex Marginean
-
Alexandru Marginean
-
Bin Meng
-
Simon Glass