- 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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
3897
drivers/mmc/octeontx_hsmmc.c
Normal file
File diff suppressed because it is too large
Load Diff
207
drivers/mmc/octeontx_hsmmc.h
Normal file
207
drivers/mmc/octeontx_hsmmc.h
Normal 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__ */
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
®);
|
||||
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
364
drivers/pci/pci_octeontx.c
Normal 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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
66
drivers/watchdog/octeontx_wdt.c
Normal file
66
drivers/watchdog/octeontx_wdt.c
Normal 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,
|
||||
};
|
||||
Reference in New Issue
Block a user