#define iob() __asm__ __volatile__("eieio; sync":::"memory")
+struct epci_private {
+ dma_addr_t dummy_page_da;
+};
-#if 0 /* test code for epci dummy read */
-static void celleb_epci_dummy_read(struct pci_dev *dev)
+static inline PCI_IO_ADDR celleb_epci_get_epci_base(
+ struct pci_controller *hose)
{
- void __iomem *epci_base;
- struct device_node *node;
- struct pci_controller *hose;
- u32 val;
+ /*
+ * Note:
+ * Celleb epci uses cfg_addr as a base address for
+ * epci control registers.
+ */
- node = (struct device_node *)dev->bus->sysdata;
- hose = pci_find_hose_for_OF_device(node);
+ return hose->cfg_addr;
+}
- if (!hose)
- return;
+static inline PCI_IO_ADDR celleb_epci_get_epci_cfg(
+ struct pci_controller *hose)
+{
+ /*
+ * Note:
+ * Celleb epci uses cfg_data as a base address for
+ * configuration area for epci devices.
+ */
+
+ return hose->cfg_data;
+}
+
+static void scc_epci_dummy_read(struct pci_controller *hose)
+{
+ PCI_IO_ADDR epci_base;
+ u32 val;
- epci_base = hose->cfg_addr;
+ epci_base = celleb_epci_get_epci_base(hose);
val = in_be32(epci_base + SCC_EPCI_WATRP);
iosync();
return;
}
-#endif
+
+void __init epci_workaround_init(struct pci_controller *hose)
+{
+ PCI_IO_ADDR epci_base;
+ PCI_IO_ADDR reg;
+ struct epci_private *private = hose->private_data;
+
+ BUG_ON(!private);
+
+ private->dummy_page_da = dma_map_single(hose->parent,
+ celleb_dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE);
+ if (private->dummy_page_da == DMA_ERROR_CODE) {
+ printk(KERN_ERR "EPCI: dummy read disabled. "
+ "Map dummy page failed.\n");
+ return;
+ }
+
+ celleb_pci_add_one(hose, scc_epci_dummy_read);
+ epci_base = celleb_epci_get_epci_base(hose);
+
+ reg = epci_base + SCC_EPCI_DUMYRADR;
+ out_be32(reg, private->dummy_page_da);
+}
static inline void clear_and_disable_master_abort_interrupt(
struct pci_controller *hose)
{
- void __iomem *addr;
- addr = hose->cfg_addr + PCI_COMMAND;
- out_be32(addr, in_be32(addr) | (PCI_STATUS_REC_MASTER_ABORT << 16));
+ PCI_IO_ADDR epci_base;
+ PCI_IO_ADDR reg;
+ epci_base = celleb_epci_get_epci_base(hose);
+ reg = epci_base + PCI_COMMAND;
+ out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16));
}
static int celleb_epci_check_abort(struct pci_controller *hose,
- void __iomem *addr)
+ PCI_IO_ADDR addr)
{
- void __iomem *reg, *epci_base;
+ PCI_IO_ADDR reg;
+ PCI_IO_ADDR epci_base;
u32 val;
iob();
- epci_base = hose->cfg_addr;
+ epci_base = celleb_epci_get_epci_base(hose);
reg = epci_base + PCI_COMMAND;
val = in_be32(reg);
return PCIBIOS_SUCCESSFUL;
}
-static void __iomem *celleb_epci_make_config_addr(struct pci_controller *hose,
+static PCI_IO_ADDR celleb_epci_make_config_addr(
+ struct pci_bus *bus,
+ struct pci_controller *hose,
unsigned int devfn, int where)
{
- void __iomem *addr;
- struct pci_bus *bus = hose->bus;
+ PCI_IO_ADDR addr;
- if (bus->self)
- addr = hose->cfg_data +
+ if (bus != hose->bus)
+ addr = celleb_epci_get_epci_cfg(hose) +
(((bus->number & 0xff) << 16)
| ((devfn & 0xff) << 8)
| (where & 0xff)
| 0x01000000);
else
- addr = hose->cfg_data +
+ addr = celleb_epci_get_epci_cfg(hose) +
(((devfn & 0xff) << 8) | (where & 0xff));
pr_debug("EPCI: config_addr = 0x%p\n", addr);
static int celleb_epci_read_config(struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 * val)
{
- void __iomem *addr;
+ PCI_IO_ADDR epci_base;
+ PCI_IO_ADDR addr;
struct device_node *node;
struct pci_controller *hose;
node = (struct device_node *)bus->sysdata;
hose = pci_find_hose_for_OF_device(node);
- if (!hose->cfg_data)
+ if (!celleb_epci_get_epci_cfg(hose))
return PCIBIOS_DEVICE_NOT_FOUND;
if (bus->number == hose->first_busno && devfn == 0) {
/* EPCI controller self */
- addr = hose->cfg_addr + where;
+ epci_base = celleb_epci_get_epci_base(hose);
+ addr = epci_base + where;
switch (size) {
case 1:
} else {
clear_and_disable_master_abort_interrupt(hose);
- addr = celleb_epci_make_config_addr(hose, devfn, where);
+ addr = celleb_epci_make_config_addr(bus, hose, devfn, where);
switch (size) {
case 1:
}
pr_debug("EPCI: "
- "addr=0x%lx, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n",
+ "addr=0x%p, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n",
addr, devfn, where, size, *val);
return celleb_epci_check_abort(hose, NULL);
static int celleb_epci_write_config(struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 val)
{
- void __iomem *addr;
+ PCI_IO_ADDR epci_base;
+ PCI_IO_ADDR addr;
struct device_node *node;
struct pci_controller *hose;
node = (struct device_node *)bus->sysdata;
hose = pci_find_hose_for_OF_device(node);
- if (!hose->cfg_data)
+
+ if (!celleb_epci_get_epci_cfg(hose))
return PCIBIOS_DEVICE_NOT_FOUND;
if (bus->number == hose->first_busno && devfn == 0) {
/* EPCI controller self */
- addr = hose->cfg_addr + where;
+ epci_base = celleb_epci_get_epci_base(hose);
+ addr = epci_base + where;
switch (size) {
case 1:
} else {
clear_and_disable_master_abort_interrupt(hose);
- addr = celleb_epci_make_config_addr(hose, devfn, where);
+ addr = celleb_epci_make_config_addr(bus, hose, devfn, where);
switch (size) {
case 1:
}
struct pci_ops celleb_epci_ops = {
- celleb_epci_read_config,
- celleb_epci_write_config,
+ .read = celleb_epci_read_config,
+ .write = celleb_epci_write_config,
};
/* to be moved in FW */
-static int __devinit celleb_epci_init(struct pci_controller *hose)
+static int __init celleb_epci_init(struct pci_controller *hose)
{
u32 val;
- void __iomem *reg, *epci_base;
+ PCI_IO_ADDR reg;
+ PCI_IO_ADDR epci_base;
int hwres = 0;
- epci_base = hose->cfg_addr;
+ epci_base = celleb_epci_get_epci_base(hose);
/* PCI core reset(Internal bus and PCI clock) */
reg = epci_base + SCC_EPCI_CKCTRL;
return 0;
}
-int __devinit celleb_setup_epci(struct device_node *node,
+int __init celleb_setup_epci(struct device_node *node,
struct pci_controller *hose)
{
struct resource r;
pr_debug("PCI: celleb_setup_epci()\n");
+ /*
+ * Note:
+ * Celleb epci uses cfg_addr and cfg_data member of
+ * pci_controller structure in irregular way.
+ *
+ * cfg_addr is used to map for control registers of
+ * celleb epci.
+ *
+ * cfg_data is used for configuration area of devices
+ * on Celleb epci buses.
+ */
+
if (of_address_to_resource(node, 0, &r))
goto error;
hose->cfg_addr = ioremap(r.start, (r.end - r.start + 1));
r.start, (unsigned long)hose->cfg_data,
(r.end - r.start + 1));
+ hose->private_data = kzalloc(sizeof(struct epci_private), GFP_KERNEL);
+ if (hose->private_data == NULL) {
+ printk(KERN_ERR "EPCI: no memory for private data.\n");
+ goto error;
+ }
+
+ hose->ops = &celleb_epci_ops;
celleb_epci_init(hose);
return 0;
error:
+ kfree(hose->private_data);
+
+ if (hose->cfg_addr)
+ iounmap(hose->cfg_addr);
+
+ if (hose->cfg_data)
+ iounmap(hose->cfg_data);
return 1;
}