diff options
author | Alexandre Bounine <alexandre.bounine@idt.com> | 2010-05-26 17:44:00 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-27 12:12:51 -0400 |
commit | 5b2074ae4f2baa64b59792477ea6d21c1a5ab4bd (patch) | |
tree | 0ddc3959154d5529adf6c058d814258279ce3bd5 | |
parent | e5cabeb3d60f9cd3e3950aff071319ae0e2d08d8 (diff) |
rapidio, powerpc/85xx: add Port-Write message handler for SRIO port
Add RapidIO Port-Write message handler for Freescale SoCs with RapidIO
port.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Tested-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | arch/powerpc/sysdev/fsl_rio.c | 263 |
1 files changed, 260 insertions, 3 deletions
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c index cb0d74927f04..73d5f3e142ab 100644 --- a/arch/powerpc/sysdev/fsl_rio.c +++ b/arch/powerpc/sysdev/fsl_rio.c | |||
@@ -1,6 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | * Freescale MPC85xx/MPC86xx RapidIO support | 2 | * Freescale MPC85xx/MPC86xx RapidIO support |
3 | * | 3 | * |
4 | * Copyright 2009 Integrated Device Technology, Inc. | ||
5 | * Alex Bounine <alexandre.bounine@idt.com> | ||
6 | * - Added Port-Write message handling | ||
7 | * - Added Machine Check exception handling | ||
8 | * | ||
4 | * Copyright (C) 2007, 2008 Freescale Semiconductor, Inc. | 9 | * Copyright (C) 2007, 2008 Freescale Semiconductor, Inc. |
5 | * Zhang Wei <wei.zhang@freescale.com> | 10 | * Zhang Wei <wei.zhang@freescale.com> |
6 | * | 11 | * |
@@ -24,19 +29,26 @@ | |||
24 | #include <linux/of_platform.h> | 29 | #include <linux/of_platform.h> |
25 | #include <linux/delay.h> | 30 | #include <linux/delay.h> |
26 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/kfifo.h> | ||
27 | 33 | ||
28 | #include <asm/io.h> | 34 | #include <asm/io.h> |
29 | 35 | ||
36 | #undef DEBUG_PW /* Port-Write debugging */ | ||
37 | |||
30 | /* RapidIO definition irq, which read from OF-tree */ | 38 | /* RapidIO definition irq, which read from OF-tree */ |
31 | #define IRQ_RIO_BELL(m) (((struct rio_priv *)(m->priv))->bellirq) | 39 | #define IRQ_RIO_BELL(m) (((struct rio_priv *)(m->priv))->bellirq) |
32 | #define IRQ_RIO_TX(m) (((struct rio_priv *)(m->priv))->txirq) | 40 | #define IRQ_RIO_TX(m) (((struct rio_priv *)(m->priv))->txirq) |
33 | #define IRQ_RIO_RX(m) (((struct rio_priv *)(m->priv))->rxirq) | 41 | #define IRQ_RIO_RX(m) (((struct rio_priv *)(m->priv))->rxirq) |
42 | #define IRQ_RIO_PW(m) (((struct rio_priv *)(m->priv))->pwirq) | ||
34 | 43 | ||
35 | #define RIO_ATMU_REGS_OFFSET 0x10c00 | 44 | #define RIO_ATMU_REGS_OFFSET 0x10c00 |
36 | #define RIO_P_MSG_REGS_OFFSET 0x11000 | 45 | #define RIO_P_MSG_REGS_OFFSET 0x11000 |
37 | #define RIO_S_MSG_REGS_OFFSET 0x13000 | 46 | #define RIO_S_MSG_REGS_OFFSET 0x13000 |
38 | #define RIO_ESCSR 0x158 | 47 | #define RIO_ESCSR 0x158 |
39 | #define RIO_CCSR 0x15c | 48 | #define RIO_CCSR 0x15c |
49 | #define RIO_LTLEDCSR 0x0608 | ||
50 | #define RIO_LTLEECSR 0x060c | ||
51 | #define RIO_EPWISR 0x10010 | ||
40 | #define RIO_ISR_AACR 0x10120 | 52 | #define RIO_ISR_AACR 0x10120 |
41 | #define RIO_ISR_AACR_AA 0x1 /* Accept All ID */ | 53 | #define RIO_ISR_AACR_AA 0x1 /* Accept All ID */ |
42 | #define RIO_MAINT_WIN_SIZE 0x400000 | 54 | #define RIO_MAINT_WIN_SIZE 0x400000 |
@@ -55,6 +67,18 @@ | |||
55 | #define RIO_MSG_ISR_QFI 0x00000010 | 67 | #define RIO_MSG_ISR_QFI 0x00000010 |
56 | #define RIO_MSG_ISR_DIQI 0x00000001 | 68 | #define RIO_MSG_ISR_DIQI 0x00000001 |
57 | 69 | ||
70 | #define RIO_IPWMR_SEN 0x00100000 | ||
71 | #define RIO_IPWMR_QFIE 0x00000100 | ||
72 | #define RIO_IPWMR_EIE 0x00000020 | ||
73 | #define RIO_IPWMR_CQ 0x00000002 | ||
74 | #define RIO_IPWMR_PWE 0x00000001 | ||
75 | |||
76 | #define RIO_IPWSR_QF 0x00100000 | ||
77 | #define RIO_IPWSR_TE 0x00000080 | ||
78 | #define RIO_IPWSR_QFI 0x00000010 | ||
79 | #define RIO_IPWSR_PWD 0x00000008 | ||
80 | #define RIO_IPWSR_PWB 0x00000004 | ||
81 | |||
58 | #define RIO_MSG_DESC_SIZE 32 | 82 | #define RIO_MSG_DESC_SIZE 32 |
59 | #define RIO_MSG_BUFFER_SIZE 4096 | 83 | #define RIO_MSG_BUFFER_SIZE 4096 |
60 | #define RIO_MIN_TX_RING_SIZE 2 | 84 | #define RIO_MIN_TX_RING_SIZE 2 |
@@ -121,7 +145,7 @@ struct rio_msg_regs { | |||
121 | u32 pad10[26]; | 145 | u32 pad10[26]; |
122 | u32 pwmr; | 146 | u32 pwmr; |
123 | u32 pwsr; | 147 | u32 pwsr; |
124 | u32 pad11; | 148 | u32 epwqbar; |
125 | u32 pwqbar; | 149 | u32 pwqbar; |
126 | }; | 150 | }; |
127 | 151 | ||
@@ -160,6 +184,14 @@ struct rio_msg_rx_ring { | |||
160 | void *dev_id; | 184 | void *dev_id; |
161 | }; | 185 | }; |
162 | 186 | ||
187 | struct rio_port_write_msg { | ||
188 | void *virt; | ||
189 | dma_addr_t phys; | ||
190 | u32 msg_count; | ||
191 | u32 err_count; | ||
192 | u32 discard_count; | ||
193 | }; | ||
194 | |||
163 | struct rio_priv { | 195 | struct rio_priv { |
164 | struct device *dev; | 196 | struct device *dev; |
165 | void __iomem *regs_win; | 197 | void __iomem *regs_win; |
@@ -172,9 +204,14 @@ struct rio_priv { | |||
172 | struct rio_dbell_ring dbell_ring; | 204 | struct rio_dbell_ring dbell_ring; |
173 | struct rio_msg_tx_ring msg_tx_ring; | 205 | struct rio_msg_tx_ring msg_tx_ring; |
174 | struct rio_msg_rx_ring msg_rx_ring; | 206 | struct rio_msg_rx_ring msg_rx_ring; |
207 | struct rio_port_write_msg port_write_msg; | ||
175 | int bellirq; | 208 | int bellirq; |
176 | int txirq; | 209 | int txirq; |
177 | int rxirq; | 210 | int rxirq; |
211 | int pwirq; | ||
212 | struct work_struct pw_work; | ||
213 | struct kfifo pw_fifo; | ||
214 | spinlock_t pw_fifo_lock; | ||
178 | }; | 215 | }; |
179 | 216 | ||
180 | /** | 217 | /** |
@@ -930,6 +967,223 @@ static int fsl_rio_doorbell_init(struct rio_mport *mport) | |||
930 | return rc; | 967 | return rc; |
931 | } | 968 | } |
932 | 969 | ||
970 | /** | ||
971 | * fsl_rio_port_write_handler - MPC85xx port write interrupt handler | ||
972 | * @irq: Linux interrupt number | ||
973 | * @dev_instance: Pointer to interrupt-specific data | ||
974 | * | ||
975 | * Handles port write interrupts. Parses a list of registered | ||
976 | * port write event handlers and executes a matching event handler. | ||
977 | */ | ||
978 | static irqreturn_t | ||
979 | fsl_rio_port_write_handler(int irq, void *dev_instance) | ||
980 | { | ||
981 | u32 ipwmr, ipwsr; | ||
982 | struct rio_mport *port = (struct rio_mport *)dev_instance; | ||
983 | struct rio_priv *priv = port->priv; | ||
984 | u32 epwisr, tmp; | ||
985 | |||
986 | ipwmr = in_be32(&priv->msg_regs->pwmr); | ||
987 | ipwsr = in_be32(&priv->msg_regs->pwsr); | ||
988 | |||
989 | epwisr = in_be32(priv->regs_win + RIO_EPWISR); | ||
990 | if (epwisr & 0x80000000) { | ||
991 | tmp = in_be32(priv->regs_win + RIO_LTLEDCSR); | ||
992 | pr_info("RIO_LTLEDCSR = 0x%x\n", tmp); | ||
993 | out_be32(priv->regs_win + RIO_LTLEDCSR, 0); | ||
994 | } | ||
995 | |||
996 | if (!(epwisr & 0x00000001)) | ||
997 | return IRQ_HANDLED; | ||
998 | |||
999 | #ifdef DEBUG_PW | ||
1000 | pr_debug("PW Int->IPWMR: 0x%08x IPWSR: 0x%08x (", ipwmr, ipwsr); | ||
1001 | if (ipwsr & RIO_IPWSR_QF) | ||
1002 | pr_debug(" QF"); | ||
1003 | if (ipwsr & RIO_IPWSR_TE) | ||
1004 | pr_debug(" TE"); | ||
1005 | if (ipwsr & RIO_IPWSR_QFI) | ||
1006 | pr_debug(" QFI"); | ||
1007 | if (ipwsr & RIO_IPWSR_PWD) | ||
1008 | pr_debug(" PWD"); | ||
1009 | if (ipwsr & RIO_IPWSR_PWB) | ||
1010 | pr_debug(" PWB"); | ||
1011 | pr_debug(" )\n"); | ||
1012 | #endif | ||
1013 | out_be32(&priv->msg_regs->pwsr, | ||
1014 | ipwsr & (RIO_IPWSR_TE | RIO_IPWSR_QFI | RIO_IPWSR_PWD)); | ||
1015 | |||
1016 | if ((ipwmr & RIO_IPWMR_EIE) && (ipwsr & RIO_IPWSR_TE)) { | ||
1017 | priv->port_write_msg.err_count++; | ||
1018 | pr_info("RIO: Port-Write Transaction Err (%d)\n", | ||
1019 | priv->port_write_msg.err_count); | ||
1020 | } | ||
1021 | if (ipwsr & RIO_IPWSR_PWD) { | ||
1022 | priv->port_write_msg.discard_count++; | ||
1023 | pr_info("RIO: Port Discarded Port-Write Msg(s) (%d)\n", | ||
1024 | priv->port_write_msg.discard_count); | ||
1025 | } | ||
1026 | |||
1027 | /* Schedule deferred processing if PW was received */ | ||
1028 | if (ipwsr & RIO_IPWSR_QFI) { | ||
1029 | /* Save PW message (if there is room in FIFO), | ||
1030 | * otherwise discard it. | ||
1031 | */ | ||
1032 | if (kfifo_avail(&priv->pw_fifo) >= RIO_PW_MSG_SIZE) { | ||
1033 | priv->port_write_msg.msg_count++; | ||
1034 | kfifo_in(&priv->pw_fifo, priv->port_write_msg.virt, | ||
1035 | RIO_PW_MSG_SIZE); | ||
1036 | } else { | ||
1037 | priv->port_write_msg.discard_count++; | ||
1038 | pr_info("RIO: ISR Discarded Port-Write Msg(s) (%d)\n", | ||
1039 | priv->port_write_msg.discard_count); | ||
1040 | } | ||
1041 | schedule_work(&priv->pw_work); | ||
1042 | } | ||
1043 | |||
1044 | /* Issue Clear Queue command. This allows another | ||
1045 | * port-write to be received. | ||
1046 | */ | ||
1047 | out_be32(&priv->msg_regs->pwmr, ipwmr | RIO_IPWMR_CQ); | ||
1048 | |||
1049 | return IRQ_HANDLED; | ||
1050 | } | ||
1051 | |||
1052 | static void fsl_pw_dpc(struct work_struct *work) | ||
1053 | { | ||
1054 | struct rio_priv *priv = container_of(work, struct rio_priv, pw_work); | ||
1055 | unsigned long flags; | ||
1056 | u32 msg_buffer[RIO_PW_MSG_SIZE/sizeof(u32)]; | ||
1057 | |||
1058 | /* | ||
1059 | * Process port-write messages | ||
1060 | */ | ||
1061 | spin_lock_irqsave(&priv->pw_fifo_lock, flags); | ||
1062 | while (kfifo_out(&priv->pw_fifo, (unsigned char *)msg_buffer, | ||
1063 | RIO_PW_MSG_SIZE)) { | ||
1064 | /* Process one message */ | ||
1065 | spin_unlock_irqrestore(&priv->pw_fifo_lock, flags); | ||
1066 | #ifdef DEBUG_PW | ||
1067 | { | ||
1068 | u32 i; | ||
1069 | pr_debug("%s : Port-Write Message:", __func__); | ||
1070 | for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32); i++) { | ||
1071 | if ((i%4) == 0) | ||
1072 | pr_debug("\n0x%02x: 0x%08x", i*4, | ||
1073 | msg_buffer[i]); | ||
1074 | else | ||
1075 | pr_debug(" 0x%08x", msg_buffer[i]); | ||
1076 | } | ||
1077 | pr_debug("\n"); | ||
1078 | } | ||
1079 | #endif | ||
1080 | /* Pass the port-write message to RIO core for processing */ | ||
1081 | rio_inb_pwrite_handler((union rio_pw_msg *)msg_buffer); | ||
1082 | spin_lock_irqsave(&priv->pw_fifo_lock, flags); | ||
1083 | } | ||
1084 | spin_unlock_irqrestore(&priv->pw_fifo_lock, flags); | ||
1085 | } | ||
1086 | |||
1087 | /** | ||
1088 | * fsl_rio_pw_enable - enable/disable port-write interface init | ||
1089 | * @mport: Master port implementing the port write unit | ||
1090 | * @enable: 1=enable; 0=disable port-write message handling | ||
1091 | */ | ||
1092 | static int fsl_rio_pw_enable(struct rio_mport *mport, int enable) | ||
1093 | { | ||
1094 | struct rio_priv *priv = mport->priv; | ||
1095 | u32 rval; | ||
1096 | |||
1097 | rval = in_be32(&priv->msg_regs->pwmr); | ||
1098 | |||
1099 | if (enable) | ||
1100 | rval |= RIO_IPWMR_PWE; | ||
1101 | else | ||
1102 | rval &= ~RIO_IPWMR_PWE; | ||
1103 | |||
1104 | out_be32(&priv->msg_regs->pwmr, rval); | ||
1105 | |||
1106 | return 0; | ||
1107 | } | ||
1108 | |||
1109 | /** | ||
1110 | * fsl_rio_port_write_init - MPC85xx port write interface init | ||
1111 | * @mport: Master port implementing the port write unit | ||
1112 | * | ||
1113 | * Initializes port write unit hardware and DMA buffer | ||
1114 | * ring. Called from fsl_rio_setup(). Returns %0 on success | ||
1115 | * or %-ENOMEM on failure. | ||
1116 | */ | ||
1117 | static int fsl_rio_port_write_init(struct rio_mport *mport) | ||
1118 | { | ||
1119 | struct rio_priv *priv = mport->priv; | ||
1120 | int rc = 0; | ||
1121 | |||
1122 | /* Following configurations require a disabled port write controller */ | ||
1123 | out_be32(&priv->msg_regs->pwmr, | ||
1124 | in_be32(&priv->msg_regs->pwmr) & ~RIO_IPWMR_PWE); | ||
1125 | |||
1126 | /* Initialize port write */ | ||
1127 | priv->port_write_msg.virt = dma_alloc_coherent(priv->dev, | ||
1128 | RIO_PW_MSG_SIZE, | ||
1129 | &priv->port_write_msg.phys, GFP_KERNEL); | ||
1130 | if (!priv->port_write_msg.virt) { | ||
1131 | pr_err("RIO: unable allocate port write queue\n"); | ||
1132 | return -ENOMEM; | ||
1133 | } | ||
1134 | |||
1135 | priv->port_write_msg.err_count = 0; | ||
1136 | priv->port_write_msg.discard_count = 0; | ||
1137 | |||
1138 | /* Point dequeue/enqueue pointers at first entry */ | ||
1139 | out_be32(&priv->msg_regs->epwqbar, 0); | ||
1140 | out_be32(&priv->msg_regs->pwqbar, (u32) priv->port_write_msg.phys); | ||
1141 | |||
1142 | pr_debug("EIPWQBAR: 0x%08x IPWQBAR: 0x%08x\n", | ||
1143 | in_be32(&priv->msg_regs->epwqbar), | ||
1144 | in_be32(&priv->msg_regs->pwqbar)); | ||
1145 | |||
1146 | /* Clear interrupt status IPWSR */ | ||
1147 | out_be32(&priv->msg_regs->pwsr, | ||
1148 | (RIO_IPWSR_TE | RIO_IPWSR_QFI | RIO_IPWSR_PWD)); | ||
1149 | |||
1150 | /* Configure port write contoller for snooping enable all reporting, | ||
1151 | clear queue full */ | ||
1152 | out_be32(&priv->msg_regs->pwmr, | ||
1153 | RIO_IPWMR_SEN | RIO_IPWMR_QFIE | RIO_IPWMR_EIE | RIO_IPWMR_CQ); | ||
1154 | |||
1155 | |||
1156 | /* Hook up port-write handler */ | ||
1157 | rc = request_irq(IRQ_RIO_PW(mport), fsl_rio_port_write_handler, 0, | ||
1158 | "port-write", (void *)mport); | ||
1159 | if (rc < 0) { | ||
1160 | pr_err("MPC85xx RIO: unable to request inbound doorbell irq"); | ||
1161 | goto err_out; | ||
1162 | } | ||
1163 | |||
1164 | INIT_WORK(&priv->pw_work, fsl_pw_dpc); | ||
1165 | spin_lock_init(&priv->pw_fifo_lock); | ||
1166 | if (kfifo_alloc(&priv->pw_fifo, RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) { | ||
1167 | pr_err("FIFO allocation failed\n"); | ||
1168 | rc = -ENOMEM; | ||
1169 | goto err_out_irq; | ||
1170 | } | ||
1171 | |||
1172 | pr_debug("IPWMR: 0x%08x IPWSR: 0x%08x\n", | ||
1173 | in_be32(&priv->msg_regs->pwmr), | ||
1174 | in_be32(&priv->msg_regs->pwsr)); | ||
1175 | |||
1176 | return rc; | ||
1177 | |||
1178 | err_out_irq: | ||
1179 | free_irq(IRQ_RIO_PW(mport), (void *)mport); | ||
1180 | err_out: | ||
1181 | dma_free_coherent(priv->dev, RIO_PW_MSG_SIZE, | ||
1182 | priv->port_write_msg.virt, | ||
1183 | priv->port_write_msg.phys); | ||
1184 | return rc; | ||
1185 | } | ||
1186 | |||
933 | static char *cmdline = NULL; | 1187 | static char *cmdline = NULL; |
934 | 1188 | ||
935 | static int fsl_rio_get_hdid(int index) | 1189 | static int fsl_rio_get_hdid(int index) |
@@ -1067,6 +1321,7 @@ int fsl_rio_setup(struct of_device *dev) | |||
1067 | ops->cread = fsl_rio_config_read; | 1321 | ops->cread = fsl_rio_config_read; |
1068 | ops->cwrite = fsl_rio_config_write; | 1322 | ops->cwrite = fsl_rio_config_write; |
1069 | ops->dsend = fsl_rio_doorbell_send; | 1323 | ops->dsend = fsl_rio_doorbell_send; |
1324 | ops->pwenable = fsl_rio_pw_enable; | ||
1070 | 1325 | ||
1071 | port = kzalloc(sizeof(struct rio_mport), GFP_KERNEL); | 1326 | port = kzalloc(sizeof(struct rio_mport), GFP_KERNEL); |
1072 | if (!port) { | 1327 | if (!port) { |
@@ -1089,11 +1344,12 @@ int fsl_rio_setup(struct of_device *dev) | |||
1089 | port->iores.flags = IORESOURCE_MEM; | 1344 | port->iores.flags = IORESOURCE_MEM; |
1090 | port->iores.name = "rio_io_win"; | 1345 | port->iores.name = "rio_io_win"; |
1091 | 1346 | ||
1347 | priv->pwirq = irq_of_parse_and_map(dev->node, 0); | ||
1092 | priv->bellirq = irq_of_parse_and_map(dev->dev.of_node, 2); | 1348 | priv->bellirq = irq_of_parse_and_map(dev->dev.of_node, 2); |
1093 | priv->txirq = irq_of_parse_and_map(dev->dev.of_node, 3); | 1349 | priv->txirq = irq_of_parse_and_map(dev->dev.of_node, 3); |
1094 | priv->rxirq = irq_of_parse_and_map(dev->dev.of_node, 4); | 1350 | priv->rxirq = irq_of_parse_and_map(dev->dev.of_node, 4); |
1095 | dev_info(&dev->dev, "bellirq: %d, txirq: %d, rxirq %d\n", priv->bellirq, | 1351 | dev_info(&dev->dev, "pwirq: %d, bellirq: %d, txirq: %d, rxirq %d\n", |
1096 | priv->txirq, priv->rxirq); | 1352 | priv->pwirq, priv->bellirq, priv->txirq, priv->rxirq); |
1097 | 1353 | ||
1098 | rio_init_dbell_res(&port->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff); | 1354 | rio_init_dbell_res(&port->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff); |
1099 | rio_init_mbox_res(&port->riores[RIO_INB_MBOX_RESOURCE], 0, 0); | 1355 | rio_init_mbox_res(&port->riores[RIO_INB_MBOX_RESOURCE], 0, 0); |
@@ -1175,6 +1431,7 @@ int fsl_rio_setup(struct of_device *dev) | |||
1175 | (law_start + RIO_MAINT_WIN_SIZE) >> 12); | 1431 | (law_start + RIO_MAINT_WIN_SIZE) >> 12); |
1176 | out_be32(&priv->dbell_atmu_regs->rowar, 0x8004200b); /* 4k */ | 1432 | out_be32(&priv->dbell_atmu_regs->rowar, 0x8004200b); /* 4k */ |
1177 | fsl_rio_doorbell_init(port); | 1433 | fsl_rio_doorbell_init(port); |
1434 | fsl_rio_port_write_init(port); | ||
1178 | 1435 | ||
1179 | return 0; | 1436 | return 0; |
1180 | err: | 1437 | err: |