Merge branches 'release', 'asus', 'sony-laptop' and 'thinkpad' into release
[pandora-kernel.git] / arch / powerpc / platforms / celleb / scc_epci.c
index c11b39c..a3c7cfb 100644 (file)
 
 #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);
@@ -108,20 +151,21 @@ static int celleb_epci_check_abort(struct pci_controller *hose,
        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);
@@ -132,7 +176,8 @@ static void __iomem *celleb_epci_make_config_addr(struct pci_controller *hose,
 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;
 
@@ -142,13 +187,14 @@ static int celleb_epci_read_config(struct pci_bus *bus,
        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:
@@ -167,7 +213,7 @@ static int celleb_epci_read_config(struct pci_bus *bus,
        } 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:
@@ -185,7 +231,7 @@ static int celleb_epci_read_config(struct pci_bus *bus,
        }
 
        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);
@@ -194,7 +240,8 @@ static int celleb_epci_read_config(struct pci_bus *bus,
 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;
 
@@ -204,13 +251,15 @@ static int celleb_epci_write_config(struct pci_bus *bus,
        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:
@@ -229,7 +278,7 @@ static int celleb_epci_write_config(struct pci_bus *bus,
        } 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:
@@ -250,18 +299,19 @@ static int celleb_epci_write_config(struct pci_bus *bus,
 }
 
 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;
@@ -375,13 +425,25 @@ static int __devinit celleb_epci_init(struct pci_controller *hose)
        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));
@@ -400,10 +462,24 @@ int __devinit celleb_setup_epci(struct device_node *node,
                 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;
 }