| 1 | /* |
|---|
| 2 | * linux/arch/arm/mach-sl2312/pci_sl2312.c |
|---|
| 3 | * |
|---|
| 4 | * PCI functions for sl2312 host PCI bridge |
|---|
| 5 | * |
|---|
| 6 | * Copyright (C) 2003 StorLink Corp. |
|---|
| 7 | * |
|---|
| 8 | * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | * it under the terms of the GNU General Public License as published by |
|---|
| 10 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 11 | * (at your option) any later version. |
|---|
| 12 | * |
|---|
| 13 | * This program is distributed in the hope that it will be useful, |
|---|
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | * GNU General Public License for more details. |
|---|
| 17 | * |
|---|
| 18 | * You should have received a copy of the GNU General Public License |
|---|
| 19 | * along with this program; if not, write to the Free Software |
|---|
| 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 21 | */ |
|---|
| 22 | #include <linux/sched.h> |
|---|
| 23 | #include <linux/kernel.h> |
|---|
| 24 | #include <linux/pci.h> |
|---|
| 25 | #include <linux/ptrace.h> |
|---|
| 26 | #include <linux/slab.h> |
|---|
| 27 | #include <linux/ioport.h> |
|---|
| 28 | #include <linux/interrupt.h> |
|---|
| 29 | #include <linux/spinlock.h> |
|---|
| 30 | #include <linux/init.h> |
|---|
| 31 | |
|---|
| 32 | #include <asm/sizes.h> |
|---|
| 33 | #include <asm/hardware.h> |
|---|
| 34 | #include <asm/irq.h> |
|---|
| 35 | #include <asm/system.h> |
|---|
| 36 | #include <asm/mach/pci.h> |
|---|
| 37 | #include <asm/mach/irq.h> |
|---|
| 38 | #include <asm/mach-types.h> |
|---|
| 39 | |
|---|
| 40 | #include <asm/arch/pci.h> |
|---|
| 41 | |
|---|
| 42 | //#define DEBUG |
|---|
| 43 | |
|---|
| 44 | // sl2312 PCI bridge access routines |
|---|
| 45 | |
|---|
| 46 | #define PCI_IOSIZE_REG (*(volatile unsigned long *) (IO_ADDRESS(SL2312_PCI_IO_BASE))) |
|---|
| 47 | #define PCI_PROT_REG (*(volatile unsigned long *) (IO_ADDRESS(SL2312_PCI_IO_BASE) + 0x04)) |
|---|
| 48 | #define PCI_CTRL_REG (*(volatile unsigned long *) (IO_ADDRESS(SL2312_PCI_IO_BASE) + 0x08)) |
|---|
| 49 | #define PCI_SOFTRST_REG (*(volatile unsigned long *) (IO_ADDRESS(SL2312_PCI_IO_BASE) + 0x10)) |
|---|
| 50 | #define PCI_CONFIG_REG (*(volatile unsigned long *) (IO_ADDRESS(SL2312_PCI_IO_BASE) + 0x28)) |
|---|
| 51 | #define PCI_DATA_REG (*(volatile unsigned long *) (IO_ADDRESS(SL2312_PCI_IO_BASE) + 0x2C)) |
|---|
| 52 | |
|---|
| 53 | static spinlock_t sl2312_pci_lock = SPIN_LOCK_UNLOCKED; |
|---|
| 54 | // for initialize PCI devices |
|---|
| 55 | struct resource pci_ioport_resource = { |
|---|
| 56 | .name = "PCI I/O Space", |
|---|
| 57 | .start = IO_ADDRESS(SL2312_PCI_IO_BASE) + 0x100, |
|---|
| 58 | .end = IO_ADDRESS(SL2312_PCI_IO_BASE) + SZ_512K - 1, |
|---|
| 59 | .flags = IORESOURCE_IO, |
|---|
| 60 | }; |
|---|
| 61 | struct resource pci_iomem_resource = { |
|---|
| 62 | .name = "PCI Mem Space", |
|---|
| 63 | .start = SL2312_PCI_MEM_BASE, |
|---|
| 64 | .end = SL2312_PCI_MEM_BASE + SZ_128M - 1, |
|---|
| 65 | .flags = IORESOURCE_MEM, |
|---|
| 66 | }; |
|---|
| 67 | |
|---|
| 68 | static int sl2312_read_config(struct pci_bus *bus, unsigned int devfn, int where,int size, u32 *val) |
|---|
| 69 | { |
|---|
| 70 | unsigned long addr,data; |
|---|
| 71 | unsigned long flags; |
|---|
| 72 | |
|---|
| 73 | spin_lock_irqsave(&sl2312_pci_lock, flags); |
|---|
| 74 | addr = 0x80000000 | (PCI_SLOT(devfn) << 11) | (PCI_FUNC(devfn) << 8) | (where & ~3); |
|---|
| 75 | PCI_CONFIG_REG = addr; |
|---|
| 76 | data = PCI_DATA_REG; |
|---|
| 77 | |
|---|
| 78 | switch (size) { |
|---|
| 79 | case 1: |
|---|
| 80 | *val = (u8) (data >> ((where & 0x03) * 8)); |
|---|
| 81 | break; |
|---|
| 82 | case 2: |
|---|
| 83 | *val = (u16) (data >> ((where & 0x02) * 8)); |
|---|
| 84 | break; |
|---|
| 85 | case 4: |
|---|
| 86 | *val = data; |
|---|
| 87 | if ((where >= 0x10) && (where <= 0x24)) { |
|---|
| 88 | if ((*val & 0xfff00000) == SL2312_PCI_IO_BASE) { |
|---|
| 89 | *val &= 0x000fffff; |
|---|
| 90 | *val |= IO_ADDRESS(SL2312_PCI_IO_BASE); |
|---|
| 91 | } |
|---|
| 92 | } |
|---|
| 93 | break; |
|---|
| 94 | } |
|---|
| 95 | spin_unlock_irqrestore(&sl2312_pci_lock, flags); |
|---|
| 96 | // printk("READ==>slot=%d fn=%d where=%d value=%x\n",PCI_SLOT(devfn),PCI_FUNC(devfn),where,*val); |
|---|
| 97 | return PCIBIOS_SUCCESSFUL; |
|---|
| 98 | } |
|---|
| 99 | |
|---|
| 100 | static int sl2312_write_config(struct pci_bus *bus, unsigned int devfn, int where,int size, u32 val) |
|---|
| 101 | { |
|---|
| 102 | unsigned long addr,data; |
|---|
| 103 | unsigned long flags; |
|---|
| 104 | |
|---|
| 105 | spin_lock_irqsave(&sl2312_pci_lock, flags); |
|---|
| 106 | addr = 0x80000000 | (PCI_SLOT(devfn) << 11) | (PCI_FUNC(devfn) << 8) | (where & ~3); |
|---|
| 107 | PCI_CONFIG_REG = addr; |
|---|
| 108 | data = PCI_DATA_REG; |
|---|
| 109 | |
|---|
| 110 | switch (size) { |
|---|
| 111 | case 1: |
|---|
| 112 | data &= ~(0xff << ((where & 0x03) * 8)); |
|---|
| 113 | data |= (val << ((where & 0x03) * 8)); |
|---|
| 114 | PCI_DATA_REG = data; |
|---|
| 115 | break; |
|---|
| 116 | case 2: |
|---|
| 117 | data &= ~(0xffff << ((where & 0x02) * 8)); |
|---|
| 118 | data |= (val << ((where & 0x02) * 8)); |
|---|
| 119 | PCI_DATA_REG = data; |
|---|
| 120 | break; |
|---|
| 121 | case 4: |
|---|
| 122 | if ((where >= 0x10) && (where <= 0x24)) { |
|---|
| 123 | if ((val & 0xfff00000) == IO_ADDRESS(SL2312_PCI_IO_BASE)) { |
|---|
| 124 | val &= 0x000fffff; |
|---|
| 125 | val |= SL2312_PCI_IO_BASE; |
|---|
| 126 | } |
|---|
| 127 | } |
|---|
| 128 | PCI_DATA_REG = val; |
|---|
| 129 | break; |
|---|
| 130 | } |
|---|
| 131 | spin_unlock_irqrestore(&sl2312_pci_lock, flags); |
|---|
| 132 | |
|---|
| 133 | // printk("WRITE==> slot=%d fn=%d where=%d value=%x \n",PCI_SLOT(devfn),PCI_FUNC(devfn),where,val); |
|---|
| 134 | return PCIBIOS_SUCCESSFUL; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | static struct pci_ops sl2312_pci_ops = { |
|---|
| 138 | .read = sl2312_read_config, |
|---|
| 139 | .write = sl2312_write_config, |
|---|
| 140 | }; |
|---|
| 141 | |
|---|
| 142 | |
|---|
| 143 | int __init sl2312_pci_setup_resources(struct resource **resource) |
|---|
| 144 | { |
|---|
| 145 | PCI_IOSIZE_REG = 0; // 1M IO size |
|---|
| 146 | PCI_CTRL_REG = 0x06; |
|---|
| 147 | |
|---|
| 148 | resource[0] = &pci_ioport_resource; |
|---|
| 149 | resource[1] = &pci_iomem_resource; |
|---|
| 150 | resource[2] = NULL; |
|---|
| 151 | |
|---|
| 152 | return 1; |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | //static int sl2312_pci_fault(unsigned long addr, struct pt_regs *regs) |
|---|
| 156 | //{ |
|---|
| 157 | // return 1; |
|---|
| 158 | //} |
|---|
| 159 | |
|---|
| 160 | |
|---|
| 161 | /********************************************************************** |
|---|
| 162 | * MASK(disable) PCI interrupt |
|---|
| 163 | * 0: PCI INTA, 1: PCI INTB, ... // for Linux interrupt routing |
|---|
| 164 | * 16: PERR // for PCI module internal use |
|---|
| 165 | * 17: SERR,.. respect to PCI CTRL2 REG |
|---|
| 166 | **********************************************************************/ |
|---|
| 167 | void sl2312_pci_mask_irq(unsigned int irq) |
|---|
| 168 | { |
|---|
| 169 | struct pci_bus bus; |
|---|
| 170 | unsigned int tmp; |
|---|
| 171 | |
|---|
| 172 | bus.number = 0; |
|---|
| 173 | sl2312_read_config(&bus, 0, SL2312_PCI_CTRL2, 4, &tmp); |
|---|
| 174 | if (irq < 16) { // for linux int routing |
|---|
| 175 | tmp &= ~(1 << (irq + 16 + 6)); |
|---|
| 176 | } |
|---|
| 177 | else { |
|---|
| 178 | tmp &= ~(1 << irq); |
|---|
| 179 | } |
|---|
| 180 | sl2312_write_config(&bus, 0, SL2312_PCI_CTRL2, 4, tmp); |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | /* UNMASK(enable) PCI interrupt */ |
|---|
| 184 | void sl2312_pci_unmask_irq(unsigned int irq) |
|---|
| 185 | { |
|---|
| 186 | struct pci_bus bus; |
|---|
| 187 | unsigned int tmp; |
|---|
| 188 | |
|---|
| 189 | bus.number = 0; |
|---|
| 190 | sl2312_read_config(&bus, 0, SL2312_PCI_CTRL2, 4, &tmp); |
|---|
| 191 | if (irq < 16) { // for linux int routing |
|---|
| 192 | tmp |= (1 << (irq + 16 + 6)); |
|---|
| 193 | } |
|---|
| 194 | else { |
|---|
| 195 | tmp |= (1 << irq); |
|---|
| 196 | } |
|---|
| 197 | sl2312_write_config(&bus, 0, SL2312_PCI_CTRL2, 4, tmp); |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | /* Get PCI interrupt source */ |
|---|
| 201 | int sl2312_pci_get_int_src(void) |
|---|
| 202 | { |
|---|
| 203 | struct pci_bus bus; |
|---|
| 204 | unsigned int tmp=0; |
|---|
| 205 | |
|---|
| 206 | bus.number = 0; |
|---|
| 207 | sl2312_read_config(&bus, 0, SL2312_PCI_CTRL2, 4, &tmp); |
|---|
| 208 | if (tmp & (1 << 28)) { // PCI INTA |
|---|
| 209 | sl2312_write_config(&bus, 0, SL2312_PCI_CTRL2, 4, tmp); |
|---|
| 210 | return IRQ_PCI_INTA; |
|---|
| 211 | } |
|---|
| 212 | if (tmp & (1 << 29)) { // PCI INTB |
|---|
| 213 | sl2312_write_config(&bus, 0, SL2312_PCI_CTRL2, 4, tmp); |
|---|
| 214 | return IRQ_PCI_INTB; |
|---|
| 215 | } |
|---|
| 216 | if (tmp & (1 << 30)) { // PCI INTC |
|---|
| 217 | sl2312_write_config(&bus, 0, SL2312_PCI_CTRL2, 4, tmp); |
|---|
| 218 | return IRQ_PCI_INTC; |
|---|
| 219 | } |
|---|
| 220 | if (tmp & (1 << 31)) { // PCI INTD |
|---|
| 221 | sl2312_write_config(&bus, 0, SL2312_PCI_CTRL2, 4, tmp); |
|---|
| 222 | return IRQ_PCI_INTD; |
|---|
| 223 | } |
|---|
| 224 | // otherwise, it should be a PCI error |
|---|
| 225 | return IRQ_PCI; |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | static irqreturn_t sl2312_pci_irq(int irq, void *devid) |
|---|
| 229 | { |
|---|
| 230 | struct irq_desc *desc; |
|---|
| 231 | struct irqaction *action; |
|---|
| 232 | int retval = 0; |
|---|
| 233 | |
|---|
| 234 | return 1; |
|---|
| 235 | |
|---|
| 236 | irq = sl2312_pci_get_int_src(); |
|---|
| 237 | desc = &irq_desc[irq]; |
|---|
| 238 | action = desc->action; |
|---|
| 239 | do { |
|---|
| 240 | retval |= action->handler(irq, devid); |
|---|
| 241 | action = action->next; |
|---|
| 242 | } while (action); |
|---|
| 243 | |
|---|
| 244 | return 1; |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | //extern int (*external_fault)(unsigned long addr, struct pt_regs *regs); |
|---|
| 248 | |
|---|
| 249 | void __init sl2312_pci_preinit(void) |
|---|
| 250 | { |
|---|
| 251 | struct pci_bus bus; |
|---|
| 252 | unsigned long flags; |
|---|
| 253 | unsigned int temp; |
|---|
| 254 | int ret; |
|---|
| 255 | |
|---|
| 256 | /* |
|---|
| 257 | * Hook in our fault handler for PCI errors |
|---|
| 258 | */ |
|---|
| 259 | // external_fault = sl2312_pci_fault; |
|---|
| 260 | |
|---|
| 261 | spin_lock_irqsave(&sl2312_pci_lock, flags); |
|---|
| 262 | |
|---|
| 263 | /* |
|---|
| 264 | * Grab the PCI interrupt. |
|---|
| 265 | */ |
|---|
| 266 | ret = request_irq(IRQ_PCI, sl2312_pci_irq, 0, "sl2312 pci int", NULL); |
|---|
| 267 | if (ret) |
|---|
| 268 | printk(KERN_ERR "PCI: unable to grab PCI error " |
|---|
| 269 | "interrupt: %d\n", ret); |
|---|
| 270 | |
|---|
| 271 | spin_unlock_irqrestore(&sl2312_pci_lock, flags); |
|---|
| 272 | |
|---|
| 273 | // setup pci bridge |
|---|
| 274 | bus.number = 0; /* device 0, function 0 */ |
|---|
| 275 | temp = (SL2312_PCI_DMA_MEM1_BASE & 0xfff00000) | (SL2312_PCI_DMA_MEM1_SIZE << 16); |
|---|
| 276 | sl2312_write_config(&bus, 0, SL2312_PCI_MEM1_BASE_SIZE, 4, temp); |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | /* |
|---|
| 280 | * No swizzle on SL2312 |
|---|
| 281 | */ |
|---|
| 282 | static u8 __init sl2312_pci_swizzle(struct pci_dev *dev, u8 *pinp) |
|---|
| 283 | { |
|---|
| 284 | return PCI_SLOT(dev->devfn); |
|---|
| 285 | } |
|---|
| 286 | |
|---|
| 287 | /* |
|---|
| 288 | * map the specified device/slot/pin to an IRQ. This works out such |
|---|
| 289 | * that slot 9 pin 1 is INT0, pin 2 is INT1, and slot 10 pin 1 is INT1. |
|---|
| 290 | */ |
|---|
| 291 | static int __init sl2312_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin) |
|---|
| 292 | { |
|---|
| 293 | int intnr = ((slot + (pin - 1)) & 3) + 4; /* the IRQ number of PCI bridge */ |
|---|
| 294 | |
|---|
| 295 | // printk("%s : slot = %d pin = %d \n",__func__,slot,pin); |
|---|
| 296 | switch (slot) |
|---|
| 297 | { |
|---|
| 298 | case 12: |
|---|
| 299 | if (pin==1) |
|---|
| 300 | { |
|---|
| 301 | intnr = 3; |
|---|
| 302 | } |
|---|
| 303 | else |
|---|
| 304 | { |
|---|
| 305 | intnr = 0; |
|---|
| 306 | } |
|---|
| 307 | break; |
|---|
| 308 | case 11: |
|---|
| 309 | intnr = (2 + (pin - 1)) & 3; |
|---|
| 310 | break; |
|---|
| 311 | case 10: |
|---|
| 312 | intnr = (1 + (pin - 1)) & 3; |
|---|
| 313 | break; |
|---|
| 314 | case 9: |
|---|
| 315 | intnr = (pin - 1) & 3; |
|---|
| 316 | break; |
|---|
| 317 | } |
|---|
| 318 | // if (slot == 10) |
|---|
| 319 | // intnr = (1 + (pin - 1)) & 3; |
|---|
| 320 | // else if (slot == 9) |
|---|
| 321 | // intnr = (pin - 1) & 3; |
|---|
| 322 | return (IRQ_PCI_INTA + intnr); |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | struct pci_bus * __init sl2312_pci_scan_bus(int nr, struct pci_sys_data *sysdata) |
|---|
| 326 | { |
|---|
| 327 | return (pci_scan_bus(0, &sl2312_pci_ops, sysdata)); |
|---|
| 328 | |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | int __init sl2312_pci_setup(int nr, struct pci_sys_data *sys) |
|---|
| 332 | { |
|---|
| 333 | int ret = 0; |
|---|
| 334 | |
|---|
| 335 | if (nr == 0) { |
|---|
| 336 | ret = sl2312_pci_setup_resources(sys->resource); |
|---|
| 337 | } |
|---|
| 338 | |
|---|
| 339 | return ret; |
|---|
| 340 | } |
|---|
| 341 | |
|---|
| 342 | |
|---|
| 343 | struct hw_pci sl2312_pci __initdata = { |
|---|
| 344 | .setup = sl2312_pci_setup, |
|---|
| 345 | .preinit = sl2312_pci_preinit, |
|---|
| 346 | .nr_controllers = 1, |
|---|
| 347 | .swizzle = sl2312_pci_swizzle, |
|---|
| 348 | .map_irq = sl2312_pci_map_irq, |
|---|
| 349 | .scan = sl2312_pci_scan_bus, |
|---|
| 350 | }; |
|---|
| 351 | |
|---|
| 352 | static int __init sl2312_pci_init(void) |
|---|
| 353 | { |
|---|
| 354 | if (machine_is_sl2312()) |
|---|
| 355 | pci_common_init(&sl2312_pci); |
|---|
| 356 | return 0; |
|---|
| 357 | } |
|---|
| 358 | |
|---|
| 359 | subsys_initcall(sl2312_pci_init); |
|---|