diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/rapidio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/rapidio/Makefile | 1 | ||||
-rw-r--r-- | drivers/rapidio/devices/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rapidio/devices/Makefile | 5 | ||||
-rw-r--r-- | drivers/rapidio/devices/tsi721.c | 2360 | ||||
-rw-r--r-- | drivers/rapidio/devices/tsi721.h | 766 |
6 files changed, 3146 insertions, 2 deletions
diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig index 070211a5955c..bc8719238793 100644 --- a/drivers/rapidio/Kconfig +++ b/drivers/rapidio/Kconfig | |||
@@ -1,6 +1,8 @@ | |||
1 | # | 1 | # |
2 | # RapidIO configuration | 2 | # RapidIO configuration |
3 | # | 3 | # |
4 | source "drivers/rapidio/devices/Kconfig" | ||
5 | |||
4 | config RAPIDIO_DISC_TIMEOUT | 6 | config RAPIDIO_DISC_TIMEOUT |
5 | int "Discovery timeout duration (seconds)" | 7 | int "Discovery timeout duration (seconds)" |
6 | depends on RAPIDIO | 8 | depends on RAPIDIO |
@@ -20,8 +22,6 @@ config RAPIDIO_ENABLE_RX_TX_PORTS | |||
20 | ports for Input/Output direction to allow other traffic | 22 | ports for Input/Output direction to allow other traffic |
21 | than Maintenance transfers. | 23 | than Maintenance transfers. |
22 | 24 | ||
23 | source "drivers/rapidio/switches/Kconfig" | ||
24 | |||
25 | config RAPIDIO_DEBUG | 25 | config RAPIDIO_DEBUG |
26 | bool "RapidIO subsystem debug messages" | 26 | bool "RapidIO subsystem debug messages" |
27 | depends on RAPIDIO | 27 | depends on RAPIDIO |
@@ -32,3 +32,5 @@ config RAPIDIO_DEBUG | |||
32 | going on. | 32 | going on. |
33 | 33 | ||
34 | If you are unsure about this, say N here. | 34 | If you are unsure about this, say N here. |
35 | |||
36 | source "drivers/rapidio/switches/Kconfig" | ||
diff --git a/drivers/rapidio/Makefile b/drivers/rapidio/Makefile index 89b8eca825b5..ec3fb8121004 100644 --- a/drivers/rapidio/Makefile +++ b/drivers/rapidio/Makefile | |||
@@ -4,5 +4,6 @@ | |||
4 | obj-y += rio.o rio-access.o rio-driver.o rio-scan.o rio-sysfs.o | 4 | obj-y += rio.o rio-access.o rio-driver.o rio-scan.o rio-sysfs.o |
5 | 5 | ||
6 | obj-$(CONFIG_RAPIDIO) += switches/ | 6 | obj-$(CONFIG_RAPIDIO) += switches/ |
7 | obj-$(CONFIG_RAPIDIO) += devices/ | ||
7 | 8 | ||
8 | subdir-ccflags-$(CONFIG_RAPIDIO_DEBUG) := -DDEBUG | 9 | subdir-ccflags-$(CONFIG_RAPIDIO_DEBUG) := -DDEBUG |
diff --git a/drivers/rapidio/devices/Kconfig b/drivers/rapidio/devices/Kconfig new file mode 100644 index 000000000000..12a9d7f7040b --- /dev/null +++ b/drivers/rapidio/devices/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # RapidIO master port configuration | ||
3 | # | ||
4 | |||
5 | config RAPIDIO_TSI721 | ||
6 | bool "IDT Tsi721 PCI Express SRIO Controller support" | ||
7 | depends on RAPIDIO && PCIEPORTBUS | ||
8 | default "n" | ||
9 | ---help--- | ||
10 | Include support for IDT Tsi721 PCI Express Serial RapidIO controller. | ||
diff --git a/drivers/rapidio/devices/Makefile b/drivers/rapidio/devices/Makefile new file mode 100644 index 000000000000..3b7b4e2dff7c --- /dev/null +++ b/drivers/rapidio/devices/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for RapidIO devices | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_RAPIDIO_TSI721) += tsi721.o | ||
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c new file mode 100644 index 000000000000..5225930a10cd --- /dev/null +++ b/drivers/rapidio/devices/tsi721.c | |||
@@ -0,0 +1,2360 @@ | |||
1 | /* | ||
2 | * RapidIO mport driver for Tsi721 PCIExpress-to-SRIO bridge | ||
3 | * | ||
4 | * Copyright 2011 Integrated Device Technology, Inc. | ||
5 | * Alexandre Bounine <alexandre.bounine@idt.com> | ||
6 | * Chul Kim <chul.kim@idt.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the Free | ||
10 | * Software Foundation; either version 2 of the License, or (at your option) | ||
11 | * any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along with | ||
19 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
20 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
21 | */ | ||
22 | |||
23 | #include <linux/io.h> | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/rio.h> | ||
31 | #include <linux/rio_drv.h> | ||
32 | #include <linux/dma-mapping.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/kfifo.h> | ||
35 | #include <linux/delay.h> | ||
36 | |||
37 | #include "tsi721.h" | ||
38 | |||
39 | #define DEBUG_PW /* Inbound Port-Write debugging */ | ||
40 | |||
41 | static void tsi721_omsg_handler(struct tsi721_device *priv, int ch); | ||
42 | static void tsi721_imsg_handler(struct tsi721_device *priv, int ch); | ||
43 | |||
44 | /** | ||
45 | * tsi721_lcread - read from local SREP config space | ||
46 | * @mport: RapidIO master port info | ||
47 | * @index: ID of RapdiIO interface | ||
48 | * @offset: Offset into configuration space | ||
49 | * @len: Length (in bytes) of the maintenance transaction | ||
50 | * @data: Value to be read into | ||
51 | * | ||
52 | * Generates a local SREP space read. Returns %0 on | ||
53 | * success or %-EINVAL on failure. | ||
54 | */ | ||
55 | static int tsi721_lcread(struct rio_mport *mport, int index, u32 offset, | ||
56 | int len, u32 *data) | ||
57 | { | ||
58 | struct tsi721_device *priv = mport->priv; | ||
59 | |||
60 | if (len != sizeof(u32)) | ||
61 | return -EINVAL; /* only 32-bit access is supported */ | ||
62 | |||
63 | *data = ioread32(priv->regs + offset); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * tsi721_lcwrite - write into local SREP config space | ||
70 | * @mport: RapidIO master port info | ||
71 | * @index: ID of RapdiIO interface | ||
72 | * @offset: Offset into configuration space | ||
73 | * @len: Length (in bytes) of the maintenance transaction | ||
74 | * @data: Value to be written | ||
75 | * | ||
76 | * Generates a local write into SREP configuration space. Returns %0 on | ||
77 | * success or %-EINVAL on failure. | ||
78 | */ | ||
79 | static int tsi721_lcwrite(struct rio_mport *mport, int index, u32 offset, | ||
80 | int len, u32 data) | ||
81 | { | ||
82 | struct tsi721_device *priv = mport->priv; | ||
83 | |||
84 | if (len != sizeof(u32)) | ||
85 | return -EINVAL; /* only 32-bit access is supported */ | ||
86 | |||
87 | iowrite32(data, priv->regs + offset); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * tsi721_maint_dma - Helper function to generate RapidIO maintenance | ||
94 | * transactions using designated Tsi721 DMA channel. | ||
95 | * @priv: pointer to tsi721 private data | ||
96 | * @sys_size: RapdiIO transport system size | ||
97 | * @destid: Destination ID of transaction | ||
98 | * @hopcount: Number of hops to target device | ||
99 | * @offset: Offset into configuration space | ||
100 | * @len: Length (in bytes) of the maintenance transaction | ||
101 | * @data: Location to be read from or write into | ||
102 | * @do_wr: Operation flag (1 == MAINT_WR) | ||
103 | * | ||
104 | * Generates a RapidIO maintenance transaction (Read or Write). | ||
105 | * Returns %0 on success and %-EINVAL or %-EFAULT on failure. | ||
106 | */ | ||
107 | static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size, | ||
108 | u16 destid, u8 hopcount, u32 offset, int len, | ||
109 | u32 *data, int do_wr) | ||
110 | { | ||
111 | struct tsi721_dma_desc *bd_ptr; | ||
112 | u32 rd_count, swr_ptr, ch_stat; | ||
113 | int i, err = 0; | ||
114 | u32 op = do_wr ? MAINT_WR : MAINT_RD; | ||
115 | |||
116 | if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32))) | ||
117 | return -EINVAL; | ||
118 | |||
119 | bd_ptr = priv->bdma[TSI721_DMACH_MAINT].bd_base; | ||
120 | |||
121 | rd_count = ioread32( | ||
122 | priv->regs + TSI721_DMAC_DRDCNT(TSI721_DMACH_MAINT)); | ||
123 | |||
124 | /* Initialize DMA descriptor */ | ||
125 | bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid); | ||
126 | bd_ptr[0].bcount = cpu_to_le32((sys_size << 26) | 0x04); | ||
127 | bd_ptr[0].raddr_lo = cpu_to_le32((hopcount << 24) | offset); | ||
128 | bd_ptr[0].raddr_hi = 0; | ||
129 | if (do_wr) | ||
130 | bd_ptr[0].data[0] = cpu_to_be32p(data); | ||
131 | else | ||
132 | bd_ptr[0].data[0] = 0xffffffff; | ||
133 | |||
134 | mb(); | ||
135 | |||
136 | /* Start DMA operation */ | ||
137 | iowrite32(rd_count + 2, | ||
138 | priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); | ||
139 | ioread32(priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); | ||
140 | i = 0; | ||
141 | |||
142 | /* Wait until DMA transfer is finished */ | ||
143 | while ((ch_stat = ioread32(priv->regs + | ||
144 | TSI721_DMAC_STS(TSI721_DMACH_MAINT))) & TSI721_DMAC_STS_RUN) { | ||
145 | udelay(1); | ||
146 | if (++i >= 5000000) { | ||
147 | dev_dbg(&priv->pdev->dev, | ||
148 | "%s : DMA[%d] read timeout ch_status=%x\n", | ||
149 | __func__, TSI721_DMACH_MAINT, ch_stat); | ||
150 | if (!do_wr) | ||
151 | *data = 0xffffffff; | ||
152 | err = -EIO; | ||
153 | goto err_out; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | if (ch_stat & TSI721_DMAC_STS_ABORT) { | ||
158 | /* If DMA operation aborted due to error, | ||
159 | * reinitialize DMA channel | ||
160 | */ | ||
161 | dev_dbg(&priv->pdev->dev, "%s : DMA ABORT ch_stat=%x\n", | ||
162 | __func__, ch_stat); | ||
163 | dev_dbg(&priv->pdev->dev, "OP=%d : destid=%x hc=%x off=%x\n", | ||
164 | do_wr ? MAINT_WR : MAINT_RD, destid, hopcount, offset); | ||
165 | iowrite32(TSI721_DMAC_INT_ALL, | ||
166 | priv->regs + TSI721_DMAC_INT(TSI721_DMACH_MAINT)); | ||
167 | iowrite32(TSI721_DMAC_CTL_INIT, | ||
168 | priv->regs + TSI721_DMAC_CTL(TSI721_DMACH_MAINT)); | ||
169 | udelay(10); | ||
170 | iowrite32(0, priv->regs + | ||
171 | TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT)); | ||
172 | udelay(1); | ||
173 | if (!do_wr) | ||
174 | *data = 0xffffffff; | ||
175 | err = -EIO; | ||
176 | goto err_out; | ||
177 | } | ||
178 | |||
179 | if (!do_wr) | ||
180 | *data = be32_to_cpu(bd_ptr[0].data[0]); | ||
181 | |||
182 | /* | ||
183 | * Update descriptor status FIFO RD pointer. | ||
184 | * NOTE: Skipping check and clear FIFO entries because we are waiting | ||
185 | * for transfer to be completed. | ||
186 | */ | ||
187 | swr_ptr = ioread32(priv->regs + TSI721_DMAC_DSWP(TSI721_DMACH_MAINT)); | ||
188 | iowrite32(swr_ptr, priv->regs + TSI721_DMAC_DSRP(TSI721_DMACH_MAINT)); | ||
189 | err_out: | ||
190 | |||
191 | return err; | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * tsi721_cread_dma - Generate a RapidIO maintenance read transaction | ||
196 | * using Tsi721 BDMA engine. | ||
197 | * @mport: RapidIO master port control structure | ||
198 | * @index: ID of RapdiIO interface | ||
199 | * @destid: Destination ID of transaction | ||
200 | * @hopcount: Number of hops to target device | ||
201 | * @offset: Offset into configuration space | ||
202 | * @len: Length (in bytes) of the maintenance transaction | ||
203 | * @val: Location to be read into | ||
204 | * | ||
205 | * Generates a RapidIO maintenance read transaction. | ||
206 | * Returns %0 on success and %-EINVAL or %-EFAULT on failure. | ||
207 | */ | ||
208 | static int tsi721_cread_dma(struct rio_mport *mport, int index, u16 destid, | ||
209 | u8 hopcount, u32 offset, int len, u32 *data) | ||
210 | { | ||
211 | struct tsi721_device *priv = mport->priv; | ||
212 | |||
213 | return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount, | ||
214 | offset, len, data, 0); | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * tsi721_cwrite_dma - Generate a RapidIO maintenance write transaction | ||
219 | * using Tsi721 BDMA engine | ||
220 | * @mport: RapidIO master port control structure | ||
221 | * @index: ID of RapdiIO interface | ||
222 | * @destid: Destination ID of transaction | ||
223 | * @hopcount: Number of hops to target device | ||
224 | * @offset: Offset into configuration space | ||
225 | * @len: Length (in bytes) of the maintenance transaction | ||
226 | * @val: Value to be written | ||
227 | * | ||
228 | * Generates a RapidIO maintenance write transaction. | ||
229 | * Returns %0 on success and %-EINVAL or %-EFAULT on failure. | ||
230 | */ | ||
231 | static int tsi721_cwrite_dma(struct rio_mport *mport, int index, u16 destid, | ||
232 | u8 hopcount, u32 offset, int len, u32 data) | ||
233 | { | ||
234 | struct tsi721_device *priv = mport->priv; | ||
235 | u32 temp = data; | ||
236 | |||
237 | return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount, | ||
238 | offset, len, &temp, 1); | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * tsi721_pw_handler - Tsi721 inbound port-write interrupt handler | ||
243 | * @mport: RapidIO master port structure | ||
244 | * | ||
245 | * Handles inbound port-write interrupts. Copies PW message from an internal | ||
246 | * buffer into PW message FIFO and schedules deferred routine to process | ||
247 | * queued messages. | ||
248 | */ | ||
249 | static int | ||
250 | tsi721_pw_handler(struct rio_mport *mport) | ||
251 | { | ||
252 | struct tsi721_device *priv = mport->priv; | ||
253 | u32 pw_stat; | ||
254 | u32 pw_buf[TSI721_RIO_PW_MSG_SIZE/sizeof(u32)]; | ||
255 | |||
256 | |||
257 | pw_stat = ioread32(priv->regs + TSI721_RIO_PW_RX_STAT); | ||
258 | |||
259 | if (pw_stat & TSI721_RIO_PW_RX_STAT_PW_VAL) { | ||
260 | pw_buf[0] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(0)); | ||
261 | pw_buf[1] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(1)); | ||
262 | pw_buf[2] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(2)); | ||
263 | pw_buf[3] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(3)); | ||
264 | |||
265 | /* Queue PW message (if there is room in FIFO), | ||
266 | * otherwise discard it. | ||
267 | */ | ||
268 | spin_lock(&priv->pw_fifo_lock); | ||
269 | if (kfifo_avail(&priv->pw_fifo) >= TSI721_RIO_PW_MSG_SIZE) | ||
270 | kfifo_in(&priv->pw_fifo, pw_buf, | ||
271 | TSI721_RIO_PW_MSG_SIZE); | ||
272 | else | ||
273 | priv->pw_discard_count++; | ||
274 | spin_unlock(&priv->pw_fifo_lock); | ||
275 | } | ||
276 | |||
277 | /* Clear pending PW interrupts */ | ||
278 | iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL, | ||
279 | priv->regs + TSI721_RIO_PW_RX_STAT); | ||
280 | |||
281 | schedule_work(&priv->pw_work); | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static void tsi721_pw_dpc(struct work_struct *work) | ||
287 | { | ||
288 | struct tsi721_device *priv = container_of(work, struct tsi721_device, | ||
289 | pw_work); | ||
290 | u32 msg_buffer[RIO_PW_MSG_SIZE/sizeof(u32)]; /* Use full size PW message | ||
291 | buffer for RIO layer */ | ||
292 | |||
293 | /* | ||
294 | * Process port-write messages | ||
295 | */ | ||
296 | while (kfifo_out_spinlocked(&priv->pw_fifo, (unsigned char *)msg_buffer, | ||
297 | TSI721_RIO_PW_MSG_SIZE, &priv->pw_fifo_lock)) { | ||
298 | /* Process one message */ | ||
299 | #ifdef DEBUG_PW | ||
300 | { | ||
301 | u32 i; | ||
302 | pr_debug("%s : Port-Write Message:", __func__); | ||
303 | for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32); ) { | ||
304 | pr_debug("0x%02x: %08x %08x %08x %08x", i*4, | ||
305 | msg_buffer[i], msg_buffer[i + 1], | ||
306 | msg_buffer[i + 2], msg_buffer[i + 3]); | ||
307 | i += 4; | ||
308 | } | ||
309 | pr_debug("\n"); | ||
310 | } | ||
311 | #endif | ||
312 | /* Pass the port-write message to RIO core for processing */ | ||
313 | rio_inb_pwrite_handler((union rio_pw_msg *)msg_buffer); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * tsi721_pw_enable - enable/disable port-write interface init | ||
319 | * @mport: Master port implementing the port write unit | ||
320 | * @enable: 1=enable; 0=disable port-write message handling | ||
321 | */ | ||
322 | static int tsi721_pw_enable(struct rio_mport *mport, int enable) | ||
323 | { | ||
324 | struct tsi721_device *priv = mport->priv; | ||
325 | u32 rval; | ||
326 | |||
327 | rval = ioread32(priv->regs + TSI721_RIO_EM_INT_ENABLE); | ||
328 | |||
329 | if (enable) | ||
330 | rval |= TSI721_RIO_EM_INT_ENABLE_PW_RX; | ||
331 | else | ||
332 | rval &= ~TSI721_RIO_EM_INT_ENABLE_PW_RX; | ||
333 | |||
334 | /* Clear pending PW interrupts */ | ||
335 | iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL, | ||
336 | priv->regs + TSI721_RIO_PW_RX_STAT); | ||
337 | /* Update enable bits */ | ||
338 | iowrite32(rval, priv->regs + TSI721_RIO_EM_INT_ENABLE); | ||
339 | |||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | /** | ||
344 | * tsi721_dsend - Send a RapidIO doorbell | ||
345 | * @mport: RapidIO master port info | ||
346 | * @index: ID of RapidIO interface | ||
347 | * @destid: Destination ID of target device | ||
348 | * @data: 16-bit info field of RapidIO doorbell | ||
349 | * | ||
350 | * Sends a RapidIO doorbell message. Always returns %0. | ||
351 | */ | ||
352 | static int tsi721_dsend(struct rio_mport *mport, int index, | ||
353 | u16 destid, u16 data) | ||
354 | { | ||
355 | struct tsi721_device *priv = mport->priv; | ||
356 | u32 offset; | ||
357 | |||
358 | offset = (((mport->sys_size) ? RIO_TT_CODE_16 : RIO_TT_CODE_8) << 18) | | ||
359 | (destid << 2); | ||
360 | |||
361 | dev_dbg(&priv->pdev->dev, | ||
362 | "Send Doorbell 0x%04x to destID 0x%x\n", data, destid); | ||
363 | iowrite16be(data, priv->odb_base + offset); | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | /** | ||
369 | * tsi721_dbell_handler - Tsi721 doorbell interrupt handler | ||
370 | * @mport: RapidIO master port structure | ||
371 | * | ||
372 | * Handles inbound doorbell interrupts. Copies doorbell entry from an internal | ||
373 | * buffer into DB message FIFO and schedules deferred routine to process | ||
374 | * queued DBs. | ||
375 | */ | ||
376 | static int | ||
377 | tsi721_dbell_handler(struct rio_mport *mport) | ||
378 | { | ||
379 | struct tsi721_device *priv = mport->priv; | ||
380 | u32 regval; | ||
381 | |||
382 | /* Disable IDB interrupts */ | ||
383 | regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); | ||
384 | regval &= ~TSI721_SR_CHINT_IDBQRCV; | ||
385 | iowrite32(regval, | ||
386 | priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); | ||
387 | |||
388 | schedule_work(&priv->idb_work); | ||
389 | |||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static void tsi721_db_dpc(struct work_struct *work) | ||
394 | { | ||
395 | struct tsi721_device *priv = container_of(work, struct tsi721_device, | ||
396 | idb_work); | ||
397 | struct rio_mport *mport; | ||
398 | struct rio_dbell *dbell; | ||
399 | int found = 0; | ||
400 | u32 wr_ptr, rd_ptr; | ||
401 | u64 *idb_entry; | ||
402 | u32 regval; | ||
403 | union { | ||
404 | u64 msg; | ||
405 | u8 bytes[8]; | ||
406 | } idb; | ||
407 | |||
408 | /* | ||
409 | * Process queued inbound doorbells | ||
410 | */ | ||
411 | mport = priv->mport; | ||
412 | |||
413 | wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)); | ||
414 | rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)); | ||
415 | |||
416 | while (wr_ptr != rd_ptr) { | ||
417 | idb_entry = (u64 *)(priv->idb_base + | ||
418 | (TSI721_IDB_ENTRY_SIZE * rd_ptr)); | ||
419 | rd_ptr++; | ||
420 | idb.msg = *idb_entry; | ||
421 | *idb_entry = 0; | ||
422 | |||
423 | /* Process one doorbell */ | ||
424 | list_for_each_entry(dbell, &mport->dbells, node) { | ||
425 | if ((dbell->res->start <= DBELL_INF(idb.bytes)) && | ||
426 | (dbell->res->end >= DBELL_INF(idb.bytes))) { | ||
427 | found = 1; | ||
428 | break; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | if (found) { | ||
433 | dbell->dinb(mport, dbell->dev_id, DBELL_SID(idb.bytes), | ||
434 | DBELL_TID(idb.bytes), DBELL_INF(idb.bytes)); | ||
435 | } else { | ||
436 | dev_dbg(&priv->pdev->dev, | ||
437 | "spurious inb doorbell, sid %2.2x tid %2.2x" | ||
438 | " info %4.4x\n", DBELL_SID(idb.bytes), | ||
439 | DBELL_TID(idb.bytes), DBELL_INF(idb.bytes)); | ||
440 | } | ||
441 | } | ||
442 | |||
443 | iowrite32(rd_ptr & (IDB_QSIZE - 1), | ||
444 | priv->regs + TSI721_IDQ_RP(IDB_QUEUE)); | ||
445 | |||
446 | /* Re-enable IDB interrupts */ | ||
447 | regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); | ||
448 | regval |= TSI721_SR_CHINT_IDBQRCV; | ||
449 | iowrite32(regval, | ||
450 | priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); | ||
451 | } | ||
452 | |||
453 | /** | ||
454 | * tsi721_irqhandler - Tsi721 interrupt handler | ||
455 | * @irq: Linux interrupt number | ||
456 | * @ptr: Pointer to interrupt-specific data (mport structure) | ||
457 | * | ||
458 | * Handles Tsi721 interrupts signaled using MSI and INTA. Checks reported | ||
459 | * interrupt events and calls an event-specific handler(s). | ||
460 | */ | ||
461 | static irqreturn_t tsi721_irqhandler(int irq, void *ptr) | ||
462 | { | ||
463 | struct rio_mport *mport = (struct rio_mport *)ptr; | ||
464 | struct tsi721_device *priv = mport->priv; | ||
465 | u32 dev_int; | ||
466 | u32 dev_ch_int; | ||
467 | u32 intval; | ||
468 | u32 ch_inte; | ||
469 | |||
470 | dev_int = ioread32(priv->regs + TSI721_DEV_INT); | ||
471 | if (!dev_int) | ||
472 | return IRQ_NONE; | ||
473 | |||
474 | dev_ch_int = ioread32(priv->regs + TSI721_DEV_CHAN_INT); | ||
475 | |||
476 | if (dev_int & TSI721_DEV_INT_SR2PC_CH) { | ||
477 | /* Service SR2PC Channel interrupts */ | ||
478 | if (dev_ch_int & TSI721_INT_SR2PC_CHAN(IDB_QUEUE)) { | ||
479 | /* Service Inbound Doorbell interrupt */ | ||
480 | intval = ioread32(priv->regs + | ||
481 | TSI721_SR_CHINT(IDB_QUEUE)); | ||
482 | if (intval & TSI721_SR_CHINT_IDBQRCV) | ||
483 | tsi721_dbell_handler(mport); | ||
484 | else | ||
485 | dev_info(&priv->pdev->dev, | ||
486 | "Unsupported SR_CH_INT %x\n", intval); | ||
487 | |||
488 | /* Clear interrupts */ | ||
489 | iowrite32(intval, | ||
490 | priv->regs + TSI721_SR_CHINT(IDB_QUEUE)); | ||
491 | ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE)); | ||
492 | } | ||
493 | } | ||
494 | |||
495 | if (dev_int & TSI721_DEV_INT_SMSG_CH) { | ||
496 | int ch; | ||
497 | |||
498 | /* | ||
499 | * Service channel interrupts from Messaging Engine | ||
500 | */ | ||
501 | |||
502 | if (dev_ch_int & TSI721_INT_IMSG_CHAN_M) { /* Inbound Msg */ | ||
503 | /* Disable signaled OB MSG Channel interrupts */ | ||
504 | ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE); | ||
505 | ch_inte &= ~(dev_ch_int & TSI721_INT_IMSG_CHAN_M); | ||
506 | iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE); | ||
507 | |||
508 | /* | ||
509 | * Process Inbound Message interrupt for each MBOX | ||
510 | */ | ||
511 | for (ch = 4; ch < RIO_MAX_MBOX + 4; ch++) { | ||
512 | if (!(dev_ch_int & TSI721_INT_IMSG_CHAN(ch))) | ||
513 | continue; | ||
514 | tsi721_imsg_handler(priv, ch); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | if (dev_ch_int & TSI721_INT_OMSG_CHAN_M) { /* Outbound Msg */ | ||
519 | /* Disable signaled OB MSG Channel interrupts */ | ||
520 | ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE); | ||
521 | ch_inte &= ~(dev_ch_int & TSI721_INT_OMSG_CHAN_M); | ||
522 | iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE); | ||
523 | |||
524 | /* | ||
525 | * Process Outbound Message interrupts for each MBOX | ||
526 | */ | ||
527 | |||
528 | for (ch = 0; ch < RIO_MAX_MBOX; ch++) { | ||
529 | if (!(dev_ch_int & TSI721_INT_OMSG_CHAN(ch))) | ||
530 | continue; | ||
531 | tsi721_omsg_handler(priv, ch); | ||
532 | } | ||
533 | } | ||
534 | } | ||
535 | |||
536 | if (dev_int & TSI721_DEV_INT_SRIO) { | ||
537 | /* Service SRIO MAC interrupts */ | ||
538 | intval = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT); | ||
539 | if (intval & TSI721_RIO_EM_INT_STAT_PW_RX) | ||
540 | tsi721_pw_handler(mport); | ||
541 | } | ||
542 | |||
543 | return IRQ_HANDLED; | ||
544 | } | ||
545 | |||
546 | static void tsi721_interrupts_init(struct tsi721_device *priv) | ||
547 | { | ||
548 | u32 intr; | ||
549 | |||
550 | /* Enable IDB interrupts */ | ||
551 | iowrite32(TSI721_SR_CHINT_ALL, | ||
552 | priv->regs + TSI721_SR_CHINT(IDB_QUEUE)); | ||
553 | iowrite32(TSI721_SR_CHINT_IDBQRCV, | ||
554 | priv->regs + TSI721_SR_CHINTE(IDB_QUEUE)); | ||
555 | iowrite32(TSI721_INT_SR2PC_CHAN(IDB_QUEUE), | ||
556 | priv->regs + TSI721_DEV_CHAN_INTE); | ||
557 | |||
558 | /* Enable SRIO MAC interrupts */ | ||
559 | iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT, | ||
560 | priv->regs + TSI721_RIO_EM_DEV_INT_EN); | ||
561 | |||
562 | if (priv->flags & TSI721_USING_MSIX) | ||
563 | intr = TSI721_DEV_INT_SRIO; | ||
564 | else | ||
565 | intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO | | ||
566 | TSI721_DEV_INT_SMSG_CH; | ||
567 | |||
568 | iowrite32(intr, priv->regs + TSI721_DEV_INTE); | ||
569 | ioread32(priv->regs + TSI721_DEV_INTE); | ||
570 | } | ||
571 | |||
572 | #ifdef CONFIG_PCI_MSI | ||
573 | /** | ||
574 | * tsi721_omsg_msix - MSI-X interrupt handler for outbound messaging | ||
575 | * @irq: Linux interrupt number | ||
576 | * @ptr: Pointer to interrupt-specific data (mport structure) | ||
577 | * | ||
578 | * Handles outbound messaging interrupts signaled using MSI-X. | ||
579 | */ | ||
580 | static irqreturn_t tsi721_omsg_msix(int irq, void *ptr) | ||
581 | { | ||
582 | struct tsi721_device *priv = ((struct rio_mport *)ptr)->priv; | ||
583 | int mbox; | ||
584 | |||
585 | mbox = (irq - priv->msix[TSI721_VECT_OMB0_DONE].vector) % RIO_MAX_MBOX; | ||
586 | tsi721_omsg_handler(priv, mbox); | ||
587 | return IRQ_HANDLED; | ||
588 | } | ||
589 | |||
590 | /** | ||
591 | * tsi721_imsg_msix - MSI-X interrupt handler for inbound messaging | ||
592 | * @irq: Linux interrupt number | ||
593 | * @ptr: Pointer to interrupt-specific data (mport structure) | ||
594 | * | ||
595 | * Handles inbound messaging interrupts signaled using MSI-X. | ||
596 | */ | ||
597 | static irqreturn_t tsi721_imsg_msix(int irq, void *ptr) | ||
598 | { | ||
599 | struct tsi721_device *priv = ((struct rio_mport *)ptr)->priv; | ||
600 | int mbox; | ||
601 | |||
602 | mbox = (irq - priv->msix[TSI721_VECT_IMB0_RCV].vector) % RIO_MAX_MBOX; | ||
603 | tsi721_imsg_handler(priv, mbox + 4); | ||
604 | return IRQ_HANDLED; | ||
605 | } | ||
606 | |||
607 | /** | ||
608 | * tsi721_srio_msix - Tsi721 MSI-X SRIO MAC interrupt handler | ||
609 | * @irq: Linux interrupt number | ||
610 | * @ptr: Pointer to interrupt-specific data (mport structure) | ||
611 | * | ||
612 | * Handles Tsi721 interrupts from SRIO MAC. | ||
613 | */ | ||
614 | static irqreturn_t tsi721_srio_msix(int irq, void *ptr) | ||
615 | { | ||
616 | struct tsi721_device *priv = ((struct rio_mport *)ptr)->priv; | ||
617 | u32 srio_int; | ||
618 | |||
619 | /* Service SRIO MAC interrupts */ | ||
620 | srio_int = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT); | ||
621 | if (srio_int & TSI721_RIO_EM_INT_STAT_PW_RX) | ||
622 | tsi721_pw_handler((struct rio_mport *)ptr); | ||
623 | |||
624 | return IRQ_HANDLED; | ||
625 | } | ||
626 | |||
627 | /** | ||
628 | * tsi721_sr2pc_ch_msix - Tsi721 MSI-X SR2PC Channel interrupt handler | ||
629 | * @irq: Linux interrupt number | ||
630 | * @ptr: Pointer to interrupt-specific data (mport structure) | ||
631 | * | ||
632 | * Handles Tsi721 interrupts from SR2PC Channel. | ||
633 | * NOTE: At this moment services only one SR2PC channel associated with inbound | ||
634 | * doorbells. | ||
635 | */ | ||
636 | static irqreturn_t tsi721_sr2pc_ch_msix(int irq, void *ptr) | ||
637 | { | ||
638 | struct tsi721_device *priv = ((struct rio_mport *)ptr)->priv; | ||
639 | u32 sr_ch_int; | ||
640 | |||
641 | /* Service Inbound DB interrupt from SR2PC channel */ | ||
642 | sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE)); | ||
643 | if (sr_ch_int & TSI721_SR_CHINT_IDBQRCV) | ||
644 | tsi721_dbell_handler((struct rio_mport *)ptr); | ||
645 | |||
646 | /* Clear interrupts */ | ||
647 | iowrite32(sr_ch_int, priv->regs + TSI721_SR_CHINT(IDB_QUEUE)); | ||
648 | /* Read back to ensure that interrupt was cleared */ | ||
649 | sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE)); | ||
650 | |||
651 | return IRQ_HANDLED; | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * tsi721_request_msix - register interrupt service for MSI-X mode. | ||
656 | * @mport: RapidIO master port structure | ||
657 | * | ||
658 | * Registers MSI-X interrupt service routines for interrupts that are active | ||
659 | * immediately after mport initialization. Messaging interrupt service routines | ||
660 | * should be registered during corresponding open requests. | ||
661 | */ | ||
662 | static int tsi721_request_msix(struct rio_mport *mport) | ||
663 | { | ||
664 | struct tsi721_device *priv = mport->priv; | ||
665 | int err = 0; | ||
666 | |||
667 | err = request_irq(priv->msix[TSI721_VECT_IDB].vector, | ||
668 | tsi721_sr2pc_ch_msix, 0, | ||
669 | priv->msix[TSI721_VECT_IDB].irq_name, (void *)mport); | ||
670 | if (err) | ||
671 | goto out; | ||
672 | |||
673 | err = request_irq(priv->msix[TSI721_VECT_PWRX].vector, | ||
674 | tsi721_srio_msix, 0, | ||
675 | priv->msix[TSI721_VECT_PWRX].irq_name, (void *)mport); | ||
676 | if (err) | ||
677 | free_irq( | ||
678 | priv->msix[TSI721_VECT_IDB].vector, | ||
679 | (void *)mport); | ||
680 | out: | ||
681 | return err; | ||
682 | } | ||
683 | |||
684 | /** | ||
685 | * tsi721_enable_msix - Attempts to enable MSI-X support for Tsi721. | ||
686 | * @priv: pointer to tsi721 private data | ||
687 | * | ||
688 | * Configures MSI-X support for Tsi721. Supports only an exact number | ||
689 | * of requested vectors. | ||
690 | */ | ||
691 | static int tsi721_enable_msix(struct tsi721_device *priv) | ||
692 | { | ||
693 | struct msix_entry entries[TSI721_VECT_MAX]; | ||
694 | int err; | ||
695 | int i; | ||
696 | |||
697 | entries[TSI721_VECT_IDB].entry = TSI721_MSIX_SR2PC_IDBQ_RCV(IDB_QUEUE); | ||
698 | entries[TSI721_VECT_PWRX].entry = TSI721_MSIX_SRIO_MAC_INT; | ||
699 | |||
700 | /* | ||
701 | * Initialize MSI-X entries for Messaging Engine: | ||
702 | * this driver supports four RIO mailboxes (inbound and outbound) | ||
703 | * NOTE: Inbound message MBOX 0...4 use IB channels 4...7. Therefore | ||
704 | * offset +4 is added to IB MBOX number. | ||
705 | */ | ||
706 | for (i = 0; i < RIO_MAX_MBOX; i++) { | ||
707 | entries[TSI721_VECT_IMB0_RCV + i].entry = | ||
708 | TSI721_MSIX_IMSG_DQ_RCV(i + 4); | ||
709 | entries[TSI721_VECT_IMB0_INT + i].entry = | ||
710 | TSI721_MSIX_IMSG_INT(i + 4); | ||
711 | entries[TSI721_VECT_OMB0_DONE + i].entry = | ||
712 | TSI721_MSIX_OMSG_DONE(i); | ||
713 | entries[TSI721_VECT_OMB0_INT + i].entry = | ||
714 | TSI721_MSIX_OMSG_INT(i); | ||
715 | } | ||
716 | |||
717 | err = pci_enable_msix(priv->pdev, entries, ARRAY_SIZE(entries)); | ||
718 | if (err) { | ||
719 | if (err > 0) | ||
720 | dev_info(&priv->pdev->dev, | ||
721 | "Only %d MSI-X vectors available, " | ||
722 | "not using MSI-X\n", err); | ||
723 | return err; | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * Copy MSI-X vector information into tsi721 private structure | ||
728 | */ | ||
729 | priv->msix[TSI721_VECT_IDB].vector = entries[TSI721_VECT_IDB].vector; | ||
730 | snprintf(priv->msix[TSI721_VECT_IDB].irq_name, IRQ_DEVICE_NAME_MAX, | ||
731 | DRV_NAME "-idb@pci:%s", pci_name(priv->pdev)); | ||
732 | priv->msix[TSI721_VECT_PWRX].vector = entries[TSI721_VECT_PWRX].vector; | ||
733 | snprintf(priv->msix[TSI721_VECT_PWRX].irq_name, IRQ_DEVICE_NAME_MAX, | ||
734 | DRV_NAME "-pwrx@pci:%s", pci_name(priv->pdev)); | ||
735 | |||
736 | for (i = 0; i < RIO_MAX_MBOX; i++) { | ||
737 | priv->msix[TSI721_VECT_IMB0_RCV + i].vector = | ||
738 | entries[TSI721_VECT_IMB0_RCV + i].vector; | ||
739 | snprintf(priv->msix[TSI721_VECT_IMB0_RCV + i].irq_name, | ||
740 | IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbr%d@pci:%s", | ||
741 | i, pci_name(priv->pdev)); | ||
742 | |||
743 | priv->msix[TSI721_VECT_IMB0_INT + i].vector = | ||
744 | entries[TSI721_VECT_IMB0_INT + i].vector; | ||
745 | snprintf(priv->msix[TSI721_VECT_IMB0_INT + i].irq_name, | ||
746 | IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbi%d@pci:%s", | ||
747 | i, pci_name(priv->pdev)); | ||
748 | |||
749 | priv->msix[TSI721_VECT_OMB0_DONE + i].vector = | ||
750 | entries[TSI721_VECT_OMB0_DONE + i].vector; | ||
751 | snprintf(priv->msix[TSI721_VECT_OMB0_DONE + i].irq_name, | ||
752 | IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombd%d@pci:%s", | ||
753 | i, pci_name(priv->pdev)); | ||
754 | |||
755 | priv->msix[TSI721_VECT_OMB0_INT + i].vector = | ||
756 | entries[TSI721_VECT_OMB0_INT + i].vector; | ||
757 | snprintf(priv->msix[TSI721_VECT_OMB0_INT + i].irq_name, | ||
758 | IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombi%d@pci:%s", | ||
759 | i, pci_name(priv->pdev)); | ||
760 | } | ||
761 | |||
762 | return 0; | ||
763 | } | ||
764 | #endif /* CONFIG_PCI_MSI */ | ||
765 | |||
766 | static int tsi721_request_irq(struct rio_mport *mport) | ||
767 | { | ||
768 | struct tsi721_device *priv = mport->priv; | ||
769 | int err; | ||
770 | |||
771 | #ifdef CONFIG_PCI_MSI | ||
772 | if (priv->flags & TSI721_USING_MSIX) | ||
773 | err = tsi721_request_msix(mport); | ||
774 | else | ||
775 | #endif | ||
776 | err = request_irq(priv->pdev->irq, tsi721_irqhandler, | ||
777 | (priv->flags & TSI721_USING_MSI) ? 0 : IRQF_SHARED, | ||
778 | DRV_NAME, (void *)mport); | ||
779 | |||
780 | if (err) | ||
781 | dev_err(&priv->pdev->dev, | ||
782 | "Unable to allocate interrupt, Error: %d\n", err); | ||
783 | |||
784 | return err; | ||
785 | } | ||
786 | |||
787 | /** | ||
788 | * tsi721_init_pc2sr_mapping - initializes outbound (PCIe->SRIO) | ||
789 | * translation regions. | ||
790 | * @priv: pointer to tsi721 private data | ||
791 | * | ||
792 | * Disables SREP translation regions. | ||
793 | */ | ||
794 | static void tsi721_init_pc2sr_mapping(struct tsi721_device *priv) | ||
795 | { | ||
796 | int i; | ||
797 | |||
798 | /* Disable all PC2SR translation windows */ | ||
799 | for (i = 0; i < TSI721_OBWIN_NUM; i++) | ||
800 | iowrite32(0, priv->regs + TSI721_OBWINLB(i)); | ||
801 | } | ||
802 | |||
803 | /** | ||
804 | * tsi721_init_sr2pc_mapping - initializes inbound (SRIO->PCIe) | ||
805 | * translation regions. | ||
806 | * @priv: pointer to tsi721 private data | ||
807 | * | ||
808 | * Disables inbound windows. | ||
809 | */ | ||
810 | static void tsi721_init_sr2pc_mapping(struct tsi721_device *priv) | ||
811 | { | ||
812 | int i; | ||
813 | |||
814 | /* Disable all SR2PC inbound windows */ | ||
815 | for (i = 0; i < TSI721_IBWIN_NUM; i++) | ||
816 | iowrite32(0, priv->regs + TSI721_IBWINLB(i)); | ||
817 | } | ||
818 | |||
819 | /** | ||
820 | * tsi721_port_write_init - Inbound port write interface init | ||
821 | * @priv: pointer to tsi721 private data | ||
822 | * | ||
823 | * Initializes inbound port write handler. | ||
824 | * Returns %0 on success or %-ENOMEM on failure. | ||
825 | */ | ||
826 | static int tsi721_port_write_init(struct tsi721_device *priv) | ||
827 | { | ||
828 | priv->pw_discard_count = 0; | ||
829 | INIT_WORK(&priv->pw_work, tsi721_pw_dpc); | ||
830 | spin_lock_init(&priv->pw_fifo_lock); | ||
831 | if (kfifo_alloc(&priv->pw_fifo, | ||
832 | TSI721_RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) { | ||
833 | dev_err(&priv->pdev->dev, "PW FIFO allocation failed\n"); | ||
834 | return -ENOMEM; | ||
835 | } | ||
836 | |||
837 | /* Use reliable port-write capture mode */ | ||
838 | iowrite32(TSI721_RIO_PW_CTL_PWC_REL, priv->regs + TSI721_RIO_PW_CTL); | ||
839 | return 0; | ||
840 | } | ||
841 | |||
842 | static int tsi721_doorbell_init(struct tsi721_device *priv) | ||
843 | { | ||
844 | /* Outbound Doorbells do not require any setup. | ||
845 | * Tsi721 uses dedicated PCI BAR1 to generate doorbells. | ||
846 | * That BAR1 was mapped during the probe routine. | ||
847 | */ | ||
848 | |||
849 | /* Initialize Inbound Doorbell processing DPC and queue */ | ||
850 | priv->db_discard_count = 0; | ||
851 | INIT_WORK(&priv->idb_work, tsi721_db_dpc); | ||
852 | |||
853 | /* Allocate buffer for inbound doorbells queue */ | ||
854 | priv->idb_base = dma_alloc_coherent(&priv->pdev->dev, | ||
855 | IDB_QSIZE * TSI721_IDB_ENTRY_SIZE, | ||
856 | &priv->idb_dma, GFP_KERNEL); | ||
857 | if (!priv->idb_base) | ||
858 | return -ENOMEM; | ||
859 | |||
860 | memset(priv->idb_base, 0, IDB_QSIZE * TSI721_IDB_ENTRY_SIZE); | ||
861 | |||
862 | dev_dbg(&priv->pdev->dev, "Allocated IDB buffer @ %p (phys = %llx)\n", | ||
863 | priv->idb_base, (unsigned long long)priv->idb_dma); | ||
864 | |||
865 | iowrite32(TSI721_IDQ_SIZE_VAL(IDB_QSIZE), | ||
866 | priv->regs + TSI721_IDQ_SIZE(IDB_QUEUE)); | ||
867 | iowrite32(((u64)priv->idb_dma >> 32), | ||
868 | priv->regs + TSI721_IDQ_BASEU(IDB_QUEUE)); | ||
869 | iowrite32(((u64)priv->idb_dma & TSI721_IDQ_BASEL_ADDR), | ||
870 | priv->regs + TSI721_IDQ_BASEL(IDB_QUEUE)); | ||
871 | /* Enable accepting all inbound doorbells */ | ||
872 | iowrite32(0, priv->regs + TSI721_IDQ_MASK(IDB_QUEUE)); | ||
873 | |||
874 | iowrite32(TSI721_IDQ_INIT, priv->regs + TSI721_IDQ_CTL(IDB_QUEUE)); | ||
875 | |||
876 | iowrite32(0, priv->regs + TSI721_IDQ_RP(IDB_QUEUE)); | ||
877 | |||
878 | return 0; | ||
879 | } | ||
880 | |||
881 | static void tsi721_doorbell_free(struct tsi721_device *priv) | ||
882 | { | ||
883 | if (priv->idb_base == NULL) | ||
884 | return; | ||
885 | |||
886 | /* Free buffer allocated for inbound doorbell queue */ | ||
887 | dma_free_coherent(&priv->pdev->dev, IDB_QSIZE * TSI721_IDB_ENTRY_SIZE, | ||
888 | priv->idb_base, priv->idb_dma); | ||
889 | priv->idb_base = NULL; | ||
890 | } | ||
891 | |||
892 | static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum) | ||
893 | { | ||
894 | struct tsi721_dma_desc *bd_ptr; | ||
895 | u64 *sts_ptr; | ||
896 | dma_addr_t bd_phys, sts_phys; | ||
897 | int sts_size; | ||
898 | int bd_num = priv->bdma[chnum].bd_num; | ||
899 | |||
900 | dev_dbg(&priv->pdev->dev, "Init Block DMA Engine, CH%d\n", chnum); | ||
901 | |||
902 | /* | ||
903 | * Initialize DMA channel for maintenance requests | ||
904 | */ | ||
905 | |||
906 | /* Allocate space for DMA descriptors */ | ||
907 | bd_ptr = dma_alloc_coherent(&priv->pdev->dev, | ||
908 | bd_num * sizeof(struct tsi721_dma_desc), | ||
909 | &bd_phys, GFP_KERNEL); | ||
910 | if (!bd_ptr) | ||
911 | return -ENOMEM; | ||
912 | |||
913 | priv->bdma[chnum].bd_phys = bd_phys; | ||
914 | priv->bdma[chnum].bd_base = bd_ptr; | ||
915 | |||
916 | memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc)); | ||
917 | |||
918 | dev_dbg(&priv->pdev->dev, "DMA descriptors @ %p (phys = %llx)\n", | ||
919 | bd_ptr, (unsigned long long)bd_phys); | ||
920 | |||
921 | /* Allocate space for descriptor status FIFO */ | ||
922 | sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ? | ||
923 | bd_num : TSI721_DMA_MINSTSSZ; | ||
924 | sts_size = roundup_pow_of_two(sts_size); | ||
925 | sts_ptr = dma_alloc_coherent(&priv->pdev->dev, | ||
926 | sts_size * sizeof(struct tsi721_dma_sts), | ||
927 | &sts_phys, GFP_KERNEL); | ||
928 | if (!sts_ptr) { | ||
929 | /* Free space allocated for DMA descriptors */ | ||
930 | dma_free_coherent(&priv->pdev->dev, | ||
931 | bd_num * sizeof(struct tsi721_dma_desc), | ||
932 | bd_ptr, bd_phys); | ||
933 | priv->bdma[chnum].bd_base = NULL; | ||
934 | return -ENOMEM; | ||
935 | } | ||
936 | |||
937 | priv->bdma[chnum].sts_phys = sts_phys; | ||
938 | priv->bdma[chnum].sts_base = sts_ptr; | ||
939 | priv->bdma[chnum].sts_size = sts_size; | ||
940 | |||
941 | memset(sts_ptr, 0, sts_size); | ||
942 | |||
943 | dev_dbg(&priv->pdev->dev, | ||
944 | "desc status FIFO @ %p (phys = %llx) size=0x%x\n", | ||
945 | sts_ptr, (unsigned long long)sts_phys, sts_size); | ||
946 | |||
947 | /* Initialize DMA descriptors ring */ | ||
948 | bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29); | ||
949 | bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys & | ||
950 | TSI721_DMAC_DPTRL_MASK); | ||
951 | bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32); | ||
952 | |||
953 | /* Setup DMA descriptor pointers */ | ||
954 | iowrite32(((u64)bd_phys >> 32), | ||
955 | priv->regs + TSI721_DMAC_DPTRH(chnum)); | ||
956 | iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK), | ||
957 | priv->regs + TSI721_DMAC_DPTRL(chnum)); | ||
958 | |||
959 | /* Setup descriptor status FIFO */ | ||
960 | iowrite32(((u64)sts_phys >> 32), | ||
961 | priv->regs + TSI721_DMAC_DSBH(chnum)); | ||
962 | iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK), | ||
963 | priv->regs + TSI721_DMAC_DSBL(chnum)); | ||
964 | iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size), | ||
965 | priv->regs + TSI721_DMAC_DSSZ(chnum)); | ||
966 | |||
967 | /* Clear interrupt bits */ | ||
968 | iowrite32(TSI721_DMAC_INT_ALL, | ||
969 | priv->regs + TSI721_DMAC_INT(chnum)); | ||
970 | |||
971 | ioread32(priv->regs + TSI721_DMAC_INT(chnum)); | ||
972 | |||
973 | /* Toggle DMA channel initialization */ | ||
974 | iowrite32(TSI721_DMAC_CTL_INIT, priv->regs + TSI721_DMAC_CTL(chnum)); | ||
975 | ioread32(priv->regs + TSI721_DMAC_CTL(chnum)); | ||
976 | udelay(10); | ||
977 | |||
978 | return 0; | ||
979 | } | ||
980 | |||
981 | static int tsi721_bdma_ch_free(struct tsi721_device *priv, int chnum) | ||
982 | { | ||
983 | u32 ch_stat; | ||
984 | |||
985 | if (priv->bdma[chnum].bd_base == NULL) | ||
986 | return 0; | ||
987 | |||
988 | /* Check if DMA channel still running */ | ||
989 | ch_stat = ioread32(priv->regs + TSI721_DMAC_STS(chnum)); | ||
990 | if (ch_stat & TSI721_DMAC_STS_RUN) | ||
991 | return -EFAULT; | ||
992 | |||
993 | /* Put DMA channel into init state */ | ||
994 | iowrite32(TSI721_DMAC_CTL_INIT, | ||
995 | priv->regs + TSI721_DMAC_CTL(chnum)); | ||
996 | |||
997 | /* Free space allocated for DMA descriptors */ | ||
998 | dma_free_coherent(&priv->pdev->dev, | ||
999 | priv->bdma[chnum].bd_num * sizeof(struct tsi721_dma_desc), | ||
1000 | priv->bdma[chnum].bd_base, priv->bdma[chnum].bd_phys); | ||
1001 | priv->bdma[chnum].bd_base = NULL; | ||
1002 | |||
1003 | /* Free space allocated for status FIFO */ | ||
1004 | dma_free_coherent(&priv->pdev->dev, | ||
1005 | priv->bdma[chnum].sts_size * sizeof(struct tsi721_dma_sts), | ||
1006 | priv->bdma[chnum].sts_base, priv->bdma[chnum].sts_phys); | ||
1007 | priv->bdma[chnum].sts_base = NULL; | ||
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | static int tsi721_bdma_init(struct tsi721_device *priv) | ||
1012 | { | ||
1013 | /* Initialize BDMA channel allocated for RapidIO maintenance read/write | ||
1014 | * request generation | ||
1015 | */ | ||
1016 | priv->bdma[TSI721_DMACH_MAINT].bd_num = 2; | ||
1017 | if (tsi721_bdma_ch_init(priv, TSI721_DMACH_MAINT)) { | ||
1018 | dev_err(&priv->pdev->dev, "Unable to initialize maintenance DMA" | ||
1019 | " channel %d, aborting\n", TSI721_DMACH_MAINT); | ||
1020 | return -ENOMEM; | ||
1021 | } | ||
1022 | |||
1023 | return 0; | ||
1024 | } | ||
1025 | |||
1026 | static void tsi721_bdma_free(struct tsi721_device *priv) | ||
1027 | { | ||
1028 | tsi721_bdma_ch_free(priv, TSI721_DMACH_MAINT); | ||
1029 | } | ||
1030 | |||
1031 | /* Enable Inbound Messaging Interrupts */ | ||
1032 | static void | ||
1033 | tsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch, | ||
1034 | u32 inte_mask) | ||
1035 | { | ||
1036 | u32 rval; | ||
1037 | |||
1038 | if (!inte_mask) | ||
1039 | return; | ||
1040 | |||
1041 | /* Clear pending Inbound Messaging interrupts */ | ||
1042 | iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch)); | ||
1043 | |||
1044 | /* Enable Inbound Messaging interrupts */ | ||
1045 | rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch)); | ||
1046 | iowrite32(rval | inte_mask, priv->regs + TSI721_IBDMAC_INTE(ch)); | ||
1047 | |||
1048 | if (priv->flags & TSI721_USING_MSIX) | ||
1049 | return; /* Finished if we are in MSI-X mode */ | ||
1050 | |||
1051 | /* | ||
1052 | * For MSI and INTA interrupt signalling we need to enable next levels | ||
1053 | */ | ||
1054 | |||
1055 | /* Enable Device Channel Interrupt */ | ||
1056 | rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE); | ||
1057 | iowrite32(rval | TSI721_INT_IMSG_CHAN(ch), | ||
1058 | priv->regs + TSI721_DEV_CHAN_INTE); | ||
1059 | } | ||
1060 | |||
1061 | /* Disable Inbound Messaging Interrupts */ | ||
1062 | static void | ||
1063 | tsi721_imsg_interrupt_disable(struct tsi721_device *priv, int ch, | ||
1064 | u32 inte_mask) | ||
1065 | { | ||
1066 | u32 rval; | ||
1067 | |||
1068 | if (!inte_mask) | ||
1069 | return; | ||
1070 | |||
1071 | /* Clear pending Inbound Messaging interrupts */ | ||
1072 | iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch)); | ||
1073 | |||
1074 | /* Disable Inbound Messaging interrupts */ | ||
1075 | rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch)); | ||
1076 | rval &= ~inte_mask; | ||
1077 | iowrite32(rval, priv->regs + TSI721_IBDMAC_INTE(ch)); | ||
1078 | |||
1079 | if (priv->flags & TSI721_USING_MSIX) | ||
1080 | return; /* Finished if we are in MSI-X mode */ | ||
1081 | |||
1082 | /* | ||
1083 | * For MSI and INTA interrupt signalling we need to disable next levels | ||
1084 | */ | ||
1085 | |||
1086 | /* Disable Device Channel Interrupt */ | ||
1087 | rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE); | ||
1088 | rval &= ~TSI721_INT_IMSG_CHAN(ch); | ||
1089 | iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE); | ||
1090 | } | ||
1091 | |||
1092 | /* Enable Outbound Messaging interrupts */ | ||
1093 | static void | ||
1094 | tsi721_omsg_interrupt_enable(struct tsi721_device *priv, int ch, | ||
1095 | u32 inte_mask) | ||
1096 | { | ||
1097 | u32 rval; | ||
1098 | |||
1099 | if (!inte_mask) | ||
1100 | return; | ||
1101 | |||
1102 | /* Clear pending Outbound Messaging interrupts */ | ||
1103 | iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch)); | ||
1104 | |||
1105 | /* Enable Outbound Messaging channel interrupts */ | ||
1106 | rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch)); | ||
1107 | iowrite32(rval | inte_mask, priv->regs + TSI721_OBDMAC_INTE(ch)); | ||
1108 | |||
1109 | if (priv->flags & TSI721_USING_MSIX) | ||
1110 | return; /* Finished if we are in MSI-X mode */ | ||
1111 | |||
1112 | /* | ||
1113 | * For MSI and INTA interrupt signalling we need to enable next levels | ||
1114 | */ | ||
1115 | |||
1116 | /* Enable Device Channel Interrupt */ | ||
1117 | rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE); | ||
1118 | iowrite32(rval | TSI721_INT_OMSG_CHAN(ch), | ||
1119 | priv->regs + TSI721_DEV_CHAN_INTE); | ||
1120 | } | ||
1121 | |||
1122 | /* Disable Outbound Messaging interrupts */ | ||
1123 | static void | ||
1124 | tsi721_omsg_interrupt_disable(struct tsi721_device *priv, int ch, | ||
1125 | u32 inte_mask) | ||
1126 | { | ||
1127 | u32 rval; | ||
1128 | |||
1129 | if (!inte_mask) | ||
1130 | return; | ||
1131 | |||
1132 | /* Clear pending Outbound Messaging interrupts */ | ||
1133 | iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch)); | ||
1134 | |||
1135 | /* Disable Outbound Messaging interrupts */ | ||
1136 | rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch)); | ||
1137 | rval &= ~inte_mask; | ||
1138 | iowrite32(rval, priv->regs + TSI721_OBDMAC_INTE(ch)); | ||
1139 | |||
1140 | if (priv->flags & TSI721_USING_MSIX) | ||
1141 | return; /* Finished if we are in MSI-X mode */ | ||
1142 | |||
1143 | /* | ||
1144 | * For MSI and INTA interrupt signalling we need to disable next levels | ||
1145 | */ | ||
1146 | |||
1147 | /* Disable Device Channel Interrupt */ | ||
1148 | rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE); | ||
1149 | rval &= ~TSI721_INT_OMSG_CHAN(ch); | ||
1150 | iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE); | ||
1151 | } | ||
1152 | |||
1153 | /** | ||
1154 | * tsi721_add_outb_message - Add message to the Tsi721 outbound message queue | ||
1155 | * @mport: Master port with outbound message queue | ||
1156 | * @rdev: Target of outbound message | ||
1157 | * @mbox: Outbound mailbox | ||
1158 | * @buffer: Message to add to outbound queue | ||
1159 | * @len: Length of message | ||
1160 | */ | ||
1161 | static int | ||
1162 | tsi721_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox, | ||
1163 | void *buffer, size_t len) | ||
1164 | { | ||
1165 | struct tsi721_device *priv = mport->priv; | ||
1166 | struct tsi721_omsg_desc *desc; | ||
1167 | u32 tx_slot; | ||
1168 | |||
1169 | if (!priv->omsg_init[mbox] || | ||
1170 | len > TSI721_MSG_MAX_SIZE || len < 8) | ||
1171 | return -EINVAL; | ||
1172 | |||
1173 | tx_slot = priv->omsg_ring[mbox].tx_slot; | ||
1174 | |||
1175 | /* Copy copy message into transfer buffer */ | ||
1176 | memcpy(priv->omsg_ring[mbox].omq_base[tx_slot], buffer, len); | ||
1177 | |||
1178 | if (len & 0x7) | ||
1179 | len += 8; | ||
1180 | |||
1181 | /* Build descriptor associated with buffer */ | ||
1182 | desc = priv->omsg_ring[mbox].omd_base; | ||
1183 | desc[tx_slot].type_id = cpu_to_le32((DTYPE4 << 29) | rdev->destid); | ||
1184 | if (tx_slot % 4 == 0) | ||
1185 | desc[tx_slot].type_id |= cpu_to_le32(TSI721_OMD_IOF); | ||
1186 | |||
1187 | desc[tx_slot].msg_info = | ||
1188 | cpu_to_le32((mport->sys_size << 26) | (mbox << 22) | | ||
1189 | (0xe << 12) | (len & 0xff8)); | ||
1190 | desc[tx_slot].bufptr_lo = | ||
1191 | cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] & | ||
1192 | 0xffffffff); | ||
1193 | desc[tx_slot].bufptr_hi = | ||
1194 | cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] >> 32); | ||
1195 | |||
1196 | priv->omsg_ring[mbox].wr_count++; | ||
1197 | |||
1198 | /* Go to next descriptor */ | ||
1199 | if (++priv->omsg_ring[mbox].tx_slot == priv->omsg_ring[mbox].size) { | ||
1200 | priv->omsg_ring[mbox].tx_slot = 0; | ||
1201 | /* Move through the ring link descriptor at the end */ | ||
1202 | priv->omsg_ring[mbox].wr_count++; | ||
1203 | } | ||
1204 | |||
1205 | mb(); | ||
1206 | |||
1207 | /* Set new write count value */ | ||
1208 | iowrite32(priv->omsg_ring[mbox].wr_count, | ||
1209 | priv->regs + TSI721_OBDMAC_DWRCNT(mbox)); | ||
1210 | ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox)); | ||
1211 | |||
1212 | return 0; | ||
1213 | } | ||
1214 | |||
1215 | /** | ||
1216 | * tsi721_omsg_handler - Outbound Message Interrupt Handler | ||
1217 | * @priv: pointer to tsi721 private data | ||
1218 | * @ch: number of OB MSG channel to service | ||
1219 | * | ||
1220 | * Services channel interrupts from outbound messaging engine. | ||
1221 | */ | ||
1222 | static void tsi721_omsg_handler(struct tsi721_device *priv, int ch) | ||
1223 | { | ||
1224 | u32 omsg_int; | ||
1225 | |||
1226 | spin_lock(&priv->omsg_ring[ch].lock); | ||
1227 | |||
1228 | omsg_int = ioread32(priv->regs + TSI721_OBDMAC_INT(ch)); | ||
1229 | |||
1230 | if (omsg_int & TSI721_OBDMAC_INT_ST_FULL) | ||
1231 | dev_info(&priv->pdev->dev, | ||
1232 | "OB MBOX%d: Status FIFO is full\n", ch); | ||
1233 | |||
1234 | if (omsg_int & (TSI721_OBDMAC_INT_DONE | TSI721_OBDMAC_INT_IOF_DONE)) { | ||
1235 | u32 srd_ptr; | ||
1236 | u64 *sts_ptr, last_ptr = 0, prev_ptr = 0; | ||
1237 | int i, j; | ||
1238 | u32 tx_slot; | ||
1239 | |||
1240 | /* | ||
1241 | * Find last successfully processed descriptor | ||
1242 | */ | ||
1243 | |||
1244 | /* Check and clear descriptor status FIFO entries */ | ||
1245 | srd_ptr = priv->omsg_ring[ch].sts_rdptr; | ||
1246 | sts_ptr = priv->omsg_ring[ch].sts_base; | ||
1247 | j = srd_ptr * 8; | ||
1248 | while (sts_ptr[j]) { | ||
1249 | for (i = 0; i < 8 && sts_ptr[j]; i++, j++) { | ||
1250 | prev_ptr = last_ptr; | ||
1251 | last_ptr = le64_to_cpu(sts_ptr[j]); | ||
1252 | sts_ptr[j] = 0; | ||
1253 | } | ||
1254 | |||
1255 | ++srd_ptr; | ||
1256 | srd_ptr %= priv->omsg_ring[ch].sts_size; | ||
1257 | j = srd_ptr * 8; | ||
1258 | } | ||
1259 | |||
1260 | if (last_ptr == 0) | ||
1261 | goto no_sts_update; | ||
1262 | |||
1263 | priv->omsg_ring[ch].sts_rdptr = srd_ptr; | ||
1264 | iowrite32(srd_ptr, priv->regs + TSI721_OBDMAC_DSRP(ch)); | ||
1265 | |||
1266 | if (!priv->mport->outb_msg[ch].mcback) | ||
1267 | goto no_sts_update; | ||
1268 | |||
1269 | /* Inform upper layer about transfer completion */ | ||
1270 | |||
1271 | tx_slot = (last_ptr - (u64)priv->omsg_ring[ch].omd_phys)/ | ||
1272 | sizeof(struct tsi721_omsg_desc); | ||
1273 | |||
1274 | /* | ||
1275 | * Check if this is a Link Descriptor (LD). | ||
1276 | * If yes, ignore LD and use descriptor processed | ||
1277 | * before LD. | ||
1278 | */ | ||
1279 | if (tx_slot == priv->omsg_ring[ch].size) { | ||
1280 | if (prev_ptr) | ||
1281 | tx_slot = (prev_ptr - | ||
1282 | (u64)priv->omsg_ring[ch].omd_phys)/ | ||
1283 | sizeof(struct tsi721_omsg_desc); | ||
1284 | else | ||
1285 | goto no_sts_update; | ||
1286 | } | ||
1287 | |||
1288 | /* Move slot index to the next message to be sent */ | ||
1289 | ++tx_slot; | ||
1290 | if (tx_slot == priv->omsg_ring[ch].size) | ||
1291 | tx_slot = 0; | ||
1292 | BUG_ON(tx_slot >= priv->omsg_ring[ch].size); | ||
1293 | priv->mport->outb_msg[ch].mcback(priv->mport, | ||
1294 | priv->omsg_ring[ch].dev_id, ch, | ||
1295 | tx_slot); | ||
1296 | } | ||
1297 | |||
1298 | no_sts_update: | ||
1299 | |||
1300 | if (omsg_int & TSI721_OBDMAC_INT_ERROR) { | ||
1301 | /* | ||
1302 | * Outbound message operation aborted due to error, | ||
1303 | * reinitialize OB MSG channel | ||
1304 | */ | ||
1305 | |||
1306 | dev_dbg(&priv->pdev->dev, "OB MSG ABORT ch_stat=%x\n", | ||
1307 | ioread32(priv->regs + TSI721_OBDMAC_STS(ch))); | ||
1308 | |||
1309 | iowrite32(TSI721_OBDMAC_INT_ERROR, | ||
1310 | priv->regs + TSI721_OBDMAC_INT(ch)); | ||
1311 | iowrite32(TSI721_OBDMAC_CTL_INIT, | ||
1312 | priv->regs + TSI721_OBDMAC_CTL(ch)); | ||
1313 | ioread32(priv->regs + TSI721_OBDMAC_CTL(ch)); | ||
1314 | |||
1315 | /* Inform upper level to clear all pending tx slots */ | ||
1316 | if (priv->mport->outb_msg[ch].mcback) | ||
1317 | priv->mport->outb_msg[ch].mcback(priv->mport, | ||
1318 | priv->omsg_ring[ch].dev_id, ch, | ||
1319 | priv->omsg_ring[ch].tx_slot); | ||
1320 | /* Synch tx_slot tracking */ | ||
1321 | iowrite32(priv->omsg_ring[ch].tx_slot, | ||
1322 | priv->regs + TSI721_OBDMAC_DRDCNT(ch)); | ||
1323 | ioread32(priv->regs + TSI721_OBDMAC_DRDCNT(ch)); | ||
1324 | priv->omsg_ring[ch].wr_count = priv->omsg_ring[ch].tx_slot; | ||
1325 | priv->omsg_ring[ch].sts_rdptr = 0; | ||
1326 | } | ||
1327 | |||
1328 | /* Clear channel interrupts */ | ||
1329 | iowrite32(omsg_int, priv->regs + TSI721_OBDMAC_INT(ch)); | ||
1330 | |||
1331 | if (!(priv->flags & TSI721_USING_MSIX)) { | ||
1332 | u32 ch_inte; | ||
1333 | |||
1334 | /* Re-enable channel interrupts */ | ||
1335 | ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE); | ||
1336 | ch_inte |= TSI721_INT_OMSG_CHAN(ch); | ||
1337 | iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE); | ||
1338 | } | ||
1339 | |||
1340 | spin_unlock(&priv->omsg_ring[ch].lock); | ||
1341 | } | ||
1342 | |||
1343 | /** | ||
1344 | * tsi721_open_outb_mbox - Initialize Tsi721 outbound mailbox | ||
1345 | * @mport: Master port implementing Outbound Messaging Engine | ||
1346 | * @dev_id: Device specific pointer to pass on event | ||
1347 | * @mbox: Mailbox to open | ||
1348 | * @entries: Number of entries in the outbound mailbox ring | ||
1349 | */ | ||
1350 | static int tsi721_open_outb_mbox(struct rio_mport *mport, void *dev_id, | ||
1351 | int mbox, int entries) | ||
1352 | { | ||
1353 | struct tsi721_device *priv = mport->priv; | ||
1354 | struct tsi721_omsg_desc *bd_ptr; | ||
1355 | int i, rc = 0; | ||
1356 | |||
1357 | if ((entries < TSI721_OMSGD_MIN_RING_SIZE) || | ||
1358 | (entries > (TSI721_OMSGD_RING_SIZE)) || | ||
1359 | (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) { | ||
1360 | rc = -EINVAL; | ||
1361 | goto out; | ||
1362 | } | ||
1363 | |||
1364 | priv->omsg_ring[mbox].dev_id = dev_id; | ||
1365 | priv->omsg_ring[mbox].size = entries; | ||
1366 | priv->omsg_ring[mbox].sts_rdptr = 0; | ||
1367 | spin_lock_init(&priv->omsg_ring[mbox].lock); | ||
1368 | |||
1369 | /* Outbound Msg Buffer allocation based on | ||
1370 | the number of maximum descriptor entries */ | ||
1371 | for (i = 0; i < entries; i++) { | ||
1372 | priv->omsg_ring[mbox].omq_base[i] = | ||
1373 | dma_alloc_coherent( | ||
1374 | &priv->pdev->dev, TSI721_MSG_BUFFER_SIZE, | ||
1375 | &priv->omsg_ring[mbox].omq_phys[i], | ||
1376 | GFP_KERNEL); | ||
1377 | if (priv->omsg_ring[mbox].omq_base[i] == NULL) { | ||
1378 | dev_dbg(&priv->pdev->dev, | ||
1379 | "Unable to allocate OB MSG data buffer for" | ||
1380 | " MBOX%d\n", mbox); | ||
1381 | rc = -ENOMEM; | ||
1382 | goto out_buf; | ||
1383 | } | ||
1384 | } | ||
1385 | |||
1386 | /* Outbound message descriptor allocation */ | ||
1387 | priv->omsg_ring[mbox].omd_base = dma_alloc_coherent( | ||
1388 | &priv->pdev->dev, | ||
1389 | (entries + 1) * sizeof(struct tsi721_omsg_desc), | ||
1390 | &priv->omsg_ring[mbox].omd_phys, GFP_KERNEL); | ||
1391 | if (priv->omsg_ring[mbox].omd_base == NULL) { | ||
1392 | dev_dbg(&priv->pdev->dev, | ||
1393 | "Unable to allocate OB MSG descriptor memory " | ||
1394 | "for MBOX%d\n", mbox); | ||
1395 | rc = -ENOMEM; | ||
1396 | goto out_buf; | ||
1397 | } | ||
1398 | |||
1399 | priv->omsg_ring[mbox].tx_slot = 0; | ||
1400 | |||
1401 | /* Outbound message descriptor status FIFO allocation */ | ||
1402 | priv->omsg_ring[mbox].sts_size = roundup_pow_of_two(entries + 1); | ||
1403 | priv->omsg_ring[mbox].sts_base = dma_alloc_coherent(&priv->pdev->dev, | ||
1404 | priv->omsg_ring[mbox].sts_size * | ||
1405 | sizeof(struct tsi721_dma_sts), | ||
1406 | &priv->omsg_ring[mbox].sts_phys, GFP_KERNEL); | ||
1407 | if (priv->omsg_ring[mbox].sts_base == NULL) { | ||
1408 | dev_dbg(&priv->pdev->dev, | ||
1409 | "Unable to allocate OB MSG descriptor status FIFO " | ||
1410 | "for MBOX%d\n", mbox); | ||
1411 | rc = -ENOMEM; | ||
1412 | goto out_desc; | ||
1413 | } | ||
1414 | |||
1415 | memset(priv->omsg_ring[mbox].sts_base, 0, | ||
1416 | entries * sizeof(struct tsi721_dma_sts)); | ||
1417 | |||
1418 | /* | ||
1419 | * Configure Outbound Messaging Engine | ||
1420 | */ | ||
1421 | |||
1422 | /* Setup Outbound Message descriptor pointer */ | ||
1423 | iowrite32(((u64)priv->omsg_ring[mbox].omd_phys >> 32), | ||
1424 | priv->regs + TSI721_OBDMAC_DPTRH(mbox)); | ||
1425 | iowrite32(((u64)priv->omsg_ring[mbox].omd_phys & | ||
1426 | TSI721_OBDMAC_DPTRL_MASK), | ||
1427 | priv->regs + TSI721_OBDMAC_DPTRL(mbox)); | ||
1428 | |||
1429 | /* Setup Outbound Message descriptor status FIFO */ | ||
1430 | iowrite32(((u64)priv->omsg_ring[mbox].sts_phys >> 32), | ||
1431 | priv->regs + TSI721_OBDMAC_DSBH(mbox)); | ||
1432 | iowrite32(((u64)priv->omsg_ring[mbox].sts_phys & | ||
1433 | TSI721_OBDMAC_DSBL_MASK), | ||
1434 | priv->regs + TSI721_OBDMAC_DSBL(mbox)); | ||
1435 | iowrite32(TSI721_DMAC_DSSZ_SIZE(priv->omsg_ring[mbox].sts_size), | ||
1436 | priv->regs + (u32)TSI721_OBDMAC_DSSZ(mbox)); | ||
1437 | |||
1438 | /* Enable interrupts */ | ||
1439 | |||
1440 | #ifdef CONFIG_PCI_MSI | ||
1441 | if (priv->flags & TSI721_USING_MSIX) { | ||
1442 | /* Request interrupt service if we are in MSI-X mode */ | ||
1443 | rc = request_irq( | ||
1444 | priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector, | ||
1445 | tsi721_omsg_msix, 0, | ||
1446 | priv->msix[TSI721_VECT_OMB0_DONE + mbox].irq_name, | ||
1447 | (void *)mport); | ||
1448 | |||
1449 | if (rc) { | ||
1450 | dev_dbg(&priv->pdev->dev, | ||
1451 | "Unable to allocate MSI-X interrupt for " | ||
1452 | "OBOX%d-DONE\n", mbox); | ||
1453 | goto out_stat; | ||
1454 | } | ||
1455 | |||
1456 | rc = request_irq(priv->msix[TSI721_VECT_OMB0_INT + mbox].vector, | ||
1457 | tsi721_omsg_msix, 0, | ||
1458 | priv->msix[TSI721_VECT_OMB0_INT + mbox].irq_name, | ||
1459 | (void *)mport); | ||
1460 | |||
1461 | if (rc) { | ||
1462 | dev_dbg(&priv->pdev->dev, | ||
1463 | "Unable to allocate MSI-X interrupt for " | ||
1464 | "MBOX%d-INT\n", mbox); | ||
1465 | free_irq( | ||
1466 | priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector, | ||
1467 | (void *)mport); | ||
1468 | goto out_stat; | ||
1469 | } | ||
1470 | } | ||
1471 | #endif /* CONFIG_PCI_MSI */ | ||
1472 | |||
1473 | tsi721_omsg_interrupt_enable(priv, mbox, TSI721_OBDMAC_INT_ALL); | ||
1474 | |||
1475 | /* Initialize Outbound Message descriptors ring */ | ||
1476 | bd_ptr = priv->omsg_ring[mbox].omd_base; | ||
1477 | bd_ptr[entries].type_id = cpu_to_le32(DTYPE5 << 29); | ||
1478 | bd_ptr[entries].msg_info = 0; | ||
1479 | bd_ptr[entries].next_lo = | ||
1480 | cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys & | ||
1481 | TSI721_OBDMAC_DPTRL_MASK); | ||
1482 | bd_ptr[entries].next_hi = | ||
1483 | cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys >> 32); | ||
1484 | priv->omsg_ring[mbox].wr_count = 0; | ||
1485 | mb(); | ||
1486 | |||
1487 | /* Initialize Outbound Message engine */ | ||
1488 | iowrite32(TSI721_OBDMAC_CTL_INIT, priv->regs + TSI721_OBDMAC_CTL(mbox)); | ||
1489 | ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox)); | ||
1490 | udelay(10); | ||
1491 | |||
1492 | priv->omsg_init[mbox] = 1; | ||
1493 | |||
1494 | return 0; | ||
1495 | |||
1496 | #ifdef CONFIG_PCI_MSI | ||
1497 | out_stat: | ||
1498 | dma_free_coherent(&priv->pdev->dev, | ||
1499 | priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts), | ||
1500 | priv->omsg_ring[mbox].sts_base, | ||
1501 | priv->omsg_ring[mbox].sts_phys); | ||
1502 | |||
1503 | priv->omsg_ring[mbox].sts_base = NULL; | ||
1504 | #endif /* CONFIG_PCI_MSI */ | ||
1505 | |||
1506 | out_desc: | ||
1507 | dma_free_coherent(&priv->pdev->dev, | ||
1508 | (entries + 1) * sizeof(struct tsi721_omsg_desc), | ||
1509 | priv->omsg_ring[mbox].omd_base, | ||
1510 | priv->omsg_ring[mbox].omd_phys); | ||
1511 | |||
1512 | priv->omsg_ring[mbox].omd_base = NULL; | ||
1513 | |||
1514 | out_buf: | ||
1515 | for (i = 0; i < priv->omsg_ring[mbox].size; i++) { | ||
1516 | if (priv->omsg_ring[mbox].omq_base[i]) { | ||
1517 | dma_free_coherent(&priv->pdev->dev, | ||
1518 | TSI721_MSG_BUFFER_SIZE, | ||
1519 | priv->omsg_ring[mbox].omq_base[i], | ||
1520 | priv->omsg_ring[mbox].omq_phys[i]); | ||
1521 | |||
1522 | priv->omsg_ring[mbox].omq_base[i] = NULL; | ||
1523 | } | ||
1524 | } | ||
1525 | |||
1526 | out: | ||
1527 | return rc; | ||
1528 | } | ||
1529 | |||
1530 | /** | ||
1531 | * tsi721_close_outb_mbox - Close Tsi721 outbound mailbox | ||
1532 | * @mport: Master port implementing the outbound message unit | ||
1533 | * @mbox: Mailbox to close | ||
1534 | */ | ||
1535 | static void tsi721_close_outb_mbox(struct rio_mport *mport, int mbox) | ||
1536 | { | ||
1537 | struct tsi721_device *priv = mport->priv; | ||
1538 | u32 i; | ||
1539 | |||
1540 | if (!priv->omsg_init[mbox]) | ||
1541 | return; | ||
1542 | priv->omsg_init[mbox] = 0; | ||
1543 | |||
1544 | /* Disable Interrupts */ | ||
1545 | |||
1546 | tsi721_omsg_interrupt_disable(priv, mbox, TSI721_OBDMAC_INT_ALL); | ||
1547 | |||
1548 | #ifdef CONFIG_PCI_MSI | ||
1549 | if (priv->flags & TSI721_USING_MSIX) { | ||
1550 | free_irq(priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector, | ||
1551 | (void *)mport); | ||
1552 | free_irq(priv->msix[TSI721_VECT_OMB0_INT + mbox].vector, | ||
1553 | (void *)mport); | ||
1554 | } | ||
1555 | #endif /* CONFIG_PCI_MSI */ | ||
1556 | |||
1557 | /* Free OMSG Descriptor Status FIFO */ | ||
1558 | dma_free_coherent(&priv->pdev->dev, | ||
1559 | priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts), | ||
1560 | priv->omsg_ring[mbox].sts_base, | ||
1561 | priv->omsg_ring[mbox].sts_phys); | ||
1562 | |||
1563 | priv->omsg_ring[mbox].sts_base = NULL; | ||
1564 | |||
1565 | /* Free OMSG descriptors */ | ||
1566 | dma_free_coherent(&priv->pdev->dev, | ||
1567 | (priv->omsg_ring[mbox].size + 1) * | ||
1568 | sizeof(struct tsi721_omsg_desc), | ||
1569 | priv->omsg_ring[mbox].omd_base, | ||
1570 | priv->omsg_ring[mbox].omd_phys); | ||
1571 | |||
1572 | priv->omsg_ring[mbox].omd_base = NULL; | ||
1573 | |||
1574 | /* Free message buffers */ | ||
1575 | for (i = 0; i < priv->omsg_ring[mbox].size; i++) { | ||
1576 | if (priv->omsg_ring[mbox].omq_base[i]) { | ||
1577 | dma_free_coherent(&priv->pdev->dev, | ||
1578 | TSI721_MSG_BUFFER_SIZE, | ||
1579 | priv->omsg_ring[mbox].omq_base[i], | ||
1580 | priv->omsg_ring[mbox].omq_phys[i]); | ||
1581 | |||
1582 | priv->omsg_ring[mbox].omq_base[i] = NULL; | ||
1583 | } | ||
1584 | } | ||
1585 | } | ||
1586 | |||
1587 | /** | ||
1588 | * tsi721_imsg_handler - Inbound Message Interrupt Handler | ||
1589 | * @priv: pointer to tsi721 private data | ||
1590 | * @ch: inbound message channel number to service | ||
1591 | * | ||
1592 | * Services channel interrupts from inbound messaging engine. | ||
1593 | */ | ||
1594 | static void tsi721_imsg_handler(struct tsi721_device *priv, int ch) | ||
1595 | { | ||
1596 | u32 mbox = ch - 4; | ||
1597 | u32 imsg_int; | ||
1598 | |||
1599 | spin_lock(&priv->imsg_ring[mbox].lock); | ||
1600 | |||
1601 | imsg_int = ioread32(priv->regs + TSI721_IBDMAC_INT(ch)); | ||
1602 | |||
1603 | if (imsg_int & TSI721_IBDMAC_INT_SRTO) | ||
1604 | dev_info(&priv->pdev->dev, "IB MBOX%d SRIO timeout\n", | ||
1605 | mbox); | ||
1606 | |||
1607 | if (imsg_int & TSI721_IBDMAC_INT_PC_ERROR) | ||
1608 | dev_info(&priv->pdev->dev, "IB MBOX%d PCIe error\n", | ||
1609 | mbox); | ||
1610 | |||
1611 | if (imsg_int & TSI721_IBDMAC_INT_FQ_LOW) | ||
1612 | dev_info(&priv->pdev->dev, | ||
1613 | "IB MBOX%d IB free queue low\n", mbox); | ||
1614 | |||
1615 | /* Clear IB channel interrupts */ | ||
1616 | iowrite32(imsg_int, priv->regs + TSI721_IBDMAC_INT(ch)); | ||
1617 | |||
1618 | /* If an IB Msg is received notify the upper layer */ | ||
1619 | if (imsg_int & TSI721_IBDMAC_INT_DQ_RCV && | ||
1620 | priv->mport->inb_msg[mbox].mcback) | ||
1621 | priv->mport->inb_msg[mbox].mcback(priv->mport, | ||
1622 | priv->imsg_ring[mbox].dev_id, mbox, -1); | ||
1623 | |||
1624 | if (!(priv->flags & TSI721_USING_MSIX)) { | ||
1625 | u32 ch_inte; | ||
1626 | |||
1627 | /* Re-enable channel interrupts */ | ||
1628 | ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE); | ||
1629 | ch_inte |= TSI721_INT_IMSG_CHAN(ch); | ||
1630 | iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE); | ||
1631 | } | ||
1632 | |||
1633 | spin_unlock(&priv->imsg_ring[mbox].lock); | ||
1634 | } | ||
1635 | |||
1636 | /** | ||
1637 | * tsi721_open_inb_mbox - Initialize Tsi721 inbound mailbox | ||
1638 | * @mport: Master port implementing the Inbound Messaging Engine | ||
1639 | * @dev_id: Device specific pointer to pass on event | ||
1640 | * @mbox: Mailbox to open | ||
1641 | * @entries: Number of entries in the inbound mailbox ring | ||
1642 | */ | ||
1643 | static int tsi721_open_inb_mbox(struct rio_mport *mport, void *dev_id, | ||
1644 | int mbox, int entries) | ||
1645 | { | ||
1646 | struct tsi721_device *priv = mport->priv; | ||
1647 | int ch = mbox + 4; | ||
1648 | int i; | ||
1649 | u64 *free_ptr; | ||
1650 | int rc = 0; | ||
1651 | |||
1652 | if ((entries < TSI721_IMSGD_MIN_RING_SIZE) || | ||
1653 | (entries > TSI721_IMSGD_RING_SIZE) || | ||
1654 | (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) { | ||
1655 | rc = -EINVAL; | ||
1656 | goto out; | ||
1657 | } | ||
1658 | |||
1659 | /* Initialize IB Messaging Ring */ | ||
1660 | priv->imsg_ring[mbox].dev_id = dev_id; | ||
1661 | priv->imsg_ring[mbox].size = entries; | ||
1662 | priv->imsg_ring[mbox].rx_slot = 0; | ||
1663 | priv->imsg_ring[mbox].desc_rdptr = 0; | ||
1664 | priv->imsg_ring[mbox].fq_wrptr = 0; | ||
1665 | for (i = 0; i < priv->imsg_ring[mbox].size; i++) | ||
1666 | priv->imsg_ring[mbox].imq_base[i] = NULL; | ||
1667 | spin_lock_init(&priv->imsg_ring[mbox].lock); | ||
1668 | |||
1669 | /* Allocate buffers for incoming messages */ | ||
1670 | priv->imsg_ring[mbox].buf_base = | ||
1671 | dma_alloc_coherent(&priv->pdev->dev, | ||
1672 | entries * TSI721_MSG_BUFFER_SIZE, | ||
1673 | &priv->imsg_ring[mbox].buf_phys, | ||
1674 | GFP_KERNEL); | ||
1675 | |||
1676 | if (priv->imsg_ring[mbox].buf_base == NULL) { | ||
1677 | dev_err(&priv->pdev->dev, | ||
1678 | "Failed to allocate buffers for IB MBOX%d\n", mbox); | ||
1679 | rc = -ENOMEM; | ||
1680 | goto out; | ||
1681 | } | ||
1682 | |||
1683 | /* Allocate memory for circular free list */ | ||
1684 | priv->imsg_ring[mbox].imfq_base = | ||
1685 | dma_alloc_coherent(&priv->pdev->dev, | ||
1686 | entries * 8, | ||
1687 | &priv->imsg_ring[mbox].imfq_phys, | ||
1688 | GFP_KERNEL); | ||
1689 | |||
1690 | if (priv->imsg_ring[mbox].imfq_base == NULL) { | ||
1691 | dev_err(&priv->pdev->dev, | ||
1692 | "Failed to allocate free queue for IB MBOX%d\n", mbox); | ||
1693 | rc = -ENOMEM; | ||
1694 | goto out_buf; | ||
1695 | } | ||
1696 | |||
1697 | /* Allocate memory for Inbound message descriptors */ | ||
1698 | priv->imsg_ring[mbox].imd_base = | ||
1699 | dma_alloc_coherent(&priv->pdev->dev, | ||
1700 | entries * sizeof(struct tsi721_imsg_desc), | ||
1701 | &priv->imsg_ring[mbox].imd_phys, GFP_KERNEL); | ||
1702 | |||
1703 | if (priv->imsg_ring[mbox].imd_base == NULL) { | ||
1704 | dev_err(&priv->pdev->dev, | ||
1705 | "Failed to allocate descriptor memory for IB MBOX%d\n", | ||
1706 | mbox); | ||
1707 | rc = -ENOMEM; | ||
1708 | goto out_dma; | ||
1709 | } | ||
1710 | |||
1711 | /* Fill free buffer pointer list */ | ||
1712 | free_ptr = priv->imsg_ring[mbox].imfq_base; | ||
1713 | for (i = 0; i < entries; i++) | ||
1714 | free_ptr[i] = cpu_to_le64( | ||
1715 | (u64)(priv->imsg_ring[mbox].buf_phys) + | ||
1716 | i * 0x1000); | ||
1717 | |||
1718 | mb(); | ||
1719 | |||
1720 | /* | ||
1721 | * For mapping of inbound SRIO Messages into appropriate queues we need | ||
1722 | * to set Inbound Device ID register in the messaging engine. We do it | ||
1723 | * once when first inbound mailbox is requested. | ||
1724 | */ | ||
1725 | if (!(priv->flags & TSI721_IMSGID_SET)) { | ||
1726 | iowrite32((u32)priv->mport->host_deviceid, | ||
1727 | priv->regs + TSI721_IB_DEVID); | ||
1728 | priv->flags |= TSI721_IMSGID_SET; | ||
1729 | } | ||
1730 | |||
1731 | /* | ||
1732 | * Configure Inbound Messaging channel (ch = mbox + 4) | ||
1733 | */ | ||
1734 | |||
1735 | /* Setup Inbound Message free queue */ | ||
1736 | iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys >> 32), | ||
1737 | priv->regs + TSI721_IBDMAC_FQBH(ch)); | ||
1738 | iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys & | ||
1739 | TSI721_IBDMAC_FQBL_MASK), | ||
1740 | priv->regs+TSI721_IBDMAC_FQBL(ch)); | ||
1741 | iowrite32(TSI721_DMAC_DSSZ_SIZE(entries), | ||
1742 | priv->regs + TSI721_IBDMAC_FQSZ(ch)); | ||
1743 | |||
1744 | /* Setup Inbound Message descriptor queue */ | ||
1745 | iowrite32(((u64)priv->imsg_ring[mbox].imd_phys >> 32), | ||
1746 | priv->regs + TSI721_IBDMAC_DQBH(ch)); | ||
1747 | iowrite32(((u32)priv->imsg_ring[mbox].imd_phys & | ||
1748 | (u32)TSI721_IBDMAC_DQBL_MASK), | ||
1749 | priv->regs+TSI721_IBDMAC_DQBL(ch)); | ||
1750 | iowrite32(TSI721_DMAC_DSSZ_SIZE(entries), | ||
1751 | priv->regs + TSI721_IBDMAC_DQSZ(ch)); | ||
1752 | |||
1753 | /* Enable interrupts */ | ||
1754 | |||
1755 | #ifdef CONFIG_PCI_MSI | ||
1756 | if (priv->flags & TSI721_USING_MSIX) { | ||
1757 | /* Request interrupt service if we are in MSI-X mode */ | ||
1758 | rc = request_irq(priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector, | ||
1759 | tsi721_imsg_msix, 0, | ||
1760 | priv->msix[TSI721_VECT_IMB0_RCV + mbox].irq_name, | ||
1761 | (void *)mport); | ||
1762 | |||
1763 | if (rc) { | ||
1764 | dev_dbg(&priv->pdev->dev, | ||
1765 | "Unable to allocate MSI-X interrupt for " | ||
1766 | "IBOX%d-DONE\n", mbox); | ||
1767 | goto out_desc; | ||
1768 | } | ||
1769 | |||
1770 | rc = request_irq(priv->msix[TSI721_VECT_IMB0_INT + mbox].vector, | ||
1771 | tsi721_imsg_msix, 0, | ||
1772 | priv->msix[TSI721_VECT_IMB0_INT + mbox].irq_name, | ||
1773 | (void *)mport); | ||
1774 | |||
1775 | if (rc) { | ||
1776 | dev_dbg(&priv->pdev->dev, | ||
1777 | "Unable to allocate MSI-X interrupt for " | ||
1778 | "IBOX%d-INT\n", mbox); | ||
1779 | free_irq( | ||
1780 | priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector, | ||
1781 | (void *)mport); | ||
1782 | goto out_desc; | ||
1783 | } | ||
1784 | } | ||
1785 | #endif /* CONFIG_PCI_MSI */ | ||
1786 | |||
1787 | tsi721_imsg_interrupt_enable(priv, ch, TSI721_IBDMAC_INT_ALL); | ||
1788 | |||
1789 | /* Initialize Inbound Message Engine */ | ||
1790 | iowrite32(TSI721_IBDMAC_CTL_INIT, priv->regs + TSI721_IBDMAC_CTL(ch)); | ||
1791 | ioread32(priv->regs + TSI721_IBDMAC_CTL(ch)); | ||
1792 | udelay(10); | ||
1793 | priv->imsg_ring[mbox].fq_wrptr = entries - 1; | ||
1794 | iowrite32(entries - 1, priv->regs + TSI721_IBDMAC_FQWP(ch)); | ||
1795 | |||
1796 | priv->imsg_init[mbox] = 1; | ||
1797 | return 0; | ||
1798 | |||
1799 | #ifdef CONFIG_PCI_MSI | ||
1800 | out_desc: | ||
1801 | dma_free_coherent(&priv->pdev->dev, | ||
1802 | priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc), | ||
1803 | priv->imsg_ring[mbox].imd_base, | ||
1804 | priv->imsg_ring[mbox].imd_phys); | ||
1805 | |||
1806 | priv->imsg_ring[mbox].imd_base = NULL; | ||
1807 | #endif /* CONFIG_PCI_MSI */ | ||
1808 | |||
1809 | out_dma: | ||
1810 | dma_free_coherent(&priv->pdev->dev, | ||
1811 | priv->imsg_ring[mbox].size * 8, | ||
1812 | priv->imsg_ring[mbox].imfq_base, | ||
1813 | priv->imsg_ring[mbox].imfq_phys); | ||
1814 | |||
1815 | priv->imsg_ring[mbox].imfq_base = NULL; | ||
1816 | |||
1817 | out_buf: | ||
1818 | dma_free_coherent(&priv->pdev->dev, | ||
1819 | priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE, | ||
1820 | priv->imsg_ring[mbox].buf_base, | ||
1821 | priv->imsg_ring[mbox].buf_phys); | ||
1822 | |||
1823 | priv->imsg_ring[mbox].buf_base = NULL; | ||
1824 | |||
1825 | out: | ||
1826 | return rc; | ||
1827 | } | ||
1828 | |||
1829 | /** | ||
1830 | * tsi721_close_inb_mbox - Shut down Tsi721 inbound mailbox | ||
1831 | * @mport: Master port implementing the Inbound Messaging Engine | ||
1832 | * @mbox: Mailbox to close | ||
1833 | */ | ||
1834 | static void tsi721_close_inb_mbox(struct rio_mport *mport, int mbox) | ||
1835 | { | ||
1836 | struct tsi721_device *priv = mport->priv; | ||
1837 | u32 rx_slot; | ||
1838 | int ch = mbox + 4; | ||
1839 | |||
1840 | if (!priv->imsg_init[mbox]) /* mbox isn't initialized yet */ | ||
1841 | return; | ||
1842 | priv->imsg_init[mbox] = 0; | ||
1843 | |||
1844 | /* Disable Inbound Messaging Engine */ | ||
1845 | |||
1846 | /* Disable Interrupts */ | ||
1847 | tsi721_imsg_interrupt_disable(priv, ch, TSI721_OBDMAC_INT_MASK); | ||
1848 | |||
1849 | #ifdef CONFIG_PCI_MSI | ||
1850 | if (priv->flags & TSI721_USING_MSIX) { | ||
1851 | free_irq(priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector, | ||
1852 | (void *)mport); | ||
1853 | free_irq(priv->msix[TSI721_VECT_IMB0_INT + mbox].vector, | ||
1854 | (void *)mport); | ||
1855 | } | ||
1856 | #endif /* CONFIG_PCI_MSI */ | ||
1857 | |||
1858 | /* Clear Inbound Buffer Queue */ | ||
1859 | for (rx_slot = 0; rx_slot < priv->imsg_ring[mbox].size; rx_slot++) | ||
1860 | priv->imsg_ring[mbox].imq_base[rx_slot] = NULL; | ||
1861 | |||
1862 | /* Free memory allocated for message buffers */ | ||
1863 | dma_free_coherent(&priv->pdev->dev, | ||
1864 | priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE, | ||
1865 | priv->imsg_ring[mbox].buf_base, | ||
1866 | priv->imsg_ring[mbox].buf_phys); | ||
1867 | |||
1868 | priv->imsg_ring[mbox].buf_base = NULL; | ||
1869 | |||
1870 | /* Free memory allocated for free pointr list */ | ||
1871 | dma_free_coherent(&priv->pdev->dev, | ||
1872 | priv->imsg_ring[mbox].size * 8, | ||
1873 | priv->imsg_ring[mbox].imfq_base, | ||
1874 | priv->imsg_ring[mbox].imfq_phys); | ||
1875 | |||
1876 | priv->imsg_ring[mbox].imfq_base = NULL; | ||
1877 | |||
1878 | /* Free memory allocated for RX descriptors */ | ||
1879 | dma_free_coherent(&priv->pdev->dev, | ||
1880 | priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc), | ||
1881 | priv->imsg_ring[mbox].imd_base, | ||
1882 | priv->imsg_ring[mbox].imd_phys); | ||
1883 | |||
1884 | priv->imsg_ring[mbox].imd_base = NULL; | ||
1885 | } | ||
1886 | |||
1887 | /** | ||
1888 | * tsi721_add_inb_buffer - Add buffer to the Tsi721 inbound message queue | ||
1889 | * @mport: Master port implementing the Inbound Messaging Engine | ||
1890 | * @mbox: Inbound mailbox number | ||
1891 | * @buf: Buffer to add to inbound queue | ||
1892 | */ | ||
1893 | static int tsi721_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf) | ||
1894 | { | ||
1895 | struct tsi721_device *priv = mport->priv; | ||
1896 | u32 rx_slot; | ||
1897 | int rc = 0; | ||
1898 | |||
1899 | rx_slot = priv->imsg_ring[mbox].rx_slot; | ||
1900 | if (priv->imsg_ring[mbox].imq_base[rx_slot]) { | ||
1901 | dev_err(&priv->pdev->dev, | ||
1902 | "Error adding inbound buffer %d, buffer exists\n", | ||
1903 | rx_slot); | ||
1904 | rc = -EINVAL; | ||
1905 | goto out; | ||
1906 | } | ||
1907 | |||
1908 | priv->imsg_ring[mbox].imq_base[rx_slot] = buf; | ||
1909 | |||
1910 | if (++priv->imsg_ring[mbox].rx_slot == priv->imsg_ring[mbox].size) | ||
1911 | priv->imsg_ring[mbox].rx_slot = 0; | ||
1912 | |||
1913 | out: | ||
1914 | return rc; | ||
1915 | } | ||
1916 | |||
1917 | /** | ||
1918 | * tsi721_get_inb_message - Fetch inbound message from the Tsi721 MSG Queue | ||
1919 | * @mport: Master port implementing the Inbound Messaging Engine | ||
1920 | * @mbox: Inbound mailbox number | ||
1921 | * | ||
1922 | * Returns pointer to the message on success or NULL on failure. | ||
1923 | */ | ||
1924 | static void *tsi721_get_inb_message(struct rio_mport *mport, int mbox) | ||
1925 | { | ||
1926 | struct tsi721_device *priv = mport->priv; | ||
1927 | struct tsi721_imsg_desc *desc; | ||
1928 | u32 rx_slot; | ||
1929 | void *rx_virt = NULL; | ||
1930 | u64 rx_phys; | ||
1931 | void *buf = NULL; | ||
1932 | u64 *free_ptr; | ||
1933 | int ch = mbox + 4; | ||
1934 | int msg_size; | ||
1935 | |||
1936 | if (!priv->imsg_init[mbox]) | ||
1937 | return NULL; | ||
1938 | |||
1939 | desc = priv->imsg_ring[mbox].imd_base; | ||
1940 | desc += priv->imsg_ring[mbox].desc_rdptr; | ||
1941 | |||
1942 | if (!(le32_to_cpu(desc->msg_info) & TSI721_IMD_HO)) | ||
1943 | goto out; | ||
1944 | |||
1945 | rx_slot = priv->imsg_ring[mbox].rx_slot; | ||
1946 | while (priv->imsg_ring[mbox].imq_base[rx_slot] == NULL) { | ||
1947 | if (++rx_slot == priv->imsg_ring[mbox].size) | ||
1948 | rx_slot = 0; | ||
1949 | } | ||
1950 | |||
1951 | rx_phys = ((u64)le32_to_cpu(desc->bufptr_hi) << 32) | | ||
1952 | le32_to_cpu(desc->bufptr_lo); | ||
1953 | |||
1954 | rx_virt = priv->imsg_ring[mbox].buf_base + | ||
1955 | (rx_phys - (u64)priv->imsg_ring[mbox].buf_phys); | ||
1956 | |||
1957 | buf = priv->imsg_ring[mbox].imq_base[rx_slot]; | ||
1958 | msg_size = le32_to_cpu(desc->msg_info) & TSI721_IMD_BCOUNT; | ||
1959 | if (msg_size == 0) | ||
1960 | msg_size = RIO_MAX_MSG_SIZE; | ||
1961 | |||
1962 | memcpy(buf, rx_virt, msg_size); | ||
1963 | priv->imsg_ring[mbox].imq_base[rx_slot] = NULL; | ||
1964 | |||
1965 | desc->msg_info &= cpu_to_le32(~TSI721_IMD_HO); | ||
1966 | if (++priv->imsg_ring[mbox].desc_rdptr == priv->imsg_ring[mbox].size) | ||
1967 | priv->imsg_ring[mbox].desc_rdptr = 0; | ||
1968 | |||
1969 | iowrite32(priv->imsg_ring[mbox].desc_rdptr, | ||
1970 | priv->regs + TSI721_IBDMAC_DQRP(ch)); | ||
1971 | |||
1972 | /* Return free buffer into the pointer list */ | ||
1973 | free_ptr = priv->imsg_ring[mbox].imfq_base; | ||
1974 | free_ptr[priv->imsg_ring[mbox].fq_wrptr] = cpu_to_le64(rx_phys); | ||
1975 | |||
1976 | if (++priv->imsg_ring[mbox].fq_wrptr == priv->imsg_ring[mbox].size) | ||
1977 | priv->imsg_ring[mbox].fq_wrptr = 0; | ||
1978 | |||
1979 | iowrite32(priv->imsg_ring[mbox].fq_wrptr, | ||
1980 | priv->regs + TSI721_IBDMAC_FQWP(ch)); | ||
1981 | out: | ||
1982 | return buf; | ||
1983 | } | ||
1984 | |||
1985 | /** | ||
1986 | * tsi721_messages_init - Initialization of Messaging Engine | ||
1987 | * @priv: pointer to tsi721 private data | ||
1988 | * | ||
1989 | * Configures Tsi721 messaging engine. | ||
1990 | */ | ||
1991 | static int tsi721_messages_init(struct tsi721_device *priv) | ||
1992 | { | ||
1993 | int ch; | ||
1994 | |||
1995 | iowrite32(0, priv->regs + TSI721_SMSG_ECC_LOG); | ||
1996 | iowrite32(0, priv->regs + TSI721_RETRY_GEN_CNT); | ||
1997 | iowrite32(0, priv->regs + TSI721_RETRY_RX_CNT); | ||
1998 | |||
1999 | /* Set SRIO Message Request/Response Timeout */ | ||
2000 | iowrite32(TSI721_RQRPTO_VAL, priv->regs + TSI721_RQRPTO); | ||
2001 | |||
2002 | /* Initialize Inbound Messaging Engine Registers */ | ||
2003 | for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++) { | ||
2004 | /* Clear interrupt bits */ | ||
2005 | iowrite32(TSI721_IBDMAC_INT_MASK, | ||
2006 | priv->regs + TSI721_IBDMAC_INT(ch)); | ||
2007 | /* Clear Status */ | ||
2008 | iowrite32(0, priv->regs + TSI721_IBDMAC_STS(ch)); | ||
2009 | |||
2010 | iowrite32(TSI721_SMSG_ECC_COR_LOG_MASK, | ||
2011 | priv->regs + TSI721_SMSG_ECC_COR_LOG(ch)); | ||
2012 | iowrite32(TSI721_SMSG_ECC_NCOR_MASK, | ||
2013 | priv->regs + TSI721_SMSG_ECC_NCOR(ch)); | ||
2014 | } | ||
2015 | |||
2016 | return 0; | ||
2017 | } | ||
2018 | |||
2019 | /** | ||
2020 | * tsi721_disable_ints - disables all device interrupts | ||
2021 | * @priv: pointer to tsi721 private data | ||
2022 | */ | ||
2023 | static void tsi721_disable_ints(struct tsi721_device *priv) | ||
2024 | { | ||
2025 | int ch; | ||
2026 | |||
2027 | /* Disable all device level interrupts */ | ||
2028 | iowrite32(0, priv->regs + TSI721_DEV_INTE); | ||
2029 | |||
2030 | /* Disable all Device Channel interrupts */ | ||
2031 | iowrite32(0, priv->regs + TSI721_DEV_CHAN_INTE); | ||
2032 | |||
2033 | /* Disable all Inbound Msg Channel interrupts */ | ||
2034 | for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++) | ||
2035 | iowrite32(0, priv->regs + TSI721_IBDMAC_INTE(ch)); | ||
2036 | |||
2037 | /* Disable all Outbound Msg Channel interrupts */ | ||
2038 | for (ch = 0; ch < TSI721_OMSG_CHNUM; ch++) | ||
2039 | iowrite32(0, priv->regs + TSI721_OBDMAC_INTE(ch)); | ||
2040 | |||
2041 | /* Disable all general messaging interrupts */ | ||
2042 | iowrite32(0, priv->regs + TSI721_SMSG_INTE); | ||
2043 | |||
2044 | /* Disable all BDMA Channel interrupts */ | ||
2045 | for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) | ||
2046 | iowrite32(0, priv->regs + TSI721_DMAC_INTE(ch)); | ||
2047 | |||
2048 | /* Disable all general BDMA interrupts */ | ||
2049 | iowrite32(0, priv->regs + TSI721_BDMA_INTE); | ||
2050 | |||
2051 | /* Disable all SRIO Channel interrupts */ | ||
2052 | for (ch = 0; ch < TSI721_SRIO_MAXCH; ch++) | ||
2053 | iowrite32(0, priv->regs + TSI721_SR_CHINTE(ch)); | ||
2054 | |||
2055 | /* Disable all general SR2PC interrupts */ | ||
2056 | iowrite32(0, priv->regs + TSI721_SR2PC_GEN_INTE); | ||
2057 | |||
2058 | /* Disable all PC2SR interrupts */ | ||
2059 | iowrite32(0, priv->regs + TSI721_PC2SR_INTE); | ||
2060 | |||
2061 | /* Disable all I2C interrupts */ | ||
2062 | iowrite32(0, priv->regs + TSI721_I2C_INT_ENABLE); | ||
2063 | |||
2064 | /* Disable SRIO MAC interrupts */ | ||
2065 | iowrite32(0, priv->regs + TSI721_RIO_EM_INT_ENABLE); | ||
2066 | iowrite32(0, priv->regs + TSI721_RIO_EM_DEV_INT_EN); | ||
2067 | } | ||
2068 | |||
2069 | /** | ||
2070 | * tsi721_setup_mport - Setup Tsi721 as RapidIO subsystem master port | ||
2071 | * @priv: pointer to tsi721 private data | ||
2072 | * | ||
2073 | * Configures Tsi721 as RapidIO master port. | ||
2074 | */ | ||
2075 | static int __devinit tsi721_setup_mport(struct tsi721_device *priv) | ||
2076 | { | ||
2077 | struct pci_dev *pdev = priv->pdev; | ||
2078 | int err = 0; | ||
2079 | struct rio_ops *ops; | ||
2080 | |||
2081 | struct rio_mport *mport; | ||
2082 | |||
2083 | ops = kzalloc(sizeof(struct rio_ops), GFP_KERNEL); | ||
2084 | if (!ops) { | ||
2085 | dev_dbg(&pdev->dev, "Unable to allocate memory for rio_ops\n"); | ||
2086 | return -ENOMEM; | ||
2087 | } | ||
2088 | |||
2089 | ops->lcread = tsi721_lcread; | ||
2090 | ops->lcwrite = tsi721_lcwrite; | ||
2091 | ops->cread = tsi721_cread_dma; | ||
2092 | ops->cwrite = tsi721_cwrite_dma; | ||
2093 | ops->dsend = tsi721_dsend; | ||
2094 | ops->open_inb_mbox = tsi721_open_inb_mbox; | ||
2095 | ops->close_inb_mbox = tsi721_close_inb_mbox; | ||
2096 | ops->open_outb_mbox = tsi721_open_outb_mbox; | ||
2097 | ops->close_outb_mbox = tsi721_close_outb_mbox; | ||
2098 | ops->add_outb_message = tsi721_add_outb_message; | ||
2099 | ops->add_inb_buffer = tsi721_add_inb_buffer; | ||
2100 | ops->get_inb_message = tsi721_get_inb_message; | ||
2101 | |||
2102 | mport = kzalloc(sizeof(struct rio_mport), GFP_KERNEL); | ||
2103 | if (!mport) { | ||
2104 | kfree(ops); | ||
2105 | dev_dbg(&pdev->dev, "Unable to allocate memory for mport\n"); | ||
2106 | return -ENOMEM; | ||
2107 | } | ||
2108 | |||
2109 | mport->ops = ops; | ||
2110 | mport->index = 0; | ||
2111 | mport->sys_size = 0; /* small system */ | ||
2112 | mport->phy_type = RIO_PHY_SERIAL; | ||
2113 | mport->priv = (void *)priv; | ||
2114 | mport->phys_efptr = 0x100; | ||
2115 | |||
2116 | INIT_LIST_HEAD(&mport->dbells); | ||
2117 | |||
2118 | rio_init_dbell_res(&mport->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff); | ||
2119 | rio_init_mbox_res(&mport->riores[RIO_INB_MBOX_RESOURCE], 0, 0); | ||
2120 | rio_init_mbox_res(&mport->riores[RIO_OUTB_MBOX_RESOURCE], 0, 0); | ||
2121 | strcpy(mport->name, "Tsi721 mport"); | ||
2122 | |||
2123 | /* Hook up interrupt handler */ | ||
2124 | |||
2125 | #ifdef CONFIG_PCI_MSI | ||
2126 | if (!tsi721_enable_msix(priv)) | ||
2127 | priv->flags |= TSI721_USING_MSIX; | ||
2128 | else if (!pci_enable_msi(pdev)) | ||
2129 | priv->flags |= TSI721_USING_MSI; | ||
2130 | else | ||
2131 | dev_info(&pdev->dev, | ||
2132 | "MSI/MSI-X is not available. Using legacy INTx.\n"); | ||
2133 | #endif /* CONFIG_PCI_MSI */ | ||
2134 | |||
2135 | err = tsi721_request_irq(mport); | ||
2136 | |||
2137 | if (!err) { | ||
2138 | tsi721_interrupts_init(priv); | ||
2139 | ops->pwenable = tsi721_pw_enable; | ||
2140 | } else | ||
2141 | dev_err(&pdev->dev, "Unable to get assigned PCI IRQ " | ||
2142 | "vector %02X err=0x%x\n", pdev->irq, err); | ||
2143 | |||
2144 | /* Enable SRIO link */ | ||
2145 | iowrite32(ioread32(priv->regs + TSI721_DEVCTL) | | ||
2146 | TSI721_DEVCTL_SRBOOT_CMPL, | ||
2147 | priv->regs + TSI721_DEVCTL); | ||
2148 | |||
2149 | rio_register_mport(mport); | ||
2150 | priv->mport = mport; | ||
2151 | |||
2152 | if (mport->host_deviceid >= 0) | ||
2153 | iowrite32(RIO_PORT_GEN_HOST | RIO_PORT_GEN_MASTER | | ||
2154 | RIO_PORT_GEN_DISCOVERED, | ||
2155 | priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR)); | ||
2156 | else | ||
2157 | iowrite32(0, priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR)); | ||
2158 | |||
2159 | return 0; | ||
2160 | } | ||
2161 | |||
2162 | static int __devinit tsi721_probe(struct pci_dev *pdev, | ||
2163 | const struct pci_device_id *id) | ||
2164 | { | ||
2165 | struct tsi721_device *priv; | ||
2166 | int i; | ||
2167 | int err; | ||
2168 | u32 regval; | ||
2169 | |||
2170 | priv = kzalloc(sizeof(struct tsi721_device), GFP_KERNEL); | ||
2171 | if (priv == NULL) { | ||
2172 | dev_err(&pdev->dev, "Failed to allocate memory for device\n"); | ||
2173 | err = -ENOMEM; | ||
2174 | goto err_exit; | ||
2175 | } | ||
2176 | |||
2177 | err = pci_enable_device(pdev); | ||
2178 | if (err) { | ||
2179 | dev_err(&pdev->dev, "Failed to enable PCI device\n"); | ||
2180 | goto err_clean; | ||
2181 | } | ||
2182 | |||
2183 | priv->pdev = pdev; | ||
2184 | |||
2185 | #ifdef DEBUG | ||
2186 | for (i = 0; i <= PCI_STD_RESOURCE_END; i++) { | ||
2187 | dev_dbg(&pdev->dev, "res[%d] @ 0x%llx (0x%lx, 0x%lx)\n", | ||
2188 | i, (unsigned long long)pci_resource_start(pdev, i), | ||
2189 | (unsigned long)pci_resource_len(pdev, i), | ||
2190 | pci_resource_flags(pdev, i)); | ||
2191 | } | ||
2192 | #endif | ||
2193 | /* | ||
2194 | * Verify BAR configuration | ||
2195 | */ | ||
2196 | |||
2197 | /* BAR_0 (registers) must be 512KB+ in 32-bit address space */ | ||
2198 | if (!(pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM) || | ||
2199 | pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM_64 || | ||
2200 | pci_resource_len(pdev, BAR_0) < TSI721_REG_SPACE_SIZE) { | ||
2201 | dev_err(&pdev->dev, | ||
2202 | "Missing or misconfigured CSR BAR0, aborting.\n"); | ||
2203 | err = -ENODEV; | ||
2204 | goto err_disable_pdev; | ||
2205 | } | ||
2206 | |||
2207 | /* BAR_1 (outbound doorbells) must be 16MB+ in 32-bit address space */ | ||
2208 | if (!(pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM) || | ||
2209 | pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM_64 || | ||
2210 | pci_resource_len(pdev, BAR_1) < TSI721_DB_WIN_SIZE) { | ||
2211 | dev_err(&pdev->dev, | ||
2212 | "Missing or misconfigured Doorbell BAR1, aborting.\n"); | ||
2213 | err = -ENODEV; | ||
2214 | goto err_disable_pdev; | ||
2215 | } | ||
2216 | |||
2217 | /* | ||
2218 | * BAR_2 and BAR_4 (outbound translation) must be in 64-bit PCIe address | ||
2219 | * space. | ||
2220 | * NOTE: BAR_2 and BAR_4 are not used by this version of driver. | ||
2221 | * It may be a good idea to keep them disabled using HW configuration | ||
2222 | * to save PCI memory space. | ||
2223 | */ | ||
2224 | if ((pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM) && | ||
2225 | (pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM_64)) { | ||
2226 | dev_info(&pdev->dev, "Outbound BAR2 is not used but enabled.\n"); | ||
2227 | } | ||
2228 | |||
2229 | if ((pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM) && | ||
2230 | (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64)) { | ||
2231 | dev_info(&pdev->dev, "Outbound BAR4 is not used but enabled.\n"); | ||
2232 | } | ||
2233 | |||
2234 | err = pci_request_regions(pdev, DRV_NAME); | ||
2235 | if (err) { | ||
2236 | dev_err(&pdev->dev, "Cannot obtain PCI resources, " | ||
2237 | "aborting.\n"); | ||
2238 | goto err_disable_pdev; | ||
2239 | } | ||
2240 | |||
2241 | pci_set_master(pdev); | ||
2242 | |||
2243 | priv->regs = pci_ioremap_bar(pdev, BAR_0); | ||
2244 | if (!priv->regs) { | ||
2245 | dev_err(&pdev->dev, | ||
2246 | "Unable to map device registers space, aborting\n"); | ||
2247 | err = -ENOMEM; | ||
2248 | goto err_free_res; | ||
2249 | } | ||
2250 | |||
2251 | priv->odb_base = pci_ioremap_bar(pdev, BAR_1); | ||
2252 | if (!priv->odb_base) { | ||
2253 | dev_err(&pdev->dev, | ||
2254 | "Unable to map outbound doorbells space, aborting\n"); | ||
2255 | err = -ENOMEM; | ||
2256 | goto err_unmap_bars; | ||
2257 | } | ||
2258 | |||
2259 | /* Configure DMA attributes. */ | ||
2260 | if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { | ||
2261 | if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { | ||
2262 | dev_info(&pdev->dev, "Unable to set DMA mask\n"); | ||
2263 | goto err_unmap_bars; | ||
2264 | } | ||
2265 | |||
2266 | if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) | ||
2267 | dev_info(&pdev->dev, "Unable to set consistent DMA mask\n"); | ||
2268 | } else { | ||
2269 | err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); | ||
2270 | if (err) | ||
2271 | dev_info(&pdev->dev, "Unable to set consistent DMA mask\n"); | ||
2272 | } | ||
2273 | |||
2274 | /* Clear "no snoop" and "relaxed ordering" bits. */ | ||
2275 | pci_read_config_dword(pdev, 0x40 + PCI_EXP_DEVCTL, ®val); | ||
2276 | regval &= ~(PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN); | ||
2277 | pci_write_config_dword(pdev, 0x40 + PCI_EXP_DEVCTL, regval); | ||
2278 | |||
2279 | /* | ||
2280 | * FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block | ||
2281 | */ | ||
2282 | pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0x01); | ||
2283 | pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXTBL, | ||
2284 | TSI721_MSIXTBL_OFFSET); | ||
2285 | pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXPBA, | ||
2286 | TSI721_MSIXPBA_OFFSET); | ||
2287 | pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0); | ||
2288 | /* End of FIXUP */ | ||
2289 | |||
2290 | tsi721_disable_ints(priv); | ||
2291 | |||
2292 | tsi721_init_pc2sr_mapping(priv); | ||
2293 | tsi721_init_sr2pc_mapping(priv); | ||
2294 | |||
2295 | if (tsi721_bdma_init(priv)) { | ||
2296 | dev_err(&pdev->dev, "BDMA initialization failed, aborting\n"); | ||
2297 | err = -ENOMEM; | ||
2298 | goto err_unmap_bars; | ||
2299 | } | ||
2300 | |||
2301 | err = tsi721_doorbell_init(priv); | ||
2302 | if (err) | ||
2303 | goto err_free_bdma; | ||
2304 | |||
2305 | tsi721_port_write_init(priv); | ||
2306 | |||
2307 | err = tsi721_messages_init(priv); | ||
2308 | if (err) | ||
2309 | goto err_free_consistent; | ||
2310 | |||
2311 | err = tsi721_setup_mport(priv); | ||
2312 | if (err) | ||
2313 | goto err_free_consistent; | ||
2314 | |||
2315 | return 0; | ||
2316 | |||
2317 | err_free_consistent: | ||
2318 | tsi721_doorbell_free(priv); | ||
2319 | err_free_bdma: | ||
2320 | tsi721_bdma_free(priv); | ||
2321 | err_unmap_bars: | ||
2322 | if (priv->regs) | ||
2323 | iounmap(priv->regs); | ||
2324 | if (priv->odb_base) | ||
2325 | iounmap(priv->odb_base); | ||
2326 | err_free_res: | ||
2327 | pci_release_regions(pdev); | ||
2328 | pci_clear_master(pdev); | ||
2329 | err_disable_pdev: | ||
2330 | pci_disable_device(pdev); | ||
2331 | err_clean: | ||
2332 | kfree(priv); | ||
2333 | err_exit: | ||
2334 | return err; | ||
2335 | } | ||
2336 | |||
2337 | static DEFINE_PCI_DEVICE_TABLE(tsi721_pci_tbl) = { | ||
2338 | { PCI_DEVICE(PCI_VENDOR_ID_IDT, PCI_DEVICE_ID_TSI721) }, | ||
2339 | { 0, } /* terminate list */ | ||
2340 | }; | ||
2341 | |||
2342 | MODULE_DEVICE_TABLE(pci, tsi721_pci_tbl); | ||
2343 | |||
2344 | static struct pci_driver tsi721_driver = { | ||
2345 | .name = "tsi721", | ||
2346 | .id_table = tsi721_pci_tbl, | ||
2347 | .probe = tsi721_probe, | ||
2348 | }; | ||
2349 | |||
2350 | static int __init tsi721_init(void) | ||
2351 | { | ||
2352 | return pci_register_driver(&tsi721_driver); | ||
2353 | } | ||
2354 | |||
2355 | static void __exit tsi721_exit(void) | ||
2356 | { | ||
2357 | pci_unregister_driver(&tsi721_driver); | ||
2358 | } | ||
2359 | |||
2360 | device_initcall(tsi721_init); | ||
diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h new file mode 100644 index 000000000000..58be4deb1402 --- /dev/null +++ b/drivers/rapidio/devices/tsi721.h | |||
@@ -0,0 +1,766 @@ | |||
1 | /* | ||
2 | * Tsi721 PCIExpress-to-SRIO bridge definitions | ||
3 | * | ||
4 | * Copyright 2011, Integrated Device Technology, Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the Free | ||
8 | * Software Foundation; either version 2 of the License, or (at your option) | ||
9 | * any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along with | ||
17 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
18 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef __TSI721_H | ||
22 | #define __TSI721_H | ||
23 | |||
24 | #define DRV_NAME "tsi721" | ||
25 | |||
26 | #define DEFAULT_HOPCOUNT 0xff | ||
27 | #define DEFAULT_DESTID 0xff | ||
28 | |||
29 | /* PCI device ID */ | ||
30 | #define PCI_DEVICE_ID_TSI721 0x80ab | ||
31 | |||
32 | #define BAR_0 0 | ||
33 | #define BAR_1 1 | ||
34 | #define BAR_2 2 | ||
35 | #define BAR_4 4 | ||
36 | |||
37 | #define TSI721_PC2SR_BARS 2 | ||
38 | #define TSI721_PC2SR_WINS 8 | ||
39 | #define TSI721_PC2SR_ZONES 8 | ||
40 | #define TSI721_MAINT_WIN 0 /* Window for outbound maintenance requests */ | ||
41 | #define IDB_QUEUE 0 /* Inbound Doorbell Queue to use */ | ||
42 | #define IDB_QSIZE 512 /* Inbound Doorbell Queue size */ | ||
43 | |||
44 | /* Memory space sizes */ | ||
45 | #define TSI721_REG_SPACE_SIZE (512 * 1024) /* 512K */ | ||
46 | #define TSI721_DB_WIN_SIZE (16 * 1024 * 1024) /* 16MB */ | ||
47 | |||
48 | #define RIO_TT_CODE_8 0x00000000 | ||
49 | #define RIO_TT_CODE_16 0x00000001 | ||
50 | |||
51 | #define TSI721_DMA_MAXCH 8 | ||
52 | #define TSI721_DMA_MINSTSSZ 32 | ||
53 | #define TSI721_DMA_STSBLKSZ 8 | ||
54 | |||
55 | #define TSI721_SRIO_MAXCH 8 | ||
56 | |||
57 | #define DBELL_SID(buf) (((u8)buf[2] << 8) | (u8)buf[3]) | ||
58 | #define DBELL_TID(buf) (((u8)buf[4] << 8) | (u8)buf[5]) | ||
59 | #define DBELL_INF(buf) (((u8)buf[0] << 8) | (u8)buf[1]) | ||
60 | |||
61 | #define TSI721_RIO_PW_MSG_SIZE 16 /* Tsi721 saves only 16 bytes of PW msg */ | ||
62 | |||
63 | /* Register definitions */ | ||
64 | |||
65 | /* | ||
66 | * Registers in PCIe configuration space | ||
67 | */ | ||
68 | |||
69 | #define TSI721_PCIECFG_MSIXTBL 0x0a4 | ||
70 | #define TSI721_MSIXTBL_OFFSET 0x2c000 | ||
71 | #define TSI721_PCIECFG_MSIXPBA 0x0a8 | ||
72 | #define TSI721_MSIXPBA_OFFSET 0x2a000 | ||
73 | #define TSI721_PCIECFG_EPCTL 0x400 | ||
74 | |||
75 | /* | ||
76 | * Event Management Registers | ||
77 | */ | ||
78 | |||
79 | #define TSI721_RIO_EM_INT_STAT 0x10910 | ||
80 | #define TSI721_RIO_EM_INT_STAT_PW_RX 0x00010000 | ||
81 | |||
82 | #define TSI721_RIO_EM_INT_ENABLE 0x10914 | ||
83 | #define TSI721_RIO_EM_INT_ENABLE_PW_RX 0x00010000 | ||
84 | |||
85 | #define TSI721_RIO_EM_DEV_INT_EN 0x10930 | ||
86 | #define TSI721_RIO_EM_DEV_INT_EN_INT 0x00000001 | ||
87 | |||
88 | /* | ||
89 | * Port-Write Block Registers | ||
90 | */ | ||
91 | |||
92 | #define TSI721_RIO_PW_CTL 0x10a04 | ||
93 | #define TSI721_RIO_PW_CTL_PW_TIMER 0xf0000000 | ||
94 | #define TSI721_RIO_PW_CTL_PWT_DIS (0 << 28) | ||
95 | #define TSI721_RIO_PW_CTL_PWT_103 (1 << 28) | ||
96 | #define TSI721_RIO_PW_CTL_PWT_205 (1 << 29) | ||
97 | #define TSI721_RIO_PW_CTL_PWT_410 (1 << 30) | ||
98 | #define TSI721_RIO_PW_CTL_PWT_820 (1 << 31) | ||
99 | #define TSI721_RIO_PW_CTL_PWC_MODE 0x01000000 | ||
100 | #define TSI721_RIO_PW_CTL_PWC_CONT 0x00000000 | ||
101 | #define TSI721_RIO_PW_CTL_PWC_REL 0x01000000 | ||
102 | |||
103 | #define TSI721_RIO_PW_RX_STAT 0x10a10 | ||
104 | #define TSI721_RIO_PW_RX_STAT_WR_SIZE 0x0000f000 | ||
105 | #define TSI_RIO_PW_RX_STAT_WDPTR 0x00000100 | ||
106 | #define TSI721_RIO_PW_RX_STAT_PW_SHORT 0x00000008 | ||
107 | #define TSI721_RIO_PW_RX_STAT_PW_TRUNC 0x00000004 | ||
108 | #define TSI721_RIO_PW_RX_STAT_PW_DISC 0x00000002 | ||
109 | #define TSI721_RIO_PW_RX_STAT_PW_VAL 0x00000001 | ||
110 | |||
111 | #define TSI721_RIO_PW_RX_CAPT(x) (0x10a20 + (x)*4) | ||
112 | |||
113 | /* | ||
114 | * Inbound Doorbells | ||
115 | */ | ||
116 | |||
117 | #define TSI721_IDB_ENTRY_SIZE 64 | ||
118 | |||
119 | #define TSI721_IDQ_CTL(x) (0x20000 + (x) * 1000) | ||
120 | #define TSI721_IDQ_SUSPEND 0x00000002 | ||
121 | #define TSI721_IDQ_INIT 0x00000001 | ||
122 | |||
123 | #define TSI721_IDQ_STS(x) (0x20004 + (x) * 1000) | ||
124 | #define TSI721_IDQ_RUN 0x00200000 | ||
125 | |||
126 | #define TSI721_IDQ_MASK(x) (0x20008 + (x) * 1000) | ||
127 | #define TSI721_IDQ_MASK_MASK 0xffff0000 | ||
128 | #define TSI721_IDQ_MASK_PATT 0x0000ffff | ||
129 | |||
130 | #define TSI721_IDQ_RP(x) (0x2000c + (x) * 1000) | ||
131 | #define TSI721_IDQ_RP_PTR 0x0007ffff | ||
132 | |||
133 | #define TSI721_IDQ_WP(x) (0x20010 + (x) * 1000) | ||
134 | #define TSI721_IDQ_WP_PTR 0x0007ffff | ||
135 | |||
136 | #define TSI721_IDQ_BASEL(x) (0x20014 + (x) * 1000) | ||
137 | #define TSI721_IDQ_BASEL_ADDR 0xffffffc0 | ||
138 | #define TSI721_IDQ_BASEU(x) (0x20018 + (x) * 1000) | ||
139 | #define TSI721_IDQ_SIZE(x) (0x2001c + (x) * 1000) | ||
140 | #define TSI721_IDQ_SIZE_VAL(size) (__fls(size) - 4) | ||
141 | #define TSI721_IDQ_SIZE_MIN 512 | ||
142 | #define TSI721_IDQ_SIZE_MAX (512 * 1024) | ||
143 | |||
144 | #define TSI721_SR_CHINT(x) (0x20040 + (x) * 1000) | ||
145 | #define TSI721_SR_CHINTE(x) (0x20044 + (x) * 1000) | ||
146 | #define TSI721_SR_CHINTSET(x) (0x20048 + (x) * 1000) | ||
147 | #define TSI721_SR_CHINT_ODBOK 0x00000020 | ||
148 | #define TSI721_SR_CHINT_IDBQRCV 0x00000010 | ||
149 | #define TSI721_SR_CHINT_SUSP 0x00000008 | ||
150 | #define TSI721_SR_CHINT_ODBTO 0x00000004 | ||
151 | #define TSI721_SR_CHINT_ODBRTRY 0x00000002 | ||
152 | #define TSI721_SR_CHINT_ODBERR 0x00000001 | ||
153 | #define TSI721_SR_CHINT_ALL 0x0000003f | ||
154 | |||
155 | #define TSI721_IBWIN_NUM 8 | ||
156 | |||
157 | #define TSI721_IBWINLB(x) (0x29000 + (x) * 20) | ||
158 | #define TSI721_IBWINLB_BA 0xfffff000 | ||
159 | #define TSI721_IBWINLB_WEN 0x00000001 | ||
160 | |||
161 | #define TSI721_SR2PC_GEN_INTE 0x29800 | ||
162 | #define TSI721_SR2PC_PWE 0x29804 | ||
163 | #define TSI721_SR2PC_GEN_INT 0x29808 | ||
164 | |||
165 | #define TSI721_DEV_INTE 0x29840 | ||
166 | #define TSI721_DEV_INT 0x29844 | ||
167 | #define TSI721_DEV_INTSET 0x29848 | ||
168 | #define TSI721_DEV_INT_SMSG_CH 0x00000800 | ||
169 | #define TSI721_DEV_INT_SMSG_NCH 0x00000400 | ||
170 | #define TSI721_DEV_INT_SR2PC_CH 0x00000200 | ||
171 | #define TSI721_DEV_INT_SRIO 0x00000020 | ||
172 | |||
173 | #define TSI721_DEV_CHAN_INTE 0x2984c | ||
174 | #define TSI721_DEV_CHAN_INT 0x29850 | ||
175 | |||
176 | #define TSI721_INT_SR2PC_CHAN_M 0xff000000 | ||
177 | #define TSI721_INT_SR2PC_CHAN(x) (1 << (24 + (x))) | ||
178 | #define TSI721_INT_IMSG_CHAN_M 0x00ff0000 | ||
179 | #define TSI721_INT_IMSG_CHAN(x) (1 << (16 + (x))) | ||
180 | #define TSI721_INT_OMSG_CHAN_M 0x0000ff00 | ||
181 | #define TSI721_INT_OMSG_CHAN(x) (1 << (8 + (x))) | ||
182 | |||
183 | /* | ||
184 | * PC2SR block registers | ||
185 | */ | ||
186 | #define TSI721_OBWIN_NUM TSI721_PC2SR_WINS | ||
187 | |||
188 | #define TSI721_OBWINLB(x) (0x40000 + (x) * 20) | ||
189 | #define TSI721_OBWINLB_BA 0xffff8000 | ||
190 | #define TSI721_OBWINLB_WEN 0x00000001 | ||
191 | |||
192 | #define TSI721_OBWINUB(x) (0x40004 + (x) * 20) | ||
193 | |||
194 | #define TSI721_OBWINSZ(x) (0x40008 + (x) * 20) | ||
195 | #define TSI721_OBWINSZ_SIZE 0x00001f00 | ||
196 | #define TSI721_OBWIN_SIZE(size) (__fls(size) - 15) | ||
197 | |||
198 | #define TSI721_ZONE_SEL 0x41300 | ||
199 | #define TSI721_ZONE_SEL_RD_WRB 0x00020000 | ||
200 | #define TSI721_ZONE_SEL_GO 0x00010000 | ||
201 | #define TSI721_ZONE_SEL_WIN 0x00000038 | ||
202 | #define TSI721_ZONE_SEL_ZONE 0x00000007 | ||
203 | |||
204 | #define TSI721_LUT_DATA0 0x41304 | ||
205 | #define TSI721_LUT_DATA0_ADD 0xfffff000 | ||
206 | #define TSI721_LUT_DATA0_RDTYPE 0x00000f00 | ||
207 | #define TSI721_LUT_DATA0_NREAD 0x00000100 | ||
208 | #define TSI721_LUT_DATA0_MNTRD 0x00000200 | ||
209 | #define TSI721_LUT_DATA0_RDCRF 0x00000020 | ||
210 | #define TSI721_LUT_DATA0_WRCRF 0x00000010 | ||
211 | #define TSI721_LUT_DATA0_WRTYPE 0x0000000f | ||
212 | #define TSI721_LUT_DATA0_NWR 0x00000001 | ||
213 | #define TSI721_LUT_DATA0_MNTWR 0x00000002 | ||
214 | #define TSI721_LUT_DATA0_NWR_R 0x00000004 | ||
215 | |||
216 | #define TSI721_LUT_DATA1 0x41308 | ||
217 | |||
218 | #define TSI721_LUT_DATA2 0x4130c | ||
219 | #define TSI721_LUT_DATA2_HC 0xff000000 | ||
220 | #define TSI721_LUT_DATA2_ADD65 0x000c0000 | ||
221 | #define TSI721_LUT_DATA2_TT 0x00030000 | ||
222 | #define TSI721_LUT_DATA2_DSTID 0x0000ffff | ||
223 | |||
224 | #define TSI721_PC2SR_INTE 0x41310 | ||
225 | |||
226 | #define TSI721_DEVCTL 0x48004 | ||
227 | #define TSI721_DEVCTL_SRBOOT_CMPL 0x00000004 | ||
228 | |||
229 | #define TSI721_I2C_INT_ENABLE 0x49120 | ||
230 | |||
231 | /* | ||
232 | * Block DMA Engine Registers | ||
233 | * x = 0..7 | ||
234 | */ | ||
235 | |||
236 | #define TSI721_DMAC_DWRCNT(x) (0x51000 + (x) * 0x1000) | ||
237 | #define TSI721_DMAC_DRDCNT(x) (0x51004 + (x) * 0x1000) | ||
238 | |||
239 | #define TSI721_DMAC_CTL(x) (0x51008 + (x) * 0x1000) | ||
240 | #define TSI721_DMAC_CTL_SUSP 0x00000002 | ||
241 | #define TSI721_DMAC_CTL_INIT 0x00000001 | ||
242 | |||
243 | #define TSI721_DMAC_INT(x) (0x5100c + (x) * 0x1000) | ||
244 | #define TSI721_DMAC_INT_STFULL 0x00000010 | ||
245 | #define TSI721_DMAC_INT_DONE 0x00000008 | ||
246 | #define TSI721_DMAC_INT_SUSP 0x00000004 | ||
247 | #define TSI721_DMAC_INT_ERR 0x00000002 | ||
248 | #define TSI721_DMAC_INT_IOFDONE 0x00000001 | ||
249 | #define TSI721_DMAC_INT_ALL 0x0000001f | ||
250 | |||
251 | #define TSI721_DMAC_INTSET(x) (0x51010 + (x) * 0x1000) | ||
252 | |||
253 | #define TSI721_DMAC_STS(x) (0x51014 + (x) * 0x1000) | ||
254 | #define TSI721_DMAC_STS_ABORT 0x00400000 | ||
255 | #define TSI721_DMAC_STS_RUN 0x00200000 | ||
256 | #define TSI721_DMAC_STS_CS 0x001f0000 | ||
257 | |||
258 | #define TSI721_DMAC_INTE(x) (0x51018 + (x) * 0x1000) | ||
259 | |||
260 | #define TSI721_DMAC_DPTRL(x) (0x51024 + (x) * 0x1000) | ||
261 | #define TSI721_DMAC_DPTRL_MASK 0xffffffe0 | ||
262 | |||
263 | #define TSI721_DMAC_DPTRH(x) (0x51028 + (x) * 0x1000) | ||
264 | |||
265 | #define TSI721_DMAC_DSBL(x) (0x5102c + (x) * 0x1000) | ||
266 | #define TSI721_DMAC_DSBL_MASK 0xffffffc0 | ||
267 | |||
268 | #define TSI721_DMAC_DSBH(x) (0x51030 + (x) * 0x1000) | ||
269 | |||
270 | #define TSI721_DMAC_DSSZ(x) (0x51034 + (x) * 0x1000) | ||
271 | #define TSI721_DMAC_DSSZ_SIZE_M 0x0000000f | ||
272 | #define TSI721_DMAC_DSSZ_SIZE(size) (__fls(size) - 4) | ||
273 | |||
274 | |||
275 | #define TSI721_DMAC_DSRP(x) (0x51038 + (x) * 0x1000) | ||
276 | #define TSI721_DMAC_DSRP_MASK 0x0007ffff | ||
277 | |||
278 | #define TSI721_DMAC_DSWP(x) (0x5103c + (x) * 0x1000) | ||
279 | #define TSI721_DMAC_DSWP_MASK 0x0007ffff | ||
280 | |||
281 | #define TSI721_BDMA_INTE 0x5f000 | ||
282 | |||
283 | /* | ||
284 | * Messaging definitions | ||
285 | */ | ||
286 | #define TSI721_MSG_BUFFER_SIZE RIO_MAX_MSG_SIZE | ||
287 | #define TSI721_MSG_MAX_SIZE RIO_MAX_MSG_SIZE | ||
288 | #define TSI721_IMSG_MAXCH 8 | ||
289 | #define TSI721_IMSG_CHNUM TSI721_IMSG_MAXCH | ||
290 | #define TSI721_IMSGD_MIN_RING_SIZE 32 | ||
291 | #define TSI721_IMSGD_RING_SIZE 512 | ||
292 | |||
293 | #define TSI721_OMSG_CHNUM 4 /* One channel per MBOX */ | ||
294 | #define TSI721_OMSGD_MIN_RING_SIZE 32 | ||
295 | #define TSI721_OMSGD_RING_SIZE 512 | ||
296 | |||
297 | /* | ||
298 | * Outbound Messaging Engine Registers | ||
299 | * x = 0..7 | ||
300 | */ | ||
301 | |||
302 | #define TSI721_OBDMAC_DWRCNT(x) (0x61000 + (x) * 0x1000) | ||
303 | |||
304 | #define TSI721_OBDMAC_DRDCNT(x) (0x61004 + (x) * 0x1000) | ||
305 | |||
306 | #define TSI721_OBDMAC_CTL(x) (0x61008 + (x) * 0x1000) | ||
307 | #define TSI721_OBDMAC_CTL_MASK 0x00000007 | ||
308 | #define TSI721_OBDMAC_CTL_RETRY_THR 0x00000004 | ||
309 | #define TSI721_OBDMAC_CTL_SUSPEND 0x00000002 | ||
310 | #define TSI721_OBDMAC_CTL_INIT 0x00000001 | ||
311 | |||
312 | #define TSI721_OBDMAC_INT(x) (0x6100c + (x) * 0x1000) | ||
313 | #define TSI721_OBDMAC_INTSET(x) (0x61010 + (x) * 0x1000) | ||
314 | #define TSI721_OBDMAC_INTE(x) (0x61018 + (x) * 0x1000) | ||
315 | #define TSI721_OBDMAC_INT_MASK 0x0000001F | ||
316 | #define TSI721_OBDMAC_INT_ST_FULL 0x00000010 | ||
317 | #define TSI721_OBDMAC_INT_DONE 0x00000008 | ||
318 | #define TSI721_OBDMAC_INT_SUSPENDED 0x00000004 | ||
319 | #define TSI721_OBDMAC_INT_ERROR 0x00000002 | ||
320 | #define TSI721_OBDMAC_INT_IOF_DONE 0x00000001 | ||
321 | #define TSI721_OBDMAC_INT_ALL TSI721_OBDMAC_INT_MASK | ||
322 | |||
323 | #define TSI721_OBDMAC_STS(x) (0x61014 + (x) * 0x1000) | ||
324 | #define TSI721_OBDMAC_STS_MASK 0x007f0000 | ||
325 | #define TSI721_OBDMAC_STS_ABORT 0x00400000 | ||
326 | #define TSI721_OBDMAC_STS_RUN 0x00200000 | ||
327 | #define TSI721_OBDMAC_STS_CS 0x001f0000 | ||
328 | |||
329 | #define TSI721_OBDMAC_PWE(x) (0x6101c + (x) * 0x1000) | ||
330 | #define TSI721_OBDMAC_PWE_MASK 0x00000002 | ||
331 | #define TSI721_OBDMAC_PWE_ERROR_EN 0x00000002 | ||
332 | |||
333 | #define TSI721_OBDMAC_DPTRL(x) (0x61020 + (x) * 0x1000) | ||
334 | #define TSI721_OBDMAC_DPTRL_MASK 0xfffffff0 | ||
335 | |||
336 | #define TSI721_OBDMAC_DPTRH(x) (0x61024 + (x) * 0x1000) | ||
337 | #define TSI721_OBDMAC_DPTRH_MASK 0xffffffff | ||
338 | |||
339 | #define TSI721_OBDMAC_DSBL(x) (0x61040 + (x) * 0x1000) | ||
340 | #define TSI721_OBDMAC_DSBL_MASK 0xffffffc0 | ||
341 | |||
342 | #define TSI721_OBDMAC_DSBH(x) (0x61044 + (x) * 0x1000) | ||
343 | #define TSI721_OBDMAC_DSBH_MASK 0xffffffff | ||
344 | |||
345 | #define TSI721_OBDMAC_DSSZ(x) (0x61048 + (x) * 0x1000) | ||
346 | #define TSI721_OBDMAC_DSSZ_MASK 0x0000000f | ||
347 | |||
348 | #define TSI721_OBDMAC_DSRP(x) (0x6104c + (x) * 0x1000) | ||
349 | #define TSI721_OBDMAC_DSRP_MASK 0x0007ffff | ||
350 | |||
351 | #define TSI721_OBDMAC_DSWP(x) (0x61050 + (x) * 0x1000) | ||
352 | #define TSI721_OBDMAC_DSWP_MASK 0x0007ffff | ||
353 | |||
354 | #define TSI721_RQRPTO 0x60010 | ||
355 | #define TSI721_RQRPTO_MASK 0x00ffffff | ||
356 | #define TSI721_RQRPTO_VAL 400 /* Response TO value */ | ||
357 | |||
358 | /* | ||
359 | * Inbound Messaging Engine Registers | ||
360 | * x = 0..7 | ||
361 | */ | ||
362 | |||
363 | #define TSI721_IB_DEVID_GLOBAL 0xffff | ||
364 | #define TSI721_IBDMAC_FQBL(x) (0x61200 + (x) * 0x1000) | ||
365 | #define TSI721_IBDMAC_FQBL_MASK 0xffffffc0 | ||
366 | |||
367 | #define TSI721_IBDMAC_FQBH(x) (0x61204 + (x) * 0x1000) | ||
368 | #define TSI721_IBDMAC_FQBH_MASK 0xffffffff | ||
369 | |||
370 | #define TSI721_IBDMAC_FQSZ_ENTRY_INX TSI721_IMSGD_RING_SIZE | ||
371 | #define TSI721_IBDMAC_FQSZ(x) (0x61208 + (x) * 0x1000) | ||
372 | #define TSI721_IBDMAC_FQSZ_MASK 0x0000000f | ||
373 | |||
374 | #define TSI721_IBDMAC_FQRP(x) (0x6120c + (x) * 0x1000) | ||
375 | #define TSI721_IBDMAC_FQRP_MASK 0x0007ffff | ||
376 | |||
377 | #define TSI721_IBDMAC_FQWP(x) (0x61210 + (x) * 0x1000) | ||
378 | #define TSI721_IBDMAC_FQWP_MASK 0x0007ffff | ||
379 | |||
380 | #define TSI721_IBDMAC_FQTH(x) (0x61214 + (x) * 0x1000) | ||
381 | #define TSI721_IBDMAC_FQTH_MASK 0x0007ffff | ||
382 | |||
383 | #define TSI721_IB_DEVID 0x60020 | ||
384 | #define TSI721_IB_DEVID_MASK 0x0000ffff | ||
385 | |||
386 | #define TSI721_IBDMAC_CTL(x) (0x61240 + (x) * 0x1000) | ||
387 | #define TSI721_IBDMAC_CTL_MASK 0x00000003 | ||
388 | #define TSI721_IBDMAC_CTL_SUSPEND 0x00000002 | ||
389 | #define TSI721_IBDMAC_CTL_INIT 0x00000001 | ||
390 | |||
391 | #define TSI721_IBDMAC_STS(x) (0x61244 + (x) * 0x1000) | ||
392 | #define TSI721_IBDMAC_STS_MASK 0x007f0000 | ||
393 | #define TSI721_IBSMAC_STS_ABORT 0x00400000 | ||
394 | #define TSI721_IBSMAC_STS_RUN 0x00200000 | ||
395 | #define TSI721_IBSMAC_STS_CS 0x001f0000 | ||
396 | |||
397 | #define TSI721_IBDMAC_INT(x) (0x61248 + (x) * 0x1000) | ||
398 | #define TSI721_IBDMAC_INTSET(x) (0x6124c + (x) * 0x1000) | ||
399 | #define TSI721_IBDMAC_INTE(x) (0x61250 + (x) * 0x1000) | ||
400 | #define TSI721_IBDMAC_INT_MASK 0x0000100f | ||
401 | #define TSI721_IBDMAC_INT_SRTO 0x00001000 | ||
402 | #define TSI721_IBDMAC_INT_SUSPENDED 0x00000008 | ||
403 | #define TSI721_IBDMAC_INT_PC_ERROR 0x00000004 | ||
404 | #define TSI721_IBDMAC_INT_FQ_LOW 0x00000002 | ||
405 | #define TSI721_IBDMAC_INT_DQ_RCV 0x00000001 | ||
406 | #define TSI721_IBDMAC_INT_ALL TSI721_IBDMAC_INT_MASK | ||
407 | |||
408 | #define TSI721_IBDMAC_PWE(x) (0x61254 + (x) * 0x1000) | ||
409 | #define TSI721_IBDMAC_PWE_MASK 0x00001700 | ||
410 | #define TSI721_IBDMAC_PWE_SRTO 0x00001000 | ||
411 | #define TSI721_IBDMAC_PWE_ILL_FMT 0x00000400 | ||
412 | #define TSI721_IBDMAC_PWE_ILL_DEC 0x00000200 | ||
413 | #define TSI721_IBDMAC_PWE_IMP_SP 0x00000100 | ||
414 | |||
415 | #define TSI721_IBDMAC_DQBL(x) (0x61300 + (x) * 0x1000) | ||
416 | #define TSI721_IBDMAC_DQBL_MASK 0xffffffc0 | ||
417 | #define TSI721_IBDMAC_DQBL_ADDR 0xffffffc0 | ||
418 | |||
419 | #define TSI721_IBDMAC_DQBH(x) (0x61304 + (x) * 0x1000) | ||
420 | #define TSI721_IBDMAC_DQBH_MASK 0xffffffff | ||
421 | |||
422 | #define TSI721_IBDMAC_DQRP(x) (0x61308 + (x) * 0x1000) | ||
423 | #define TSI721_IBDMAC_DQRP_MASK 0x0007ffff | ||
424 | |||
425 | #define TSI721_IBDMAC_DQWR(x) (0x6130c + (x) * 0x1000) | ||
426 | #define TSI721_IBDMAC_DQWR_MASK 0x0007ffff | ||
427 | |||
428 | #define TSI721_IBDMAC_DQSZ(x) (0x61314 + (x) * 0x1000) | ||
429 | #define TSI721_IBDMAC_DQSZ_MASK 0x0000000f | ||
430 | |||
431 | /* | ||
432 | * Messaging Engine Interrupts | ||
433 | */ | ||
434 | |||
435 | #define TSI721_SMSG_PWE 0x6a004 | ||
436 | |||
437 | #define TSI721_SMSG_INTE 0x6a000 | ||
438 | #define TSI721_SMSG_INT 0x6a008 | ||
439 | #define TSI721_SMSG_INTSET 0x6a010 | ||
440 | #define TSI721_SMSG_INT_MASK 0x0086ffff | ||
441 | #define TSI721_SMSG_INT_UNS_RSP 0x00800000 | ||
442 | #define TSI721_SMSG_INT_ECC_NCOR 0x00040000 | ||
443 | #define TSI721_SMSG_INT_ECC_COR 0x00020000 | ||
444 | #define TSI721_SMSG_INT_ECC_NCOR_CH 0x0000ff00 | ||
445 | #define TSI721_SMSG_INT_ECC_COR_CH 0x000000ff | ||
446 | |||
447 | #define TSI721_SMSG_ECC_LOG 0x6a014 | ||
448 | #define TSI721_SMSG_ECC_LOG_MASK 0x00070007 | ||
449 | #define TSI721_SMSG_ECC_LOG_ECC_NCOR_M 0x00070000 | ||
450 | #define TSI721_SMSG_ECC_LOG_ECC_COR_M 0x00000007 | ||
451 | |||
452 | #define TSI721_RETRY_GEN_CNT 0x6a100 | ||
453 | #define TSI721_RETRY_GEN_CNT_MASK 0xffffffff | ||
454 | |||
455 | #define TSI721_RETRY_RX_CNT 0x6a104 | ||
456 | #define TSI721_RETRY_RX_CNT_MASK 0xffffffff | ||
457 | |||
458 | #define TSI721_SMSG_ECC_COR_LOG(x) (0x6a300 + (x) * 4) | ||
459 | #define TSI721_SMSG_ECC_COR_LOG_MASK 0x000000ff | ||
460 | |||
461 | #define TSI721_SMSG_ECC_NCOR(x) (0x6a340 + (x) * 4) | ||
462 | #define TSI721_SMSG_ECC_NCOR_MASK 0x000000ff | ||
463 | |||
464 | /* | ||
465 | * Block DMA Descriptors | ||
466 | */ | ||
467 | |||
468 | struct tsi721_dma_desc { | ||
469 | __le32 type_id; | ||
470 | |||
471 | #define TSI721_DMAD_DEVID 0x0000ffff | ||
472 | #define TSI721_DMAD_CRF 0x00010000 | ||
473 | #define TSI721_DMAD_PRIO 0x00060000 | ||
474 | #define TSI721_DMAD_RTYPE 0x00780000 | ||
475 | #define TSI721_DMAD_IOF 0x08000000 | ||
476 | #define TSI721_DMAD_DTYPE 0xe0000000 | ||
477 | |||
478 | __le32 bcount; | ||
479 | |||
480 | #define TSI721_DMAD_BCOUNT1 0x03ffffff /* if DTYPE == 1 */ | ||
481 | #define TSI721_DMAD_BCOUNT2 0x0000000f /* if DTYPE == 2 */ | ||
482 | #define TSI721_DMAD_TT 0x0c000000 | ||
483 | #define TSI721_DMAD_RADDR0 0xc0000000 | ||
484 | |||
485 | union { | ||
486 | __le32 raddr_lo; /* if DTYPE == (1 || 2) */ | ||
487 | __le32 next_lo; /* if DTYPE == 3 */ | ||
488 | }; | ||
489 | |||
490 | #define TSI721_DMAD_CFGOFF 0x00ffffff | ||
491 | #define TSI721_DMAD_HOPCNT 0xff000000 | ||
492 | |||
493 | union { | ||
494 | __le32 raddr_hi; /* if DTYPE == (1 || 2) */ | ||
495 | __le32 next_hi; /* if DTYPE == 3 */ | ||
496 | }; | ||
497 | |||
498 | union { | ||
499 | struct { /* if DTYPE == 1 */ | ||
500 | __le32 bufptr_lo; | ||
501 | __le32 bufptr_hi; | ||
502 | __le32 s_dist; | ||
503 | __le32 s_size; | ||
504 | } t1; | ||
505 | __le32 data[4]; /* if DTYPE == 2 */ | ||
506 | u32 reserved[4]; /* if DTYPE == 3 */ | ||
507 | }; | ||
508 | } __aligned(32); | ||
509 | |||
510 | /* | ||
511 | * Inbound Messaging Descriptor | ||
512 | */ | ||
513 | struct tsi721_imsg_desc { | ||
514 | __le32 type_id; | ||
515 | |||
516 | #define TSI721_IMD_DEVID 0x0000ffff | ||
517 | #define TSI721_IMD_CRF 0x00010000 | ||
518 | #define TSI721_IMD_PRIO 0x00060000 | ||
519 | #define TSI721_IMD_TT 0x00180000 | ||
520 | #define TSI721_IMD_DTYPE 0xe0000000 | ||
521 | |||
522 | __le32 msg_info; | ||
523 | |||
524 | #define TSI721_IMD_BCOUNT 0x00000ff8 | ||
525 | #define TSI721_IMD_SSIZE 0x0000f000 | ||
526 | #define TSI721_IMD_LETER 0x00030000 | ||
527 | #define TSI721_IMD_XMBOX 0x003c0000 | ||
528 | #define TSI721_IMD_MBOX 0x00c00000 | ||
529 | #define TSI721_IMD_CS 0x78000000 | ||
530 | #define TSI721_IMD_HO 0x80000000 | ||
531 | |||
532 | __le32 bufptr_lo; | ||
533 | __le32 bufptr_hi; | ||
534 | u32 reserved[12]; | ||
535 | |||
536 | } __aligned(64); | ||
537 | |||
538 | /* | ||
539 | * Outbound Messaging Descriptor | ||
540 | */ | ||
541 | struct tsi721_omsg_desc { | ||
542 | __le32 type_id; | ||
543 | |||
544 | #define TSI721_OMD_DEVID 0x0000ffff | ||
545 | #define TSI721_OMD_CRF 0x00010000 | ||
546 | #define TSI721_OMD_PRIO 0x00060000 | ||
547 | #define TSI721_OMD_IOF 0x08000000 | ||
548 | #define TSI721_OMD_DTYPE 0xe0000000 | ||
549 | #define TSI721_OMD_RSRVD 0x17f80000 | ||
550 | |||
551 | __le32 msg_info; | ||
552 | |||
553 | #define TSI721_OMD_BCOUNT 0x00000ff8 | ||
554 | #define TSI721_OMD_SSIZE 0x0000f000 | ||
555 | #define TSI721_OMD_LETER 0x00030000 | ||
556 | #define TSI721_OMD_XMBOX 0x003c0000 | ||
557 | #define TSI721_OMD_MBOX 0x00c00000 | ||
558 | #define TSI721_OMD_TT 0x0c000000 | ||
559 | |||
560 | union { | ||
561 | __le32 bufptr_lo; /* if DTYPE == 4 */ | ||
562 | __le32 next_lo; /* if DTYPE == 5 */ | ||
563 | }; | ||
564 | |||
565 | union { | ||
566 | __le32 bufptr_hi; /* if DTYPE == 4 */ | ||
567 | __le32 next_hi; /* if DTYPE == 5 */ | ||
568 | }; | ||
569 | |||
570 | } __aligned(16); | ||
571 | |||
572 | struct tsi721_dma_sts { | ||
573 | __le64 desc_sts[8]; | ||
574 | } __aligned(64); | ||
575 | |||
576 | struct tsi721_desc_sts_fifo { | ||
577 | union { | ||
578 | __le64 da64; | ||
579 | struct { | ||
580 | __le32 lo; | ||
581 | __le32 hi; | ||
582 | } da32; | ||
583 | } stat[8]; | ||
584 | } __aligned(64); | ||
585 | |||
586 | /* Descriptor types for BDMA and Messaging blocks */ | ||
587 | enum dma_dtype { | ||
588 | DTYPE1 = 1, /* Data Transfer DMA Descriptor */ | ||
589 | DTYPE2 = 2, /* Immediate Data Transfer DMA Descriptor */ | ||
590 | DTYPE3 = 3, /* Block Pointer DMA Descriptor */ | ||
591 | DTYPE4 = 4, /* Outbound Msg DMA Descriptor */ | ||
592 | DTYPE5 = 5, /* OB Messaging Block Pointer Descriptor */ | ||
593 | DTYPE6 = 6 /* Inbound Messaging Descriptor */ | ||
594 | }; | ||
595 | |||
596 | enum dma_rtype { | ||
597 | NREAD = 0, | ||
598 | LAST_NWRITE_R = 1, | ||
599 | ALL_NWRITE = 2, | ||
600 | ALL_NWRITE_R = 3, | ||
601 | MAINT_RD = 4, | ||
602 | MAINT_WR = 5 | ||
603 | }; | ||
604 | |||
605 | /* | ||
606 | * mport Driver Definitions | ||
607 | */ | ||
608 | #define TSI721_DMA_CHNUM TSI721_DMA_MAXCH | ||
609 | |||
610 | #define TSI721_DMACH_MAINT 0 /* DMA channel for maint requests */ | ||
611 | #define TSI721_DMACH_MAINT_NBD 32 /* Number of BDs for maint requests */ | ||
612 | |||
613 | #define MSG_DMA_ENTRY_INX_TO_SIZE(x) ((0x10 << (x)) & 0xFFFF0) | ||
614 | |||
615 | enum tsi721_smsg_int_flag { | ||
616 | SMSG_INT_NONE = 0x00000000, | ||
617 | SMSG_INT_ECC_COR_CH = 0x000000ff, | ||
618 | SMSG_INT_ECC_NCOR_CH = 0x0000ff00, | ||
619 | SMSG_INT_ECC_COR = 0x00020000, | ||
620 | SMSG_INT_ECC_NCOR = 0x00040000, | ||
621 | SMSG_INT_UNS_RSP = 0x00800000, | ||
622 | SMSG_INT_ALL = 0x0006ffff | ||
623 | }; | ||
624 | |||
625 | /* Structures */ | ||
626 | |||
627 | struct tsi721_bdma_chan { | ||
628 | int bd_num; /* number of buffer descriptors */ | ||
629 | void *bd_base; /* start of DMA descriptors */ | ||
630 | dma_addr_t bd_phys; | ||
631 | void *sts_base; /* start of DMA BD status FIFO */ | ||
632 | dma_addr_t sts_phys; | ||
633 | int sts_size; | ||
634 | }; | ||
635 | |||
636 | struct tsi721_imsg_ring { | ||
637 | u32 size; | ||
638 | /* VA/PA of data buffers for incoming messages */ | ||
639 | void *buf_base; | ||
640 | dma_addr_t buf_phys; | ||
641 | /* VA/PA of circular free buffer list */ | ||
642 | void *imfq_base; | ||
643 | dma_addr_t imfq_phys; | ||
644 | /* VA/PA of Inbound message descriptors */ | ||
645 | void *imd_base; | ||
646 | dma_addr_t imd_phys; | ||
647 | /* Inbound Queue buffer pointers */ | ||
648 | void *imq_base[TSI721_IMSGD_RING_SIZE]; | ||
649 | |||
650 | u32 rx_slot; | ||
651 | void *dev_id; | ||
652 | u32 fq_wrptr; | ||
653 | u32 desc_rdptr; | ||
654 | spinlock_t lock; | ||
655 | }; | ||
656 | |||
657 | struct tsi721_omsg_ring { | ||
658 | u32 size; | ||
659 | /* VA/PA of OB Msg descriptors */ | ||
660 | void *omd_base; | ||
661 | dma_addr_t omd_phys; | ||
662 | /* VA/PA of OB Msg data buffers */ | ||
663 | void *omq_base[TSI721_OMSGD_RING_SIZE]; | ||
664 | dma_addr_t omq_phys[TSI721_OMSGD_RING_SIZE]; | ||
665 | /* VA/PA of OB Msg descriptor status FIFO */ | ||
666 | void *sts_base; | ||
667 | dma_addr_t sts_phys; | ||
668 | u32 sts_size; /* # of allocated status entries */ | ||
669 | u32 sts_rdptr; | ||
670 | |||
671 | u32 tx_slot; | ||
672 | void *dev_id; | ||
673 | u32 wr_count; | ||
674 | spinlock_t lock; | ||
675 | }; | ||
676 | |||
677 | enum tsi721_flags { | ||
678 | TSI721_USING_MSI = (1 << 0), | ||
679 | TSI721_USING_MSIX = (1 << 1), | ||
680 | TSI721_IMSGID_SET = (1 << 2), | ||
681 | }; | ||
682 | |||
683 | #ifdef CONFIG_PCI_MSI | ||
684 | /* | ||
685 | * MSI-X Table Entries (0 ... 69) | ||
686 | */ | ||
687 | #define TSI721_MSIX_DMACH_DONE(x) (0 + (x)) | ||
688 | #define TSI721_MSIX_DMACH_INT(x) (8 + (x)) | ||
689 | #define TSI721_MSIX_BDMA_INT 16 | ||
690 | #define TSI721_MSIX_OMSG_DONE(x) (17 + (x)) | ||
691 | #define TSI721_MSIX_OMSG_INT(x) (25 + (x)) | ||
692 | #define TSI721_MSIX_IMSG_DQ_RCV(x) (33 + (x)) | ||
693 | #define TSI721_MSIX_IMSG_INT(x) (41 + (x)) | ||
694 | #define TSI721_MSIX_MSG_INT 49 | ||
695 | #define TSI721_MSIX_SR2PC_IDBQ_RCV(x) (50 + (x)) | ||
696 | #define TSI721_MSIX_SR2PC_CH_INT(x) (58 + (x)) | ||
697 | #define TSI721_MSIX_SR2PC_INT 66 | ||
698 | #define TSI721_MSIX_PC2SR_INT 67 | ||
699 | #define TSI721_MSIX_SRIO_MAC_INT 68 | ||
700 | #define TSI721_MSIX_I2C_INT 69 | ||
701 | |||
702 | /* MSI-X vector and init table entry indexes */ | ||
703 | enum tsi721_msix_vect { | ||
704 | TSI721_VECT_IDB, | ||
705 | TSI721_VECT_PWRX, /* PW_RX is part of SRIO MAC Interrupt reporting */ | ||
706 | TSI721_VECT_OMB0_DONE, | ||
707 | TSI721_VECT_OMB1_DONE, | ||
708 | TSI721_VECT_OMB2_DONE, | ||
709 | TSI721_VECT_OMB3_DONE, | ||
710 | TSI721_VECT_OMB0_INT, | ||
711 | TSI721_VECT_OMB1_INT, | ||
712 | TSI721_VECT_OMB2_INT, | ||
713 | TSI721_VECT_OMB3_INT, | ||
714 | TSI721_VECT_IMB0_RCV, | ||
715 | TSI721_VECT_IMB1_RCV, | ||
716 | TSI721_VECT_IMB2_RCV, | ||
717 | TSI721_VECT_IMB3_RCV, | ||
718 | TSI721_VECT_IMB0_INT, | ||
719 | TSI721_VECT_IMB1_INT, | ||
720 | TSI721_VECT_IMB2_INT, | ||
721 | TSI721_VECT_IMB3_INT, | ||
722 | TSI721_VECT_MAX | ||
723 | }; | ||
724 | |||
725 | #define IRQ_DEVICE_NAME_MAX 64 | ||
726 | |||
727 | struct msix_irq { | ||
728 | u16 vector; | ||
729 | char irq_name[IRQ_DEVICE_NAME_MAX]; | ||
730 | }; | ||
731 | #endif /* CONFIG_PCI_MSI */ | ||
732 | |||
733 | struct tsi721_device { | ||
734 | struct pci_dev *pdev; | ||
735 | struct rio_mport *mport; | ||
736 | u32 flags; | ||
737 | void __iomem *regs; | ||
738 | #ifdef CONFIG_PCI_MSI | ||
739 | struct msix_irq msix[TSI721_VECT_MAX]; | ||
740 | #endif | ||
741 | /* Doorbells */ | ||
742 | void __iomem *odb_base; | ||
743 | void *idb_base; | ||
744 | dma_addr_t idb_dma; | ||
745 | struct work_struct idb_work; | ||
746 | u32 db_discard_count; | ||
747 | |||
748 | /* Inbound Port-Write */ | ||
749 | struct work_struct pw_work; | ||
750 | struct kfifo pw_fifo; | ||
751 | spinlock_t pw_fifo_lock; | ||
752 | u32 pw_discard_count; | ||
753 | |||
754 | /* BDMA Engine */ | ||
755 | struct tsi721_bdma_chan bdma[TSI721_DMA_CHNUM]; | ||
756 | |||
757 | /* Inbound Messaging */ | ||
758 | int imsg_init[TSI721_IMSG_CHNUM]; | ||
759 | struct tsi721_imsg_ring imsg_ring[TSI721_IMSG_CHNUM]; | ||
760 | |||
761 | /* Outbound Messaging */ | ||
762 | int omsg_init[TSI721_OMSG_CHNUM]; | ||
763 | struct tsi721_omsg_ring omsg_ring[TSI721_OMSG_CHNUM]; | ||
764 | }; | ||
765 | |||
766 | #endif | ||