| 1 | /****************************************************************************** |
| 2 | Copyright (c) 2007, Infineon Technologies. All rights reserved. |
| 3 | |
| 4 | No Warranty |
| 5 | Because the program is licensed free of charge, there is no warranty for |
| 6 | the program, to the extent permitted by applicable law. Except when |
| 7 | otherwise stated in writing the copyright holders and/or other parties |
| 8 | provide the program "as is" without warranty of any kind, either |
| 9 | expressed or implied, including, but not limited to, the implied |
| 10 | warranties of merchantability and fitness for a particular purpose. The |
| 11 | entire risk as to the quality and performance of the program is with |
| 12 | you. should the program prove defective, you assume the cost of all |
| 13 | necessary servicing, repair or correction. |
| 14 | |
| 15 | In no event unless required by applicable law or agreed to in writing |
| 16 | will any copyright holder, or any other party who may modify and/or |
| 17 | redistribute the program as permitted above, be liable to you for |
| 18 | damages, including any general, special, incidental or consequential |
| 19 | damages arising out of the use or inability to use the program |
| 20 | (including but not limited to loss of data or data being rendered |
| 21 | inaccurate or losses sustained by you or third parties or a failure of |
| 22 | the program to operate with any other programs), even if such holder or |
| 23 | other party has been advised of the possibility of such damages. |
| 24 | ****************************************************************************** |
| 25 | Module : switchip_setup.c |
| 26 | Date : 2007-11-09 |
| 27 | Description : Basic setup of embedded ethernet switch "SwitchIP" |
| 28 | Remarks: andreas.schmidt@infineon.com |
| 29 | |
| 30 | *****************************************************************************/ |
| 31 | |
| 32 | /* TODO: get rid of #ifdef CONFIG_LANTIQ_MACH_EASY336 */ |
| 33 | |
| 34 | #include <linux/kernel.h> |
| 35 | #include <linux/module.h> |
| 36 | #include <linux/version.h> |
| 37 | #include <linux/init.h> |
| 38 | #include <linux/delay.h> |
| 39 | #include <linux/workqueue.h> |
| 40 | #include <linux/time.h> |
| 41 | |
| 42 | #include <base_reg.h> |
| 43 | #include <es_reg.h> |
| 44 | #include <sys1_reg.h> |
| 45 | #include <dma_reg.h> |
| 46 | #include <lantiq_soc.h> |
| 47 | |
| 48 | static struct svip_reg_sys1 *const sys1 = (struct svip_reg_sys1 *)LTQ_SYS1_BASE; |
| 49 | static struct svip_reg_es *const es = (struct svip_reg_es *)LTQ_ES_BASE; |
| 50 | |
| 51 | /* PHY Organizationally Unique Identifier (OUI) */ |
| 52 | #define PHY_OUI_PMC 0x00E004 |
| 53 | #define PHY_OUI_VITESSE 0x008083 |
| 54 | #define PHY_OUI_DEFAULT 0xFFFFFF |
| 55 | |
| 56 | unsigned short switchip_phy_read(unsigned int phyaddr, unsigned int regaddr); |
| 57 | void switchip_phy_write(unsigned int phyaddr, unsigned int regaddr, |
| 58 | unsigned short data); |
| 59 | |
| 60 | static int phy_address[2] = {0, 1}; |
| 61 | static u32 phy_oui; |
| 62 | static void switchip_mdio_poll_init(void); |
| 63 | static void _switchip_mdio_poll(struct work_struct *work); |
| 64 | |
| 65 | /* struct workqueue_struct mdio_poll_task; */ |
| 66 | static struct workqueue_struct *mdio_poll_workqueue; |
| 67 | DECLARE_DELAYED_WORK(mdio_poll_work, _switchip_mdio_poll); |
| 68 | static int old_link_status[2] = {-1, -1}; |
| 69 | |
| 70 | /** |
| 71 | * Autonegotiation check. |
| 72 | * This funtion checks for link changes. If a link change has occured it will |
| 73 | * update certain switch registers. |
| 74 | */ |
| 75 | static void _switchip_check_phy_status(int port) |
| 76 | { |
| 77 | int new_link_status; |
| 78 | unsigned short reg1; |
| 79 | |
| 80 | reg1 = switchip_phy_read(phy_address[port], 1); |
| 81 | if ((reg1 == 0xFFFF) || (reg1 == 0x0000)) |
| 82 | return; /* no PHY connected */ |
| 83 | |
| 84 | new_link_status = reg1 & 4; |
| 85 | if (old_link_status[port] ^ new_link_status) { |
| 86 | /* link status change */ |
| 87 | if (!new_link_status) { |
| 88 | if (port == 0) |
| 89 | es_w32_mask(LTQ_ES_P0_CTL_REG_FLP, 0, p0_ctl); |
| 90 | else |
| 91 | es_w32_mask(LTQ_ES_P0_CTL_REG_FLP, 0, p1_ctl); |
| 92 | |
| 93 | /* read again; link bit is latched low! */ |
| 94 | reg1 = switchip_phy_read(phy_address[port], 1); |
| 95 | new_link_status = reg1 & 4; |
| 96 | } |
| 97 | |
| 98 | if (new_link_status) { |
| 99 | unsigned short reg0, reg4, reg5, reg9, reg10; |
| 100 | int phy_pause, phy_speed, phy_duplex; |
| 101 | int aneg_enable, aneg_cmpt; |
| 102 | |
| 103 | reg0 = switchip_phy_read(phy_address[port], 0); |
| 104 | reg4 = switchip_phy_read(phy_address[port], 4); |
| 105 | aneg_enable = reg0 & 0x1000; |
| 106 | aneg_cmpt = reg1 & 0x20; |
| 107 | |
| 108 | if (aneg_enable && aneg_cmpt) { |
| 109 | reg5 = switchip_phy_read(phy_address[port], 5); |
| 110 | switch (phy_oui) { |
| 111 | #ifdef CONFIG_LANTIQ_MACH_EASY336 |
| 112 | case PHY_OUI_PMC: |
| 113 | /* PMC Sierra supports 1Gigabit FD, |
| 114 | * only. On successful |
| 115 | * auto-negotiation, we are sure this |
| 116 | * is what the LP can. */ |
| 117 | phy_pause = ((reg4 & reg5) & 0x0080) >> 7; |
| 118 | phy_speed = 2; |
| 119 | phy_duplex = 1; |
| 120 | break; |
| 121 | #endif |
| 122 | case PHY_OUI_VITESSE: |
| 123 | case PHY_OUI_DEFAULT: |
| 124 | reg9 = switchip_phy_read(phy_address[port], 9); |
| 125 | reg10 = switchip_phy_read(phy_address[port], 10); |
| 126 | |
| 127 | /* Check if advertise and partner |
| 128 | * agree on pause */ |
| 129 | phy_pause = ((reg4 & reg5) & 0x0400) >> 10; |
| 130 | |
| 131 | /* Find the best mode both partners |
| 132 | * support |
| 133 | * Priority: 1GB-FD, 1GB-HD, 100MB-FD, |
| 134 | * 100MB-HD, 10MB-FD, 10MB-HD */ |
| 135 | phy_speed = ((((reg9<<2) & reg10) |
| 136 | & 0x0c00) >> 6) | |
| 137 | (((reg4 & reg5) & 0x01e0) >> 5); |
| 138 | |
| 139 | if (phy_speed >= 0x0020) { |
| 140 | phy_speed = 2; |
| 141 | phy_duplex = 1; |
| 142 | } else if (phy_speed >= 0x0010) { |
| 143 | phy_speed = 2; |
| 144 | phy_duplex = 0; |
| 145 | } else if (phy_speed >= 0x0008) { |
| 146 | phy_speed = 1; |
| 147 | phy_duplex = 1; |
| 148 | } else if (phy_speed >= 0x0004) { |
| 149 | phy_speed = 1; |
| 150 | phy_duplex = 0; |
| 151 | } else if (phy_speed >= 0x0002) { |
| 152 | phy_speed = 0; |
| 153 | phy_duplex = 1; |
| 154 | } else { |
| 155 | phy_speed = 0; |
| 156 | phy_duplex = 0; |
| 157 | } |
| 158 | break; |
| 159 | default: |
| 160 | phy_pause = (reg4 & 0x0400) >> 10; |
| 161 | phy_speed = (reg0 & 0x40 ? 2 : (reg0 >> 13)&1); |
| 162 | phy_duplex = (reg0 >> 8)&1; |
| 163 | break; |
| 164 | } |
| 165 | } else { |
| 166 | /* parallel detection or fixed speed */ |
| 167 | phy_pause = (reg4 & 0x0400) >> 10; |
| 168 | phy_speed = (reg0 & 0x40 ? 2 : (reg0 >> 13)&1); |
| 169 | phy_duplex = (reg0 >> 8)&1; |
| 170 | } |
| 171 | |
| 172 | if (port == 0) { |
| 173 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0SPD, |
| 174 | LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(phy_speed), |
| 175 | rgmii_ctl); |
| 176 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0DUP, |
| 177 | LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(phy_duplex), |
| 178 | rgmii_ctl); |
| 179 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0FCE, |
| 180 | LTQ_ES_RGMII_CTL_REG_P0FCE_VAL(phy_pause), |
| 181 | rgmii_ctl); |
| 182 | |
| 183 | es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl); |
| 184 | } else { |
| 185 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1SPD, |
| 186 | LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(phy_speed), |
| 187 | rgmii_ctl); |
| 188 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1DUP, |
| 189 | LTQ_ES_RGMII_CTL_REG_P1DUP_VAL(phy_duplex), |
| 190 | rgmii_ctl); |
| 191 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1FCE, |
| 192 | LTQ_ES_RGMII_CTL_REG_P0FCE_VAL(phy_pause), |
| 193 | rgmii_ctl); |
| 194 | |
| 195 | es_w32_mask(1, LTQ_ES_P0_CTL_REG_FLP, p1_ctl); |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | old_link_status[port] = new_link_status; |
| 200 | } |
| 201 | |
| 202 | static void _switchip_mdio_poll(struct work_struct *work) |
| 203 | { |
| 204 | if (es_r32(sw_gctl0) & LTQ_ES_SW_GCTL0_REG_SE) { |
| 205 | _switchip_check_phy_status(0); |
| 206 | _switchip_check_phy_status(1); |
| 207 | } |
| 208 | |
| 209 | queue_delayed_work(mdio_poll_workqueue, &mdio_poll_work, HZ/2); |
| 210 | } |
| 211 | |
| 212 | static void switchip_mdio_poll_init(void) |
| 213 | { |
| 214 | mdio_poll_workqueue = create_workqueue("SVIP MDIP poll"); |
| 215 | INIT_DELAYED_WORK(&mdio_poll_work, _switchip_mdio_poll); |
| 216 | |
| 217 | queue_delayed_work(mdio_poll_workqueue, &mdio_poll_work, HZ/2); |
| 218 | |
| 219 | } |
| 220 | |
| 221 | unsigned short switchip_phy_read(unsigned int phyaddr, unsigned int regaddr) |
| 222 | { |
| 223 | /* TODO: protect MDIO access with semaphore */ |
| 224 | es_w32(LTQ_ES_MDIO_CTL_REG_MBUSY |
| 225 | | LTQ_ES_MDIO_CTL_REG_OP_VAL(2) /* read operation */ |
| 226 | | LTQ_ES_MDIO_CTL_REG_PHYAD_VAL(phyaddr) |
| 227 | | LTQ_ES_MDIO_CTL_REG_REGAD_VAL(regaddr), mdio_ctl); |
| 228 | while (es_r32(mdio_ctl) & LTQ_ES_MDIO_CTL_REG_MBUSY); |
| 229 | |
| 230 | return es_r32(mdio_data) & 0xFFFF; |
| 231 | } |
| 232 | EXPORT_SYMBOL(switchip_phy_read); |
| 233 | |
| 234 | void switchip_phy_write(unsigned int phyaddr, unsigned int regaddr, |
| 235 | unsigned short data) |
| 236 | { |
| 237 | /* TODO: protect MDIO access with semaphore */ |
| 238 | es_w32(LTQ_ES_MDIO_CTL_REG_WD_VAL(data) |
| 239 | | LTQ_ES_MDIO_CTL_REG_MBUSY |
| 240 | | LTQ_ES_MDIO_CTL_REG_OP_VAL(1) /* write operation */ |
| 241 | | LTQ_ES_MDIO_CTL_REG_PHYAD_VAL(phyaddr) |
| 242 | | LTQ_ES_MDIO_CTL_REG_REGAD_VAL(regaddr), mdio_ctl); |
| 243 | while (es_r32(mdio_ctl) & LTQ_ES_MDIO_CTL_REG_MBUSY); |
| 244 | |
| 245 | return; |
| 246 | } |
| 247 | EXPORT_SYMBOL(switchip_phy_write); |
| 248 | |
| 249 | const static u32 switch_reset_offset_000[] = { |
| 250 | /*b8000000:*/ 0xffffffff, 0x00000001, 0x00000001, 0x00000003, |
| 251 | /*b8000010:*/ 0x04070001, 0x04070001, 0x04070001, 0xffffffff, |
| 252 | /*b8000020:*/ 0x00001be8, 0x00001be8, 0x00001be8, 0xffffffff, |
| 253 | /*b8000030:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001, |
| 254 | /*b8000040:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001, |
| 255 | /*b8000050:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001, |
| 256 | /*b8000060:*/ 0x00000000, 0x00000000, 0x00081000, 0x001f7777, |
| 257 | /*b8000070:*/ 0x00000000, 0x00000000, 0x0c00ac2b, 0x0000fa50, |
| 258 | /*b8000080:*/ 0x00001000, 0x00001800, 0x00000000, 0x00000000, |
| 259 | /*b8000090:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 260 | /*b80000a0:*/ 0x00000000, 0x00000050, 0x00000010, 0x00000000, |
| 261 | /*b80000b0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 262 | /*b80000c0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 263 | /*b80000d0:*/ 0xffffffff, 0x00000000, 0x00000000 |
| 264 | }; |
| 265 | const static u32 switch_reset_offset_100[] = { |
| 266 | /*b8000100:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 267 | /*b8000110:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 268 | /*b8000120:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 269 | /*b8000130:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 270 | /*b8000140:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 271 | /*b8000150:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 272 | /*b8000160:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 273 | /*b8000170:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 274 | /*b8000180:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 275 | /*b8000190:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 276 | /*b80001a0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 277 | /*b80001b0:*/ 0x00000000, 0x00000000 |
| 278 | }; |
| 279 | |
| 280 | /* |
| 281 | * Switch Reset. |
| 282 | */ |
| 283 | void switchip_reset(void) |
| 284 | { |
| 285 | volatile unsigned int *reg; |
| 286 | volatile unsigned int rdreg; |
| 287 | int i; |
| 288 | |
| 289 | sys1_w32(SYS1_CLKENR_ETHSW, clkenr); |
| 290 | asm("sync"); |
| 291 | |
| 292 | /* disable P0 */ |
| 293 | es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p0_ctl); |
| 294 | /* disable P1 */ |
| 295 | es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p1_ctl); |
| 296 | /* disable P2 */ |
| 297 | es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p2_ctl); |
| 298 | |
| 299 | /************************************** |
| 300 | * BEGIN: Procedure to clear MAC table |
| 301 | **************************************/ |
| 302 | for (i = 0; i < 3; i++) { |
| 303 | int result; |
| 304 | |
| 305 | /* check if access engine is available */ |
| 306 | while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY); |
| 307 | |
| 308 | /* initialise to first address */ |
| 309 | es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(3) |
| 310 | | LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(0), adr_tb_ctl2); |
| 311 | |
| 312 | /* wait while busy */ |
| 313 | while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY); |
| 314 | |
| 315 | /* setup the portmap */ |
| 316 | es_w32_mask(0, LTQ_ES_ADR_TB_CTL1_REG_PMAP_VAL(1 << i), |
| 317 | adr_tb_ctl1); |
| 318 | |
| 319 | do { |
| 320 | /* search for addresses by port */ |
| 321 | es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(2) |
| 322 | | LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(9), adr_tb_ctl2); |
| 323 | |
| 324 | /* wait while busy */ |
| 325 | while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY); |
| 326 | |
| 327 | result = LTQ_ES_ADR_TB_ST2_REG_RSLT_GET(es_r32(adr_tb_st2)); |
| 328 | if (result == 0x101) { |
| 329 | printk(KERN_ERR "%s, cmd error\n", __func__); |
| 330 | return; |
| 331 | } |
| 332 | /* if Command OK, address found... */ |
| 333 | if (result == 0) { |
| 334 | unsigned char mac[6]; |
| 335 | |
| 336 | mac[5] = (es_r32(adr_tb_st0) >> 0) & 0xff; |
| 337 | mac[4] = (es_r32(adr_tb_st0) >> 8) & 0xff; |
| 338 | mac[3] = (es_r32(adr_tb_st0) >> 16) & 0xff; |
| 339 | mac[2] = (es_r32(adr_tb_st0) >> 24) & 0xff; |
| 340 | mac[1] = (es_r32(adr_tb_st1) >> 0) & 0xff; |
| 341 | mac[0] = (es_r32(adr_tb_st1) >> 8) & 0xff; |
| 342 | |
| 343 | /* setup address */ |
| 344 | es_w32((mac[5] << 0) | |
| 345 | (mac[4] << 8) | |
| 346 | (mac[3] << 16) | |
| 347 | (mac[2] << 24), adr_tb_ctl0); |
| 348 | es_w32(LTQ_ES_ADR_TB_CTL1_REG_PMAP_VAL(1<<i) | |
| 349 | LTQ_ES_ADR_TB_CTL1_REG_FID_VAL(0) | |
| 350 | (mac[0] << 8) | |
| 351 | (mac[1] << 0), adr_tb_ctl1); |
| 352 | /* erase address */ |
| 353 | |
| 354 | es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(1) | |
| 355 | LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(15), |
| 356 | adr_tb_ctl2); |
| 357 | |
| 358 | /* wait, while busy */ |
| 359 | while (es_r32(adr_tb_st2) & |
| 360 | LTQ_ES_ADR_TB_ST2_REG_BUSY); |
| 361 | } |
| 362 | } while (result == 0); |
| 363 | } |
| 364 | /************************************** |
| 365 | * END: Procedure to clear MAC table |
| 366 | **************************************/ |
| 367 | |
| 368 | /* reset RMON counters */ |
| 369 | es_w32(LTQ_ES_RMON_CTL_REG_BAS | LTQ_ES_RMON_CTL_REG_CAC_VAL(3), |
| 370 | rmon_ctl); |
| 371 | |
| 372 | /* bring all registers to reset state */ |
| 373 | reg = LTQ_ES_PS_REG; |
| 374 | for (i = 0; i < ARRAY_SIZE(switch_reset_offset_000); i++) { |
| 375 | if ((reg == LTQ_ES_PS_REG) || |
| 376 | (reg >= LTQ_ES_ADR_TB_CTL0_REG && |
| 377 | reg <= LTQ_ES_ADR_TB_ST2_REG)) |
| 378 | continue; |
| 379 | |
| 380 | if (switch_reset_offset_000[i] != 0xFFFFFFFF) { |
| 381 | /* write reset value to register */ |
| 382 | *reg = switch_reset_offset_000[i]; |
| 383 | /* read register value back */ |
| 384 | rdreg = *reg; |
| 385 | if (reg == LTQ_ES_SW_GCTL1_REG) |
| 386 | rdreg &= ~LTQ_ES_SW_GCTL1_REG_BISTDN; |
| 387 | /* compare read value with written one */ |
| 388 | if (rdreg != switch_reset_offset_000[i]) { |
| 389 | printk(KERN_ERR "%s,%d: reg %08x mismatch " |
| 390 | "[has:%08x, expect:%08x]\n", |
| 391 | __func__, __LINE__, |
| 392 | (unsigned int)reg, rdreg, |
| 393 | switch_reset_offset_000[i]); |
| 394 | } |
| 395 | } |
| 396 | reg++; |
| 397 | } |
| 398 | |
| 399 | reg = LTQ_ES_VLAN_FLT0_REG; |
| 400 | for (i = 0; i < ARRAY_SIZE(switch_reset_offset_100); i++) { |
| 401 | *reg = switch_reset_offset_100[i]; |
| 402 | rdreg = *reg; |
| 403 | if (rdreg != switch_reset_offset_100[i]) { |
| 404 | printk(KERN_ERR "%s,%d: reg %08x mismatch " |
| 405 | "[has:%08x, expect:%08x]\n", __func__, __LINE__, |
| 406 | (unsigned int)reg, rdreg, |
| 407 | switch_reset_offset_100[i]); |
| 408 | } |
| 409 | reg++; |
| 410 | } |
| 411 | } |
| 412 | EXPORT_SYMBOL(switchip_reset); |
| 413 | |
| 414 | static u32 get_phy_oui(unsigned char phy_addr) |
| 415 | { |
| 416 | u32 oui; |
| 417 | int i, bit, byte, shift, w; |
| 418 | u16 reg_id[2]; |
| 419 | |
| 420 | /* read PHY identifier registers 1 and 2 */ |
| 421 | reg_id[0] = switchip_phy_read(phy_addr, 2); |
| 422 | reg_id[1] = switchip_phy_read(phy_addr, 3); |
| 423 | |
| 424 | oui = 0; |
| 425 | w = 1; |
| 426 | shift = 7; |
| 427 | byte = 1; |
| 428 | for (i = 0, bit = 10; i <= 21; i++, bit++) { |
| 429 | oui |= ((reg_id[w] & (1<<bit)) ? 1 : 0) << shift; |
| 430 | if (!(shift % 8)) { |
| 431 | byte++; |
| 432 | if (byte == 2) |
| 433 | shift = 15; |
| 434 | else |
| 435 | shift = 21; |
| 436 | } else { |
| 437 | shift--; |
| 438 | } |
| 439 | if (w == 1 && bit == 15) { |
| 440 | bit = -1; |
| 441 | w = 0; |
| 442 | } |
| 443 | } |
| 444 | return oui; |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | * Switch Initialization. |
| 449 | */ |
| 450 | int switchip_init(void) |
| 451 | { |
| 452 | int eth_port, phy_present = 0; |
| 453 | u16 reg, mode; |
| 454 | |
| 455 | sys1_w32(SYS1_CLKENR_ETHSW, clkenr); |
| 456 | asm("sync"); |
| 457 | |
| 458 | /* Enable Switch, if not already done so */ |
| 459 | if ((es_r32(sw_gctl0) & LTQ_ES_SW_GCTL0_REG_SE) == 0) |
| 460 | es_w32_mask(0, LTQ_ES_SW_GCTL0_REG_SE, sw_gctl0); |
| 461 | /* Wait for completion of MBIST */ |
| 462 | while (LTQ_ES_SW_GCTL1_REG_BISTDN_GET(es_r32(sw_gctl1)) == 0); |
| 463 | |
| 464 | switchip_reset(); |
| 465 | |
| 466 | mode = LTQ_ES_RGMII_CTL_REG_IS_GET(es_r32(rgmii_ctl)); |
| 467 | eth_port = (mode == 2 ? 1 : 0); |
| 468 | |
| 469 | /* Set the primary port(port toward backplane) as sniffer port, |
| 470 | changing from P2 which is the reset setting */ |
| 471 | es_w32_mask(LTQ_ES_SW_GCTL0_REG_SNIFFPN, |
| 472 | LTQ_ES_SW_GCTL0_REG_SNIFFPN_VAL(eth_port), |
| 473 | sw_gctl0); |
| 474 | |
| 475 | /* Point MDIO state machine to invalid PHY addresses 8 and 9 */ |
| 476 | es_w32_mask(0, LTQ_ES_SW_GCTL0_REG_PHYBA, sw_gctl0); |
| 477 | |
| 478 | /* Add CRC for packets from DMA to PMAC. |
| 479 | Remove CRC for packets from PMAC to DMA. */ |
| 480 | es_w32(LTQ_ES_PMAC_HD_CTL_RC | LTQ_ES_PMAC_HD_CTL_AC, pmac_hd_ctl); |
| 481 | |
| 482 | phy_oui = get_phy_oui(0); |
| 483 | switch (phy_oui) { |
| 484 | #ifdef CONFIG_LANTIQ_MACH_EASY336 |
| 485 | case PHY_OUI_PMC: |
| 486 | phy_address[0] = (mode == 2 ? -1 : 2); |
| 487 | phy_address[1] = (mode == 2 ? 2 : -1); |
| 488 | break; |
| 489 | #endif |
| 490 | case PHY_OUI_VITESSE: |
| 491 | default: |
| 492 | phy_oui = PHY_OUI_DEFAULT; |
| 493 | phy_address[0] = (mode == 2 ? 1 : 0); |
| 494 | phy_address[1] = (mode == 2 ? 0 : 1); |
| 495 | break; |
| 496 | } |
| 497 | |
| 498 | /****** PORT 0 *****/ |
| 499 | reg = switchip_phy_read(phy_address[0], 1); |
| 500 | if ((reg != 0x0000) && (reg != 0xffff)) { |
| 501 | /* PHY connected? */ |
| 502 | phy_present |= 1; |
| 503 | /* Set Rx- and TxDelay in case of RGMII */ |
| 504 | switch (mode) { |
| 505 | case 0: /* *RGMII,RGMII */ |
| 506 | case 2: /* RGMII,*GMII */ |
| 507 | /* program clock delay in PHY, not in SVIP */ |
| 508 | |
| 509 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0RDLY, 0, rgmii_ctl); |
| 510 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0TDLY, 0, rgmii_ctl); |
| 511 | if (phy_oui == PHY_OUI_VITESSE || |
| 512 | phy_oui == PHY_OUI_DEFAULT) { |
| 513 | switchip_phy_write(phy_address[0], 31, 0x0001); |
| 514 | switchip_phy_write(phy_address[0], 28, 0xA000); |
| 515 | switchip_phy_write(phy_address[0], 31, 0x0000); |
| 516 | } |
| 517 | default: |
| 518 | break; |
| 519 | } |
| 520 | if (phy_oui == PHY_OUI_VITESSE || |
| 521 | phy_oui == PHY_OUI_DEFAULT) { |
| 522 | /* Program PHY advertisements and |
| 523 | * restart auto-negotiation */ |
| 524 | switchip_phy_write(phy_address[0], 4, 0x05E1); |
| 525 | switchip_phy_write(phy_address[0], 9, 0x0300); |
| 526 | switchip_phy_write(phy_address[0], 0, 0x3300); |
| 527 | } else { |
| 528 | reg = switchip_phy_read(phy_address[1], 0); |
| 529 | reg |= 0x1000; /* auto-negotiation enable */ |
| 530 | switchip_phy_write(phy_address[1], 0, reg); |
| 531 | reg |= 0x0200; /* auto-negotiation restart */ |
| 532 | switchip_phy_write(phy_address[1], 0, reg); |
| 533 | } |
| 534 | } else { |
| 535 | /* Force SWITCH link with highest capability: |
| 536 | * 100M FD for MII |
| 537 | * 1G FD for GMII/RGMII |
| 538 | */ |
| 539 | switch (mode) { |
| 540 | case 1: /* *MII,MII */ |
| 541 | case 3: /* *MII,RGMII */ |
| 542 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(1), |
| 543 | rgmii_ctl); |
| 544 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(1), |
| 545 | rgmii_ctl); |
| 546 | break; |
| 547 | case 0: /* *RGMII,RGMII */ |
| 548 | case 2: /* RGMII,*GMII */ |
| 549 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(2), |
| 550 | rgmii_ctl); |
| 551 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(1), |
| 552 | rgmii_ctl); |
| 553 | |
| 554 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0RDLY, 0, rgmii_ctl); |
| 555 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0TDLY_VAL(2), |
| 556 | rgmii_ctl); |
| 557 | break; |
| 558 | } |
| 559 | |
| 560 | es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl); |
| 561 | } |
| 562 | |
| 563 | /****** PORT 1 *****/ |
| 564 | reg = switchip_phy_read(phy_address[1], 1); |
| 565 | if ((reg != 0x0000) && (reg != 0xffff)) { |
| 566 | /* PHY connected? */ |
| 567 | phy_present |= 2; |
| 568 | /* Set Rx- and TxDelay in case of RGMII */ |
| 569 | switch (mode) { |
| 570 | case 0: /* *RGMII,RGMII */ |
| 571 | case 3: /* *MII,RGMII */ |
| 572 | /* program clock delay in PHY, not in SVIP */ |
| 573 | |
| 574 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1RDLY, 0, rgmii_ctl); |
| 575 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1TDLY, 0, rgmii_ctl); |
| 576 | if (phy_oui == PHY_OUI_VITESSE || |
| 577 | phy_oui == PHY_OUI_DEFAULT) { |
| 578 | switchip_phy_write(phy_address[1], 31, 0x0001); |
| 579 | switchip_phy_write(phy_address[1], 28, 0xA000); |
| 580 | switchip_phy_write(phy_address[1], 31, 0x0000); |
| 581 | } |
| 582 | break; |
| 583 | case 2: /* RGMII,*GMII */ |
| 584 | |
| 585 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2), |
| 586 | rgmii_ctl); |
| 587 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl); |
| 588 | #ifdef CONFIG_LANTIQ_MACH_EASY336 |
| 589 | if (phy_oui == PHY_OUI_PMC) { |
| 590 | switchip_phy_write(phy_address[1], 24, 0x0510); |
| 591 | switchip_phy_write(phy_address[1], 17, 0xA38C); |
| 592 | switchip_phy_write(phy_address[1], 17, 0xA384); |
| 593 | } |
| 594 | #endif |
| 595 | break; |
| 596 | default: |
| 597 | break; |
| 598 | } |
| 599 | /* Program PHY advertisements and restart auto-negotiation */ |
| 600 | if (phy_oui == PHY_OUI_VITESSE || |
| 601 | phy_oui == PHY_OUI_DEFAULT) { |
| 602 | switchip_phy_write(phy_address[1], 4, 0x05E1); |
| 603 | switchip_phy_write(phy_address[1], 9, 0x0300); |
| 604 | switchip_phy_write(phy_address[1], 0, 0x3300); |
| 605 | } else { |
| 606 | reg = switchip_phy_read(phy_address[1], 0); |
| 607 | reg |= 0x1000; /* auto-negotiation enable */ |
| 608 | switchip_phy_write(phy_address[1], 0, reg); |
| 609 | reg |= 0x0200; /* auto-negotiation restart */ |
| 610 | switchip_phy_write(phy_address[1], 0, reg); |
| 611 | } |
| 612 | } else { |
| 613 | /* Force SWITCH link with highest capability: |
| 614 | * 100M FD for MII |
| 615 | * 1G FD for GMII/RGMII |
| 616 | */ |
| 617 | switch (mode) { |
| 618 | case 1: /* *MII,MII */ |
| 619 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(1), |
| 620 | rgmii_ctl); |
| 621 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl); |
| 622 | break; |
| 623 | case 0: /* *RGMII,RGMII */ |
| 624 | case 3: /* *MII,RGMII */ |
| 625 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2), |
| 626 | rgmii_ctl); |
| 627 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl); |
| 628 | es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1RDLY, 0, rgmii_ctl); |
| 629 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1TDLY_VAL(2), |
| 630 | rgmii_ctl); |
| 631 | break; |
| 632 | case 2: /* RGMII,*GMII */ |
| 633 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2), |
| 634 | rgmii_ctl); |
| 635 | es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl); |
| 636 | break; |
| 637 | } |
| 638 | es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl); |
| 639 | } |
| 640 | |
| 641 | /* |
| 642 | * Allow unknown unicast/multicast and broadcasts |
| 643 | * on all ports. |
| 644 | */ |
| 645 | |
| 646 | es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_UP_VAL(7), sw_gctl1); |
| 647 | es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_BP_VAL(7), sw_gctl1); |
| 648 | es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_MP_VAL(7), sw_gctl1); |
| 649 | es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_RP_VAL(7), sw_gctl1); |
| 650 | |
| 651 | /* Enable LAN port(s) */ |
| 652 | if (eth_port == 0) |
| 653 | es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p0_ctl); |
| 654 | else |
| 655 | es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p1_ctl); |
| 656 | /* Enable CPU Port (Forwarding State) */ |
| 657 | es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p2_ctl); |
| 658 | |
| 659 | if (phy_present) |
| 660 | switchip_mdio_poll_init(); |
| 661 | |
| 662 | return 0; |
| 663 | } |
| 664 | EXPORT_SYMBOL(switchip_init); |
| 665 | |
| 666 | device_initcall(switchip_init); |
| 667 | |