aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/lantiq/files/arch/mips/lantiq/svip/switchip_setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/lantiq/files/arch/mips/lantiq/svip/switchip_setup.c')
-rw-r--r--target/linux/lantiq/files/arch/mips/lantiq/svip/switchip_setup.c666
1 files changed, 666 insertions, 0 deletions
diff --git a/target/linux/lantiq/files/arch/mips/lantiq/svip/switchip_setup.c b/target/linux/lantiq/files/arch/mips/lantiq/svip/switchip_setup.c
new file mode 100644
index 000000000..5da15327e
--- /dev/null
+++ b/target/linux/lantiq/files/arch/mips/lantiq/svip/switchip_setup.c
@@ -0,0 +1,666 @@
+/******************************************************************************
+ Copyright (c) 2007, Infineon Technologies. All rights reserved.
+
+ No Warranty
+ Because the program is licensed free of charge, there is no warranty for
+ the program, to the extent permitted by applicable law. Except when
+ otherwise stated in writing the copyright holders and/or other parties
+ provide the program "as is" without warranty of any kind, either
+ expressed or implied, including, but not limited to, the implied
+ warranties of merchantability and fitness for a particular purpose. The
+ entire risk as to the quality and performance of the program is with
+ you. should the program prove defective, you assume the cost of all
+ necessary servicing, repair or correction.
+
+ In no event unless required by applicable law or agreed to in writing
+ will any copyright holder, or any other party who may modify and/or
+ redistribute the program as permitted above, be liable to you for
+ damages, including any general, special, incidental or consequential
+ damages arising out of the use or inability to use the program
+ (including but not limited to loss of data or data being rendered
+ inaccurate or losses sustained by you or third parties or a failure of
+ the program to operate with any other programs), even if such holder or
+ other party has been advised of the possibility of such damages.
+ ******************************************************************************
+ Module : switchip_setup.c
+ Date : 2007-11-09
+ Description : Basic setup of embedded ethernet switch "SwitchIP"
+ Remarks: andreas.schmidt@infineon.com
+
+ *****************************************************************************/
+
+/* TODO: get rid of #ifdef CONFIG_LANTIQ_MACH_EASY336 */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/time.h>
+
+#include <base_reg.h>
+#include <es_reg.h>
+#include <sys1_reg.h>
+#include <dma_reg.h>
+#include <lantiq_soc.h>
+
+static struct svip_reg_sys1 *const sys1 = (struct svip_reg_sys1 *)LTQ_SYS1_BASE;
+static struct svip_reg_es *const es = (struct svip_reg_es *)LTQ_ES_BASE;
+
+/* PHY Organizationally Unique Identifier (OUI) */
+#define PHY_OUI_PMC 0x00E004
+#define PHY_OUI_VITESSE 0x008083
+#define PHY_OUI_DEFAULT 0xFFFFFF
+
+unsigned short switchip_phy_read(unsigned int phyaddr, unsigned int regaddr);
+void switchip_phy_write(unsigned int phyaddr, unsigned int regaddr,
+ unsigned short data);
+
+static int phy_address[2] = {0, 1};
+static u32 phy_oui;
+static void switchip_mdio_poll_init(void);
+static void _switchip_mdio_poll(struct work_struct *work);
+
+/* struct workqueue_struct mdio_poll_task; */
+static struct workqueue_struct *mdio_poll_workqueue;
+DECLARE_DELAYED_WORK(mdio_poll_work, _switchip_mdio_poll);
+static int old_link_status[2] = {-1, -1};
+
+/**
+ * Autonegotiation check.
+ * This funtion checks for link changes. If a link change has occured it will
+ * update certain switch registers.
+ */
+static void _switchip_check_phy_status(int port)
+{
+ int new_link_status;
+ unsigned short reg1;
+
+ reg1 = switchip_phy_read(phy_address[port], 1);
+ if ((reg1 == 0xFFFF) || (reg1 == 0x0000))
+ return; /* no PHY connected */
+
+ new_link_status = reg1 & 4;
+ if (old_link_status[port] ^ new_link_status) {
+ /* link status change */
+ if (!new_link_status) {
+ if (port == 0)
+ es_w32_mask(LTQ_ES_P0_CTL_REG_FLP, 0, p0_ctl);
+ else
+ es_w32_mask(LTQ_ES_P0_CTL_REG_FLP, 0, p1_ctl);
+
+ /* read again; link bit is latched low! */
+ reg1 = switchip_phy_read(phy_address[port], 1);
+ new_link_status = reg1 & 4;
+ }
+
+ if (new_link_status) {
+ unsigned short reg0, reg4, reg5, reg9, reg10;
+ int phy_pause, phy_speed, phy_duplex;
+ int aneg_enable, aneg_cmpt;
+
+ reg0 = switchip_phy_read(phy_address[port], 0);
+ reg4 = switchip_phy_read(phy_address[port], 4);
+ aneg_enable = reg0 & 0x1000;
+ aneg_cmpt = reg1 & 0x20;
+
+ if (aneg_enable && aneg_cmpt) {
+ reg5 = switchip_phy_read(phy_address[port], 5);
+ switch (phy_oui) {
+#ifdef CONFIG_LANTIQ_MACH_EASY336
+ case PHY_OUI_PMC:
+ /* PMC Sierra supports 1Gigabit FD,
+ * only. On successful
+ * auto-negotiation, we are sure this
+ * is what the LP can. */
+ phy_pause = ((reg4 & reg5) & 0x0080) >> 7;
+ phy_speed = 2;
+ phy_duplex = 1;
+ break;
+#endif
+ case PHY_OUI_VITESSE:
+ case PHY_OUI_DEFAULT:
+ reg9 = switchip_phy_read(phy_address[port], 9);
+ reg10 = switchip_phy_read(phy_address[port], 10);
+
+ /* Check if advertise and partner
+ * agree on pause */
+ phy_pause = ((reg4 & reg5) & 0x0400) >> 10;
+
+ /* Find the best mode both partners
+ * support
+ * Priority: 1GB-FD, 1GB-HD, 100MB-FD,
+ * 100MB-HD, 10MB-FD, 10MB-HD */
+ phy_speed = ((((reg9<<2) & reg10)
+ & 0x0c00) >> 6) |
+ (((reg4 & reg5) & 0x01e0) >> 5);
+
+ if (phy_speed >= 0x0020) {
+ phy_speed = 2;
+ phy_duplex = 1;
+ } else if (phy_speed >= 0x0010) {
+ phy_speed = 2;
+ phy_duplex = 0;
+ } else if (phy_speed >= 0x0008) {
+ phy_speed = 1;
+ phy_duplex = 1;
+ } else if (phy_speed >= 0x0004) {
+ phy_speed = 1;
+ phy_duplex = 0;
+ } else if (phy_speed >= 0x0002) {
+ phy_speed = 0;
+ phy_duplex = 1;
+ } else {
+ phy_speed = 0;
+ phy_duplex = 0;
+ }
+ break;
+ default:
+ phy_pause = (reg4 & 0x0400) >> 10;
+ phy_speed = (reg0 & 0x40 ? 2 : (reg0 >> 13)&1);
+ phy_duplex = (reg0 >> 8)&1;
+ break;
+ }
+ } else {
+ /* parallel detection or fixed speed */
+ phy_pause = (reg4 & 0x0400) >> 10;
+ phy_speed = (reg0 & 0x40 ? 2 : (reg0 >> 13)&1);
+ phy_duplex = (reg0 >> 8)&1;
+ }
+
+ if (port == 0) {
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0SPD,
+ LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(phy_speed),
+ rgmii_ctl);
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0DUP,
+ LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(phy_duplex),
+ rgmii_ctl);
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0FCE,
+ LTQ_ES_RGMII_CTL_REG_P0FCE_VAL(phy_pause),
+ rgmii_ctl);
+
+ es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl);
+ } else {
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1SPD,
+ LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(phy_speed),
+ rgmii_ctl);
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1DUP,
+ LTQ_ES_RGMII_CTL_REG_P1DUP_VAL(phy_duplex),
+ rgmii_ctl);
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1FCE,
+ LTQ_ES_RGMII_CTL_REG_P0FCE_VAL(phy_pause),
+ rgmii_ctl);
+
+ es_w32_mask(1, LTQ_ES_P0_CTL_REG_FLP, p1_ctl);
+ }
+ }
+ }
+ old_link_status[port] = new_link_status;
+}
+
+static void _switchip_mdio_poll(struct work_struct *work)
+{
+ if (es_r32(sw_gctl0) & LTQ_ES_SW_GCTL0_REG_SE) {
+ _switchip_check_phy_status(0);
+ _switchip_check_phy_status(1);
+ }
+
+ queue_delayed_work(mdio_poll_workqueue, &mdio_poll_work, HZ/2);
+}
+
+static void switchip_mdio_poll_init(void)
+{
+ mdio_poll_workqueue = create_workqueue("SVIP MDIP poll");
+ INIT_DELAYED_WORK(&mdio_poll_work, _switchip_mdio_poll);
+
+ queue_delayed_work(mdio_poll_workqueue, &mdio_poll_work, HZ/2);
+
+}
+
+unsigned short switchip_phy_read(unsigned int phyaddr, unsigned int regaddr)
+{
+ /* TODO: protect MDIO access with semaphore */
+ es_w32(LTQ_ES_MDIO_CTL_REG_MBUSY
+ | LTQ_ES_MDIO_CTL_REG_OP_VAL(2) /* read operation */
+ | LTQ_ES_MDIO_CTL_REG_PHYAD_VAL(phyaddr)
+ | LTQ_ES_MDIO_CTL_REG_REGAD_VAL(regaddr), mdio_ctl);
+ while (es_r32(mdio_ctl) & LTQ_ES_MDIO_CTL_REG_MBUSY);
+
+ return es_r32(mdio_data) & 0xFFFF;
+}
+EXPORT_SYMBOL(switchip_phy_read);
+
+void switchip_phy_write(unsigned int phyaddr, unsigned int regaddr,
+ unsigned short data)
+{
+ /* TODO: protect MDIO access with semaphore */
+ es_w32(LTQ_ES_MDIO_CTL_REG_WD_VAL(data)
+ | LTQ_ES_MDIO_CTL_REG_MBUSY
+ | LTQ_ES_MDIO_CTL_REG_OP_VAL(1) /* write operation */
+ | LTQ_ES_MDIO_CTL_REG_PHYAD_VAL(phyaddr)
+ | LTQ_ES_MDIO_CTL_REG_REGAD_VAL(regaddr), mdio_ctl);
+ while (es_r32(mdio_ctl) & LTQ_ES_MDIO_CTL_REG_MBUSY);
+
+ return;
+}
+EXPORT_SYMBOL(switchip_phy_write);
+
+const static u32 switch_reset_offset_000[] = {
+ /*b8000000:*/ 0xffffffff, 0x00000001, 0x00000001, 0x00000003,
+ /*b8000010:*/ 0x04070001, 0x04070001, 0x04070001, 0xffffffff,
+ /*b8000020:*/ 0x00001be8, 0x00001be8, 0x00001be8, 0xffffffff,
+ /*b8000030:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001,
+ /*b8000040:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001,
+ /*b8000050:*/ 0x00000000, 0x00000000, 0x00080004, 0x00020001,
+ /*b8000060:*/ 0x00000000, 0x00000000, 0x00081000, 0x001f7777,
+ /*b8000070:*/ 0x00000000, 0x00000000, 0x0c00ac2b, 0x0000fa50,
+ /*b8000080:*/ 0x00001000, 0x00001800, 0x00000000, 0x00000000,
+ /*b8000090:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b80000a0:*/ 0x00000000, 0x00000050, 0x00000010, 0x00000000,
+ /*b80000b0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b80000c0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b80000d0:*/ 0xffffffff, 0x00000000, 0x00000000
+};
+const static u32 switch_reset_offset_100[] = {
+ /*b8000100:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000110:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000120:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000130:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000140:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000150:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000160:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000170:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000180:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b8000190:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b80001a0:*/ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /*b80001b0:*/ 0x00000000, 0x00000000
+};
+
+/*
+ * Switch Reset.
+ */
+void switchip_reset(void)
+{
+ volatile unsigned int *reg;
+ volatile unsigned int rdreg;
+ int i;
+
+ sys1_w32(SYS1_CLKENR_ETHSW, clkenr);
+ asm("sync");
+
+ /* disable P0 */
+ es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p0_ctl);
+ /* disable P1 */
+ es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p1_ctl);
+ /* disable P2 */
+ es_w32_mask(0, LTQ_ES_P0_CTL_REG_SPS_VAL(1), p2_ctl);
+
+ /**************************************
+ * BEGIN: Procedure to clear MAC table
+ **************************************/
+ for (i = 0; i < 3; i++) {
+ int result;
+
+ /* check if access engine is available */
+ while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY);
+
+ /* initialise to first address */
+ es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(3)
+ | LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(0), adr_tb_ctl2);
+
+ /* wait while busy */
+ while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY);
+
+ /* setup the portmap */
+ es_w32_mask(0, LTQ_ES_ADR_TB_CTL1_REG_PMAP_VAL(1 << i),
+ adr_tb_ctl1);
+
+ do {
+ /* search for addresses by port */
+ es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(2)
+ | LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(9), adr_tb_ctl2);
+
+ /* wait while busy */
+ while (es_r32(adr_tb_st2) & LTQ_ES_ADR_TB_ST2_REG_BUSY);
+
+ result = LTQ_ES_ADR_TB_ST2_REG_RSLT_GET(es_r32(adr_tb_st2));
+ if (result == 0x101) {
+ printk(KERN_ERR "%s, cmd error\n", __func__);
+ return;
+ }
+ /* if Command OK, address found... */
+ if (result == 0) {
+ unsigned char mac[6];
+
+ mac[5] = (es_r32(adr_tb_st0) >> 0) & 0xff;
+ mac[4] = (es_r32(adr_tb_st0) >> 8) & 0xff;
+ mac[3] = (es_r32(adr_tb_st0) >> 16) & 0xff;
+ mac[2] = (es_r32(adr_tb_st0) >> 24) & 0xff;
+ mac[1] = (es_r32(adr_tb_st1) >> 0) & 0xff;
+ mac[0] = (es_r32(adr_tb_st1) >> 8) & 0xff;
+
+ /* setup address */
+ es_w32((mac[5] << 0) |
+ (mac[4] << 8) |
+ (mac[3] << 16) |
+ (mac[2] << 24), adr_tb_ctl0);
+ es_w32(LTQ_ES_ADR_TB_CTL1_REG_PMAP_VAL(1<<i) |
+ LTQ_ES_ADR_TB_CTL1_REG_FID_VAL(0) |
+ (mac[0] << 8) |
+ (mac[1] << 0), adr_tb_ctl1);
+ /* erase address */
+
+ es_w32(LTQ_ES_ADR_TB_CTL2_REG_CMD_VAL(1) |
+ LTQ_ES_ADR_TB_CTL2_REG_AC_VAL(15),
+ adr_tb_ctl2);
+
+ /* wait, while busy */
+ while (es_r32(adr_tb_st2) &
+ LTQ_ES_ADR_TB_ST2_REG_BUSY);
+ }
+ } while (result == 0);
+ }
+ /**************************************
+ * END: Procedure to clear MAC table
+ **************************************/
+
+ /* reset RMON counters */
+ es_w32(LTQ_ES_RMON_CTL_REG_BAS | LTQ_ES_RMON_CTL_REG_CAC_VAL(3),
+ rmon_ctl);
+
+ /* bring all registers to reset state */
+ reg = LTQ_ES_PS_REG;
+ for (i = 0; i < ARRAY_SIZE(switch_reset_offset_000); i++) {
+ if ((reg == LTQ_ES_PS_REG) ||
+ (reg >= LTQ_ES_ADR_TB_CTL0_REG &&
+ reg <= LTQ_ES_ADR_TB_ST2_REG))
+ continue;
+
+ if (switch_reset_offset_000[i] != 0xFFFFFFFF) {
+ /* write reset value to register */
+ *reg = switch_reset_offset_000[i];
+ /* read register value back */
+ rdreg = *reg;
+ if (reg == LTQ_ES_SW_GCTL1_REG)
+ rdreg &= ~LTQ_ES_SW_GCTL1_REG_BISTDN;
+ /* compare read value with written one */
+ if (rdreg != switch_reset_offset_000[i]) {
+ printk(KERN_ERR "%s,%d: reg %08x mismatch "
+ "[has:%08x, expect:%08x]\n",
+ __func__, __LINE__,
+ (unsigned int)reg, rdreg,
+ switch_reset_offset_000[i]);
+ }
+ }
+ reg++;
+ }
+
+ reg = LTQ_ES_VLAN_FLT0_REG;
+ for (i = 0; i < ARRAY_SIZE(switch_reset_offset_100); i++) {
+ *reg = switch_reset_offset_100[i];
+ rdreg = *reg;
+ if (rdreg != switch_reset_offset_100[i]) {
+ printk(KERN_ERR "%s,%d: reg %08x mismatch "
+ "[has:%08x, expect:%08x]\n", __func__, __LINE__,
+ (unsigned int)reg, rdreg,
+ switch_reset_offset_100[i]);
+ }
+ reg++;
+ }
+}
+EXPORT_SYMBOL(switchip_reset);
+
+static u32 get_phy_oui(unsigned char phy_addr)
+{
+ u32 oui;
+ int i, bit, byte, shift, w;
+ u16 reg_id[2];
+
+ /* read PHY identifier registers 1 and 2 */
+ reg_id[0] = switchip_phy_read(phy_addr, 2);
+ reg_id[1] = switchip_phy_read(phy_addr, 3);
+
+ oui = 0;
+ w = 1;
+ shift = 7;
+ byte = 1;
+ for (i = 0, bit = 10; i <= 21; i++, bit++) {
+ oui |= ((reg_id[w] & (1<<bit)) ? 1 : 0) << shift;
+ if (!(shift % 8)) {
+ byte++;
+ if (byte == 2)
+ shift = 15;
+ else
+ shift = 21;
+ } else {
+ shift--;
+ }
+ if (w == 1 && bit == 15) {
+ bit = -1;
+ w = 0;
+ }
+ }
+ return oui;
+}
+
+/*
+ * Switch Initialization.
+ */
+int switchip_init(void)
+{
+ int eth_port, phy_present = 0;
+ u16 reg, mode;
+
+ sys1_w32(SYS1_CLKENR_ETHSW, clkenr);
+ asm("sync");
+
+ /* Enable Switch, if not already done so */
+ if ((es_r32(sw_gctl0) & LTQ_ES_SW_GCTL0_REG_SE) == 0)
+ es_w32_mask(0, LTQ_ES_SW_GCTL0_REG_SE, sw_gctl0);
+ /* Wait for completion of MBIST */
+ while (LTQ_ES_SW_GCTL1_REG_BISTDN_GET(es_r32(sw_gctl1)) == 0);
+
+ switchip_reset();
+
+ mode = LTQ_ES_RGMII_CTL_REG_IS_GET(es_r32(rgmii_ctl));
+ eth_port = (mode == 2 ? 1 : 0);
+
+ /* Set the primary port(port toward backplane) as sniffer port,
+ changing from P2 which is the reset setting */
+ es_w32_mask(LTQ_ES_SW_GCTL0_REG_SNIFFPN,
+ LTQ_ES_SW_GCTL0_REG_SNIFFPN_VAL(eth_port),
+ sw_gctl0);
+
+ /* Point MDIO state machine to invalid PHY addresses 8 and 9 */
+ es_w32_mask(0, LTQ_ES_SW_GCTL0_REG_PHYBA, sw_gctl0);
+
+ /* Add CRC for packets from DMA to PMAC.
+ Remove CRC for packets from PMAC to DMA. */
+ es_w32(LTQ_ES_PMAC_HD_CTL_RC | LTQ_ES_PMAC_HD_CTL_AC, pmac_hd_ctl);
+
+ phy_oui = get_phy_oui(0);
+ switch (phy_oui) {
+#ifdef CONFIG_LANTIQ_MACH_EASY336
+ case PHY_OUI_PMC:
+ phy_address[0] = (mode == 2 ? -1 : 2);
+ phy_address[1] = (mode == 2 ? 2 : -1);
+ break;
+#endif
+ case PHY_OUI_VITESSE:
+ default:
+ phy_oui = PHY_OUI_DEFAULT;
+ phy_address[0] = (mode == 2 ? 1 : 0);
+ phy_address[1] = (mode == 2 ? 0 : 1);
+ break;
+ }
+
+ /****** PORT 0 *****/
+ reg = switchip_phy_read(phy_address[0], 1);
+ if ((reg != 0x0000) && (reg != 0xffff)) {
+ /* PHY connected? */
+ phy_present |= 1;
+ /* Set Rx- and TxDelay in case of RGMII */
+ switch (mode) {
+ case 0: /* *RGMII,RGMII */
+ case 2: /* RGMII,*GMII */
+ /* program clock delay in PHY, not in SVIP */
+
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0RDLY, 0, rgmii_ctl);
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0TDLY, 0, rgmii_ctl);
+ if (phy_oui == PHY_OUI_VITESSE ||
+ phy_oui == PHY_OUI_DEFAULT) {
+ switchip_phy_write(phy_address[0], 31, 0x0001);
+ switchip_phy_write(phy_address[0], 28, 0xA000);
+ switchip_phy_write(phy_address[0], 31, 0x0000);
+ }
+ default:
+ break;
+ }
+ if (phy_oui == PHY_OUI_VITESSE ||
+ phy_oui == PHY_OUI_DEFAULT) {
+ /* Program PHY advertisements and
+ * restart auto-negotiation */
+ switchip_phy_write(phy_address[0], 4, 0x05E1);
+ switchip_phy_write(phy_address[0], 9, 0x0300);
+ switchip_phy_write(phy_address[0], 0, 0x3300);
+ } else {
+ reg = switchip_phy_read(phy_address[1], 0);
+ reg |= 0x1000; /* auto-negotiation enable */
+ switchip_phy_write(phy_address[1], 0, reg);
+ reg |= 0x0200; /* auto-negotiation restart */
+ switchip_phy_write(phy_address[1], 0, reg);
+ }
+ } else {
+ /* Force SWITCH link with highest capability:
+ * 100M FD for MII
+ * 1G FD for GMII/RGMII
+ */
+ switch (mode) {
+ case 1: /* *MII,MII */
+ case 3: /* *MII,RGMII */
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(1),
+ rgmii_ctl);
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(1),
+ rgmii_ctl);
+ break;
+ case 0: /* *RGMII,RGMII */
+ case 2: /* RGMII,*GMII */
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0SPD_VAL(2),
+ rgmii_ctl);
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0DUP_VAL(1),
+ rgmii_ctl);
+
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P0RDLY, 0, rgmii_ctl);
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P0TDLY_VAL(2),
+ rgmii_ctl);
+ break;
+ }
+
+ es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl);
+ }
+
+ /****** PORT 1 *****/
+ reg = switchip_phy_read(phy_address[1], 1);
+ if ((reg != 0x0000) && (reg != 0xffff)) {
+ /* PHY connected? */
+ phy_present |= 2;
+ /* Set Rx- and TxDelay in case of RGMII */
+ switch (mode) {
+ case 0: /* *RGMII,RGMII */
+ case 3: /* *MII,RGMII */
+ /* program clock delay in PHY, not in SVIP */
+
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1RDLY, 0, rgmii_ctl);
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1TDLY, 0, rgmii_ctl);
+ if (phy_oui == PHY_OUI_VITESSE ||
+ phy_oui == PHY_OUI_DEFAULT) {
+ switchip_phy_write(phy_address[1], 31, 0x0001);
+ switchip_phy_write(phy_address[1], 28, 0xA000);
+ switchip_phy_write(phy_address[1], 31, 0x0000);
+ }
+ break;
+ case 2: /* RGMII,*GMII */
+
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2),
+ rgmii_ctl);
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl);
+#ifdef CONFIG_LANTIQ_MACH_EASY336
+ if (phy_oui == PHY_OUI_PMC) {
+ switchip_phy_write(phy_address[1], 24, 0x0510);
+ switchip_phy_write(phy_address[1], 17, 0xA38C);
+ switchip_phy_write(phy_address[1], 17, 0xA384);
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+ /* Program PHY advertisements and restart auto-negotiation */
+ if (phy_oui == PHY_OUI_VITESSE ||
+ phy_oui == PHY_OUI_DEFAULT) {
+ switchip_phy_write(phy_address[1], 4, 0x05E1);
+ switchip_phy_write(phy_address[1], 9, 0x0300);
+ switchip_phy_write(phy_address[1], 0, 0x3300);
+ } else {
+ reg = switchip_phy_read(phy_address[1], 0);
+ reg |= 0x1000; /* auto-negotiation enable */
+ switchip_phy_write(phy_address[1], 0, reg);
+ reg |= 0x0200; /* auto-negotiation restart */
+ switchip_phy_write(phy_address[1], 0, reg);
+ }
+ } else {
+ /* Force SWITCH link with highest capability:
+ * 100M FD for MII
+ * 1G FD for GMII/RGMII
+ */
+ switch (mode) {
+ case 1: /* *MII,MII */
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(1),
+ rgmii_ctl);
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl);
+ break;
+ case 0: /* *RGMII,RGMII */
+ case 3: /* *MII,RGMII */
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2),
+ rgmii_ctl);
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl);
+ es_w32_mask(LTQ_ES_RGMII_CTL_REG_P1RDLY, 0, rgmii_ctl);
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1TDLY_VAL(2),
+ rgmii_ctl);
+ break;
+ case 2: /* RGMII,*GMII */
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1SPD_VAL(2),
+ rgmii_ctl);
+ es_w32_mask(0, LTQ_ES_RGMII_CTL_REG_P1DUP, rgmii_ctl);
+ break;
+ }
+ es_w32_mask(0, LTQ_ES_P0_CTL_REG_FLP, p0_ctl);
+ }
+
+ /*
+ * Allow unknown unicast/multicast and broadcasts
+ * on all ports.
+ */
+
+ es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_UP_VAL(7), sw_gctl1);
+ es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_BP_VAL(7), sw_gctl1);
+ es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_MP_VAL(7), sw_gctl1);
+ es_w32_mask(0, LTQ_ES_SW_GCTL1_REG_RP_VAL(7), sw_gctl1);
+
+ /* Enable LAN port(s) */
+ if (eth_port == 0)
+ es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p0_ctl);
+ else
+ es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p1_ctl);
+ /* Enable CPU Port (Forwarding State) */
+ es_w32_mask(LTQ_ES_P0_CTL_REG_SPS, 0, p2_ctl);
+
+ if (phy_present)
+ switchip_mdio_poll_init();
+
+ return 0;
+}
+EXPORT_SYMBOL(switchip_init);
+
+device_initcall(switchip_init);