/* * mdio/smi related functions * ========================== * * Disassembled from original rtl8367rb.ko */ /* * Basic read/write function to control the rtl8367rb switch * --------------------------------------------------------- * * My guess: * - The rtl8367rb's smi lines are connected indirectly via some other * hardware to the MDIO bus. * - Hence extra effort needed for accessing rtl8367rb registers * * References: * - Disassembled code as presented here * - Kernel source drivers/net/phy/rtl8366_smi.c * - Kernel source drivers/net/phy/rtl8367b.c * - Kernel source drivers/net/ethernet/lantiq_xrx200.c * - RTL8366/RTL8369 datasheet from Realtek, Ch 9.2 "EEPROM SMI SLAVE FOR EXTERNAL CPU". * Found via google (no datasheet closer to rtl8367rb found). */ /* * Read/write two bytes from/to a rtl8367rb register */ u32 RTL83XX_SMI_READ(u32 addr) { ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x1F, 0xE); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x17, addr & 0xFFFF) ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x15, 1); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); return ifx_vr9_mdio_read(0, 0x19); } void RTL83XX_SMI_WRITE(u32 addr, u32 data) { ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x1F, 0xE); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x17, addr & 0xFFFF); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x18, data & 0xFFFF); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x15, 3); } /* * rtl8367rb switch reset function * ------------------------------- * * My guess: * - Chip's hardware reset line (like pin 64 "RESET#" for rtl8366) is * connected to our pin io41. */ u32 rtl8367_switch_hwrst(u32 msec) { *0xBE100B7C &= ~0x200; // Clear io41 Alternate Function Select Register 0 *0xBE100B80 &= ~0x200; // Clear io41 Alternate Function Select Register 1 (00=function 'gpio') *0xBE100B78 |= 0x200; // Set io41 Direction Register (1=output) *0xBE100B84 |= 0x200; // Set io41 Open Drain Control Register (1=inactive) *0xBE100B70 |= 0x200; // Set io41 Data Output Register *0xBE100B70 &= ~0x200; // Clear io41 Data Output Register __sync(); rtk_msleep(msec); *0xBE100B70 |= 0x200; // Set io41 Data Output Register return 0; } /* * Basic mdio functions * -------------------- */ /* * Configure pins io42 and io43: * - Select alternate function 1 (MDIO) * - Set io43 as output pin * - Deactivate open drain mode for both io42 and io43 * * My guess: * - Function MDIO implements an i2c like bus * - with us (this chip) as master * - controlled by addresses in range 0xBE10B120-0xBE10B128 * - Pin io43 is used as clock (CLK), as it is configured here for outout * - Pin io42 is used for bidirectional data transfer (SDA) */ void ifx_vr9_mdio_init(void) { * (u32*) 0xBE100B7C |= 0xC00; // Set io42, io43 Alternate Function Select Register 0 (lo sel bit) * (u32*) 0xBE100B80 &= ~0xC00; // Clear io42, io43 Alternate Function Select Register 1 (hi sel bit) * (u32*) 0xBE100B78 |= 0x800; // Set io43 Direction Register (1=output) * (u32*) 0xBE100B84 |= 0xC00; // Set io42, io43 Set Open Drain Control Register (1=no open drain) } /* * Output adressing bits and two data bytes on the bus * * Address seems to consist of two parts: * - 5 bits 'addr' * - 5 bits 'reg' (subaddress?) */ u32 ifx_vr9_mdio_write(u32 addr, u32 reg, u32 val) { u32 count; // Write data to output register * (u32*) 0xBE10B128 = val & 0xFFFF; // Wait till bit 0x1000 in control register comes clear while (*(u32*)BE10B120 & 0x1000) ; // Could this take forever? // Start transfer by writing addresses and certain control bits into control register * (u32*) 0xBE10B120 = 0x1400 | (addr & 0xFF)<<5 | (reg & 0xFF); // Masks should better be 0x1F !?! // Wait up to 0x8000 loop iterations till control bit 0x1000 comes clear for (count=0;;) { count += 1; if ((*(u32*)0xBE10B120 & 0x1000) == 0) return count; if (count & 0x8000) return 0xFFFF8000; } } /* * Output addressing bits and read two bytes from bus */ u32 ifx_vr9_mdio_read(u32 addr, u32 reg) { u32 count; while (*(u32*)BE10B120 & 0x1000) ; * (u32*) 0xBE19B120 = 0x1800 | (addr & 0xFF)<<5 | (reg & 0xFF); for (count=0; *(u32*)0xBE10B120 & 0x1000; ) { count += 1; if (count & 0x8000) break; } return * (u32*) 0xBE10B124 & 0xFFFF; // Read from input register } /* * Switch initialization code * -------------------------- */ u32 rtl8367_switch_coreinit() { if (rtl8367_switch_hwrst(100) != 0) return 2; rtk_msleep(1000); do { rtk_msleep(100); } while (RTL83XX_SMI_READ(0x1202) != 0x88A8); s4 = kmem_cache_alloc(?"malloc_sizes+0x2C"?, 0xD0); if (s4 == 0) { loc_560(0); return 10; } memset(s4, 0, 0xE4); if (rtk_switch_init() != 0) { kfree(s4); rtl8367_switch_portpower(0, 0); rtl8367_switch_portpower(1, 0); rtl8367_switch_portpower(2, 0); rtl8367_switch_portpower(3, 0); return 3; } rtl8367_switch_portpower(0, 0); // WTF, quite crude to repeat this code rtl8367_switch_portpower(1, 0); rtl8367_switch_portpower(2, 0); rtl8367_switch_portpower(3, 0); s1 = &unk_1D230; ... ... ... } u32 *init_para; // WTF, global variables defined, but used temporary and locally only u16 init_size; u32 rtk_switch_init(void) { u32 i, v0, var_20; v0 = rtl8367b_setAsicReg(0x1F, 7); if (v0 != 0) return v0; v0 = rtl8367b_getAsicReg(0x1301, &var_20); if (v0 != 0) return v0; if (var_20 & 0xF000) { init_para = &ChipData31 init_size = 0x96; } else { init_para = &ChipData30 init_size = 0x346; } for (i=0; i!=5; i++) { v0 = rtl8367b_setAsicPHYReg(i, 0x1F, 7); if (v0 != 0) return v0; v0 = rtl8367b_setAsicPHYReg(i, 0x1E, 0x2C); if (v0 != 0) return v0; v0 = rtl8367b_setAsicPHYReg(i, 0x19, 0x504); if (v0 != 0) return v0; v0 = rtl8367b_setAsicPHYReg(i, 0x1F, 0); if (v0 != 0) return v0; } for (i=0; i 0) return v0; } v0 = rtl8367b_setAsicPHYReg(1, 0x1F, 2); if (v0 != 0) return v0; v0 = rtl8367b_getAsicPHYReg(1, 0x11, &var_20); if (v0 != 0) return v0; var_20 |= 0x1E0; v0 = rtl8367b_setAsicPHYReg(1, 0x11, var_20); if (v0 != 0) return v0; v0 = rtl8367b_setAsicPHYReg(1, 0x1F, 0); if (v0 != 0) return v0; v0 = rtl8367b_setAsicRegBit(0x18E0, 0, 0); if (v0 != 0) return v0; v0 = rtl8367b_SetAscicReg(0x1303, 0x778); if (v0 != 0) return v0; v0 = rtl8367b_SetAsicReg(0x1304, 0x7777); if (v0 != 0) return v0; return rtl8367b_SetAsicReg(0x13E2, 0x1FE); } /* * Control port power * ------------------ * Used as example how rtl8367rb is controlled. */ u32 rtl8367_switch_portpower(u32 param1, u32 param2) { u32 data; param1 &= 0xFFFF; param2 &= 0xFF; data = RTL83XX_SMI_READ((0x2000 + (param1<<5)) & 0xFFE0); data &= 0xF7FF; if (param2 != 1) data |= 0x800; return rtk_port_phyReg_set(param1, 0, data); } /* * Some helper functions */ void rtk_msleep(u32 msec) { u32 i; for (i=0; i= 5) return 3; tmp = rtl8367b_setAsicPHYReg(param1, 0x1F, 0); if (tmp != 0) return tmp; return rtl8367b_setAsicPHYReg(param1, param2, data); } u32 rtl8367b_setAsicPHYReg(u32 param1, u32 param2, u32 data) { if (param1 >= 5) return 3; if (param2 < 0x20) return 14; return rtl8367b_setAsicReg((param1<<5) + param2 + 0x2000, data); } u32 rtl8367b_getAsicReg(u32 addr, u32 *data) { u32 var_10; v0 = smi_read(addr, &var10); if (v0 != 0) return 14; *data = var10; return 0; } u32 rtl8367b_setAsicReg(u32 addr, u32 data) { return smi_write(addr,data)==0 ? 0 : 14; } // WTF, duplicate code // Differs from RTL83XX_SMI_WRITE() only in that it provides a return code (always 0) u32 smi_write(u32 addr, u32 data) { ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x1F, 0xE); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x17, addr & 0xFFFF); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x18, data & 0xFFFF); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x15, 3); return 0; } // Better should have used RTL83XX_SMI_READ() instead duplicate coding u32 smi_read(u32 addr, u32 *data) { ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x1F, 0xE); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x17, addr & 0xFFFF); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); ifx_vr9_mdio_write(0, 0x15, 1); ifx_vr9_mdio_write(0, 0x1D, 0xFFFF); *data = ifx_vr9_mdio_read(0, 0x19); return 0; }