Gary Jennejohn | d38bfdd | 2008-11-09 12:45:03 +0100 | [diff] [blame] | 1 | /* |
| 2 | * (C) Copyright 2008 |
| 3 | * Gary Jennejohn, DENX Software Engineering GmbH, garyj@denx.de. |
| 4 | * |
| 5 | * Based in part on cpu/mpc8260/ether_scc.c. |
| 6 | * |
| 7 | * See file CREDITS for list of people who contributed to this |
| 8 | * project. |
| 9 | * |
| 10 | * This program is free software; you can redistribute it and/or |
| 11 | * modify it under the terms of the GNU General Public License as |
| 12 | * published by the Free Software Foundation; either version 2 of |
| 13 | * the License, or (at your option) any later version. |
| 14 | * |
| 15 | * This program is distributed in the hope that it will be useful, |
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | * GNU General Public License for more details. |
| 19 | * |
| 20 | * You should have received a copy of the GNU General Public License |
| 21 | * along with this program; if not, write to the Free Software |
| 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| 23 | * MA 02111-1307 USA |
| 24 | */ |
| 25 | |
| 26 | #include <common.h> |
| 27 | #include <malloc.h> |
| 28 | #include <net.h> |
| 29 | |
| 30 | #ifdef CONFIG_KEYMILE_HDLC_ENET |
| 31 | |
| 32 | #include "../common/keymile_hdlc_enet.h" |
| 33 | |
| 34 | char keymile_slot; /* our slot number in the backplane */ |
| 35 | |
| 36 | /* |
| 37 | * Since, except during initialization, ethact is always HDLC ETHERNET |
| 38 | * while we're in the driver, just use serial_printf() everywhere for |
| 39 | * output. This avoids possible conflicts when netconsole is being |
| 40 | * used. |
| 41 | */ |
| 42 | #define dprintf(fmt, args...) serial_printf(fmt, ##args) |
| 43 | |
| 44 | static int already_inited; |
| 45 | |
| 46 | /* |
| 47 | * SCC Ethernet Tx and Rx buffer descriptors allocated at the |
| 48 | * immr->udata_bd address on Dual-Port RAM |
| 49 | * Provide for Double Buffering |
| 50 | */ |
| 51 | typedef volatile struct CommonBufferDescriptor { |
| 52 | cbd_t txbd; /* Tx BD */ |
| 53 | cbd_t rxbd[HDLC_PKTBUFSRX]; /* Rx BD */ |
| 54 | } RTXBD; |
| 55 | |
| 56 | static RTXBD *rtx; |
| 57 | |
| 58 | int keymile_hdlc_enet_init(struct eth_device *, bd_t *); |
| 59 | void keymile_hdlc_enet_halt(struct eth_device *); |
| 60 | extern void keymile_hdlc_enet_init_bds(RTXBD *); |
| 61 | extern void initCachedNumbers(int); |
| 62 | |
| 63 | /* Use SCC1 */ |
| 64 | #define CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE |
| 65 | #define CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK |
| 66 | #define CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ |
| 67 | CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) |
| 68 | #define CMXSCR_VALUE (CMXSCR_RS1CS_CLK11|CMXSCR_TS1CS_CLK11) |
| 69 | #define MGC_PROFF_HDLC PROFF_SCC1 |
| 70 | #define MGC_SCC_HDLC 0 /* Index, not number! */ |
| 71 | |
| 72 | int keymile_hdlc_enet_init(struct eth_device *dev, bd_t *bis) |
| 73 | { |
| 74 | /* int i; */ |
| 75 | uint dpr; |
| 76 | /* volatile cbd_t *bdp; */ |
| 77 | volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR; |
| 78 | volatile cpm8260_t *cp = &(im->im_cpm); |
| 79 | volatile scc_t *sccp; |
| 80 | volatile scc_hdlc_t *hpr; |
| 81 | volatile iop8260_t *iop; |
| 82 | |
| 83 | if (already_inited) |
| 84 | return 0; |
| 85 | |
| 86 | hpr = (scc_hdlc_t *)(&im->im_dprambase[MGC_PROFF_HDLC]); |
| 87 | sccp = (scc_t *)(&im->im_scc[MGC_SCC_HDLC]); |
| 88 | iop = &im->im_ioport; |
| 89 | |
| 90 | /* |
| 91 | * Disable receive and transmit just in case. |
| 92 | */ |
| 93 | sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); |
| 94 | |
| 95 | /* |
| 96 | * Avoid exhausting DPRAM, which would cause a panic. |
| 97 | */ |
| 98 | if (rtx == NULL) { |
| 99 | /* dpr is an offset into dpram */ |
| 100 | dpr = m8260_cpm_dpalloc(sizeof(RTXBD), 8); |
| 101 | rtx = (RTXBD *)&im->im_dprambase[dpr]; |
| 102 | } |
| 103 | |
| 104 | /* We need the slot number for addressing. */ |
| 105 | keymile_slot = *(char *)(CONFIG_SYS_SLOT_ID_BASE + |
| 106 | CONFIG_SYS_SLOT_ID_OFF) & CONFIG_SYS_SLOT_ID_MASK; |
| 107 | /* |
| 108 | * Be consistent with the Linux driver and set |
| 109 | * only enetaddr[0]. |
| 110 | * |
| 111 | * Always add 1 to the slot number so that |
| 112 | * there are no problems with an ethaddr which |
| 113 | * is all 0s. This should be acceptable because |
| 114 | * a board should never have a slot number of 255, |
| 115 | * which is the broadcast address. The HDLC addressing |
| 116 | * uses only the slot number. |
| 117 | */ |
| 118 | dev->enetaddr[0] = keymile_slot + 1; |
| 119 | #ifdef TEST_IT |
| 120 | dprintf("slot %d\n", keymile_slot); |
| 121 | #endif |
| 122 | |
| 123 | /* use pd30, pd31 pins for TXD1, RXD1 respectively */ |
| 124 | iop->iop_ppard |= (0x80000000 >> 30) | (0x80000000 >> 31); |
| 125 | iop->iop_pdird |= (0x80000000 >> 30); |
| 126 | iop->iop_psord |= (0x80000000 >> 30); |
| 127 | |
| 128 | /* use pc21 as CLK11 */ |
| 129 | iop->iop_pparc |= (0x80000000 >> 21); |
| 130 | iop->iop_pdirc &= ~(0x80000000 >> 21); |
| 131 | iop->iop_psorc &= ~(0x80000000 >> 21); |
| 132 | |
| 133 | /* use pc15 as CTS1 */ |
| 134 | iop->iop_pparc |= (0x80000000 >> 15); |
| 135 | iop->iop_pdirc &= ~(0x80000000 >> 15); |
| 136 | iop->iop_psorc &= ~(0x80000000 >> 15); |
| 137 | |
| 138 | /* |
| 139 | * SI clock routing |
| 140 | * use CLK11 |
| 141 | * this also connects SCC1 to NMSI |
| 142 | */ |
| 143 | im->im_cpmux.cmx_scr = (im->im_cpmux.cmx_scr & ~CMXSCR_MASK) | |
| 144 | CMXSCR_VALUE; |
| 145 | |
| 146 | /* keymile_rxIdx = 0; */ |
| 147 | |
| 148 | /* |
| 149 | * Initialize function code registers for big-endian. |
| 150 | */ |
| 151 | hpr->sh_genscc.scc_rfcr = CPMFCR_EB; |
| 152 | hpr->sh_genscc.scc_tfcr = CPMFCR_EB; |
| 153 | |
| 154 | /* |
| 155 | * Set maximum bytes per receive buffer. |
| 156 | */ |
| 157 | hpr->sh_genscc.scc_mrblr = MAX_FRAME_LENGTH; |
| 158 | |
| 159 | /* Setup CRC generator values for HDLC */ |
| 160 | hpr->sh_cmask = 0x0000F0B8; |
| 161 | hpr->sh_cpres = 0x0000FFFF; |
| 162 | |
| 163 | /* Initialize all error counters to 0 */ |
| 164 | hpr->sh_disfc = 0; |
| 165 | hpr->sh_crcec = 0; |
| 166 | hpr->sh_abtsc = 0; |
| 167 | hpr->sh_nmarc = 0; |
| 168 | hpr->sh_retrc = 0; |
| 169 | |
| 170 | /* Set maximum frame length size */ |
| 171 | hpr->sh_mflr = MAX_FRAME_LENGTH; |
| 172 | |
| 173 | /* set to 1 for per frame processing change later if needed */ |
| 174 | hpr->sh_rfthr = 1; |
| 175 | |
| 176 | hpr->sh_hmask = 0xff; |
| 177 | |
| 178 | hpr->sh_haddr2 = SET_HDLC_UUA(keymile_slot); |
| 179 | hpr->sh_haddr3 = hpr->sh_haddr2; |
| 180 | hpr->sh_haddr4 = hpr->sh_haddr2; |
| 181 | /* broadcast */ |
| 182 | hpr->sh_haddr1 = HDLC_BCAST; |
| 183 | |
| 184 | hpr->sh_genscc.scc_rbase = (unsigned int) &rtx->rxbd[0]; |
| 185 | hpr->sh_genscc.scc_tbase = (unsigned int) &rtx->txbd; |
| 186 | |
| 187 | #if 0 |
| 188 | /* |
| 189 | * Initialize the buffer descriptors. |
| 190 | */ |
| 191 | bdp = &rtx->txbd; |
| 192 | bdp->cbd_sc = 0; |
| 193 | bdp->cbd_bufaddr = 0; |
| 194 | bdp->cbd_sc = BD_SC_WRAP; |
| 195 | |
| 196 | /* |
| 197 | * Setup RX packet buffers, aligned correctly. |
| 198 | * Borrowed from net/net.c. |
| 199 | */ |
| 200 | MyRxPackets[0] = &MyPktBuf[0] + (PKTALIGN - 1); |
| 201 | MyRxPackets[0] -= (ulong)MyRxPackets[0] % PKTALIGN; |
| 202 | for (i = 1; i < HDLC_PKTBUFSRX; i++) |
| 203 | MyRxPackets[i] = MyRxPackets[0] + i * PKT_MAXBLR_SIZE; |
| 204 | |
| 205 | bdp = &rtx->rxbd[0]; |
| 206 | for (i = 0; i < HDLC_PKTBUFSRX; i++) { |
| 207 | bdp->cbd_sc = BD_SC_EMPTY; |
| 208 | /* Leave space at the start for INET header. */ |
| 209 | bdp->cbd_bufaddr = (unsigned int)(MyRxPackets[i] + |
| 210 | INET_HDR_ALIGN); |
| 211 | bdp++; |
| 212 | } |
| 213 | bdp--; |
| 214 | bdp->cbd_sc |= BD_SC_WRAP; |
| 215 | #else |
| 216 | keymile_hdlc_enet_init_bds(rtx); |
| 217 | #endif |
| 218 | |
| 219 | /* Let's re-initialize the channel now. We have to do it later |
| 220 | * than the manual describes because we have just now finished |
| 221 | * the BD initialization. |
| 222 | */ |
| 223 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC_PAGE, CPM_CR_SCC_SBLOCK, |
| 224 | 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; |
| 225 | while (cp->cp_cpcr & CPM_CR_FLG); |
| 226 | |
| 227 | sccp->scc_gsmrl = SCC_GSMRL_MODE_HDLC; |
| 228 | /* CTSS=1 */ |
| 229 | sccp->scc_gsmrh = SCC_GSMRH_CTSS; |
| 230 | /* NOF=0, RTE=1, DRT=0, BUS=1 */ |
| 231 | sccp->scc_psmr = ((0x8000 >> 6) | (0x8000 >> 10)); |
| 232 | |
| 233 | /* loopback for local testing */ |
| 234 | #ifdef GJTEST |
| 235 | dprintf("LOOPBACK!\n"); |
| 236 | sccp->scc_gsmrl |= SCC_GSMRL_DIAG_LOOP; |
| 237 | #endif |
| 238 | |
| 239 | /* |
| 240 | * Disable all interrupts and clear all pending |
| 241 | * events. |
| 242 | */ |
| 243 | sccp->scc_sccm = 0; |
| 244 | sccp->scc_scce = 0xffff; |
| 245 | |
| 246 | /* |
| 247 | * And last, enable the transmit and receive processing. |
| 248 | */ |
| 249 | sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); |
| 250 | |
| 251 | dprintf("%s: HDLC ENET Version 0.3 on SCC%d\n", dev->name, |
| 252 | MGC_SCC_HDLC + 1); |
| 253 | |
| 254 | /* |
| 255 | * We may not get an ARP packet because ARP was already done on |
| 256 | * a different interface, so initialize the cached values now. |
| 257 | */ |
| 258 | initCachedNumbers(1); |
| 259 | |
| 260 | already_inited = 1; |
| 261 | |
| 262 | return 0; |
| 263 | } |
| 264 | |
| 265 | void keymile_hdlc_enet_halt(struct eth_device *dev) |
| 266 | { |
| 267 | #if 0 /* just return, but keep this for reference */ |
| 268 | volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; |
| 269 | |
| 270 | /* maybe should do a graceful stop here? */ |
| 271 | immr->im_scc[MGC_SCC_HDLC].scc_gsmrl &= |
| 272 | ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); |
| 273 | #endif |
| 274 | } |
| 275 | |
| 276 | #endif /* CONFIG_MGCOGE_HDLC_ENET */ |