Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 1 | /* |
| 2 | * Support for SCC external PCI |
| 3 | * |
| 4 | * (C) Copyright 2004-2007 TOSHIBA CORPORATION |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License along |
| 17 | * with this program; if not, write to the Free Software Foundation, Inc., |
| 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 19 | */ |
| 20 | |
| 21 | #undef DEBUG |
| 22 | |
| 23 | #include <linux/kernel.h> |
| 24 | #include <linux/threads.h> |
| 25 | #include <linux/pci.h> |
| 26 | #include <linux/init.h> |
| 27 | #include <linux/pci_regs.h> |
| 28 | #include <linux/bootmem.h> |
| 29 | |
| 30 | #include <asm/io.h> |
| 31 | #include <asm/irq.h> |
| 32 | #include <asm/prom.h> |
| 33 | #include <asm/machdep.h> |
| 34 | #include <asm/pci-bridge.h> |
| 35 | #include <asm/ppc-pci.h> |
| 36 | |
| 37 | #include "scc.h" |
| 38 | #include "pci.h" |
| 39 | #include "interrupt.h" |
| 40 | |
| 41 | #define MAX_PCI_DEVICES 32 |
| 42 | #define MAX_PCI_FUNCTIONS 8 |
| 43 | |
| 44 | #define iob() __asm__ __volatile__("eieio; sync":::"memory") |
| 45 | |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 46 | struct epci_private { |
| 47 | dma_addr_t dummy_page_da; |
| 48 | }; |
| 49 | |
| 50 | static inline PCI_IO_ADDR celleb_epci_get_epci_base( |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 51 | struct pci_controller *hose) |
| 52 | { |
| 53 | /* |
| 54 | * Note: |
| 55 | * Celleb epci uses cfg_addr as a base address for |
| 56 | * epci control registers. |
| 57 | */ |
| 58 | |
| 59 | return hose->cfg_addr; |
| 60 | } |
| 61 | |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 62 | static inline PCI_IO_ADDR celleb_epci_get_epci_cfg( |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 63 | struct pci_controller *hose) |
| 64 | { |
| 65 | /* |
| 66 | * Note: |
| 67 | * Celleb epci uses cfg_data as a base address for |
| 68 | * configuration area for epci devices. |
| 69 | */ |
| 70 | |
| 71 | return hose->cfg_data; |
| 72 | } |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 73 | |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 74 | static void scc_epci_dummy_read(struct pci_controller *hose) |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 75 | { |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 76 | PCI_IO_ADDR epci_base; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 77 | u32 val; |
| 78 | |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 79 | epci_base = celleb_epci_get_epci_base(hose); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 80 | |
| 81 | val = in_be32(epci_base + SCC_EPCI_WATRP); |
| 82 | iosync(); |
| 83 | |
| 84 | return; |
| 85 | } |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 86 | |
| 87 | void __init epci_workaround_init(struct pci_controller *hose) |
| 88 | { |
| 89 | PCI_IO_ADDR epci_base; |
| 90 | PCI_IO_ADDR reg; |
| 91 | struct epci_private *private = hose->private_data; |
| 92 | |
| 93 | BUG_ON(!private); |
| 94 | |
| 95 | private->dummy_page_da = dma_map_single(hose->parent, |
| 96 | celleb_dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE); |
| 97 | if (private->dummy_page_da == DMA_ERROR_CODE) { |
joe@perches.com | df3c901 | 2007-11-20 12:47:55 +1100 | [diff] [blame] | 98 | printk(KERN_ERR "EPCI: dummy read disabled. " |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 99 | "Map dummy page failed.\n"); |
| 100 | return; |
| 101 | } |
| 102 | |
| 103 | celleb_pci_add_one(hose, scc_epci_dummy_read); |
| 104 | epci_base = celleb_epci_get_epci_base(hose); |
| 105 | |
| 106 | reg = epci_base + SCC_EPCI_DUMYRADR; |
| 107 | out_be32(reg, private->dummy_page_da); |
| 108 | } |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 109 | |
| 110 | static inline void clear_and_disable_master_abort_interrupt( |
| 111 | struct pci_controller *hose) |
| 112 | { |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 113 | PCI_IO_ADDR epci_base; |
| 114 | PCI_IO_ADDR reg; |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 115 | epci_base = celleb_epci_get_epci_base(hose); |
| 116 | reg = epci_base + PCI_COMMAND; |
| 117 | out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16)); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | static int celleb_epci_check_abort(struct pci_controller *hose, |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 121 | PCI_IO_ADDR addr) |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 122 | { |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 123 | PCI_IO_ADDR reg; |
| 124 | PCI_IO_ADDR epci_base; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 125 | u32 val; |
| 126 | |
| 127 | iob(); |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 128 | epci_base = celleb_epci_get_epci_base(hose); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 129 | |
| 130 | reg = epci_base + PCI_COMMAND; |
| 131 | val = in_be32(reg); |
| 132 | |
| 133 | if (val & (PCI_STATUS_REC_MASTER_ABORT << 16)) { |
| 134 | out_be32(reg, |
| 135 | (val & 0xffff) | (PCI_STATUS_REC_MASTER_ABORT << 16)); |
| 136 | |
| 137 | /* clear PCI Controller error, FRE, PMFE */ |
| 138 | reg = epci_base + SCC_EPCI_STATUS; |
| 139 | out_be32(reg, SCC_EPCI_INT_PAI); |
| 140 | |
| 141 | reg = epci_base + SCC_EPCI_VCSR; |
| 142 | val = in_be32(reg) & 0xffff; |
| 143 | val |= SCC_EPCI_VCSR_FRE; |
| 144 | out_be32(reg, val); |
| 145 | |
| 146 | reg = epci_base + SCC_EPCI_VISTAT; |
| 147 | out_be32(reg, SCC_EPCI_VISTAT_PMFE); |
| 148 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 149 | } |
| 150 | |
| 151 | return PCIBIOS_SUCCESSFUL; |
| 152 | } |
| 153 | |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 154 | static PCI_IO_ADDR celleb_epci_make_config_addr( |
Ishizaki Kou | 0f6e74a | 2007-05-09 17:36:40 +1000 | [diff] [blame] | 155 | struct pci_bus *bus, |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 156 | struct pci_controller *hose, |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 157 | unsigned int devfn, int where) |
| 158 | { |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 159 | PCI_IO_ADDR addr; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 160 | |
Ishizaki Kou | 0f6e74a | 2007-05-09 17:36:40 +1000 | [diff] [blame] | 161 | if (bus != hose->bus) |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 162 | addr = celleb_epci_get_epci_cfg(hose) + |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 163 | (((bus->number & 0xff) << 16) |
| 164 | | ((devfn & 0xff) << 8) |
| 165 | | (where & 0xff) |
| 166 | | 0x01000000); |
| 167 | else |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 168 | addr = celleb_epci_get_epci_cfg(hose) + |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 169 | (((devfn & 0xff) << 8) | (where & 0xff)); |
| 170 | |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 171 | pr_debug("EPCI: config_addr = 0x%p\n", addr); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 172 | |
| 173 | return addr; |
| 174 | } |
| 175 | |
| 176 | static int celleb_epci_read_config(struct pci_bus *bus, |
| 177 | unsigned int devfn, int where, int size, u32 * val) |
| 178 | { |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 179 | PCI_IO_ADDR epci_base; |
| 180 | PCI_IO_ADDR addr; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 181 | struct device_node *node; |
| 182 | struct pci_controller *hose; |
| 183 | |
| 184 | /* allignment check */ |
| 185 | BUG_ON(where % size); |
| 186 | |
| 187 | node = (struct device_node *)bus->sysdata; |
| 188 | hose = pci_find_hose_for_OF_device(node); |
| 189 | |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 190 | if (!celleb_epci_get_epci_cfg(hose)) |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 191 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 192 | |
| 193 | if (bus->number == hose->first_busno && devfn == 0) { |
| 194 | /* EPCI controller self */ |
| 195 | |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 196 | epci_base = celleb_epci_get_epci_base(hose); |
| 197 | addr = epci_base + where; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 198 | |
| 199 | switch (size) { |
| 200 | case 1: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 201 | *val = in_8(addr); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 202 | break; |
| 203 | case 2: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 204 | *val = in_be16(addr); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 205 | break; |
| 206 | case 4: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 207 | *val = in_be32(addr); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 208 | break; |
| 209 | default: |
| 210 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 211 | } |
| 212 | |
| 213 | } else { |
| 214 | |
| 215 | clear_and_disable_master_abort_interrupt(hose); |
Ishizaki Kou | 0f6e74a | 2007-05-09 17:36:40 +1000 | [diff] [blame] | 216 | addr = celleb_epci_make_config_addr(bus, hose, devfn, where); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 217 | |
| 218 | switch (size) { |
| 219 | case 1: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 220 | *val = in_8(addr); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 221 | break; |
| 222 | case 2: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 223 | *val = in_le16(addr); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 224 | break; |
| 225 | case 4: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 226 | *val = in_le32(addr); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 227 | break; |
| 228 | default: |
| 229 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | pr_debug("EPCI: " |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 234 | "addr=0x%p, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n", |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 235 | addr, devfn, where, size, *val); |
| 236 | |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 237 | return celleb_epci_check_abort(hose, NULL); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 238 | } |
| 239 | |
| 240 | static int celleb_epci_write_config(struct pci_bus *bus, |
| 241 | unsigned int devfn, int where, int size, u32 val) |
| 242 | { |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 243 | PCI_IO_ADDR epci_base; |
| 244 | PCI_IO_ADDR addr; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 245 | struct device_node *node; |
| 246 | struct pci_controller *hose; |
| 247 | |
| 248 | /* allignment check */ |
| 249 | BUG_ON(where % size); |
| 250 | |
| 251 | node = (struct device_node *)bus->sysdata; |
| 252 | hose = pci_find_hose_for_OF_device(node); |
| 253 | |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 254 | |
| 255 | if (!celleb_epci_get_epci_cfg(hose)) |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 256 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 257 | |
| 258 | if (bus->number == hose->first_busno && devfn == 0) { |
| 259 | /* EPCI controller self */ |
| 260 | |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 261 | epci_base = celleb_epci_get_epci_base(hose); |
| 262 | addr = epci_base + where; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 263 | |
| 264 | switch (size) { |
| 265 | case 1: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 266 | out_8(addr, val); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 267 | break; |
| 268 | case 2: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 269 | out_be16(addr, val); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 270 | break; |
| 271 | case 4: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 272 | out_be32(addr, val); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 273 | break; |
| 274 | default: |
| 275 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 276 | } |
| 277 | |
| 278 | } else { |
| 279 | |
| 280 | clear_and_disable_master_abort_interrupt(hose); |
Ishizaki Kou | 0f6e74a | 2007-05-09 17:36:40 +1000 | [diff] [blame] | 281 | addr = celleb_epci_make_config_addr(bus, hose, devfn, where); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 282 | |
| 283 | switch (size) { |
| 284 | case 1: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 285 | out_8(addr, val); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 286 | break; |
| 287 | case 2: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 288 | out_le16(addr, val); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 289 | break; |
| 290 | case 4: |
Al Viro | f1fda89 | 2007-02-09 16:39:50 +0000 | [diff] [blame] | 291 | out_le32(addr, val); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 292 | break; |
| 293 | default: |
| 294 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | return celleb_epci_check_abort(hose, addr); |
| 299 | } |
| 300 | |
| 301 | struct pci_ops celleb_epci_ops = { |
Nathan Lynch | 3e02aeb | 2007-08-10 05:18:38 +1000 | [diff] [blame] | 302 | .read = celleb_epci_read_config, |
| 303 | .write = celleb_epci_write_config, |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 304 | }; |
| 305 | |
| 306 | /* to be moved in FW */ |
Ishizaki Kou | a4ebd01 | 2007-07-26 20:02:27 +1000 | [diff] [blame] | 307 | static int __init celleb_epci_init(struct pci_controller *hose) |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 308 | { |
| 309 | u32 val; |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 310 | PCI_IO_ADDR reg; |
| 311 | PCI_IO_ADDR epci_base; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 312 | int hwres = 0; |
| 313 | |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 314 | epci_base = celleb_epci_get_epci_base(hose); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 315 | |
| 316 | /* PCI core reset(Internal bus and PCI clock) */ |
| 317 | reg = epci_base + SCC_EPCI_CKCTRL; |
| 318 | val = in_be32(reg); |
| 319 | if (val == 0x00030101) |
| 320 | hwres = 1; |
| 321 | else { |
| 322 | val &= ~(SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1); |
| 323 | out_be32(reg, val); |
| 324 | |
| 325 | /* set PCI core clock */ |
| 326 | val = in_be32(reg); |
| 327 | val |= (SCC_EPCI_CKCTRL_OCLKEN | SCC_EPCI_CKCTRL_LCLKEN); |
| 328 | out_be32(reg, val); |
| 329 | |
| 330 | /* release PCI core reset (internal bus) */ |
| 331 | val = in_be32(reg); |
| 332 | val |= SCC_EPCI_CKCTRL_CRST0; |
| 333 | out_be32(reg, val); |
| 334 | |
| 335 | /* set PCI clock select */ |
| 336 | reg = epci_base + SCC_EPCI_CLKRST; |
| 337 | val = in_be32(reg); |
| 338 | val &= ~SCC_EPCI_CLKRST_CKS_MASK; |
| 339 | val |= SCC_EPCI_CLKRST_CKS_2; |
| 340 | out_be32(reg, val); |
| 341 | |
| 342 | /* set arbiter */ |
| 343 | reg = epci_base + SCC_EPCI_ABTSET; |
| 344 | out_be32(reg, 0x0f1f001f); /* temporary value */ |
| 345 | |
| 346 | /* buffer on */ |
| 347 | reg = epci_base + SCC_EPCI_CLKRST; |
| 348 | val = in_be32(reg); |
| 349 | val |= SCC_EPCI_CLKRST_BC; |
| 350 | out_be32(reg, val); |
| 351 | |
| 352 | /* PCI clock enable */ |
| 353 | val = in_be32(reg); |
| 354 | val |= SCC_EPCI_CLKRST_PCKEN; |
| 355 | out_be32(reg, val); |
| 356 | |
| 357 | /* release PCI core reset (all) */ |
| 358 | reg = epci_base + SCC_EPCI_CKCTRL; |
| 359 | val = in_be32(reg); |
| 360 | val |= (SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1); |
| 361 | out_be32(reg, val); |
| 362 | |
| 363 | /* set base translation registers. (already set by Beat) */ |
| 364 | |
| 365 | /* set base address masks. (already set by Beat) */ |
| 366 | } |
| 367 | |
| 368 | /* release interrupt masks and clear all interrupts */ |
| 369 | reg = epci_base + SCC_EPCI_INTSET; |
| 370 | out_be32(reg, 0x013f011f); /* all interrupts enable */ |
| 371 | reg = epci_base + SCC_EPCI_VIENAB; |
| 372 | val = SCC_EPCI_VIENAB_PMPEE | SCC_EPCI_VIENAB_PMFEE; |
| 373 | out_be32(reg, val); |
| 374 | reg = epci_base + SCC_EPCI_STATUS; |
| 375 | out_be32(reg, 0xffffffff); |
| 376 | reg = epci_base + SCC_EPCI_VISTAT; |
| 377 | out_be32(reg, 0xffffffff); |
| 378 | |
| 379 | /* disable PCI->IB address translation */ |
| 380 | reg = epci_base + SCC_EPCI_VCSR; |
| 381 | val = in_be32(reg); |
| 382 | val &= ~(SCC_EPCI_VCSR_DR | SCC_EPCI_VCSR_AT); |
| 383 | out_be32(reg, val); |
| 384 | |
| 385 | /* set base addresses. (no need to set?) */ |
| 386 | |
| 387 | /* memory space, bus master enable */ |
| 388 | reg = epci_base + PCI_COMMAND; |
| 389 | val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; |
| 390 | out_be32(reg, val); |
| 391 | |
| 392 | /* endian mode setup */ |
| 393 | reg = epci_base + SCC_EPCI_ECMODE; |
| 394 | val = 0x00550155; |
| 395 | out_be32(reg, val); |
| 396 | |
| 397 | /* set control option */ |
| 398 | reg = epci_base + SCC_EPCI_CNTOPT; |
| 399 | val = in_be32(reg); |
| 400 | val |= SCC_EPCI_CNTOPT_O2PMB; |
| 401 | out_be32(reg, val); |
| 402 | |
| 403 | /* XXX: temporay: set registers for address conversion setup */ |
| 404 | reg = epci_base + SCC_EPCI_CNF10_REG; |
| 405 | out_be32(reg, 0x80000008); |
| 406 | reg = epci_base + SCC_EPCI_CNF14_REG; |
| 407 | out_be32(reg, 0x40000008); |
| 408 | |
| 409 | reg = epci_base + SCC_EPCI_BAM0; |
| 410 | out_be32(reg, 0x80000000); |
| 411 | reg = epci_base + SCC_EPCI_BAM1; |
| 412 | out_be32(reg, 0xe0000000); |
| 413 | |
| 414 | reg = epci_base + SCC_EPCI_PVBAT; |
| 415 | out_be32(reg, 0x80000000); |
| 416 | |
| 417 | if (!hwres) { |
| 418 | /* release external PCI reset */ |
| 419 | reg = epci_base + SCC_EPCI_CLKRST; |
| 420 | val = in_be32(reg); |
| 421 | val |= SCC_EPCI_CLKRST_PCIRST; |
| 422 | out_be32(reg, val); |
| 423 | } |
| 424 | |
| 425 | return 0; |
| 426 | } |
| 427 | |
Ishizaki Kou | a4ebd01 | 2007-07-26 20:02:27 +1000 | [diff] [blame] | 428 | int __init celleb_setup_epci(struct device_node *node, |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 429 | struct pci_controller *hose) |
| 430 | { |
| 431 | struct resource r; |
| 432 | |
| 433 | pr_debug("PCI: celleb_setup_epci()\n"); |
| 434 | |
Ishizaki Kou | 8388374 | 2007-03-02 16:59:25 +0900 | [diff] [blame] | 435 | /* |
| 436 | * Note: |
| 437 | * Celleb epci uses cfg_addr and cfg_data member of |
| 438 | * pci_controller structure in irregular way. |
| 439 | * |
| 440 | * cfg_addr is used to map for control registers of |
| 441 | * celleb epci. |
| 442 | * |
| 443 | * cfg_data is used for configuration area of devices |
| 444 | * on Celleb epci buses. |
| 445 | */ |
| 446 | |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 447 | if (of_address_to_resource(node, 0, &r)) |
| 448 | goto error; |
| 449 | hose->cfg_addr = ioremap(r.start, (r.end - r.start + 1)); |
| 450 | if (!hose->cfg_addr) |
| 451 | goto error; |
| 452 | pr_debug("EPCI: cfg_addr map 0x%016lx->0x%016lx + 0x%016lx\n", |
| 453 | r.start, (unsigned long)hose->cfg_addr, |
| 454 | (r.end - r.start + 1)); |
| 455 | |
| 456 | if (of_address_to_resource(node, 2, &r)) |
| 457 | goto error; |
| 458 | hose->cfg_data = ioremap(r.start, (r.end - r.start + 1)); |
| 459 | if (!hose->cfg_data) |
| 460 | goto error; |
| 461 | pr_debug("EPCI: cfg_data map 0x%016lx->0x%016lx + 0x%016lx\n", |
| 462 | r.start, (unsigned long)hose->cfg_data, |
| 463 | (r.end - r.start + 1)); |
| 464 | |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 465 | hose->private_data = kzalloc(sizeof(struct epci_private), GFP_KERNEL); |
| 466 | if (hose->private_data == NULL) { |
| 467 | printk(KERN_ERR "EPCI: no memory for private data.\n"); |
| 468 | goto error; |
| 469 | } |
| 470 | |
| 471 | hose->ops = &celleb_epci_ops; |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 472 | celleb_epci_init(hose); |
| 473 | |
| 474 | return 0; |
| 475 | |
| 476 | error: |
Ishizaki Kou | da0bd34 | 2007-10-02 18:26:53 +1000 | [diff] [blame] | 477 | kfree(hose->private_data); |
| 478 | |
| 479 | if (hose->cfg_addr) |
| 480 | iounmap(hose->cfg_addr); |
| 481 | |
| 482 | if (hose->cfg_data) |
| 483 | iounmap(hose->cfg_data); |
Ishizaki Kou | 551a3d8 | 2007-01-12 10:03:28 +0900 | [diff] [blame] | 484 | return 1; |
| 485 | } |