//========================================================================== // // devs_eth_arm_tx25.inl // // Board ethernet I/O definitions. // //========================================================================== //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 or (at your option) any later version. // // eCos is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with eCos; if not, write to the Free Software Foundation, Inc., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // // As a special exception, if other files instantiate templates or use macros // or inline functions from this file, or you compile this file and link it // with other works to produce a work based on this file, this file does not // by itself cause the resulting work to be covered by the GNU General Public // License. However the source code for this file must still be made available // in accordance with section (3) of the GNU General Public License. // // This exception does not invalidate any other reasons why a work based on // this file might be covered by the GNU General Public License. // // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. // at http://sources.redhat.com/ecos/ecos-license/ // ------------------------------------------- //####ECOSGPLCOPYRIGHTEND#### //=========================================================================== #include // CYGNUM_HAL_INTERRUPT_ETHR #include #ifdef CYGPKG_REDBOOT #include #ifdef CYGSEM_REDBOOT_FLASH_CONFIG #include #include #endif #endif #ifdef __WANT_DEVS #ifdef CYGPKG_DEVS_ETH_ARM_MXCBOARD_ETH0 #ifdef CYGPKG_DEVS_ETH_PHY static char mxc_fec_name[] = "mxc_fec"; #define OCR_SHIFT(bit) (((bit) * 2) % 32) #define OCR_MASK(bit) (3 << (OCR_SHIFT(bit))) #define OCR_VAL(bit,val) (((val) << (OCR_SHIFT(bit))) & (OCR_MASK(bit))) #define GPR_SHIFT(bit) (bit) #define GPR_MASK(bit) (1 << (GPR_SHIFT(bit))) #define GPR_VAL(bit,val) (((val) << (GPR_SHIFT(bit))) & (GPR_MASK(bit))) #ifdef CYGSEM_REDBOOT_PLF_ESA_VALIDATE // // Verify that the given ESA is valid for this platform // static char oui[3] = CYGDAT_DEVS_ETH_ARM_TX25KARO_OUI; bool cyg_plf_redboot_esa_validate(unsigned char *val) { return (val[0] == oui[0]) && (val[1] == oui[1]) && (val[2] == oui[2]); } #endif extern int tx25_mac_addr_program(unsigned char mac_addr[ETHER_ADDR_LEN]); static inline CYG_ADDRESS MX25_GPIO_ADDR(int bank) { switch (bank) { case 1: return GPIO1_BASE_ADDR; case 2: return GPIO2_BASE_ADDR; case 3: return GPIO3_BASE_ADDR; case 4: return GPIO4_BASE_ADDR; } return ~0; } /* TX27 -> TX25 GPIO cross reference TX27 GP Fkt GPIO Pad IOMUXC SW_PAD SW_PAD strap GPIO ALT ALT OFFSET CTRL MUX option FEC_MDC PD9 5 0 GPIO3_5 FEC_MDC 0x1c8 0x3c0 FEC_MDIO PD8 5 0 GPIO3_6 FEC_MDIO 0x1cc 0x3c4 FEC_RX_CLK PD14 - - NC REGOFF: 0 FEC_RX_DV PD13 - - NC FEC_RXD0 PD12 5 0 GPIO3_10 FEC_RDATA0 0x1dc 0x3d4 MODE0: 1 FEC_RXD1 PD5 5 0 GPIO3_11 FEC_RDATA1 0x1e0 0x3d8 MODE1: 1 FEC_RXD2 PD6 - - NC PULLUP MODE2: 1 FEC_RXD3 PD7 - - NC INTSEL: 0 FEC_RX_ER PD4 5 5 GPIO4_10 D10 0x09c 0x294 FEC_TX_CLK PD11 5 0 GPIO3_13 FEC_TX_CLK 0x1e8 0x3e0 FEC_TX_EN PF23 5 0 GPIO3_9 FEC_TX_EN 0x1d8 0x3d0 FEC_TXD0 PD0 5 0 GPIO3_7 FEC_TDATA0 0x1d0 0x3c8 FEC_TXD1 PD1 5 0 GPIO3_8 FEC_TDATA1 0x1d4 0x3cc FEC_TXD2 PD2 - - NC FEC_TXD3 PD3 - - NC FEC_COL PD15 5 0 GPIO3_12 FEC_RX_DV 0x1e4 0x3dc RMII: 1 FEC_CRS PD10 - - NC PHYAD4: 0 FEC_TX_ER PD16 5 5 GPIO4_8 D12 0x094 0x28c GPIO2_5 A19 0x024 0x240 0x518 FEC_RX_ER FEC_RESET~ PB30 5 5 GPIO4_7 D13 0x090 0x288 FEC_ENABLE PB27 5 5 GPIO4_9 D11 0x098 0x290 */ static inline void tx25_write_reg(CYG_ADDRWORD base_addr, CYG_WORD32 offset, CYG_WORD32 val) { if (net_debug) diag_printf("Writing %08x to reg %08x\n", val, base_addr + offset); HAL_WRITE_UINT32(base_addr + offset, val); } static inline CYG_WORD32 tx25_read_reg(CYG_ADDRWORD base_addr, CYG_WORD32 offset) { CYG_WORD32 val; HAL_READ_UINT32(base_addr + offset, val); if (net_debug) diag_printf("Read %08x from reg %08x\n", val, base_addr + offset); return val; } static inline void tx25_set_reg(CYG_ADDRWORD base_addr, CYG_WORD32 offset, CYG_WORD32 set_mask, CYG_WORD32 clr_mask) { CYG_WORD32 val; HAL_READ_UINT32(base_addr + offset, val); if (net_debug) diag_printf("Changing reg %08x from %08x to %08x\n", base_addr + offset, val, (val & ~clr_mask) | set_mask); val = (val & ~clr_mask) | set_mask; HAL_WRITE_UINT32(base_addr + offset, val); } static struct tx25_gpio_setup { cyg_uint16 iomux_addr; cyg_uint8 on_func; cyg_uint8 off_func; cyg_uint8 grp; cyg_uint8 shift; } tx25_fec_gpio_data[] = { /* iomux, func, gpfn, gpgrp, gpshift */ { 0x1c8, 0, 0x15, 3, 5, }, { 0x1cc, 0, 0x15, 3, 6, }, { 0x1dc, 0, 0x15, 3, 10, }, { 0x1e0, 0, 0x15, 3, 11, }, { 0x09c, 0x85, 5, 4, 10, }, { 0x1e8, 0, 0x15, 3, 13, }, { 0x1d8, 0, 0x15, 3, 9, }, { 0x1d0, 0, 0x15, 3, 7, }, { 0x1d4, 0, 0x15, 3, 8, }, { 0x1e4, 0x80, 0x15, 3, 12, }, { 0x024, 0x05, 0x05, 2, 5, }, /* RX_ER signal; make sure it's a GPIO _without_ SION! */ { 0x094, 0x85, 5, 4, 8, }, { 0x090, 5, 5, 4, 7, }, { 0x098, 5, 5, 4, 9, }, }; static struct tx25_gpio_setup tx25_fec_strap_pins[] = { { 0x1dc, 0, 0x15, 3, 10, }, { 0x1e0, 0, 0x15, 3, 11, }, { 0x1e4, 0, 0x15, 3, 12, }, }; static inline void tx25_phy_power_off(void) { int i; if (net_debug) diag_printf("Switching PHY POWER off\n"); #if 1 for (i = 0; i < NUM_ELEMS(tx25_fec_gpio_data); i++) { struct tx25_gpio_setup *gs = &tx25_fec_gpio_data[i]; if (net_debug) diag_printf("%s: GPIO%d_%d[%d] is %d\n", __FUNCTION__, gs->grp, gs->shift, i, gpio_tst_bit(gs->grp, gs->shift)); } #endif /* deassert all pins attached to the PHY */ for (i = 0; i < NUM_ELEMS(tx25_fec_gpio_data); i++) { struct tx25_gpio_setup *gs = &tx25_fec_gpio_data[i]; tx25_write_reg(IOMUXC_BASE_ADDR, gs->iomux_addr, gs->off_func); if (gs->on_func & 0x80) { /* configure as input */ tx25_set_reg(MX25_GPIO_ADDR(gs->grp), GPIO_GDIR, 0, 1 << gs->shift); } else { /* configure as output */ tx25_set_reg(MX25_GPIO_ADDR(gs->grp), GPIO_DR, 0, 1 << gs->shift); tx25_set_reg(MX25_GPIO_ADDR(gs->grp), GPIO_GDIR, 1 << gs->shift, 0); } tx25_write_reg(IOMUXC_BASE_ADDR, gs->iomux_addr, gs->off_func); } for (i = 0; i < NUM_ELEMS(tx25_fec_gpio_data); i++) { struct tx25_gpio_setup *gs = &tx25_fec_gpio_data[i]; if (!(gs->on_func & 0x80) && gpio_tst_bit(gs->grp, gs->shift)) { if (net_debug) diag_printf("%s: GPIO%d_%d[%d] is not low\n", __FUNCTION__, gs->grp, gs->shift, i); } } if (net_debug) diag_printf("PHY POWER off done\n"); } static bool tx25_fec_init(struct cyg_netdevtab_entry *tab) { cyg_bool esa_set; int ok; /* Check, whether MAC address is enabled */ ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET, "fec_esa", &esa_set, CONFIG_BOOL); if (!(ok && esa_set)) { diag_printf("FEC disabled; set fec_esa=true to enable networking\n"); tx25_phy_power_off(); return false; } /* call init function in devs/eth/fec/current/if_fec.c */ return mxc_fec_init(tab); } static void tx25_fec_phy_init(void) { int i; int phy_reset_delay = 100; /* * make sure the ETH PHY strap pins are pulled to the right voltage * before deasserting the PHY reset GPIO * REGOFF: PD14 * RMII: PD15 * nINTSEL: PD7 * MODE0: PD12 * MODE1: PD5 * MODE2: PD6 * PHYAD0: - * PHYAD1: GND * PHYAD2: GND * PHYAD3: - * PHYAD4: PD10 */ // assert FEC PHY Reset (PB30) and switch PHY power on /* PB22, PB27, PB30 => GPIO out */ #if 0 tx25_phy_power_off(); #endif #if 0 tx25_write_reg(IOMUXC_BASE_ADDR, IOMUXC_GPIO1_7, 0x11); tx25_write_reg(IOMUXC_BASE_ADDR, IOMUXC_NANDF_CS1, 0x14); #endif #if 0 while (1) { #if 0 gpio_set_bit(4, 9); #else tx25_set_reg(MX25_GPIO_ADDR(4), GPIO_DR, 1 << 9, 0); #endif tx25_read_reg(MX25_GPIO_ADDR(4), GPIO_DR); HAL_DELAY_US(1000000); #if 0 gpio_clr_bit(4, 9); #else tx25_set_reg(MX25_GPIO_ADDR(4), GPIO_DR, 0, 1 << 9); #endif tx25_read_reg(MX25_GPIO_ADDR(4), GPIO_DR); HAL_DELAY_US(1000000); } #endif /* Switch PHY power on and assert PHY reset */ if (net_debug) diag_printf("Switching PHY POWER on\n"); gpio_clr_bit(4, 7); gpio_set_bit(4, 9); /* wait for 22ms for LAN8700 to power up */ phy_reset_delay = 22000; /* configure FEC strap pins to their required values */ for (i = 0; i < NUM_ELEMS(tx25_fec_strap_pins); i++) { struct tx25_gpio_setup *gs = &tx25_fec_strap_pins[i]; if (net_debug) diag_printf("Asserting GPIO%d_%d\n", gs->grp, gs->shift); #if 0 tx25_set_reg(MX25_GPIO_ADDR(gs->grp), GPIO_GDIR, 1 << gs->shift, 0); tx25_set_reg(MX25_GPIO_ADDR(gs->grp), GPIO_DR, 1 << gs->shift, 0); tx25_write_reg(IOMUXC_BASE_ADDR, gs->iomux_addr, gs->off_func); #else gpio_set_bit(gs->grp, gs->shift); #endif } #if 0 for (i = 0; i < NUM_ELEMS(tx25_fec_gpio_data); i++) { struct tx25_gpio_setup *gs = &tx25_fec_gpio_data[i]; int j; int strap = 0; for (j = 0; j < NUM_ELEMS(tx25_fec_strap_pins); j++) { struct tx25_gpio_setup *sp = &tx25_fec_strap_pins[j]; if (gs->grp == sp->grp && gs->shift == sp->shift) { strap = 1; break; } } if (strap || gs->on_func & 0x80) { if (!gpio_tst_bit(gs->grp, gs->shift)) { if (net_debug) diag_printf("GPIO%d_%d[%d] is low instead of high\n", gs->grp, gs->shift, i); } } else { if (gpio_tst_bit(gs->grp, gs->shift)) { if (net_debug) diag_printf("GPIO%d_%d[%d] is high instead of low\n", gs->grp, gs->shift, i); } } } #endif /* wait for 100us according to LAN8700 spec. before ... */ HAL_DELAY_US(phy_reset_delay); /* ... deasserting FEC PHY reset */ if (net_debug) diag_printf("Releasing PHY RESET\n"); tx25_set_reg(MX25_GPIO_ADDR(4), GPIO_DR, 1 << 7, 0); #if 0 for (i = 0; i < NUM_ELEMS(tx25_fec_gpio_data); i++) { struct tx25_gpio_setup *gs = &tx25_fec_gpio_data[i]; int j; int strap = 0; for (j = 0; j < NUM_ELEMS(tx25_fec_strap_pins); j++) { struct tx25_gpio_setup *sp = &tx25_fec_strap_pins[j]; if (gs->grp == sp->grp && gs->shift == sp->shift) { strap = 1; break; } } if (strap || gs->on_func & 0x80) { if (!gpio_tst_bit(gs->grp, gs->shift)) { if (net_debug) diag_printf("GPIO%d_%d[%d] is low instead of high\n", gs->grp, gs->shift, i); } } else { if (gpio_tst_bit(gs->grp, gs->shift)) { if (net_debug) diag_printf("GPIO%d_%d[%d] is high instead of low\n", gs->grp, gs->shift, i); } } } #endif /* configure all FEC pins to their required functions */ for (i = 0; i < NUM_ELEMS(tx25_fec_gpio_data); i++) { struct tx25_gpio_setup *gs = &tx25_fec_gpio_data[i]; tx25_write_reg(IOMUXC_BASE_ADDR, gs->iomux_addr, gs->on_func & ~0x80); } } ETH_PHY_REG_LEVEL_ACCESS_FUNS(eth0_phy, tx25_fec_phy_init, mxc_fec_phy_reset, mxc_fec_phy_write, mxc_fec_phy_read); #define SOC_MAC_ADDR_LOCK_BIT 2 cyg_bool _tx25_provide_fec_esa(unsigned char *addr) { cyg_bool enabled; int ok; ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET, "fec_esa", &enabled, CONFIG_BOOL); if (ok && enabled) { #ifdef CYGSEM_REDBOOT_PLF_ESA_VALIDATE cyg_uint8 addr2[ETHER_ADDR_LEN]; addr[0] = readl(SOC_MAC_ADDR_BASE + 0x0); addr[1] = readl(SOC_MAC_ADDR_BASE + 0x4); addr[2] = readl(SOC_MAC_ADDR_BASE + 0x8); addr[3] = readl(SOC_MAC_ADDR_BASE + 0xC); addr[4] = readl(SOC_MAC_ADDR_BASE + 0x10); addr[5] = readl(SOC_MAC_ADDR_BASE + 0x14); if (cyg_plf_redboot_esa_validate(addr)) { diag_printf("Ethernet FEC MAC address from fuse bank: "); diag_printf("%02x:%02x:%02x:%02x:%02x:%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET, "fec_esa_data", addr2, CONFIG_ESA); if (memcmp(addr, addr2, sizeof(addr)) != 0) { CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_SET, "fec_esa_data", addr, CONFIG_ESA); } #ifdef SOC_MAC_ADDR_LOCK_BIT if ((readl(SOC_MAC_ADDR_BASE - 0x68) & SOC_MAC_ADDR_LOCK_BIT) == 0) { tx25_mac_addr_program(addr); } #endif // SOC_MAC_ADDR_LOCK_BIT return true; } #endif // CYGSEM_REDBOOT_PLF_ESA_VALIDATE CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET, "fec_esa_data", addr, CONFIG_ESA); diag_printf("Ethernet FEC MAC address from fconfig: "); diag_printf("%02x:%02x:%02x:%02x:%02x:%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); #ifdef CYGSEM_REDBOOT_PLF_ESA_VALIDATE if (cyg_plf_redboot_esa_validate(addr)) { tx25_mac_addr_program(addr); return true; } diag_printf("** Error: Invalid MAC address: "); diag_printf("%02x:%02x:%02x:%02x:%02x:%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); #ifdef SOC_MAC_ADDR_LOCK_BIT if ((readl(SOC_MAC_ADDR_BASE - 0x68) & SOC_MAC_ADDR_LOCK_BIT) == 0) { diag_printf("Use 'fconfig fec_esa_data' to set the MAC address\n"); return false; } else { diag_printf("Using MAC address from fconfig\n"); } #else diag_printf("Using MAC address from fconfig\n"); #endif // SOC_MAC_ADDR_LOCK_BIT #endif // CYGSEM_REDBOOT_PLF_ESA_VALIDATE return true; } return false; } static mxc_fec_priv_t mxc_fec_private = { .phy = ð0_phy, // PHY access routines .provide_esa = _tx25_provide_fec_esa, }; ETH_DRV_SC(mxc_fec_sc, &mxc_fec_private, // Driver specific data mxc_fec_name, mxc_fec_start, mxc_fec_stop, mxc_fec_control, mxc_fec_can_send, mxc_fec_send, mxc_fec_recv, mxc_fec_deliver, // "pseudoDSR" called from fast net thread mxc_fec_poll, // poll function, encapsulates ISR and DSR mxc_fec_int_vector); NETDEVTAB_ENTRY(mxc_fec_netdev, mxc_fec_name, tx25_fec_init, &mxc_fec_sc); #endif #if defined(CYGPKG_REDBOOT) && defined(CYGSEM_REDBOOT_FLASH_CONFIG) RedBoot_config_option("Set FEC network hardware address [MAC]", fec_esa, ALWAYS_ENABLED, true, CONFIG_BOOL, false ); RedBoot_config_option("FEC network hardware address [MAC]", fec_esa_data, "fec_esa", true, CONFIG_ESA, 0 ); #endif // CYGPKG_REDBOOT && CYGSEM_REDBOOT_FLASH_CONFIG #ifdef CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT // Note that this section *is* active in an application, outside RedBoot, // where the above section is not included. #endif // CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT #endif // CYGPKG_DEVS_ETH_ARM_MXCBOARD_ETH0 #endif // __WANT_DEVS