1 /******************************************************************************
3 ** FILE NAME : ifxmips_pcie_msi.c
4 ** PROJECT : IFX UEIP for VRX200
5 ** MODULES : PCI MSI sub module
8 ** AUTHOR : Lei Chuanhua
9 ** DESCRIPTION : PCIe MSI Driver
10 ** COPYRIGHT : Copyright (c) 2009
11 ** Infineon Technologies AG
12 ** Am Campeon 1-12, 85579 Neubiberg, Germany
14 ** This program is free software; you can redistribute it and/or modify
15 ** it under the terms of the GNU General Public License as published by
16 ** the Free Software Foundation; either version 2 of the License, or
17 ** (at your option) any later version.
19 ** $Date $Author $Comment
20 ** 02 Mar,2009 Lei Chuanhua Initial version
21 *******************************************************************************/
23 \defgroup IFX_PCIE_MSI MSI OS APIs
25 \brief PCIe bus driver OS interface functions
29 \file ifxmips_pcie_msi.c
31 \brief PCIe MSI OS interface file
34 #include <linux/init.h>
35 #include <linux/sched.h>
36 #include <linux/slab.h>
37 #include <linux/interrupt.h>
38 #include <linux/kernel_stat.h>
39 #include <linux/pci.h>
40 #include <linux/msi.h>
41 #include <linux/module.h>
42 #include <asm/bootinfo.h>
44 #include <asm/traps.h>
46 #include "pcie-lantiq.h"
48 #define IFX_MSI_IRQ_NUM 16
49 #define SM(_v, _f) (((_v) << _f##_S) & (_f))
51 #define IFX_MSI_PIC_REG_BASE (KSEG1 | 0x1F700000)
52 #define IFX_PCIE_MSI_IR0 (INT_NUM_IM4_IRL0 + 27)
53 #define IFX_PCIE_MSI_IR1 (INT_NUM_IM4_IRL0 + 28)
54 #define IFX_PCIE_MSI_IR2 (INT_NUM_IM4_IRL0 + 29)
55 #define IFX_PCIE_MSI_IR3 (INT_NUM_IM0_IRL0 + 30)
57 #define IFX_MSI_PCI_INT_DISABLE 0x80000000
58 #define IFX_MSI_PIC_INT_LINE 0x30000000
59 #define IFX_MSI_PIC_MSG_ADDR 0x0FFF0000
60 #define IFX_MSI_PIC_MSG_DATA 0x0000FFFF
61 #define IFX_MSI_PIC_BIG_ENDIAN 1
62 #define IFX_MSI_PIC_INT_LINE_S 28
63 #define IFX_MSI_PIC_MSG_ADDR_S 16
64 #define IFX_MSI_PIC_MSG_DATA_S 0x0
67 IFX_PCIE_MSI_IDX0
= 0,
73 typedef struct ifx_msi_irq_idx
{
79 volatile u32 pic_table
[IFX_MSI_IRQ_NUM
];
80 volatile u32 pic_endian
; /* 0x40 */
82 typedef struct ifx_msi_pic
*ifx_msi_pic_t
;
84 typedef struct ifx_msi_irq
{
85 const volatile ifx_msi_pic_t msi_pic_p
;
86 const u32 msi_phy_base
;
87 const ifx_msi_irq_idx_t msi_irq_idx
[IFX_MSI_IRQ_NUM
];
89 * Each bit in msi_free_irq_bitmask represents a MSI interrupt that is
92 u16 msi_free_irq_bitmask
;
95 * Each bit in msi_multiple_irq_bitmask tells that the device using
96 * this bit in msi_free_irq_bitmask is also using the next bit. This
97 * is used so we can disable all of the MSI interrupts when a device
100 u16 msi_multiple_irq_bitmask
;
103 static ifx_msi_irq_t msi_irqs
[IFX_PCIE_CORE_NR
] = {
105 .msi_pic_p
= (const volatile ifx_msi_pic_t
)IFX_MSI_PIC_REG_BASE
,
106 .msi_phy_base
= PCIE_MSI_PHY_BASE
,
108 {IFX_PCIE_MSI_IR0
, IFX_PCIE_MSI_IDX0
}, {IFX_PCIE_MSI_IR1
, IFX_PCIE_MSI_IDX1
},
109 {IFX_PCIE_MSI_IR2
, IFX_PCIE_MSI_IDX2
}, {IFX_PCIE_MSI_IR3
, IFX_PCIE_MSI_IDX3
},
110 {IFX_PCIE_MSI_IR0
, IFX_PCIE_MSI_IDX0
}, {IFX_PCIE_MSI_IR1
, IFX_PCIE_MSI_IDX1
},
111 {IFX_PCIE_MSI_IR2
, IFX_PCIE_MSI_IDX2
}, {IFX_PCIE_MSI_IR3
, IFX_PCIE_MSI_IDX3
},
112 {IFX_PCIE_MSI_IR0
, IFX_PCIE_MSI_IDX0
}, {IFX_PCIE_MSI_IR1
, IFX_PCIE_MSI_IDX1
},
113 {IFX_PCIE_MSI_IR2
, IFX_PCIE_MSI_IDX2
}, {IFX_PCIE_MSI_IR3
, IFX_PCIE_MSI_IDX3
},
114 {IFX_PCIE_MSI_IR0
, IFX_PCIE_MSI_IDX0
}, {IFX_PCIE_MSI_IR1
, IFX_PCIE_MSI_IDX1
},
115 {IFX_PCIE_MSI_IR2
, IFX_PCIE_MSI_IDX2
}, {IFX_PCIE_MSI_IR3
, IFX_PCIE_MSI_IDX3
},
117 .msi_free_irq_bitmask
= 0,
118 .msi_multiple_irq_bitmask
= 0,
120 #ifdef CONFIG_IFX_PCIE_2ND_CORE
122 .msi_pic_p
= (const volatile ifx_msi_pic_t
)IFX_MSI1_PIC_REG_BASE
,
123 .msi_phy_base
= PCIE1_MSI_PHY_BASE
,
125 {IFX_PCIE1_MSI_IR0
, IFX_PCIE_MSI_IDX0
}, {IFX_PCIE1_MSI_IR1
, IFX_PCIE_MSI_IDX1
},
126 {IFX_PCIE1_MSI_IR2
, IFX_PCIE_MSI_IDX2
}, {IFX_PCIE1_MSI_IR3
, IFX_PCIE_MSI_IDX3
},
127 {IFX_PCIE1_MSI_IR0
, IFX_PCIE_MSI_IDX0
}, {IFX_PCIE1_MSI_IR1
, IFX_PCIE_MSI_IDX1
},
128 {IFX_PCIE1_MSI_IR2
, IFX_PCIE_MSI_IDX2
}, {IFX_PCIE1_MSI_IR3
, IFX_PCIE_MSI_IDX3
},
129 {IFX_PCIE1_MSI_IR0
, IFX_PCIE_MSI_IDX0
}, {IFX_PCIE1_MSI_IR1
, IFX_PCIE_MSI_IDX1
},
130 {IFX_PCIE1_MSI_IR2
, IFX_PCIE_MSI_IDX2
}, {IFX_PCIE1_MSI_IR3
, IFX_PCIE_MSI_IDX3
},
131 {IFX_PCIE1_MSI_IR0
, IFX_PCIE_MSI_IDX0
}, {IFX_PCIE1_MSI_IR1
, IFX_PCIE_MSI_IDX1
},
132 {IFX_PCIE1_MSI_IR2
, IFX_PCIE_MSI_IDX2
}, {IFX_PCIE1_MSI_IR3
, IFX_PCIE_MSI_IDX3
},
134 .msi_free_irq_bitmask
= 0,
135 .msi_multiple_irq_bitmask
= 0,
138 #endif /* CONFIG_IFX_PCIE_2ND_CORE */
142 * This lock controls updates to msi_free_irq_bitmask,
143 * msi_multiple_irq_bitmask and pic register settting
145 static DEFINE_SPINLOCK(ifx_pcie_msi_lock
);
147 void pcie_msi_pic_init(int pcie_port
)
149 spin_lock(&ifx_pcie_msi_lock
);
150 msi_irqs
[pcie_port
].msi_pic_p
->pic_endian
= IFX_MSI_PIC_BIG_ENDIAN
;
151 spin_unlock(&ifx_pcie_msi_lock
);
155 * \fn int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
156 * \brief Called when a driver request MSI interrupts instead of the
157 * legacy INT A-D. This routine will allocate multiple interrupts
158 * for MSI devices that support them. A device can override this by
159 * programming the MSI control bits [6:4] before calling
162 * \param[in] pdev Device requesting MSI interrupts
163 * \param[in] desc MSI descriptor
165 * \return -EINVAL Invalid pcie root port or invalid msi bit
167 * \ingroup IFX_PCIE_MSI
170 arch_setup_msi_irq(struct pci_dev
*pdev
, struct msi_desc
*desc
)
176 int configured_private_bits
;
177 int request_private_bits
;
180 struct ifx_pci_controller
*ctrl
= pdev
->bus
->sysdata
;
181 int pcie_port
= ctrl
->port
;
183 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "%s %s enter\n", __func__
, pci_name(pdev
));
185 /* XXX, skip RC MSI itself */
186 if (pdev
->pcie_type
== PCI_EXP_TYPE_ROOT_PORT
) {
187 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "%s RC itself doesn't use MSI interrupt\n", __func__
);
192 * Read the MSI config to figure out how many IRQs this device
193 * wants. Most devices only want 1, which will give
194 * configured_private_bits and request_private_bits equal 0.
196 pci_read_config_word(pdev
, desc
->msi_attrib
.pos
+ PCI_MSI_FLAGS
, &control
);
199 * If the number of private bits has been configured then use
200 * that value instead of the requested number. This gives the
201 * driver the chance to override the number of interrupts
202 * before calling pci_enable_msi().
204 configured_private_bits
= (control
& PCI_MSI_FLAGS_QSIZE
) >> 4;
205 if (configured_private_bits
== 0) {
206 /* Nothing is configured, so use the hardware requested size */
207 request_private_bits
= (control
& PCI_MSI_FLAGS_QMASK
) >> 1;
211 * Use the number of configured bits, assuming the
212 * driver wanted to override the hardware request
215 request_private_bits
= configured_private_bits
;
219 * The PCI 2.3 spec mandates that there are at most 32
220 * interrupts. If this device asks for more, only give it one.
222 if (request_private_bits
> 5) {
223 request_private_bits
= 0;
227 * The IRQs have to be aligned on a power of two based on the
228 * number being requested.
230 irq_step
= (1 << request_private_bits
);
232 /* Mask with one bit for each IRQ */
233 search_mask
= (1 << irq_step
) - 1;
236 * We're going to search msi_free_irq_bitmask_lock for zero
237 * bits. This represents an MSI interrupt number that isn't in
240 spin_lock(&ifx_pcie_msi_lock
);
241 for (pos
= 0; pos
< IFX_MSI_IRQ_NUM
; pos
+= irq_step
) {
242 if ((msi_irqs
[pcie_port
].msi_free_irq_bitmask
& (search_mask
<< pos
)) == 0) {
243 msi_irqs
[pcie_port
].msi_free_irq_bitmask
|= search_mask
<< pos
;
244 msi_irqs
[pcie_port
].msi_multiple_irq_bitmask
|= (search_mask
>> 1) << pos
;
248 spin_unlock(&ifx_pcie_msi_lock
);
250 /* Make sure the search for available interrupts didn't fail */
251 if (pos
>= IFX_MSI_IRQ_NUM
) {
252 if (request_private_bits
) {
253 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "%s: Unable to find %d free "
254 "interrupts, trying just one", __func__
, 1 << request_private_bits
);
255 request_private_bits
= 0;
259 printk(KERN_ERR
"%s: Unable to find a free MSI interrupt\n", __func__
);
263 irq
= msi_irqs
[pcie_port
].msi_irq_idx
[pos
].irq
;
264 irq_idx
= msi_irqs
[pcie_port
].msi_irq_idx
[pos
].idx
;
266 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "pos %d, irq %d irq_idx %d\n", pos
, irq
, irq_idx
);
269 * Initialize MSI. This has to match the memory-write endianess from the device
270 * Address bits [23:12]
272 spin_lock(&ifx_pcie_msi_lock
);
273 msi_irqs
[pcie_port
].msi_pic_p
->pic_table
[pos
] = SM(irq_idx
, IFX_MSI_PIC_INT_LINE
) |
274 SM((msi_irqs
[pcie_port
].msi_phy_base
>> 12), IFX_MSI_PIC_MSG_ADDR
) |
275 SM((1 << pos
), IFX_MSI_PIC_MSG_DATA
);
277 /* Enable this entry */
278 msi_irqs
[pcie_port
].msi_pic_p
->pic_table
[pos
] &= ~IFX_MSI_PCI_INT_DISABLE
;
279 spin_unlock(&ifx_pcie_msi_lock
);
281 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "pic_table[%d]: 0x%08x\n",
282 pos
, msi_irqs
[pcie_port
].msi_pic_p
->pic_table
[pos
]);
284 /* Update the number of IRQs the device has available to it */
285 control
&= ~PCI_MSI_FLAGS_QSIZE
;
286 control
|= (request_private_bits
<< 4);
287 pci_write_config_word(pdev
, desc
->msi_attrib
.pos
+ PCI_MSI_FLAGS
, control
);
289 irq_set_msi_desc(irq
, desc
);
290 msg
.address_hi
= 0x0;
291 msg
.address_lo
= msi_irqs
[pcie_port
].msi_phy_base
;
292 msg
.data
= SM((1 << pos
), IFX_MSI_PIC_MSG_DATA
);
293 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "msi_data: pos %d 0x%08x\n", pos
, msg
.data
);
295 write_msi_msg(irq
, &msg
);
296 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "%s exit\n", __func__
);
301 pcie_msi_irq_to_port(unsigned int irq
, int *port
)
305 if (irq
== IFX_PCIE_MSI_IR0
|| irq
== IFX_PCIE_MSI_IR1
||
306 irq
== IFX_PCIE_MSI_IR2
|| irq
== IFX_PCIE_MSI_IR3
) {
307 *port
= IFX_PCIE_PORT0
;
309 #ifdef CONFIG_IFX_PCIE_2ND_CORE
310 else if (irq
== IFX_PCIE1_MSI_IR0
|| irq
== IFX_PCIE1_MSI_IR1
||
311 irq
== IFX_PCIE1_MSI_IR2
|| irq
== IFX_PCIE1_MSI_IR3
) {
312 *port
= IFX_PCIE_PORT1
;
314 #endif /* CONFIG_IFX_PCIE_2ND_CORE */
316 printk(KERN_ERR
"%s: Attempted to teardown illegal "
317 "MSI interrupt (%d)\n", __func__
, irq
);
324 * \fn void arch_teardown_msi_irq(unsigned int irq)
325 * \brief Called when a device no longer needs its MSI interrupts. All
326 * MSI interrupts for the device are freed.
328 * \param irq The devices first irq number. There may be multple in sequence.
330 * \ingroup IFX_PCIE_MSI
333 arch_teardown_msi_irq(unsigned int irq
)
340 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "%s enter\n", __func__
);
342 BUG_ON(irq
> (INT_NUM_IM4_IRL0
+ 31));
344 if (pcie_msi_irq_to_port(irq
, &pcie_port
) != 0) {
348 /* Shift the mask to the correct bit location, not always correct
349 * Probally, the first match will be chosen.
351 for (pos
= 0; pos
< IFX_MSI_IRQ_NUM
; pos
++) {
352 if ((msi_irqs
[pcie_port
].msi_irq_idx
[pos
].irq
== irq
)
353 && (msi_irqs
[pcie_port
].msi_free_irq_bitmask
& ( 1 << pos
))) {
357 if (pos
>= IFX_MSI_IRQ_NUM
) {
358 printk(KERN_ERR
"%s: Unable to find a matched MSI interrupt\n", __func__
);
361 spin_lock(&ifx_pcie_msi_lock
);
362 /* Disable this entry */
363 msi_irqs
[pcie_port
].msi_pic_p
->pic_table
[pos
] |= IFX_MSI_PCI_INT_DISABLE
;
364 msi_irqs
[pcie_port
].msi_pic_p
->pic_table
[pos
] &= ~(IFX_MSI_PIC_INT_LINE
| IFX_MSI_PIC_MSG_ADDR
| IFX_MSI_PIC_MSG_DATA
);
365 spin_unlock(&ifx_pcie_msi_lock
);
367 * Count the number of IRQs we need to free by looking at the
368 * msi_multiple_irq_bitmask. Each bit set means that the next
369 * IRQ is also owned by this device.
372 while (((pos
+ number_irqs
) < IFX_MSI_IRQ_NUM
) &&
373 (msi_irqs
[pcie_port
].msi_multiple_irq_bitmask
& (1 << (pos
+ number_irqs
)))) {
378 /* Mask with one bit for each IRQ */
379 bitmask
= (1 << number_irqs
) - 1;
382 if ((msi_irqs
[pcie_port
].msi_free_irq_bitmask
& bitmask
) != bitmask
) {
383 printk(KERN_ERR
"%s: Attempted to teardown MSI "
384 "interrupt (%d) not in use\n", __func__
, irq
);
387 /* Checks are done, update the in use bitmask */
388 spin_lock(&ifx_pcie_msi_lock
);
389 msi_irqs
[pcie_port
].msi_free_irq_bitmask
&= ~bitmask
;
390 msi_irqs
[pcie_port
].msi_multiple_irq_bitmask
&= ~(bitmask
>> 1);
391 spin_unlock(&ifx_pcie_msi_lock
);
392 IFX_PCIE_PRINT(PCIE_MSG_MSI
, "%s exit\n", __func__
);
395 MODULE_LICENSE("GPL");
396 MODULE_AUTHOR("Chuanhua.Lei@infineon.com");
397 MODULE_SUPPORTED_DEVICE("Infineon PCIe IP builtin MSI PIC module");
398 MODULE_DESCRIPTION("Infineon PCIe IP builtin MSI PIC driver");