1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
/*
* Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
*
* Based on the Linux implementation.
* Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc.
* Authors: Carsten Langgaard <carstenl@mips.com>
* Maciej W. Rozycki <macro@mips.com>
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <gt64120.h>
#include <pci.h>
#include <pci_gt64120.h>
#include <asm/io.h>
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
struct gt64120_regs {
u8 unused_000[0xc18];
u32 intrcause;
u8 unused_c1c[0x0dc];
u32 pci0_cfgaddr;
u32 pci0_cfgdata;
};
struct gt64120_pci_controller {
struct pci_controller hose;
struct gt64120_regs *regs;
};
static inline struct gt64120_pci_controller *
hose_to_gt64120(struct pci_controller *hose)
{
return container_of(hose, struct gt64120_pci_controller, hose);
}
#define GT_INTRCAUSE_ABORT_BITS \
(GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT)
static int gt_config_access(struct gt64120_pci_controller *gt,
unsigned char access_type, pci_dev_t bdf,
int where, u32 *data)
{
unsigned int bus = PCI_BUS(bdf);
unsigned int dev = PCI_DEV(bdf);
unsigned int devfn = PCI_DEV(bdf) << 3 | PCI_FUNC(bdf);
u32 intr;
u32 addr;
u32 val;
if (bus == 0 && dev >= 31) {
/* Because of a bug in the galileo (for slot 31). */
return -1;
}
if (access_type == PCI_ACCESS_WRITE)
debug("PCI WR %02x:%02x.%x reg:%02d data:%08x\n",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data);
/* Clear cause register bits */
writel(~GT_INTRCAUSE_ABORT_BITS, >->regs->intrcause);
addr = GT_PCI0_CFGADDR_CONFIGEN_BIT;
addr |= bus << GT_PCI0_CFGADDR_BUSNUM_SHF;
addr |= devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF;
addr |= (where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF;
/* Setup address */
writel(addr, >->regs->pci0_cfgaddr);
if (access_type == PCI_ACCESS_WRITE) {
if (bus == 0 && dev == 0) {
/*
* The Galileo system controller is acting
* differently than other devices.
*/
val = *data;
} else {
val = cpu_to_le32(*data);
}
writel(val, >->regs->pci0_cfgdata);
} else {
val = readl(>->regs->pci0_cfgdata);
if (bus == 0 && dev == 0) {
/*
* The Galileo system controller is acting
* differently than other devices.
*/
*data = val;
} else {
*data = le32_to_cpu(val);
}
}
/* Check for master or target abort */
intr = readl(>->regs->intrcause);
if (intr & GT_INTRCAUSE_ABORT_BITS) {
/* Error occurred, clear abort bits */
writel(~GT_INTRCAUSE_ABORT_BITS, >->regs->intrcause);
return -1;
}
if (access_type == PCI_ACCESS_READ)
debug("PCI RD %02x:%02x.%x reg:%02d data:%08x\n",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data);
return 0;
}
static int gt_read_config_dword(struct pci_controller *hose, pci_dev_t dev,
int where, u32 *value)
{
struct gt64120_pci_controller *gt = hose_to_gt64120(hose);
*value = 0xffffffff;
return gt_config_access(gt, PCI_ACCESS_READ, dev, where, value);
}
static int gt_write_config_dword(struct pci_controller *hose, pci_dev_t dev,
int where, u32 value)
{
struct gt64120_pci_controller *gt = hose_to_gt64120(hose);
u32 data = value;
return gt_config_access(gt, PCI_ACCESS_WRITE, dev, where, &data);
}
void gt64120_pci_init(void *regs, unsigned long sys_bus, unsigned long sys_phys,
unsigned long sys_size, unsigned long mem_bus,
unsigned long mem_phys, unsigned long mem_size,
unsigned long io_bus, unsigned long io_phys,
unsigned long io_size)
{
static struct gt64120_pci_controller global_gt;
struct gt64120_pci_controller *gt;
struct pci_controller *hose;
gt = &global_gt;
gt->regs = regs;
hose = >->hose;
hose->first_busno = 0;
hose->last_busno = 0;
/* System memory space */
pci_set_region(&hose->regions[0], sys_bus, sys_phys, sys_size,
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
/* PCI memory space */
pci_set_region(&hose->regions[1], mem_bus, mem_phys, mem_size,
PCI_REGION_MEM);
/* PCI I/O space */
pci_set_region(&hose->regions[2], io_bus, io_phys, io_size,
PCI_REGION_IO);
hose->region_count = 3;
pci_set_ops(hose,
pci_hose_read_config_byte_via_dword,
pci_hose_read_config_word_via_dword,
gt_read_config_dword,
pci_hose_write_config_byte_via_dword,
pci_hose_write_config_word_via_dword,
gt_write_config_dword);
pci_register_hose(hose);
hose->last_busno = pci_hose_scan(hose);
}
|