- Add basic Marvell/Cavium OcteonTX/TX2 support (Suneel)
- Infrastructure changes to PCI uclass to support these SoC's (Suneel)
- Add PCI, MMC & watchdog driver drivers for OcteonTX/TX2 (Suneel)
- Increase CONFIG_SYS_MALLOC_F_LEN for qemu-x86 (Stefan)
This commit is contained in:
Tom Rini
2020-08-25 10:24:40 -04:00
77 changed files with 34981 additions and 94 deletions

View File

@@ -1198,10 +1198,25 @@ int ahci_probe_scsi(struct udevice *ahci_dev, ulong base)
int ahci_probe_scsi_pci(struct udevice *ahci_dev)
{
ulong base;
u16 vendor, device;
base = (ulong)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_5,
PCI_REGION_MEM);
/*
* Note:
* Right now, we have only one quirk here, which is not enough to
* introduce a new Kconfig option to select this. Once we have more
* quirks in this AHCI code, we should add a Kconfig option for
* this though.
*/
dm_pci_read_config16(ahci_dev, PCI_VENDOR_ID, &vendor);
dm_pci_read_config16(ahci_dev, PCI_DEVICE_ID, &device);
if (vendor == PCI_VENDOR_ID_CAVIUM &&
device == PCI_DEVICE_ID_CAVIUM_SATA)
base = (uintptr_t)dm_pci_map_bar(ahci_dev, PCI_BASE_ADDRESS_0,
PCI_REGION_MEM);
return ahci_probe_scsi(ahci_dev, base);
}
#endif

View File

@@ -10,6 +10,7 @@
#include <mapmem.h>
#include <asm/types.h>
#include <asm/io.h>
#include <linux/ioport.h>
int dev_read_u32(const struct udevice *dev, const char *propname, u32 *outp)
{
@@ -359,3 +360,19 @@ int dev_get_child_count(const struct udevice *dev)
{
return ofnode_get_child_count(dev_ofnode(dev));
}
int dev_read_pci_bus_range(const struct udevice *dev,
struct resource *res)
{
const u32 *values;
int len;
values = dev_read_prop(dev, "bus-range", &len);
if (!values || len < sizeof(*values) * 2)
return -EINVAL;
res->start = *values++;
res->end = *values;
return 0;
}

View File

@@ -305,6 +305,15 @@ config MMC_PCI
This selects PCI-based MMC controllers.
If you have an MMC controller on a PCI bus, say Y here.
config MMC_OCTEONTX
bool "Marvell OcteonTX Multimedia Card Interface support"
depends on (ARCH_OCTEONTX || ARCH_OCTEONTX2)
depends on DM_MMC
help
This selects the OcteonTX Multimedia card Interface.
If you have an OcteonTX/TX2 board with a Multimedia Card slot,
say Y here.
If unsure, say N.
config PXA_MMC_GENERIC

View File

@@ -36,6 +36,7 @@ obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o
obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxsmmc.o
obj-$(CONFIG_MMC_OCTEONTX) += octeontx_hsmmc.o
obj-$(CONFIG_MMC_PCI) += pci_mmc.o
obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
obj-$(CONFIG_$(SPL_TPL_)SUPPORT_EMMC_RPMB) += rpmb.o

3897
drivers/mmc/octeontx_hsmmc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,207 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2019 Marvell International Ltd.
*
* https://spdx.org/licenses
*/
#ifndef __OCTEONTX_HSMMC_H__
#define __OCTEONTX_HSMMC_H__
#include <asm/gpio.h>
/** Name of our driver */
#define OCTEONTX_MMC_DRIVER_NAME "octeontx-hsmmc"
/** Maximum supported MMC slots */
#define OCTEONTX_MAX_MMC_SLOT 3
#define POWER_ON_TIME 40 /** See SD 4.1 spec figure 6-5 */
/**
* Timeout used when waiting for commands to complete. We need to keep this
* above the hardware watchdog timeout which is usually limited to 1000ms
*/
#define WATCHDOG_COUNT (1100) /* in msecs */
/**
* Long timeout for commands which might take a while to complete.
*/
#define MMC_TIMEOUT_LONG 1000
/**
* Short timeout used for most commands in msecs
*/
#define MMC_TIMEOUT_SHORT 20
#define NSEC_PER_SEC 1000000000L
#define MAX_NO_OF_TAPS 64
#define EXT_CSD_POWER_CLASS 187 /* R/W */
/* default HS400 tuning block number */
#define DEFAULT_HS400_TUNING_BLOCK 1
struct octeontx_mmc_host;
/** MMC/SD slot data structure */
struct octeontx_mmc_slot {
struct mmc mmc;
struct mmc_config cfg;
struct octeontx_mmc_host *host;
struct udevice *dev;
void *base_addr; /** Same as host base_addr */
u64 clock;
int bus_id; /** slot number */
uint bus_width;
uint max_width;
int hs200_tap_adj;
int hs400_tap_adj;
int hs400_tuning_block;
struct gpio_desc cd_gpio;
struct gpio_desc wp_gpio;
struct gpio_desc power_gpio;
enum bus_mode mode;
union mio_emm_switch cached_switch;
union mio_emm_switch want_switch;
union mio_emm_rca cached_rca;
union mio_emm_timing taps; /* otx2: MIO_EMM_TIMING */
union mio_emm_timing hs200_taps;
union mio_emm_timing hs400_taps;
/* These are used to see if our tuning is still valid or not */
enum bus_mode last_mode;
u32 last_clock;
u32 block_len;
u32 block_count;
int cmd_clk_skew;
int dat_clk_skew;
uint cmd_cnt; /* otx: sample cmd in delay */
uint dat_cnt; /* otx: sample data in delay */
uint drive; /* Current drive */
uint slew; /* clock skew */
uint cmd_out_hs200_delay;
uint data_out_hs200_delay;
uint cmd_out_hs400_delay;
uint data_out_hs400_delay;
uint clk_period;
bool valid:1;
bool is_acmd:1;
bool tuned:1;
bool hs200_tuned:1;
bool hs400_tuned:1;
bool is_1_8v:1;
bool is_3_3v:1;
bool is_ddr:1;
bool is_asim:1;
bool is_emul:1;
bool cd_inverted:1;
bool wp_inverted:1;
bool disable_ddr:1;
bool non_removable:1;
};
struct octeontx_mmc_cr_mods {
u8 ctype_xor;
u8 rtype_xor;
};
struct octeontx_mmc_cr {
u8 c;
u8 r;
};
struct octeontx_sd_mods {
struct octeontx_mmc_cr mmc;
struct octeontx_mmc_cr sd;
struct octeontx_mmc_cr sdacmd;
};
/** Host controller data structure */
struct octeontx_mmc_host {
struct udevice *dev;
void *base_addr;
struct octeontx_mmc_slot slots[OCTEONTX_MAX_MMC_SLOT + 1];
pci_dev_t pdev;
u64 sys_freq;
union mio_emm_cfg emm_cfg;
u64 timing_taps;
struct mmc *last_mmc; /** Last mmc used */
ofnode node;
int cur_slotid;
int last_slotid;
int max_width;
uint per_tap_delay;
uint num_slots;
uint dma_wait_delay; /* Delay before polling DMA in usecs */
bool initialized:1;
bool timing_calibrated:1;
bool is_asim:1;
bool is_emul:1;
bool calibrate_glitch:1;
bool cond_clock_glitch:1;
bool tap_requires_noclk:1;
bool hs400_skew_needed:1;
};
/*
* NOTE: This was copied from the Linux kernel.
*
* MMC status in R1, for native mode (SPI bits are different)
* Type
* e:error bit
* s:status bit
* r:detected and set for the actual command response
* x:detected and set during command execution. the host must poll
* the card by sending status command in order to read these bits.
* Clear condition
* a:according to the card state
* b:always related to the previous command. Reception of
* a valid command will clear it (with a delay of one command)
* c:clear by read
*/
#define R1_OUT_OF_RANGE BIT(31) /* er, c */
#define R1_ADDRESS_ERROR BIT(30) /* erx, c */
#define R1_BLOCK_LEN_ERROR BIT(29) /* er, c */
#define R1_ERASE_SEQ_ERROR BIT(28) /* er, c */
#define R1_ERASE_PARAM BIT(27) /* ex, c */
#define R1_WP_VIOLATION BIT(26) /* erx, c */
#define R1_CARD_IS_LOCKED BIT(25) /* sx, a */
#define R1_LOCK_UNLOCK_FAILED BIT(24) /* erx, c */
#define R1_COM_CRC_ERROR BIT(23) /* er, b */
/*#define R1_ILLEGAL_COMMAND BIT(22)*/ /* er, b */
#define R1_CARD_ECC_FAILED BIT(21) /* ex, c */
#define R1_CC_ERROR BIT(20) /* erx, c */
#define R1_ERROR BIT(19) /* erx, c */
#define R1_UNDERRUN BIT(18) /* ex, c */
#define R1_OVERRUN BIT(17) /* ex, c */
#define R1_CID_CSD_OVERWRITE BIT(16) /* erx, c, CID/CSD overwrite */
#define R1_WP_ERASE_SKIP BIT(15) /* sx, c */
#define R1_CARD_ECC_DISABLED BIT(14) /* sx, a */
#define R1_ERASE_RESET BIT(13) /* sr, c */
#define R1_STATUS(x) ((x) & 0xFFFFE000)
#define R1_CURRENT_STATE(x) (((x) & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA BIT(8) /* sx, a */
#define R1_SWITCH_ERROR BIT(7) /* sx, c */
#define R1_BLOCK_READ_MASK R1_OUT_OF_RANGE | \
R1_ADDRESS_ERROR | \
R1_BLOCK_LEN_ERROR | \
R1_CARD_IS_LOCKED | \
R1_COM_CRC_ERROR | \
R1_ILLEGAL_COMMAND | \
R1_CARD_ECC_FAILED | \
R1_CC_ERROR | \
R1_ERROR
#define R1_BLOCK_WRITE_MASK R1_OUT_OF_RANGE | \
R1_ADDRESS_ERROR | \
R1_BLOCK_LEN_ERROR | \
R1_WP_VIOLATION | \
R1_CARD_IS_LOCKED | \
R1_COM_CRC_ERROR | \
R1_ILLEGAL_COMMAND | \
R1_CARD_ECC_FAILED | \
R1_CC_ERROR | \
R1_ERROR | \
R1_UNDERRUN | \
R1_OVERRUN
#endif /* __OCTEONTX_HSMMC_H__ */

View File

@@ -43,6 +43,35 @@ config PCI_PNP
help
Enable PCI memory and I/O space resource allocation and assignment.
config PCI_REGION_MULTI_ENTRY
bool "Enable Multiple entries of region type MEMORY in ranges for PCI"
depends on PCI || DM_PCI
default n
help
Enable PCI memory regions to be of multiple entry. Multiple entry
here refers to allow more than one count of address ranges for MEMORY
region type. This helps to add support for SoC's like OcteonTX/TX2
where every peripheral is on the PCI bus.
config PCI_SRIOV
bool "Enable Single Root I/O Virtualization support for PCI"
depends on PCI || DM_PCI
default n
help
Say Y here if you want to enable PCI Single Root I/O Virtualization
capability support. This helps to enumerate Virtual Function devices
if available on a PCI Physical Function device and probe for
applicable drivers.
config PCI_ARID
bool "Enable Alternate Routing-ID support for PCI"
depends on PCI || DM_PCI
default n
help
Say Y here if you want to enable Alternate Routing-ID capability
support on PCI devices. This helps to skip some devices in BDF
scan that are not present.
config PCIE_ECAM_GENERIC
bool "Generic ECAM-based PCI host controller support"
default n
@@ -120,6 +149,14 @@ config PCI_TEGRA
with a total of 5 lanes. Some boards require this for Ethernet
support to work (e.g. beaver, jetson-tk1).
config PCI_OCTEONTX
bool "OcteonTX PCI support"
depends on (ARCH_OCTEONTX || ARCH_OCTEONTX2)
help
Enable support for the OcteonTX/TX2 SoC family ECAM/PEM controllers.
These controllers provide PCI configuration access to all on-board
peripherals so it should only be disabled for testing purposes
config PCI_XILINX
bool "Xilinx AXI Bridge for PCI Express"
depends on DM_PCI

View File

@@ -49,3 +49,4 @@ obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o
obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o
obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o
obj-$(CONFIG_PCI_BRCMSTB) += pcie_brcmstb.o
obj-$(CONFIG_PCI_OCTEONTX) += pci_octeontx.o

View File

@@ -539,7 +539,8 @@ int pci_auto_config_devices(struct udevice *bus)
int ret;
debug("%s: device %s\n", __func__, dev->name);
if (dev_read_bool(dev, "pci,no-autoconfig"))
if (dev_of_valid(dev) &&
dev_read_bool(dev, "pci,no-autoconfig"))
continue;
ret = dm_pciauto_config_device(dev);
if (ret < 0)
@@ -620,10 +621,19 @@ int dm_pci_hose_probe_bus(struct udevice *bus)
{
int sub_bus;
int ret;
int ea_pos;
u8 reg;
debug("%s\n", __func__);
sub_bus = pci_get_bus_max() + 1;
ea_pos = dm_pci_find_capability(bus, PCI_CAP_ID_EA);
if (ea_pos) {
dm_pci_read_config8(bus, ea_pos + sizeof(u32) + sizeof(u8),
&reg);
sub_bus = reg;
} else {
sub_bus = pci_get_bus_max() + 1;
}
debug("%s: bus = %d/%s\n", __func__, sub_bus, bus->name);
dm_pciauto_prescan_setup_bridge(bus, sub_bus);
@@ -633,12 +643,15 @@ int dm_pci_hose_probe_bus(struct udevice *bus)
ret);
return ret;
}
if (sub_bus != bus->seq) {
printf("%s: Internal error, bus '%s' got seq %d, expected %d\n",
__func__, bus->name, bus->seq, sub_bus);
return -EPIPE;
if (!ea_pos) {
if (sub_bus != bus->seq) {
debug("%s: Internal error, bus '%s' got seq %d, expected %d\n",
__func__, bus->name, bus->seq, sub_bus);
return -EPIPE;
}
sub_bus = pci_get_bus_max();
}
sub_bus = pci_get_bus_max();
dm_pciauto_postscan_setup_bridge(bus, sub_bus);
return sub_bus;
@@ -696,7 +709,8 @@ static int pci_find_and_bind_driver(struct udevice *parent,
find_id->vendor, find_id->device);
/* Determine optional OF node */
pci_dev_find_ofnode(parent, bdf, &node);
if (ofnode_valid(dev_ofnode(parent)))
pci_dev_find_ofnode(parent, bdf, &node);
if (ofnode_valid(node) && !ofnode_is_available(node)) {
debug("%s: Ignoring disabled device\n", __func__);
@@ -785,6 +799,7 @@ int pci_bind_bus_devices(struct udevice *bus)
ulong header_type;
pci_dev_t bdf, end;
bool found_multi;
int ari_off;
int ret;
found_multi = false;
@@ -858,6 +873,31 @@ int pci_bind_bus_devices(struct udevice *bus)
pplat->vendor = vendor;
pplat->device = device;
pplat->class = class;
if (IS_ENABLED(CONFIG_PCI_ARID)) {
ari_off = dm_pci_find_ext_capability(dev,
PCI_EXT_CAP_ID_ARI);
if (ari_off) {
u16 ari_cap;
/*
* Read Next Function number in ARI Cap
* Register
*/
dm_pci_read_config16(dev, ari_off + 4,
&ari_cap);
/*
* Update next scan on this function number,
* subtract 1 in BDF to satisfy loop increment.
*/
if (ari_cap & 0xff00) {
bdf = PCI_BDF(PCI_BUS(bdf),
PCI_DEV(ari_cap),
PCI_FUNC(ari_cap));
bdf = bdf - 0x100;
}
}
}
}
return 0;
@@ -871,8 +911,10 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
ofnode node)
{
int pci_addr_cells, addr_cells, size_cells;
struct bd_info *bd = gd->bd;
int cells_per_record;
const u32 *prop;
int max_regions;
int len;
int i;
@@ -892,7 +934,13 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
hose->region_count = 0;
debug("%s: len=%d, cells_per_record=%d\n", __func__, len,
cells_per_record);
for (i = 0; i < MAX_PCI_REGIONS; i++, len -= cells_per_record) {
/* Dynamically allocate the regions array */
max_regions = len / cells_per_record + CONFIG_NR_DRAM_BANKS;
hose->regions = (struct pci_region *)
calloc(1, max_regions * sizeof(struct pci_region));
for (i = 0; i < max_regions; i++, len -= cells_per_record) {
u64 pci_addr, addr, size;
int space_code;
u32 flags;
@@ -927,10 +975,13 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
}
pos = -1;
for (i = 0; i < hose->region_count; i++) {
if (hose->regions[i].flags == type)
pos = i;
if (!IS_ENABLED(CONFIG_PCI_REGION_MULTI_ENTRY)) {
for (i = 0; i < hose->region_count; i++) {
if (hose->regions[i].flags == type)
pos = i;
}
}
if (pos == -1)
pos = hose->region_count++;
debug(" - type=%d, pos=%d\n", type, pos);
@@ -938,18 +989,10 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
}
/* Add a region for our local memory */
#ifdef CONFIG_NR_DRAM_BANKS
struct bd_info *bd = gd->bd;
if (!bd)
return;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) {
if (hose->region_count == MAX_PCI_REGIONS) {
pr_err("maximum number of regions parsed, aborting\n");
break;
}
if (bd->bi_dram[i].size) {
pci_set_region(hose->regions + hose->region_count++,
bd->bi_dram[i].start,
@@ -958,19 +1001,6 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
}
}
#else
phys_addr_t base = 0, size;
size = gd->ram_size;
#ifdef CONFIG_SYS_SDRAM_BASE
base = CONFIG_SYS_SDRAM_BASE;
#endif
if (gd->pci_ram_top && gd->pci_ram_top < base + size)
size = gd->pci_ram_top - base;
if (size)
pci_set_region(hose->regions + hose->region_count++, base,
base, size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
#endif
return;
}
@@ -996,8 +1026,11 @@ static int pci_uclass_pre_probe(struct udevice *bus)
hose->bus = bus;
hose->first_busno = bus->seq;
hose->last_busno = bus->seq;
hose->skip_auto_config_until_reloc =
dev_read_bool(bus, "u-boot,skip-auto-config-until-reloc");
if (dev_of_valid(bus)) {
hose->skip_auto_config_until_reloc =
dev_read_bool(bus,
"u-boot,skip-auto-config-until-reloc");
}
return 0;
}
@@ -1406,14 +1439,55 @@ pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
return bus_addr;
}
static phys_addr_t dm_pci_map_ea_virt(struct udevice *dev, int ea_off,
struct pci_child_platdata *pdata)
{
phys_addr_t addr = 0;
/*
* In the case of a Virtual Function device using BAR
* base and size, add offset for VFn BAR(1, 2, 3...n)
*/
if (pdata->is_virtfn) {
size_t sz;
u32 ea_entry;
/* MaxOffset, 1st DW */
dm_pci_read_config32(dev, ea_off + 8, &ea_entry);
sz = ea_entry & PCI_EA_FIELD_MASK;
/* Fill up lower 2 bits */
sz |= (~PCI_EA_FIELD_MASK);
if (ea_entry & PCI_EA_IS_64) {
/* MaxOffset 2nd DW */
dm_pci_read_config32(dev, ea_off + 16, &ea_entry);
sz |= ((u64)ea_entry) << 32;
}
addr = (pdata->virtid - 1) * (sz + 1);
}
return addr;
}
static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags,
int ea_off)
int ea_off, struct pci_child_platdata *pdata)
{
int ea_cnt, i, entry_size;
int bar_id = (bar - PCI_BASE_ADDRESS_0) >> 2;
u32 ea_entry;
phys_addr_t addr;
if (IS_ENABLED(CONFIG_PCI_SRIOV)) {
/*
* In the case of a Virtual Function device, device is
* Physical function, so pdata will point to required VF
* specific data.
*/
if (pdata->is_virtfn)
bar_id += PCI_EA_BEI_VF_BAR0;
}
/* EA capability structure header */
dm_pci_read_config32(dev, ea_off, &ea_entry);
ea_cnt = (ea_entry >> 16) & PCI_EA_NUM_ENT_MASK;
@@ -1436,8 +1510,11 @@ static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags,
addr |= ((u64)ea_entry) << 32;
}
if (IS_ENABLED(CONFIG_PCI_SRIOV))
addr += dm_pci_map_ea_virt(dev, ea_off, pdata);
/* size ignored for now */
return map_physmem(addr, flags, 0);
return map_physmem(addr, 0, flags);
}
return 0;
@@ -1445,29 +1522,42 @@ static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags,
void *dm_pci_map_bar(struct udevice *dev, int bar, int flags)
{
struct pci_child_platdata *pdata = dev_get_parent_platdata(dev);
struct udevice *udev = dev;
pci_addr_t pci_bus_addr;
u32 bar_response;
int ea_off;
if (IS_ENABLED(CONFIG_PCI_SRIOV)) {
/*
* In case of Virtual Function devices, use PF udevice
* as EA capability is defined in Physical Function
*/
if (pdata->is_virtfn)
udev = pdata->pfdev;
}
/*
* if the function supports Enhanced Allocation use that instead of
* BARs
* Incase of virtual functions, pdata will help read VF BEI
* and EA entry size.
*/
ea_off = dm_pci_find_capability(dev, PCI_CAP_ID_EA);
ea_off = dm_pci_find_capability(udev, PCI_CAP_ID_EA);
if (ea_off)
return dm_pci_map_ea_bar(dev, bar, flags, ea_off);
return dm_pci_map_ea_bar(udev, bar, flags, ea_off, pdata);
/* read BAR address */
dm_pci_read_config32(dev, bar, &bar_response);
dm_pci_read_config32(udev, bar, &bar_response);
pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
/*
* Pass "0" as the length argument to pci_bus_to_virt. The arg
* isn't actualy used on any platform because u-boot assumes a static
* isn't actually used on any platform because U-Boot assumes a static
* linear mapping. In the future, this could read the BAR size
* and pass that as the size if needed.
*/
return dm_pci_bus_to_virt(dev, pci_bus_addr, flags, 0, MAP_NOCACHE);
return dm_pci_bus_to_virt(udev, pci_bus_addr, flags, 0, MAP_NOCACHE);
}
static int _dm_pci_find_next_capability(struct udevice *dev, u8 pos, int cap)
@@ -1583,6 +1673,120 @@ int dm_pci_flr(struct udevice *dev)
return 0;
}
#if defined(CONFIG_PCI_SRIOV)
int pci_sriov_init(struct udevice *pdev, int vf_en)
{
u16 vendor, device;
struct udevice *bus;
struct udevice *dev;
pci_dev_t bdf;
u16 ctrl;
u16 num_vfs;
u16 total_vf;
u16 vf_offset;
u16 vf_stride;
int vf, ret;
int pos;
pos = dm_pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
if (!pos) {
debug("Error: SRIOV capability not found\n");
return -ENOENT;
}
dm_pci_read_config16(pdev, pos + PCI_SRIOV_CTRL, &ctrl);
dm_pci_read_config16(pdev, pos + PCI_SRIOV_TOTAL_VF, &total_vf);
if (vf_en > total_vf)
vf_en = total_vf;
dm_pci_write_config16(pdev, pos + PCI_SRIOV_NUM_VF, vf_en);
ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
dm_pci_write_config16(pdev, pos + PCI_SRIOV_CTRL, ctrl);
dm_pci_read_config16(pdev, pos + PCI_SRIOV_NUM_VF, &num_vfs);
if (num_vfs > vf_en)
num_vfs = vf_en;
dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_OFFSET, &vf_offset);
dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_STRIDE, &vf_stride);
dm_pci_read_config16(pdev, PCI_VENDOR_ID, &vendor);
dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_DID, &device);
bdf = dm_pci_get_bdf(pdev);
pci_get_bus(PCI_BUS(bdf), &bus);
if (!bus)
return -ENODEV;
bdf += PCI_BDF(0, 0, vf_offset);
for (vf = 0; vf < num_vfs; vf++) {
struct pci_child_platdata *pplat;
ulong class;
pci_bus_read_config(bus, bdf, PCI_CLASS_DEVICE,
&class, PCI_SIZE_16);
debug("%s: bus %d/%s: found VF %x:%x\n", __func__,
bus->seq, bus->name, PCI_DEV(bdf), PCI_FUNC(bdf));
/* Find this device in the device tree */
ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), &dev);
if (ret == -ENODEV) {
struct pci_device_id find_id;
memset(&find_id, '\0', sizeof(find_id));
find_id.vendor = vendor;
find_id.device = device;
find_id.class = class;
ret = pci_find_and_bind_driver(bus, &find_id,
bdf, &dev);
if (ret)
return ret;
}
/* Update the platform data */
pplat = dev_get_parent_platdata(dev);
pplat->devfn = PCI_MASK_BUS(bdf);
pplat->vendor = vendor;
pplat->device = device;
pplat->class = class;
pplat->is_virtfn = true;
pplat->pfdev = pdev;
pplat->virtid = vf * vf_stride + vf_offset;
debug("%s: bus %d/%s: found VF %x:%x %x:%x class %lx id %x\n",
__func__, dev->seq, dev->name, PCI_DEV(bdf),
PCI_FUNC(bdf), vendor, device, class, pplat->virtid);
bdf += PCI_BDF(0, 0, vf_stride);
}
return 0;
}
int pci_sriov_get_totalvfs(struct udevice *pdev)
{
u16 total_vf;
int pos;
pos = dm_pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
if (!pos) {
debug("Error: SRIOV capability not found\n");
return -ENOENT;
}
dm_pci_read_config16(pdev, pos + PCI_SRIOV_TOTAL_VF, &total_vf);
return total_vf;
}
#endif /* SRIOV */
UCLASS_DRIVER(pci) = {
.id = UCLASS_PCI,
.name = "pci",

364
drivers/pci/pci_octeontx.c Normal file
View File

@@ -0,0 +1,364 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Marvell International Ltd.
*
* https://spdx.org/licenses
*/
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <log.h>
#include <malloc.h>
#include <pci.h>
#include <asm/io.h>
#include <linux/ioport.h>
DECLARE_GLOBAL_DATA_PTR;
/*
* This driver supports multiple types of operations / host bridges / busses:
*
* OTX_ECAM: Octeon TX & TX2 ECAM (Enhanced Configuration Access Mechanism)
* Used to access the internal on-chip devices which are connected
* to internal buses
* OTX_PEM: Octeon TX PEM (PCI Express MAC)
* Used to access the external (off-chip) PCI devices
* OTX2_PEM: Octeon TX2 PEM (PCI Express MAC)
* Used to access the external (off-chip) PCI devices
*/
enum {
OTX_ECAM,
OTX_PEM,
OTX2_PEM,
};
/**
* struct octeontx_pci - Driver private data
* @type: Device type matched via compatible (e.g. OTX_ECAM etc)
* @cfg: Config resource
* @bus: Bus resource
*/
struct octeontx_pci {
unsigned int type;
struct resource cfg;
struct resource bus;
};
static uintptr_t octeontx_cfg_addr(struct octeontx_pci *pcie,
int bus_offs, int shift_offs,
pci_dev_t bdf, uint offset)
{
u32 bus, dev, func;
uintptr_t address;
bus = PCI_BUS(bdf) + bus_offs;
dev = PCI_DEV(bdf);
func = PCI_FUNC(bdf);
address = (bus << (20 + shift_offs)) |
(dev << (15 + shift_offs)) |
(func << (12 + shift_offs)) | offset;
address += pcie->cfg.start;
return address;
}
static ulong readl_size(uintptr_t addr, enum pci_size_t size)
{
ulong val;
switch (size) {
case PCI_SIZE_8:
val = readb(addr);
break;
case PCI_SIZE_16:
val = readw(addr);
break;
case PCI_SIZE_32:
val = readl(addr);
break;
default:
printf("Invalid size\n");
return -EINVAL;
};
return val;
}
static void writel_size(uintptr_t addr, enum pci_size_t size, ulong valuep)
{
switch (size) {
case PCI_SIZE_8:
writeb(valuep, addr);
break;
case PCI_SIZE_16:
writew(valuep, addr);
break;
case PCI_SIZE_32:
writel(valuep, addr);
break;
default:
printf("Invalid size\n");
};
}
static bool octeontx_bdf_invalid(pci_dev_t bdf)
{
if (PCI_BUS(bdf) == 1 && PCI_DEV(bdf) > 0)
return true;
return false;
}
static int octeontx_ecam_read_config(const struct udevice *bus, pci_dev_t bdf,
uint offset, ulong *valuep,
enum pci_size_t size)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
struct pci_controller *hose = dev_get_uclass_priv(bus);
uintptr_t address;
address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
0, bdf, offset);
*valuep = readl_size(address, size);
debug("%02x.%02x.%02x: u%d %x -> %lx\n",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep);
return 0;
}
static int octeontx_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
uint offset, ulong value,
enum pci_size_t size)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
struct pci_controller *hose = dev_get_uclass_priv(bus);
uintptr_t address;
address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
0, bdf, offset);
writel_size(address, size, value);
debug("%02x.%02x.%02x: u%d %x <- %lx\n",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, value);
return 0;
}
static int octeontx_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
uint offset, ulong *valuep,
enum pci_size_t size)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
struct pci_controller *hose = dev_get_uclass_priv(bus);
uintptr_t address;
u8 hdrtype;
u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4,
bdf, 0);
*valuep = pci_conv_32_to_size(~0UL, offset, size);
if (octeontx_bdf_invalid(bdf))
return -EPERM;
*valuep = readl_size(address + offset, size);
hdrtype = readb(address + PCI_HEADER_TYPE);
if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
offset >= PCI_PRIMARY_BUS &&
offset <= PCI_SUBORDINATE_BUS &&
*valuep != pci_conv_32_to_size(~0UL, offset, size))
*valuep -= pci_conv_32_to_size(bus_offs, offset, size);
return 0;
}
static int octeontx_pem_write_config(struct udevice *bus, pci_dev_t bdf,
uint offset, ulong value,
enum pci_size_t size)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
struct pci_controller *hose = dev_get_uclass_priv(bus);
uintptr_t address;
u8 hdrtype;
u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4, bdf, 0);
hdrtype = readb(address + PCI_HEADER_TYPE);
if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
offset >= PCI_PRIMARY_BUS &&
offset <= PCI_SUBORDINATE_BUS &&
value != pci_conv_32_to_size(~0UL, offset, size))
value += pci_conv_32_to_size(bus_offs, offset, size);
if (octeontx_bdf_invalid(bdf))
return -EPERM;
writel_size(address + offset, size, value);
debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
address, value);
return 0;
}
static int octeontx2_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
uint offset, ulong *valuep,
enum pci_size_t size)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
struct pci_controller *hose = dev_get_uclass_priv(bus);
uintptr_t address;
address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
bdf, 0);
*valuep = pci_conv_32_to_size(~0UL, offset, size);
if (octeontx_bdf_invalid(bdf))
return -EPERM;
*valuep = readl_size(address + offset, size);
debug("%02x.%02x.%02x: u%d %x (%lx) -> %lx\n",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
address, *valuep);
return 0;
}
static int octeontx2_pem_write_config(struct udevice *bus, pci_dev_t bdf,
uint offset, ulong value,
enum pci_size_t size)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
struct pci_controller *hose = dev_get_uclass_priv(bus);
uintptr_t address;
address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
bdf, 0);
if (octeontx_bdf_invalid(bdf))
return -EPERM;
writel_size(address + offset, size, value);
debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
address, value);
return 0;
}
int pci_octeontx_read_config(const struct udevice *bus, pci_dev_t bdf,
uint offset, ulong *valuep,
enum pci_size_t size)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
int ret = -EIO;
switch (pcie->type) {
case OTX_ECAM:
ret = octeontx_ecam_read_config(bus, bdf, offset, valuep,
size);
break;
case OTX_PEM:
ret = octeontx_pem_read_config(bus, bdf, offset, valuep,
size);
break;
case OTX2_PEM:
ret = octeontx2_pem_read_config(bus, bdf, offset, valuep,
size);
break;
}
return ret;
}
int pci_octeontx_write_config(struct udevice *bus, pci_dev_t bdf,
uint offset, ulong value,
enum pci_size_t size)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
int ret = -EIO;
switch (pcie->type) {
case OTX_ECAM:
ret = octeontx_ecam_write_config(bus, bdf, offset, value,
size);
break;
case OTX_PEM:
ret = octeontx_pem_write_config(bus, bdf, offset, value,
size);
break;
case OTX2_PEM:
ret = octeontx2_pem_write_config(bus, bdf, offset, value,
size);
break;
}
return ret;
}
static int pci_octeontx_ofdata_to_platdata(struct udevice *dev)
{
return 0;
}
static int pci_octeontx_probe(struct udevice *dev)
{
struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(dev);
int err;
pcie->type = dev_get_driver_data(dev);
err = dev_read_resource(dev, 0, &pcie->cfg);
if (err) {
debug("Error reading resource: %s\n", fdt_strerror(err));
return err;
}
err = dev_read_pci_bus_range(dev, &pcie->bus);
if (err) {
debug("Error reading resource: %s\n", fdt_strerror(err));
return err;
}
return 0;
}
static const struct dm_pci_ops pci_octeontx_ops = {
.read_config = pci_octeontx_read_config,
.write_config = pci_octeontx_write_config,
};
static const struct udevice_id pci_octeontx_ids[] = {
{ .compatible = "cavium,pci-host-thunder-ecam", .data = OTX_ECAM },
{ .compatible = "cavium,pci-host-octeontx-ecam", .data = OTX_ECAM },
{ .compatible = "pci-host-ecam-generic", .data = OTX_ECAM },
{ .compatible = "cavium,pci-host-thunder-pem", .data = OTX_PEM },
{ .compatible = "marvell,pci-host-octeontx2-pem", .data = OTX2_PEM },
{ }
};
U_BOOT_DRIVER(pci_octeontx) = {
.name = "pci_octeontx",
.id = UCLASS_PCI,
.of_match = pci_octeontx_ids,
.ops = &pci_octeontx_ops,
.ofdata_to_platdata = pci_octeontx_ofdata_to_platdata,
.probe = pci_octeontx_probe,
.priv_auto_alloc_size = sizeof(struct octeontx_pci),
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -139,6 +139,16 @@ config WDT_MTK
The watchdog timer is stopped when initialized.
It performs full SoC reset.
config WDT_OCTEONTX
bool "OcteonTX core watchdog support"
depends on WDT && (ARCH_OCTEONTX || ARCH_OCTEONTX2)
default y
imply WATCHDOG
help
This enables OcteonTX watchdog driver, which can be
found on OcteonTX/TX2 chipsets and inline with driver model.
Only supports watchdog reset.
config WDT_OMAP3
bool "TI OMAP watchdog timer support"
depends on WDT && ARCH_OMAP2PLUS

View File

@@ -26,6 +26,7 @@ obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o
obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
obj-$(CONFIG_WDT_OCTEONTX) += octeontx_wdt.o
obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o
obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o
obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o

View File

@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Marvell International Ltd.
*
* https://spdx.org/licenses
*/
#include <dm.h>
#include <errno.h>
#include <wdt.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
#define CORE0_POKE_OFFSET 0x50000
#define CORE0_POKE_OFFSET_MASK 0xfffffULL
struct octeontx_wdt {
void __iomem *reg;
};
static int octeontx_wdt_reset(struct udevice *dev)
{
struct octeontx_wdt *priv = dev_get_priv(dev);
writeq(~0ULL, priv->reg);
return 0;
}
static int octeontx_wdt_probe(struct udevice *dev)
{
struct octeontx_wdt *priv = dev_get_priv(dev);
priv->reg = dev_remap_addr(dev);
if (!priv->reg)
return -EINVAL;
/*
* Save core poke register address in reg (its not 0xa0000 as
* extracted from the DT but 0x50000 instead)
*/
priv->reg = (void __iomem *)(((u64)priv->reg &
~CORE0_POKE_OFFSET_MASK) |
CORE0_POKE_OFFSET);
return 0;
}
static const struct wdt_ops octeontx_wdt_ops = {
.reset = octeontx_wdt_reset,
};
static const struct udevice_id octeontx_wdt_ids[] = {
{ .compatible = "arm,sbsa-gwdt" },
{}
};
U_BOOT_DRIVER(wdt_octeontx) = {
.name = "wdt_octeontx",
.id = UCLASS_WDT,
.of_match = octeontx_wdt_ids,
.ops = &octeontx_wdt_ops,
.priv_auto_alloc_size = sizeof(struct octeontx_wdt),
.probe = octeontx_wdt_probe,
};