blob: d91556168f9f5b62022fdb24c8ae53cf7736f6dc [file] [log] [blame]
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -06001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13/*
14 * MSM PCIe controller IRQ driver.
15 */
16
17#define pr_fmt(fmt) "%s: " fmt, __func__
18
19#include <linux/bitops.h>
20#include <linux/interrupt.h>
21#include <linux/irq.h>
22#include <linux/kernel.h>
23#include <linux/msi.h>
24#include <linux/pci.h>
25#include <mach/irqs.h>
26
27#include "pcie.h"
28
29/* Any address will do here, as it won't be dereferenced */
30#define MSM_PCIE_MSI_PHY 0xa0000000
31
32#define PCIE20_MSI_CTRL_ADDR (0x820)
33#define PCIE20_MSI_CTRL_UPPER_ADDR (0x824)
34#define PCIE20_MSI_CTRL_INTR_EN (0x828)
35#define PCIE20_MSI_CTRL_INTR_MASK (0x82C)
36#define PCIE20_MSI_CTRL_INTR_STATUS (0x830)
37
38#define PCIE20_MSI_CTRL_MAX 8
39
40static DECLARE_BITMAP(msi_irq_in_use, NR_PCIE_MSI_IRQS);
41
42irqreturn_t handle_msi_irq(int irq, void *data)
43{
44 int i, j;
45 unsigned long val;
46 struct msm_pcie_dev_t *dev = data;
47 void __iomem *ctrl_status;
48
49 /* check for set bits, clear it by setting that bit
50 and trigger corresponding irq */
51 for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) {
52 ctrl_status = dev->pcie20 +
53 PCIE20_MSI_CTRL_INTR_STATUS + (i * 12);
54
55 val = readl_relaxed(ctrl_status);
56 while (val) {
57 j = find_first_bit(&val, 32);
58 writel_relaxed(BIT(j), ctrl_status);
59 /* ensure that interrupt is cleared (acked) */
60 wmb();
61
62 generic_handle_irq(MSM_PCIE_MSI_INT(j + (32 * i)));
63 val = readl_relaxed(ctrl_status);
64 }
65 }
66
67 return IRQ_HANDLED;
68}
69
Niranjana Vishwanathapurad3803302012-05-25 15:13:09 -060070uint32_t __init msm_pcie_irq_init(struct msm_pcie_dev_t *dev)
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -060071{
72 int i, rc;
73
74 PCIE_DBG("\n");
75
76 /* program MSI controller and enable all interrupts */
77 writel_relaxed(MSM_PCIE_MSI_PHY, dev->pcie20 + PCIE20_MSI_CTRL_ADDR);
78 writel_relaxed(0, dev->pcie20 + PCIE20_MSI_CTRL_UPPER_ADDR);
79
80 for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++)
81 writel_relaxed(~0, dev->pcie20 +
82 PCIE20_MSI_CTRL_INTR_EN + (i * 12));
83
84 /* ensure that hardware is configured before proceeding */
85 wmb();
86
87 /* register handler for physical MSI interrupt line */
88 rc = request_irq(PCIE20_INT_MSI, handle_msi_irq, IRQF_TRIGGER_RISING,
89 "msm_pcie_msi", dev);
90 if (rc)
91 pr_err("Unable to allocate msi interrupt\n");
92
93 return rc;
94}
95
Niranjana Vishwanathapurad3803302012-05-25 15:13:09 -060096void __exit msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev)
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -060097{
98 free_irq(PCIE20_INT_MSI, dev);
99}
100
101void msm_pcie_destroy_irq(unsigned int irq)
102{
103 int pos = irq - MSM_PCIE_MSI_INT(0);
104
105 dynamic_irq_cleanup(irq);
106 clear_bit(pos, msi_irq_in_use);
107}
108
109/* hookup to linux pci msi framework */
110void arch_teardown_msi_irq(unsigned int irq)
111{
112 PCIE_DBG("irq %d deallocated\n", irq);
113 msm_pcie_destroy_irq(irq);
114}
115
116static void msm_pcie_msi_nop(struct irq_data *d)
117{
118 return;
119}
120
121static struct irq_chip pcie_msi_chip = {
122 .name = "msm-pcie-msi",
123 .irq_ack = msm_pcie_msi_nop,
124 .irq_enable = unmask_msi_irq,
125 .irq_disable = mask_msi_irq,
126 .irq_mask = mask_msi_irq,
127 .irq_unmask = unmask_msi_irq,
128};
129
130static int msm_pcie_create_irq(void)
131{
132 int irq, pos;
133
134again:
135 pos = find_first_zero_bit(msi_irq_in_use, NR_PCIE_MSI_IRQS);
136 irq = MSM_PCIE_MSI_INT(pos);
137 if (irq >= (MSM_PCIE_MSI_INT(0) + NR_PCIE_MSI_IRQS))
138 return -ENOSPC;
139
140 if (test_and_set_bit(pos, msi_irq_in_use))
141 goto again;
142
143 dynamic_irq_init(irq);
144 return irq;
145}
146
147/* hookup to linux pci msi framework */
148int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
149{
150 int irq;
151 struct msi_msg msg;
152
153 irq = msm_pcie_create_irq();
154 if (irq < 0)
155 return irq;
156
157 PCIE_DBG("irq %d allocated\n", irq);
158
159 irq_set_msi_desc(irq, desc);
160
161 /* write msi vector and data */
162 msg.address_hi = 0;
163 msg.address_lo = MSM_PCIE_MSI_PHY;
164 msg.data = irq - MSM_PCIE_MSI_INT(0);
165 write_msi_msg(irq, &msg);
166
167 irq_set_chip_and_handler(irq, &pcie_msi_chip, handle_simple_irq);
168 set_irq_flags(irq, IRQF_VALID);
169 return 0;
170}