blob: f59082746c45032df40f9b6e3a9bde7deb7e5462 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -06002 *
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
Niranjana Vishwanathapura459a27d2012-07-20 12:23:55 -060042static irqreturn_t handle_wake_irq(int irq, void *data)
43{
44 PCIE_DBG("\n");
45 return IRQ_HANDLED;
46}
47
48static irqreturn_t handle_msi_irq(int irq, void *data)
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -060049{
50 int i, j;
51 unsigned long val;
52 struct msm_pcie_dev_t *dev = data;
53 void __iomem *ctrl_status;
54
55 /* check for set bits, clear it by setting that bit
56 and trigger corresponding irq */
57 for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) {
58 ctrl_status = dev->pcie20 +
59 PCIE20_MSI_CTRL_INTR_STATUS + (i * 12);
60
61 val = readl_relaxed(ctrl_status);
62 while (val) {
63 j = find_first_bit(&val, 32);
64 writel_relaxed(BIT(j), ctrl_status);
65 /* ensure that interrupt is cleared (acked) */
66 wmb();
67
68 generic_handle_irq(MSM_PCIE_MSI_INT(j + (32 * i)));
69 val = readl_relaxed(ctrl_status);
70 }
71 }
72
73 return IRQ_HANDLED;
74}
75
Niranjana Vishwanathapurad3803302012-05-25 15:13:09 -060076uint32_t __init msm_pcie_irq_init(struct msm_pcie_dev_t *dev)
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -060077{
78 int i, rc;
79
80 PCIE_DBG("\n");
81
82 /* program MSI controller and enable all interrupts */
83 writel_relaxed(MSM_PCIE_MSI_PHY, dev->pcie20 + PCIE20_MSI_CTRL_ADDR);
84 writel_relaxed(0, dev->pcie20 + PCIE20_MSI_CTRL_UPPER_ADDR);
85
86 for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++)
87 writel_relaxed(~0, dev->pcie20 +
88 PCIE20_MSI_CTRL_INTR_EN + (i * 12));
89
90 /* ensure that hardware is configured before proceeding */
91 wmb();
92
93 /* register handler for physical MSI interrupt line */
94 rc = request_irq(PCIE20_INT_MSI, handle_msi_irq, IRQF_TRIGGER_RISING,
95 "msm_pcie_msi", dev);
Niranjana Vishwanathapura459a27d2012-07-20 12:23:55 -060096 if (rc) {
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -060097 pr_err("Unable to allocate msi interrupt\n");
Niranjana Vishwanathapura459a27d2012-07-20 12:23:55 -060098 goto out;
99 }
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -0600100
Niranjana Vishwanathapura459a27d2012-07-20 12:23:55 -0600101 /* register handler for PCIE_WAKE_N interrupt line */
102 rc = request_irq(dev->wake_n, handle_wake_irq, IRQF_TRIGGER_FALLING,
103 "msm_pcie_wake", dev);
104 if (rc) {
105 pr_err("Unable to allocate wake interrupt\n");
106 free_irq(PCIE20_INT_MSI, dev);
107 goto out;
108 }
109
110 enable_irq_wake(dev->wake_n);
111
112 /* PCIE_WAKE_N should be enabled only during system suspend */
113 disable_irq(dev->wake_n);
114out:
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -0600115 return rc;
116}
117
Niranjana Vishwanathapurad3803302012-05-25 15:13:09 -0600118void __exit msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev)
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -0600119{
120 free_irq(PCIE20_INT_MSI, dev);
Niranjana Vishwanathapura459a27d2012-07-20 12:23:55 -0600121 free_irq(dev->wake_n, dev);
Niranjana Vishwanathapuraf1427ac2012-05-03 14:28:21 -0600122}
123
124void msm_pcie_destroy_irq(unsigned int irq)
125{
126 int pos = irq - MSM_PCIE_MSI_INT(0);
127
128 dynamic_irq_cleanup(irq);
129 clear_bit(pos, msi_irq_in_use);
130}
131
132/* hookup to linux pci msi framework */
133void arch_teardown_msi_irq(unsigned int irq)
134{
135 PCIE_DBG("irq %d deallocated\n", irq);
136 msm_pcie_destroy_irq(irq);
137}
138
139static void msm_pcie_msi_nop(struct irq_data *d)
140{
141 return;
142}
143
144static struct irq_chip pcie_msi_chip = {
145 .name = "msm-pcie-msi",
146 .irq_ack = msm_pcie_msi_nop,
147 .irq_enable = unmask_msi_irq,
148 .irq_disable = mask_msi_irq,
149 .irq_mask = mask_msi_irq,
150 .irq_unmask = unmask_msi_irq,
151};
152
153static int msm_pcie_create_irq(void)
154{
155 int irq, pos;
156
157again:
158 pos = find_first_zero_bit(msi_irq_in_use, NR_PCIE_MSI_IRQS);
159 irq = MSM_PCIE_MSI_INT(pos);
160 if (irq >= (MSM_PCIE_MSI_INT(0) + NR_PCIE_MSI_IRQS))
161 return -ENOSPC;
162
163 if (test_and_set_bit(pos, msi_irq_in_use))
164 goto again;
165
166 dynamic_irq_init(irq);
167 return irq;
168}
169
170/* hookup to linux pci msi framework */
171int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
172{
173 int irq;
174 struct msi_msg msg;
175
176 irq = msm_pcie_create_irq();
177 if (irq < 0)
178 return irq;
179
180 PCIE_DBG("irq %d allocated\n", irq);
181
182 irq_set_msi_desc(irq, desc);
183
184 /* write msi vector and data */
185 msg.address_hi = 0;
186 msg.address_lo = MSM_PCIE_MSI_PHY;
187 msg.data = irq - MSM_PCIE_MSI_INT(0);
188 write_msi_msg(irq, &msg);
189
190 irq_set_chip_and_handler(irq, &pcie_msi_chip, handle_simple_irq);
191 set_irq_flags(irq, IRQF_VALID);
192 return 0;
193}