Root/target/linux/mcs814x/files-3.3/arch/arm/mach-mcs814x/pci.c

1/*
2 * Moschip MCS8140 PCI support
3 *
4 * Copyright (C) 2003 Moschip Semiconductors Ltd.
5 * Copyright (C) 2003 Artec Design Ltd.
6 * Copyright (C) 2012 Florian Fainelli <florian@openwrt.org>
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 version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/pci.h>
16#include <linux/ptrace.h>
17#include <linux/slab.h>
18#include <linux/ioport.h>
19#include <linux/interrupt.h>
20#include <linux/spinlock.h>
21#include <linux/init.h>
22#include <linux/platform_device.h>
23#include <linux/of.h>
24#include <linux/io.h>
25
26#include <asm/irq.h>
27#include <asm/system.h>
28#include <asm/mach/pci.h>
29#include <asm/mach/map.h>
30#include <mach/mcs814x.h>
31#include <mach/irqs.h>
32
33#define MCS8140_PCI_CONFIG_SIZE SZ_64M
34#define MCS8140_PCI_IOMISC_SIZE SZ_64M
35
36#define MCS8140_PCI_HOST_BASE 0x80000000
37#define MCS8140_PCI_IOMISC_BASE 0x00000000
38#define MCS8140_PCI_PRE_BASE 0x10000000
39#define MCS8140_PCI_NONPRE_BASE 0x30000000
40
41#define MCS8140_PCI_CFG_BASE (MCS8140_PCI_HOST_BASE + 0x04000000)
42#define MCS8140_PCI_IO_BASE (MCS8140_PCI_HOST_BASE)
43
44#define MCS8140_PCI_IO_VIRT_BASE (MCS814X_IO_BASE - MCS8140_PCI_CONFIG_SIZE - \
45                     MCS8140_PCI_IOMISC_SIZE)
46#define MCS8140_PCI_CFG_VIRT_BASE (MCS814X_IO_BASE - MCS8140_PCI_CONFIG_SIZE)
47
48#define PCI_FATAL_ERROR 1
49#define EXTERNAL_ABORT_NON_LINE_FETCH 8
50#define EPRM_DONE 0x80
51#define EPRM_SDRAM_FUNC0 0xAC
52#define PCI_INTD 4
53#define MCS8140_PCI_DEVICE_ID 0xA0009710
54#define MCS8140_PCI_CLASS_ID 0x02000011 /* Host-Class id :0x0600 */
55#define PCI_IF_CONFIG 0x200
56
57static void __iomem *mcs8140_pci_master_base;
58static void __iomem *mcs8140_eeprom_emu_base;
59
60static unsigned long __pci_addr(struct pci_bus *bus,
61                unsigned int devfn, int offset)
62{
63    unsigned int busnr = bus->number;
64    unsigned int slot;
65
66    /* we only support bus 0 */
67    if (busnr != 0)
68        return 0;
69
70    /*
71     * Trap out illegal values
72     */
73    BUG_ON(devfn > 255 || busnr > 255 || devfn > 255);
74
75    /* Scan 3 slots */
76    slot = PCI_SLOT(devfn);
77    switch (slot) {
78    case 1:
79    case 2:
80    case 3:
81        if (PCI_FUNC(devfn) >= 4)
82            return 0;
83
84        return MCS8140_PCI_CFG_VIRT_BASE | (PCI_SLOT(devfn) << 11) |
85            (PCI_FUNC(devfn) << 8) | offset;
86    default:
87        pr_warn("Ignoring: PCI Slot is %x\n", PCI_SLOT(devfn));
88        return 0;
89    }
90}
91
92static int mcs8140_pci_host_status(void)
93{
94    u32 host_status;
95
96    host_status = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG);
97    if (host_status & PCI_FATAL_ERROR) {
98        writel_relaxed(host_status & 0xfffffff0,
99            mcs8140_pci_master_base + PCI_IF_CONFIG);
100        /* flush write */
101        host_status =
102            readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG);
103        return 1;
104    }
105
106    return 0;
107}
108
109static int mcs8140_pci_read_config(struct pci_bus *bus,
110                    unsigned int devfn, int where,
111                    int size, u32 *val)
112{
113    unsigned long v = 0xFFFFFFFF;
114    unsigned long addr = __pci_addr(bus, devfn, where);
115
116    if (addr != 0) {
117        switch (size) {
118        case 1:
119            v = readb_relaxed(addr);
120            break;
121        case 2:
122            addr &= ~1;
123            v = readw_relaxed(addr);
124            break;
125        default:
126            addr &= ~3;
127            v = readl_relaxed(addr);
128            break;
129        }
130    } else
131        v = 0xffffffff;
132
133    if (mcs8140_pci_host_status())
134        v = 0xffffffff;
135
136    *val = v;
137
138    return PCIBIOS_SUCCESSFUL;
139}
140
141static void mcs8140_eeprom_emu_init(void)
142{
143    writel_relaxed(0x0000000F, mcs8140_eeprom_emu_base + EPRM_SDRAM_FUNC0);
144    writel_relaxed(0x08000000, MCS8140_PCI_CFG_VIRT_BASE + 0x10);
145    /* Set the DONE bit of the EEPROM emulator */
146    writel_relaxed(0x01, mcs8140_eeprom_emu_base + EPRM_DONE);
147}
148
149static int mcs8140_pci_write_config(struct pci_bus *bus,
150                    unsigned int devfn, int where,
151                    int size, u32 val)
152{
153    unsigned long addr = __pci_addr(bus, devfn, where);
154
155    if (addr != 0) {
156        switch (size) {
157        case 1:
158            writeb_relaxed((u8)val, addr);
159            break;
160        case 2:
161            writew_relaxed((u16)val, addr);
162            break;
163        case 4:
164            writel_relaxed(val, addr);
165            break;
166        }
167    }
168
169    return PCIBIOS_SUCCESSFUL;
170}
171
172static struct pci_ops pci_mcs8140_ops = {
173    .read = mcs8140_pci_read_config,
174    .write = mcs8140_pci_write_config,
175};
176
177
178static struct resource io_mem = {
179    .name = "PCI I/O space",
180    .start = MCS8140_PCI_HOST_BASE + MCS8140_PCI_IOMISC_BASE,
181    .end = MCS8140_PCI_HOST_BASE + MCS8140_PCI_IOMISC_BASE + SZ_64M,
182    .flags = IORESOURCE_IO,
183};
184
185static struct resource pre_mem = {
186    .name = "PCI prefetchable",
187    .start = MCS8140_PCI_HOST_BASE + MCS8140_PCI_PRE_BASE,
188    .end = MCS8140_PCI_HOST_BASE + MCS8140_PCI_PRE_BASE + SZ_512M,
189    .flags = IORESOURCE_MEM | IORESOURCE_PREFETCH,
190};
191
192static struct resource non_mem = {
193    .name = "PCI non-prefetchable",
194    .start = MCS8140_PCI_HOST_BASE + MCS8140_PCI_NONPRE_BASE,
195    .end = MCS8140_PCI_HOST_BASE + MCS8140_PCI_NONPRE_BASE + SZ_256M,
196    .flags = IORESOURCE_MEM,
197};
198
199int __init pci_mcs8140_setup_resources(struct pci_sys_data *sys)
200{
201    int ret = 0;
202
203    ret = request_resource(&iomem_resource, &io_mem);
204    if (ret) {
205        pr_err("PCI: unable to allocate I/O "
206            "memory region (%d)\n", ret);
207        goto out;
208    }
209
210    ret = request_resource(&iomem_resource, &non_mem);
211    if (ret) {
212        pr_err("PCI: unable to allocate non-prefetchable "
213            "memory region (%d)\n", ret);
214        goto release_io_mem;
215    }
216
217    ret = request_resource(&iomem_resource, &pre_mem);
218    if (ret) {
219        pr_err("PCI: unable to allocate prefetchable "
220            "memory region (%d)\n", ret);
221        goto release_non_mem;
222    }
223
224    mcs8140_eeprom_emu_init();
225
226    pci_add_resource(&sys->resources, &io_mem);
227    pci_add_resource(&sys->resources, &non_mem);
228    pci_add_resource(&sys->resources, &pre_mem);
229
230    return ret;
231
232release_non_mem:
233    release_resource(&non_mem);
234release_io_mem:
235    release_resource(&io_mem);
236out:
237    return ret;
238}
239
240struct pci_bus *pci_mcs8140_scan_bus(int nr, struct pci_sys_data *sys)
241{
242    return pci_scan_bus(sys->busnr, &pci_mcs8140_ops, sys);
243}
244
245
246int __init pci_mcs8140_setup(int nr, struct pci_sys_data *sys)
247{
248    int ret = 0;
249    u32 val;
250
251    if (nr > 0)
252        return 0;
253
254    sys->mem_offset = MCS8140_PCI_IO_VIRT_BASE - MCS8140_PCI_IO_BASE;
255    sys->io_offset = 0;
256
257    ret = pci_mcs8140_setup_resources(sys);
258    if (ret < 0) {
259        pr_err("unable to setup mcs8140 resources\n");
260        goto out;
261    }
262
263    val = readl_relaxed(MCS8140_PCI_CFG_VIRT_BASE);
264    if (val != MCS8140_PCI_DEVICE_ID) {
265        pr_err("cannot find MCS8140 PCI Core: %08x\n", val);
266        ret = -EIO;
267        goto out;
268    }
269
270    pr_info("MCS8140 PCI core found\n");
271
272    val = readl_relaxed(MCS8140_PCI_CFG_VIRT_BASE + PCI_COMMAND);
273    /* Added to support wireless cards */
274    writel_relaxed(0, MCS8140_PCI_CFG_VIRT_BASE + 0x40);
275    writel_relaxed(val | 0x147, MCS8140_PCI_CFG_VIRT_BASE + PCI_COMMAND);
276    val = readl_relaxed(MCS8140_PCI_CFG_VIRT_BASE + PCI_COMMAND);
277    ret = 1;
278out:
279    return ret;
280}
281
282
283static int __init mcs8140_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
284{
285    int line = IRQ_PCI_INTA;
286
287    if (pin != 0) {
288        /* IRQ_PCIA - 22 */
289        if (pin == PCI_INTD)
290            line = IRQ_PCI_INTA + pin; /* IRQ_PCIA - 22 */
291        else
292            line = IRQ_PCI_INTA + pin - 1; /* IRQ_PCIA - 22 */
293    }
294
295    pr_info("PCI: Map interrupt slot 0x%02x pin 0x%02x line 0x%02x\n",
296        slot, pin, line);
297
298    return line;
299}
300
301static irqreturn_t mcs8140_pci_abort_interrupt(int irq, void *dummy)
302{
303    u32 word;
304
305    word = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG);
306    if (!(word & (1 << 24)))
307        return IRQ_NONE;
308
309    writel_relaxed(word & 0xfffffff0,
310        mcs8140_pci_master_base + PCI_IF_CONFIG);
311    /* flush write */
312    word = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG);
313
314    return IRQ_HANDLED;
315}
316
317static int mcs8140_pci_abort_irq_init(int irq)
318{
319    u32 word;
320
321    /* Enable Interrupt in PCI Master Core */
322    word = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG);
323    word |= (1 << 24);
324    writel_relaxed(word, mcs8140_pci_master_base + PCI_IF_CONFIG);
325
326    /* flush write */
327    word = readl_relaxed(mcs8140_pci_master_base + PCI_IF_CONFIG);
328
329    return request_irq(irq, mcs8140_pci_abort_interrupt, 0,
330            "PCI abort", NULL);
331}
332
333static int mcs8140_pci_host_abort(unsigned long addr,
334                unsigned int fsr, struct pt_regs *regs)
335{
336    pr_warn("PCI Data abort: address = 0x%08lx fsr = 0x%03x"
337        "PC = 0x%08lx LR = 0x%08lx\n",
338        addr, fsr, regs->ARM_pc, regs->ARM_lr);
339
340    /*
341     * If it was an imprecise abort, then we need to correct the
342     * return address to be _after_ the instruction.
343     */
344    if (fsr & (1 << 10) || mcs8140_pci_host_status())
345        regs->ARM_pc += 4;
346
347    return 0;
348}
349
350static void mcs8140_data_abort_init(void)
351{
352    hook_fault_code(EXTERNAL_ABORT_NON_LINE_FETCH,
353        mcs8140_pci_host_abort, SIGBUS,
354        0, "external abort on non-line fetch");
355}
356
357static struct hw_pci mcs8140_pci __initdata = {
358    .map_irq = mcs8140_map_irq,
359    .nr_controllers = 1,
360    .setup = pci_mcs8140_setup,
361    .scan = pci_mcs8140_scan_bus,
362};
363
364static struct map_desc mcs8140_pci_io_desc[] __initdata = {
365    {
366        .virtual = MCS8140_PCI_CFG_VIRT_BASE,
367        .pfn = __phys_to_pfn(MCS8140_PCI_CFG_BASE),
368        .length = MCS8140_PCI_CONFIG_SIZE,
369        .type = MT_DEVICE
370    },
371    {
372        .virtual = MCS8140_PCI_IO_VIRT_BASE,
373        .pfn = __phys_to_pfn(MCS8140_PCI_IO_BASE),
374        .length = MCS8140_PCI_IOMISC_SIZE,
375        .type = MT_DEVICE
376    },
377};
378
379static int __devinit mcs8140_pci_probe(struct platform_device *pdev)
380{
381    struct resource *res;
382    int ret, irq;
383
384    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
385    if (!res) {
386        dev_err(&pdev->dev, "failed to get mem resource 0\n");
387        return -ENODEV;
388    }
389
390    mcs8140_pci_master_base = devm_ioremap(&pdev->dev, res->start,
391                    resource_size(res));
392    if (!mcs8140_pci_master_base) {
393        dev_err(&pdev->dev, "failed to remap PCI master regs\n");
394        return -ENODEV;
395    }
396
397    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
398    if (!res) {
399        dev_err(&pdev->dev, "failed to get mem resource 1\n");
400        return -ENOMEM;
401    }
402
403    mcs8140_eeprom_emu_base = devm_ioremap(&pdev->dev, res->start,
404                    resource_size(res));
405    if (!mcs8140_eeprom_emu_base) {
406        dev_err(&pdev->dev, "failed to remap EEPROM regs\n");
407        return -ENOMEM;
408    }
409
410    irq = platform_get_irq(pdev, 0);
411    if (irq < 0) {
412        dev_err(&pdev->dev, "failed to get pci abort irq\n");
413        return -ENODEV;
414    }
415
416    /* Setup static mappins for PCI CFG space */
417    iotable_init(mcs8140_pci_io_desc, ARRAY_SIZE(mcs8140_pci_io_desc));
418
419    pcibios_min_io = MCS8140_PCI_HOST_BASE;
420    pcibios_min_mem = MCS8140_PCI_HOST_BASE + MCS8140_PCI_PRE_BASE;
421
422    mcs8140_data_abort_init();
423    ret = mcs8140_pci_abort_irq_init(irq);
424    if (ret) {
425        dev_err(&pdev->dev, "failed to setup abort irq\n");
426        return ret;
427    }
428
429    pci_common_init(&mcs8140_pci);
430
431    return 0;
432}
433
434static struct of_device_id mcs8140_of_ids[] __devinitdata = {
435    { .compatible = "moschip,mcs8140-pci" },
436    { .compatible = "moschip,mcs814x-pci" },
437    { /* sentinel */ },
438};
439
440static struct platform_driver mcs8140_pci_driver = {
441    .driver = {
442        .name = "mcs8140-pci",
443        .of_match_table = mcs8140_of_ids,
444    },
445    .probe = mcs8140_pci_probe,
446};
447
448static int __init mcs8140_pci_init(void)
449{
450    return platform_driver_register(&mcs8140_pci_driver);
451}
452subsys_initcall(mcs8140_pci_init);
453
454

Archive Download this file



interactive