blob: b930d686a4d18d1b06c5fe0915674f67492df900 [file] [log] [blame]
Li Yang98658532006-10-03 23:10:46 -05001/*
2 * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved.
3 *
4 * Authors: Shlomi Gridish <gridish@freescale.com>
5 * Li Yang <leoli@freescale.com>
6 *
7 * Description:
8 * QE UCC Slow API Set - UCC Slow specific routines implementations.
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 */
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/errno.h>
18#include <linux/slab.h>
19#include <linux/stddef.h>
20#include <linux/interrupt.h>
21
Li Yang98658532006-10-03 23:10:46 -050022#include <asm/io.h>
23#include <asm/immap_qe.h>
24#include <asm/qe.h>
25
26#include <asm/ucc.h>
27#include <asm/ucc_slow.h>
28
Li Yang98658532006-10-03 23:10:46 -050029u32 ucc_slow_get_qe_cr_subblock(int uccs_num)
30{
31 switch (uccs_num) {
32 case 0: return QE_CR_SUBBLOCK_UCCSLOW1;
33 case 1: return QE_CR_SUBBLOCK_UCCSLOW2;
34 case 2: return QE_CR_SUBBLOCK_UCCSLOW3;
35 case 3: return QE_CR_SUBBLOCK_UCCSLOW4;
36 case 4: return QE_CR_SUBBLOCK_UCCSLOW5;
37 case 5: return QE_CR_SUBBLOCK_UCCSLOW6;
38 case 6: return QE_CR_SUBBLOCK_UCCSLOW7;
39 case 7: return QE_CR_SUBBLOCK_UCCSLOW8;
40 default: return QE_CR_SUBBLOCK_INVALID;
41 }
42}
43
44void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs)
45{
46 out_be16(&uccs->us_regs->utodr, UCC_SLOW_TOD);
47}
48
49void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs)
50{
51 struct ucc_slow_info *us_info = uccs->us_info;
52 u32 id;
53
54 id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
55 qe_issue_cmd(QE_GRACEFUL_STOP_TX, id,
56 QE_CR_PROTOCOL_UNSPECIFIED, 0);
57}
58
59void ucc_slow_stop_tx(struct ucc_slow_private * uccs)
60{
61 struct ucc_slow_info *us_info = uccs->us_info;
62 u32 id;
63
64 id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
65 qe_issue_cmd(QE_STOP_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0);
66}
67
68void ucc_slow_restart_tx(struct ucc_slow_private * uccs)
69{
70 struct ucc_slow_info *us_info = uccs->us_info;
71 u32 id;
72
73 id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
74 qe_issue_cmd(QE_RESTART_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0);
75}
76
77void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode)
78{
79 struct ucc_slow *us_regs;
80 u32 gumr_l;
81
82 us_regs = uccs->us_regs;
83
84 /* Enable reception and/or transmission on this UCC. */
85 gumr_l = in_be32(&us_regs->gumr_l);
86 if (mode & COMM_DIR_TX) {
87 gumr_l |= UCC_SLOW_GUMR_L_ENT;
88 uccs->enabled_tx = 1;
89 }
90 if (mode & COMM_DIR_RX) {
91 gumr_l |= UCC_SLOW_GUMR_L_ENR;
92 uccs->enabled_rx = 1;
93 }
94 out_be32(&us_regs->gumr_l, gumr_l);
95}
96
97void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode)
98{
99 struct ucc_slow *us_regs;
100 u32 gumr_l;
101
102 us_regs = uccs->us_regs;
103
104 /* Disable reception and/or transmission on this UCC. */
105 gumr_l = in_be32(&us_regs->gumr_l);
106 if (mode & COMM_DIR_TX) {
107 gumr_l &= ~UCC_SLOW_GUMR_L_ENT;
108 uccs->enabled_tx = 0;
109 }
110 if (mode & COMM_DIR_RX) {
111 gumr_l &= ~UCC_SLOW_GUMR_L_ENR;
112 uccs->enabled_rx = 0;
113 }
114 out_be32(&us_regs->gumr_l, gumr_l);
115}
116
117int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** uccs_ret)
118{
Timur Tabi5af68af2007-02-16 22:31:21 -0600119 struct ucc_slow_private *uccs;
Li Yang98658532006-10-03 23:10:46 -0500120 u32 i;
121 struct ucc_slow *us_regs;
122 u32 gumr;
Timur Tabi5af68af2007-02-16 22:31:21 -0600123 struct qe_bd *bd;
Li Yang98658532006-10-03 23:10:46 -0500124 u32 id;
125 u32 command;
Timur Tabi5af68af2007-02-16 22:31:21 -0600126 int ret = 0;
Li Yang98658532006-10-03 23:10:46 -0500127
128 if (!us_info)
129 return -EINVAL;
130
131 /* check if the UCC port number is in range. */
132 if ((us_info->ucc_num < 0) || (us_info->ucc_num > UCC_MAX_NUM - 1)) {
Timur Tabi5af68af2007-02-16 22:31:21 -0600133 printk(KERN_ERR "%s: illegal UCC number", __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500134 return -EINVAL;
135 }
136
137 /*
138 * Set mrblr
139 * Check that 'max_rx_buf_length' is properly aligned (4), unless
Timur Tabi5af68af2007-02-16 22:31:21 -0600140 * rfw is 1, meaning that QE accepts one byte at a time, unlike normal
Li Yang98658532006-10-03 23:10:46 -0500141 * case when QE accepts 32 bits at a time.
142 */
143 if ((!us_info->rfw) &&
144 (us_info->max_rx_buf_length & (UCC_SLOW_MRBLR_ALIGNMENT - 1))) {
Timur Tabi5af68af2007-02-16 22:31:21 -0600145 printk(KERN_ERR "max_rx_buf_length not aligned.");
Li Yang98658532006-10-03 23:10:46 -0500146 return -EINVAL;
147 }
148
Yan Burmanf8485352006-12-02 13:26:57 +0200149 uccs = kzalloc(sizeof(struct ucc_slow_private), GFP_KERNEL);
Li Yang98658532006-10-03 23:10:46 -0500150 if (!uccs) {
Timur Tabi5af68af2007-02-16 22:31:21 -0600151 printk(KERN_ERR "%s: Cannot allocate private data", __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500152 return -ENOMEM;
153 }
Li Yang98658532006-10-03 23:10:46 -0500154
155 /* Fill slow UCC structure */
156 uccs->us_info = us_info;
Timur Tabi5af68af2007-02-16 22:31:21 -0600157 /* Set the PHY base address */
158 uccs->us_regs = ioremap(us_info->regs, sizeof(struct ucc_slow));
159 if (uccs->us_regs == NULL) {
160 printk(KERN_ERR "%s: Cannot map UCC registers", __FUNCTION__);
161 return -ENOMEM;
162 }
163
Li Yang98658532006-10-03 23:10:46 -0500164 uccs->saved_uccm = 0;
165 uccs->p_rx_frame = 0;
Li Yang98658532006-10-03 23:10:46 -0500166 us_regs = uccs->us_regs;
167 uccs->p_ucce = (u16 *) & (us_regs->ucce);
168 uccs->p_uccm = (u16 *) & (us_regs->uccm);
169#ifdef STATISTICS
170 uccs->rx_frames = 0;
171 uccs->tx_frames = 0;
172 uccs->rx_discarded = 0;
173#endif /* STATISTICS */
174
175 /* Get PRAM base */
Timur Tabi5af68af2007-02-16 22:31:21 -0600176 uccs->us_pram_offset =
177 qe_muram_alloc(UCC_SLOW_PRAM_SIZE, ALIGNMENT_OF_UCC_SLOW_PRAM);
Li Yang98658532006-10-03 23:10:46 -0500178 if (IS_MURAM_ERR(uccs->us_pram_offset)) {
Timur Tabi5af68af2007-02-16 22:31:21 -0600179 printk(KERN_ERR "%s: cannot allocate MURAM for PRAM", __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500180 ucc_slow_free(uccs);
181 return -ENOMEM;
182 }
183 id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
184 qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, id, QE_CR_PROTOCOL_UNSPECIFIED,
Timur Tabi5af68af2007-02-16 22:31:21 -0600185 uccs->us_pram_offset);
Li Yang98658532006-10-03 23:10:46 -0500186
187 uccs->us_pram = qe_muram_addr(uccs->us_pram_offset);
188
189 /* Init Guemr register */
Timur Tabide74f702007-03-15 09:48:53 -0500190 if ((ret = ucc_init_guemr((struct ucc_common *) us_regs))) {
Timur Tabi5af68af2007-02-16 22:31:21 -0600191 printk(KERN_ERR "%s: cannot init GUEMR", __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500192 ucc_slow_free(uccs);
193 return ret;
194 }
195
196 /* Set UCC to slow type */
197 if ((ret = ucc_set_type(us_info->ucc_num,
Timur Tabide74f702007-03-15 09:48:53 -0500198 (struct ucc_common *) us_regs,
Li Yang98658532006-10-03 23:10:46 -0500199 UCC_SPEED_TYPE_SLOW))) {
Timur Tabi5af68af2007-02-16 22:31:21 -0600200 printk(KERN_ERR "%s: cannot set UCC type", __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500201 ucc_slow_free(uccs);
202 return ret;
203 }
204
205 out_be16(&uccs->us_pram->mrblr, us_info->max_rx_buf_length);
206
207 INIT_LIST_HEAD(&uccs->confQ);
208
209 /* Allocate BDs. */
210 uccs->rx_base_offset =
211 qe_muram_alloc(us_info->rx_bd_ring_len * sizeof(struct qe_bd),
212 QE_ALIGNMENT_OF_BD);
213 if (IS_MURAM_ERR(uccs->rx_base_offset)) {
Timur Tabi5af68af2007-02-16 22:31:21 -0600214 printk(KERN_ERR "%s: cannot allocate RX BDs", __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500215 uccs->rx_base_offset = 0;
216 ucc_slow_free(uccs);
217 return -ENOMEM;
218 }
219
220 uccs->tx_base_offset =
221 qe_muram_alloc(us_info->tx_bd_ring_len * sizeof(struct qe_bd),
222 QE_ALIGNMENT_OF_BD);
223 if (IS_MURAM_ERR(uccs->tx_base_offset)) {
Timur Tabi5af68af2007-02-16 22:31:21 -0600224 printk(KERN_ERR "%s: cannot allocate TX BDs", __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500225 uccs->tx_base_offset = 0;
226 ucc_slow_free(uccs);
227 return -ENOMEM;
228 }
229
230 /* Init Tx bds */
231 bd = uccs->confBd = uccs->tx_bd = qe_muram_addr(uccs->tx_base_offset);
Timur Tabi5af68af2007-02-16 22:31:21 -0600232 for (i = 0; i < us_info->tx_bd_ring_len - 1; i++) {
Li Yang98658532006-10-03 23:10:46 -0500233 /* clear bd buffer */
Timur Tabi5af68af2007-02-16 22:31:21 -0600234 out_be32(&bd->buf, 0);
Li Yang98658532006-10-03 23:10:46 -0500235 /* set bd status and length */
Timur Tabi5af68af2007-02-16 22:31:21 -0600236 out_be32((u32 *) bd, 0);
237 bd++;
Li Yang98658532006-10-03 23:10:46 -0500238 }
Timur Tabi5af68af2007-02-16 22:31:21 -0600239 /* for last BD set Wrap bit */
240 out_be32(&bd->buf, 0);
241 out_be32((u32 *) bd, cpu_to_be32(T_W));
Li Yang98658532006-10-03 23:10:46 -0500242
243 /* Init Rx bds */
244 bd = uccs->rx_bd = qe_muram_addr(uccs->rx_base_offset);
Timur Tabi5af68af2007-02-16 22:31:21 -0600245 for (i = 0; i < us_info->rx_bd_ring_len - 1; i++) {
Li Yang98658532006-10-03 23:10:46 -0500246 /* set bd status and length */
247 out_be32((u32*)bd, 0);
248 /* clear bd buffer */
Timur Tabi5af68af2007-02-16 22:31:21 -0600249 out_be32(&bd->buf, 0);
250 bd++;
Li Yang98658532006-10-03 23:10:46 -0500251 }
Timur Tabi5af68af2007-02-16 22:31:21 -0600252 /* for last BD set Wrap bit */
253 out_be32((u32*)bd, cpu_to_be32(R_W));
254 out_be32(&bd->buf, 0);
Li Yang98658532006-10-03 23:10:46 -0500255
256 /* Set GUMR (For more details see the hardware spec.). */
257 /* gumr_h */
Timur Tabi5af68af2007-02-16 22:31:21 -0600258 gumr = us_info->tcrc;
Li Yang98658532006-10-03 23:10:46 -0500259 if (us_info->cdp)
260 gumr |= UCC_SLOW_GUMR_H_CDP;
261 if (us_info->ctsp)
262 gumr |= UCC_SLOW_GUMR_H_CTSP;
263 if (us_info->cds)
264 gumr |= UCC_SLOW_GUMR_H_CDS;
265 if (us_info->ctss)
266 gumr |= UCC_SLOW_GUMR_H_CTSS;
267 if (us_info->tfl)
268 gumr |= UCC_SLOW_GUMR_H_TFL;
269 if (us_info->rfw)
270 gumr |= UCC_SLOW_GUMR_H_RFW;
271 if (us_info->txsy)
272 gumr |= UCC_SLOW_GUMR_H_TXSY;
273 if (us_info->rtsm)
274 gumr |= UCC_SLOW_GUMR_H_RTSM;
275 out_be32(&us_regs->gumr_h, gumr);
276
277 /* gumr_l */
Timur Tabi5af68af2007-02-16 22:31:21 -0600278 gumr = us_info->tdcr | us_info->rdcr | us_info->tenc | us_info->renc |
279 us_info->diag | us_info->mode;
Li Yang98658532006-10-03 23:10:46 -0500280 if (us_info->tci)
281 gumr |= UCC_SLOW_GUMR_L_TCI;
282 if (us_info->rinv)
283 gumr |= UCC_SLOW_GUMR_L_RINV;
284 if (us_info->tinv)
285 gumr |= UCC_SLOW_GUMR_L_TINV;
286 if (us_info->tend)
287 gumr |= UCC_SLOW_GUMR_L_TEND;
Li Yang98658532006-10-03 23:10:46 -0500288 out_be32(&us_regs->gumr_l, gumr);
289
290 /* Function code registers */
Li Yang98658532006-10-03 23:10:46 -0500291
292 /* if the data is in cachable memory, the 'global' */
293 /* in the function code should be set. */
Timur Tabi5af68af2007-02-16 22:31:21 -0600294 uccs->us_pram->tfcr = uccs->us_pram->rfcr =
295 us_info->data_mem_part | QE_BMR_BYTE_ORDER_BO_MOT;
Li Yang98658532006-10-03 23:10:46 -0500296
297 /* rbase, tbase are offsets from MURAM base */
298 out_be16(&uccs->us_pram->rbase, uccs->us_pram_offset);
299 out_be16(&uccs->us_pram->tbase, uccs->us_pram_offset);
300
301 /* Mux clocking */
302 /* Grant Support */
303 ucc_set_qe_mux_grant(us_info->ucc_num, us_info->grant_support);
304 /* Breakpoint Support */
305 ucc_set_qe_mux_bkpt(us_info->ucc_num, us_info->brkpt_support);
306 /* Set Tsa or NMSI mode. */
307 ucc_set_qe_mux_tsa(us_info->ucc_num, us_info->tsa);
308 /* If NMSI (not Tsa), set Tx and Rx clock. */
309 if (!us_info->tsa) {
310 /* Rx clock routing */
Timur Tabi5af68af2007-02-16 22:31:21 -0600311 if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->rx_clock,
312 COMM_DIR_RX)) {
313 printk(KERN_ERR "%s: illegal value for RX clock",
314 __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500315 ucc_slow_free(uccs);
316 return -EINVAL;
317 }
318 /* Tx clock routing */
Timur Tabi5af68af2007-02-16 22:31:21 -0600319 if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->tx_clock,
320 COMM_DIR_TX)) {
321 printk(KERN_ERR "%s: illegal value for TX clock",
322 __FUNCTION__);
Li Yang98658532006-10-03 23:10:46 -0500323 ucc_slow_free(uccs);
324 return -EINVAL;
325 }
326 }
327
Li Yang98658532006-10-03 23:10:46 -0500328 /* Set interrupt mask register at UCC level. */
329 out_be16(&us_regs->uccm, us_info->uccm_mask);
330
Timur Tabi5af68af2007-02-16 22:31:21 -0600331 /* First, clear anything pending at UCC level,
332 * otherwise, old garbage may come through
333 * as soon as the dam is opened. */
Li Yang98658532006-10-03 23:10:46 -0500334
335 /* Writing '1' clears */
336 out_be16(&us_regs->ucce, 0xffff);
337
338 /* Issue QE Init command */
339 if (us_info->init_tx && us_info->init_rx)
340 command = QE_INIT_TX_RX;
341 else if (us_info->init_tx)
342 command = QE_INIT_TX;
343 else
344 command = QE_INIT_RX; /* We know at least one is TRUE */
345 id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num);
346 qe_issue_cmd(command, id, QE_CR_PROTOCOL_UNSPECIFIED, 0);
347
348 *uccs_ret = uccs;
349 return 0;
350}
351
352void ucc_slow_free(struct ucc_slow_private * uccs)
353{
354 if (!uccs)
355 return;
356
357 if (uccs->rx_base_offset)
358 qe_muram_free(uccs->rx_base_offset);
359
360 if (uccs->tx_base_offset)
361 qe_muram_free(uccs->tx_base_offset);
362
363 if (uccs->us_pram) {
364 qe_muram_free(uccs->us_pram_offset);
365 uccs->us_pram = NULL;
366 }
367
368 kfree(uccs);
369}
Timur Tabi5af68af2007-02-16 22:31:21 -0600370
371