1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Bestcomm FEC tasks driver 4 * 5 * Copyright (C) 2006-2007 Sylvain Munaut <tnt@246tNt.com> 6 * Copyright (C) 2003-2004 MontaVista, Software, Inc. 7 * ( by Dale Farnsworth <dfarnsworth@mvista.com> ) 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/types.h> 13 #include <asm/io.h> 14 15 #include <linux/fsl/bestcomm/bestcomm.h> 16 #include <linux/fsl/bestcomm/bestcomm_priv.h> 17 #include <linux/fsl/bestcomm/fec.h> 18 19 20 /* ======================================================================== */ 21 /* Task image/var/inc */ 22 /* ======================================================================== */ 23 24 /* fec tasks images */ 25 extern u32 bcom_fec_rx_task[]; 26 extern u32 bcom_fec_tx_task[]; 27 28 /* rx task vars that need to be set before enabling the task */ 29 struct bcom_fec_rx_var { 30 u32 enable; /* (u16*) address of task's control register */ 31 u32 fifo; /* (u32*) address of fec's fifo */ 32 u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ 33 u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ 34 u32 bd_start; /* (struct bcom_bd*) current bd */ 35 u32 buffer_size; /* size of receive buffer */ 36 }; 37 38 /* rx task incs that need to be set before enabling the task */ 39 struct bcom_fec_rx_inc { 40 u16 pad0; 41 s16 incr_bytes; 42 u16 pad1; 43 s16 incr_dst; 44 u16 pad2; 45 s16 incr_dst_ma; 46 }; 47 48 /* tx task vars that need to be set before enabling the task */ 49 struct bcom_fec_tx_var { 50 u32 DRD; /* (u32*) address of self-modified DRD */ 51 u32 fifo; /* (u32*) address of fec's fifo */ 52 u32 enable; /* (u16*) address of task's control register */ 53 u32 bd_base; /* (struct bcom_bd*) beginning of ring buffer */ 54 u32 bd_last; /* (struct bcom_bd*) end of ring buffer */ 55 u32 bd_start; /* (struct bcom_bd*) current bd */ 56 u32 buffer_size; /* set by uCode for each packet */ 57 }; 58 59 /* tx task incs that need to be set before enabling the task */ 60 struct bcom_fec_tx_inc { 61 u16 pad0; 62 s16 incr_bytes; 63 u16 pad1; 64 s16 incr_src; 65 u16 pad2; 66 s16 incr_src_ma; 67 }; 68 69 /* private structure in the task */ 70 struct bcom_fec_priv { 71 phys_addr_t fifo; 72 int maxbufsize; 73 }; 74 75 76 /* ======================================================================== */ 77 /* Task support code */ 78 /* ======================================================================== */ 79 80 struct bcom_task * 81 bcom_fec_rx_init(int queue_len, phys_addr_t fifo, int maxbufsize) 82 { 83 struct bcom_task *tsk; 84 struct bcom_fec_priv *priv; 85 86 tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd), 87 sizeof(struct bcom_fec_priv)); 88 if (!tsk) 89 return NULL; 90 91 tsk->flags = BCOM_FLAGS_NONE; 92 93 priv = tsk->priv; 94 priv->fifo = fifo; 95 priv->maxbufsize = maxbufsize; 96 97 if (bcom_fec_rx_reset(tsk)) { 98 bcom_task_free(tsk); 99 return NULL; 100 } 101 102 return tsk; 103 } 104 EXPORT_SYMBOL_GPL(bcom_fec_rx_init); 105 106 int 107 bcom_fec_rx_reset(struct bcom_task *tsk) 108 { 109 struct bcom_fec_priv *priv = tsk->priv; 110 struct bcom_fec_rx_var *var; 111 struct bcom_fec_rx_inc *inc; 112 113 /* Shutdown the task */ 114 bcom_disable_task(tsk->tasknum); 115 116 /* Reset the microcode */ 117 var = (struct bcom_fec_rx_var *) bcom_task_var(tsk->tasknum); 118 inc = (struct bcom_fec_rx_inc *) bcom_task_inc(tsk->tasknum); 119 120 if (bcom_load_image(tsk->tasknum, bcom_fec_rx_task)) 121 return -1; 122 123 var->enable = bcom_eng->regs_base + 124 offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); 125 var->fifo = (u32) priv->fifo; 126 var->bd_base = tsk->bd_pa; 127 var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); 128 var->bd_start = tsk->bd_pa; 129 var->buffer_size = priv->maxbufsize; 130 131 inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */ 132 inc->incr_dst = sizeof(u32); /* task image, but we stick */ 133 inc->incr_dst_ma= sizeof(u8); /* to the official ones */ 134 135 /* Reset the BDs */ 136 tsk->index = 0; 137 tsk->outdex = 0; 138 139 memset_io(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); 140 141 /* Configure some stuff */ 142 bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_RX_BD_PRAGMA); 143 bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); 144 145 out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_RX], BCOM_IPR_FEC_RX); 146 147 out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */ 148 149 return 0; 150 } 151 EXPORT_SYMBOL_GPL(bcom_fec_rx_reset); 152 153 void 154 bcom_fec_rx_release(struct bcom_task *tsk) 155 { 156 /* Nothing special for the FEC tasks */ 157 bcom_task_free(tsk); 158 } 159 EXPORT_SYMBOL_GPL(bcom_fec_rx_release); 160 161 162 163 /* Return 2nd to last DRD */ 164 /* This is an ugly hack, but at least it's only done 165 once at initialization */ 166 static u32 *self_modified_drd(int tasknum) 167 { 168 u32 *desc; 169 int num_descs; 170 int drd_count; 171 int i; 172 173 num_descs = bcom_task_num_descs(tasknum); 174 desc = bcom_task_desc(tasknum) + num_descs - 1; 175 drd_count = 0; 176 for (i=0; i<num_descs; i++, desc--) 177 if (bcom_desc_is_drd(*desc) && ++drd_count == 3) 178 break; 179 return desc; 180 } 181 182 struct bcom_task * 183 bcom_fec_tx_init(int queue_len, phys_addr_t fifo) 184 { 185 struct bcom_task *tsk; 186 struct bcom_fec_priv *priv; 187 188 tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_fec_bd), 189 sizeof(struct bcom_fec_priv)); 190 if (!tsk) 191 return NULL; 192 193 tsk->flags = BCOM_FLAGS_ENABLE_TASK; 194 195 priv = tsk->priv; 196 priv->fifo = fifo; 197 198 if (bcom_fec_tx_reset(tsk)) { 199 bcom_task_free(tsk); 200 return NULL; 201 } 202 203 return tsk; 204 } 205 EXPORT_SYMBOL_GPL(bcom_fec_tx_init); 206 207 int 208 bcom_fec_tx_reset(struct bcom_task *tsk) 209 { 210 struct bcom_fec_priv *priv = tsk->priv; 211 struct bcom_fec_tx_var *var; 212 struct bcom_fec_tx_inc *inc; 213 214 /* Shutdown the task */ 215 bcom_disable_task(tsk->tasknum); 216 217 /* Reset the microcode */ 218 var = (struct bcom_fec_tx_var *) bcom_task_var(tsk->tasknum); 219 inc = (struct bcom_fec_tx_inc *) bcom_task_inc(tsk->tasknum); 220 221 if (bcom_load_image(tsk->tasknum, bcom_fec_tx_task)) 222 return -1; 223 224 var->enable = bcom_eng->regs_base + 225 offsetof(struct mpc52xx_sdma, tcr[tsk->tasknum]); 226 var->fifo = (u32) priv->fifo; 227 var->DRD = bcom_sram_va2pa(self_modified_drd(tsk->tasknum)); 228 var->bd_base = tsk->bd_pa; 229 var->bd_last = tsk->bd_pa + ((tsk->num_bd-1) * tsk->bd_size); 230 var->bd_start = tsk->bd_pa; 231 232 inc->incr_bytes = -(s16)sizeof(u32); /* These should be in the */ 233 inc->incr_src = sizeof(u32); /* task image, but we stick */ 234 inc->incr_src_ma= sizeof(u8); /* to the official ones */ 235 236 /* Reset the BDs */ 237 tsk->index = 0; 238 tsk->outdex = 0; 239 240 memset_io(tsk->bd, 0x00, tsk->num_bd * tsk->bd_size); 241 242 /* Configure some stuff */ 243 bcom_set_task_pragma(tsk->tasknum, BCOM_FEC_TX_BD_PRAGMA); 244 bcom_set_task_auto_start(tsk->tasknum, tsk->tasknum); 245 246 out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_FEC_TX], BCOM_IPR_FEC_TX); 247 248 out_be32(&bcom_eng->regs->IntPend, 1<<tsk->tasknum); /* Clear ints */ 249 250 return 0; 251 } 252 EXPORT_SYMBOL_GPL(bcom_fec_tx_reset); 253 254 void 255 bcom_fec_tx_release(struct bcom_task *tsk) 256 { 257 /* Nothing special for the FEC tasks */ 258 bcom_task_free(tsk); 259 } 260 EXPORT_SYMBOL_GPL(bcom_fec_tx_release); 261 262 263 MODULE_DESCRIPTION("BestComm FEC tasks driver"); 264 MODULE_AUTHOR("Dale Farnsworth <dfarnsworth@mvista.com>"); 265 MODULE_LICENSE("GPL v2"); 266