~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to arch/powerpc/sysdev/mpic_u3msi.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2006, Segher Boessenkool, IBM Corporation.
 
3
 * Copyright 2006-2007, Michael Ellerman, IBM Corporation.
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU General Public License
 
7
 * as published by the Free Software Foundation; version 2 of the
 
8
 * License.
 
9
 *
 
10
 */
 
11
 
 
12
#include <linux/irq.h>
 
13
#include <linux/bootmem.h>
 
14
#include <linux/msi.h>
 
15
#include <asm/mpic.h>
 
16
#include <asm/prom.h>
 
17
#include <asm/hw_irq.h>
 
18
#include <asm/ppc-pci.h>
 
19
#include <asm/msi_bitmap.h>
 
20
 
 
21
#include "mpic.h"
 
22
 
 
23
/* A bit ugly, can we get this from the pci_dev somehow? */
 
24
static struct mpic *msi_mpic;
 
25
 
 
26
static void mpic_u3msi_mask_irq(struct irq_data *data)
 
27
{
 
28
        mask_msi_irq(data);
 
29
        mpic_mask_irq(data);
 
30
}
 
31
 
 
32
static void mpic_u3msi_unmask_irq(struct irq_data *data)
 
33
{
 
34
        mpic_unmask_irq(data);
 
35
        unmask_msi_irq(data);
 
36
}
 
37
 
 
38
static struct irq_chip mpic_u3msi_chip = {
 
39
        .irq_shutdown           = mpic_u3msi_mask_irq,
 
40
        .irq_mask               = mpic_u3msi_mask_irq,
 
41
        .irq_unmask             = mpic_u3msi_unmask_irq,
 
42
        .irq_eoi                = mpic_end_irq,
 
43
        .irq_set_type           = mpic_set_irq_type,
 
44
        .irq_set_affinity       = mpic_set_affinity,
 
45
        .name                   = "MPIC-U3MSI",
 
46
};
 
47
 
 
48
static u64 read_ht_magic_addr(struct pci_dev *pdev, unsigned int pos)
 
49
{
 
50
        u8 flags;
 
51
        u32 tmp;
 
52
        u64 addr;
 
53
 
 
54
        pci_read_config_byte(pdev, pos + HT_MSI_FLAGS, &flags);
 
55
 
 
56
        if (flags & HT_MSI_FLAGS_FIXED)
 
57
                return HT_MSI_FIXED_ADDR;
 
58
 
 
59
        pci_read_config_dword(pdev, pos + HT_MSI_ADDR_LO, &tmp);
 
60
        addr = tmp & HT_MSI_ADDR_LO_MASK;
 
61
        pci_read_config_dword(pdev, pos + HT_MSI_ADDR_HI, &tmp);
 
62
        addr = addr | ((u64)tmp << 32);
 
63
 
 
64
        return addr;
 
65
}
 
66
 
 
67
static u64 find_ht_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
 
68
{
 
69
        struct pci_bus *bus;
 
70
        unsigned int pos;
 
71
 
 
72
        for (bus = pdev->bus; bus && bus->self; bus = bus->parent) {
 
73
                pos = pci_find_ht_capability(bus->self, HT_CAPTYPE_MSI_MAPPING);
 
74
                if (pos)
 
75
                        return read_ht_magic_addr(bus->self, pos);
 
76
        }
 
77
 
 
78
        return 0;
 
79
}
 
80
 
 
81
static u64 find_u4_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
 
82
{
 
83
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
 
84
 
 
85
        /* U4 PCIe MSIs need to write to the special register in
 
86
         * the bridge that generates interrupts. There should be
 
87
         * theorically a register at 0xf8005000 where you just write
 
88
         * the MSI number and that triggers the right interrupt, but
 
89
         * unfortunately, this is busted in HW, the bridge endian swaps
 
90
         * the value and hits the wrong nibble in the register.
 
91
         *
 
92
         * So instead we use another register set which is used normally
 
93
         * for converting HT interrupts to MPIC interrupts, which decodes
 
94
         * the interrupt number as part of the low address bits
 
95
         *
 
96
         * This will not work if we ever use more than one legacy MSI in
 
97
         * a block but we never do. For one MSI or multiple MSI-X where
 
98
         * each interrupt address can be specified separately, it works
 
99
         * just fine.
 
100
         */
 
101
        if (of_device_is_compatible(hose->dn, "u4-pcie") ||
 
102
            of_device_is_compatible(hose->dn, "U4-pcie"))
 
103
                return 0xf8004000 | (hwirq << 4);
 
104
 
 
105
        return 0;
 
106
}
 
107
 
 
108
static int u3msi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
 
109
{
 
110
        if (type == PCI_CAP_ID_MSIX)
 
111
                pr_debug("u3msi: MSI-X untested, trying anyway.\n");
 
112
 
 
113
        /* If we can't find a magic address then MSI ain't gonna work */
 
114
        if (find_ht_magic_addr(pdev, 0) == 0 &&
 
115
            find_u4_magic_addr(pdev, 0) == 0) {
 
116
                pr_debug("u3msi: no magic address found for %s\n",
 
117
                         pci_name(pdev));
 
118
                return -ENXIO;
 
119
        }
 
120
 
 
121
        return 0;
 
122
}
 
123
 
 
124
static void u3msi_teardown_msi_irqs(struct pci_dev *pdev)
 
125
{
 
126
        struct msi_desc *entry;
 
127
 
 
128
        list_for_each_entry(entry, &pdev->msi_list, list) {
 
129
                if (entry->irq == NO_IRQ)
 
130
                        continue;
 
131
 
 
132
                irq_set_msi_desc(entry->irq, NULL);
 
133
                msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap,
 
134
                                       virq_to_hw(entry->irq), 1);
 
135
                irq_dispose_mapping(entry->irq);
 
136
        }
 
137
 
 
138
        return;
 
139
}
 
140
 
 
141
static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 
142
{
 
143
        unsigned int virq;
 
144
        struct msi_desc *entry;
 
145
        struct msi_msg msg;
 
146
        u64 addr;
 
147
        int hwirq;
 
148
 
 
149
        list_for_each_entry(entry, &pdev->msi_list, list) {
 
150
                hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap, 1);
 
151
                if (hwirq < 0) {
 
152
                        pr_debug("u3msi: failed allocating hwirq\n");
 
153
                        return hwirq;
 
154
                }
 
155
 
 
156
                addr = find_ht_magic_addr(pdev, hwirq);
 
157
                if (addr == 0)
 
158
                        addr = find_u4_magic_addr(pdev, hwirq);
 
159
                msg.address_lo = addr & 0xFFFFFFFF;
 
160
                msg.address_hi = addr >> 32;
 
161
 
 
162
                virq = irq_create_mapping(msi_mpic->irqhost, hwirq);
 
163
                if (virq == NO_IRQ) {
 
164
                        pr_debug("u3msi: failed mapping hwirq 0x%x\n", hwirq);
 
165
                        msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, hwirq, 1);
 
166
                        return -ENOSPC;
 
167
                }
 
168
 
 
169
                irq_set_msi_desc(virq, entry);
 
170
                irq_set_chip(virq, &mpic_u3msi_chip);
 
171
                irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
 
172
 
 
173
                pr_debug("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n",
 
174
                          virq, hwirq, (unsigned long)addr);
 
175
 
 
176
                printk("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n",
 
177
                          virq, hwirq, (unsigned long)addr);
 
178
                msg.data = hwirq;
 
179
                write_msi_msg(virq, &msg);
 
180
 
 
181
                hwirq++;
 
182
        }
 
183
 
 
184
        return 0;
 
185
}
 
186
 
 
187
int mpic_u3msi_init(struct mpic *mpic)
 
188
{
 
189
        int rc;
 
190
 
 
191
        rc = mpic_msi_init_allocator(mpic);
 
192
        if (rc) {
 
193
                pr_debug("u3msi: Error allocating bitmap!\n");
 
194
                return rc;
 
195
        }
 
196
 
 
197
        pr_debug("u3msi: Registering MPIC U3 MSI callbacks.\n");
 
198
 
 
199
        BUG_ON(msi_mpic);
 
200
        msi_mpic = mpic;
 
201
 
 
202
        WARN_ON(ppc_md.setup_msi_irqs);
 
203
        ppc_md.setup_msi_irqs = u3msi_setup_msi_irqs;
 
204
        ppc_md.teardown_msi_irqs = u3msi_teardown_msi_irqs;
 
205
        ppc_md.msi_check_device = u3msi_msi_check_device;
 
206
 
 
207
        return 0;
 
208
}