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(bd_count: queue_len, bd_size: sizeof(struct bcom_fec_bd), |
87 | priv_size: 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(task: tsk->tasknum); |
115 | |
116 | /* Reset the microcode */ |
117 | var = (struct bcom_fec_rx_var *) bcom_task_var(task: tsk->tasknum); |
118 | inc = (struct bcom_fec_rx_inc *) bcom_task_inc(task: tsk->tasknum); |
119 | |
120 | if (bcom_load_image(task: tsk->tasknum, task_image: 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(task: tsk->tasknum, BCOM_FEC_RX_BD_PRAGMA); |
143 | bcom_set_task_auto_start(task: tsk->tasknum, next_task: 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(task: tasknum); |
174 | desc = bcom_task_desc(task: tasknum) + num_descs - 1; |
175 | drd_count = 0; |
176 | for (i=0; i<num_descs; i++, desc--) |
177 | if (bcom_desc_is_drd(desc: *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(bd_count: queue_len, bd_size: sizeof(struct bcom_fec_bd), |
189 | priv_size: 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(task: tsk->tasknum); |
216 | |
217 | /* Reset the microcode */ |
218 | var = (struct bcom_fec_tx_var *) bcom_task_var(task: tsk->tasknum); |
219 | inc = (struct bcom_fec_tx_inc *) bcom_task_inc(task: tsk->tasknum); |
220 | |
221 | if (bcom_load_image(task: tsk->tasknum, task_image: 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(va: self_modified_drd(tasknum: 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(task: tsk->tasknum, BCOM_FEC_TX_BD_PRAGMA); |
244 | bcom_set_task_auto_start(task: tsk->tasknum, next_task: 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 | |