target/linux/ar71xx/base-files/etc/diag.sh | 3 + .../ar71xx/base-files/etc/uci-defaults/01_leds | 6 + .../ar71xx/base-files/etc/uci-defaults/02_network | 8 + target/linux/ar71xx/base-files/lib/ar71xx.sh | 3 + .../ar71xx/base-files/lib/upgrade/platform.sh | 1 + target/linux/ar71xx/config-3.8 | 26 +- .../ar71xx/files/arch/mips/ath79/mach-dir-632-a1.c | 403 +++ .../mips/include/asm/mach-ath79/ag71xx_platform.h | 6 + .../drivers/net/ethernet/atheros/ag7240/Kconfig | 124 + .../drivers/net/ethernet/atheros/ag7240/Makefile | 14 + .../drivers/net/ethernet/atheros/ag7240/ag7240.c | 2584 ++++++++++++++++++++ .../drivers/net/ethernet/atheros/ag7240/ag7240.h | 706 ++++++ .../net/ethernet/atheros/ag7240/ag7240_phy.h | 169 ++ .../net/ethernet/atheros/ag7240/ag7240_trc.h | 99 + .../drivers/net/ethernet/atheros/ag7240/ar7240.h | 984 ++++++++ .../net/ethernet/atheros/ag7240/ar7240_s26_phy.c | 1431 +++++++++++ .../ethernet/atheros/ag7240/ar7240_s26_phy.c.new | 1386 +++++++++++ .../net/ethernet/atheros/ag7240/ar7240_s26_phy.h | 224 ++ .../drivers/net/ethernet/atheros/ag7240/ar8216.c | 843 +++++++ .../drivers/net/ethernet/atheros/ag7240/ar8216.h | 187 ++ .../net/ethernet/atheros/ag7240/athrf1_phy.c | 385 +++ .../net/ethernet/atheros/ag7240/athrf1_phy.h | 26 + .../net/ethernet/atheros/ag7240/athrs16_phy.c | 896 +++++++ .../net/ethernet/atheros/ag7240/athrs16_phy.h | 245 ++ .../net/ethernet/atheros/ag7240/athrs_ioctl.h | 135 + .../net/ethernet/atheros/ag7240/athrs_qos.c | 196 ++ .../net/ethernet/atheros/ag7240/athrs_qos.h | 289 +++ .../drivers/net/ethernet/atheros/ag7240/eth_diag.h | 65 + .../net/ethernet/atheros/ag7240/python_vlan_igmp.c | 686 ++++++ .../net/ethernet/atheros/ag7240/rtl8309g_phy.c | 245 ++ .../net/ethernet/atheros/ag7240/rtl8309g_phy.h | 35 + .../net/ethernet/atheros/ag7240/tools/Makefile | 47 + .../net/ethernet/atheros/ag7240/tools/Makefile.inc | 84 + .../net/ethernet/atheros/ag7240/tools/ethreg.c | 141 ++ target/linux/ar71xx/generic/profiles/d-link.mk | 11 + target/linux/ar71xx/image/Makefile | 12 + .../617-MIPS-ath79-add-DIR-632-A1-support.patch | 39 + .../620-net-add-ag7240-driver-for-DIR-632-A1.patch | 19 + 38 files changed, 12760 insertions(+), 3 deletions(-) diff --git a/target/linux/ar71xx/base-files/etc/diag.sh b/target/linux/ar71xx/base-files/etc/diag.sh index c107f41..fa0c476 100755 --- a/target/linux/ar71xx/base-files/etc/diag.sh +++ b/target/linux/ar71xx/base-files/etc/diag.sh @@ -47,6 +47,9 @@ get_status_led() { dir-615-e4) status_led="d-link:green:power" ;; + dir-632-a1) + status_led="d-link:amber:power" + ;; dir-615-c1) status_led="d-link:green:status" ;; diff --git a/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds b/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds index afeae42..e023ba8 100755 --- a/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds +++ b/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds @@ -60,6 +60,12 @@ dir-615-e4) ucidef_set_led_switch "lan4" "LAN4" "d-link:green:lan4" "switch0" "0x10" ;; +dir-632-a1) + ucidef_set_led_usbdev "usb" "USB" "d-link:green:usb" "1-1" + ucidef_set_led_netdev "wan" "WAN" "d-link:green:wan" "eth1" + ucidef_set_led_wlan "wlan" "WLAN" "d-link:green:wireless" "phy0tpt" + ;; + dir-825-b1 | \ dir-825-c1) ucidef_set_led_usbdev "usb" "USB" "d-link:blue:usb" "1-1" diff --git a/target/linux/ar71xx/base-files/etc/uci-defaults/02_network b/target/linux/ar71xx/base-files/etc/uci-defaults/02_network index 0329ea5..9bd2f07 100755 --- a/target/linux/ar71xx/base-files/etc/uci-defaults/02_network +++ b/target/linux/ar71xx/base-files/etc/uci-defaults/02_network @@ -214,6 +214,14 @@ wpe72) ucidef_set_interfaces_lan_wan "eth1" "eth0" ;; +dir-632-a1) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" "1" "1" + ucidef_add_switch_vlan "switch0" "1" "0 1 2 3 4 5 6 7 8" + ;; + + + ap121 |\ ap121-mini |\ ap96 |\ diff --git a/target/linux/ar71xx/base-files/lib/ar71xx.sh b/target/linux/ar71xx/base-files/lib/ar71xx.sh index a9e063b..cb8b642 100755 --- a/target/linux/ar71xx/base-files/lib/ar71xx.sh +++ b/target/linux/ar71xx/base-files/lib/ar71xx.sh @@ -219,6 +219,9 @@ ar71xx_board_detect() { *"DIR-615 rev. E4") name="dir-615-e4" ;; + *"DIR-632 rev. A1") + name="dir-632-a1" + ;; *"DIR-825 rev. B1") name="dir-825-b1" ;; diff --git a/target/linux/ar71xx/base-files/lib/upgrade/platform.sh b/target/linux/ar71xx/base-files/lib/upgrade/platform.sh index 8f2f610..7e7d05d 100755 --- a/target/linux/ar71xx/base-files/lib/upgrade/platform.sh +++ b/target/linux/ar71xx/base-files/lib/upgrade/platform.sh @@ -108,6 +108,7 @@ platform_check_image() { dir-600-a1 | \ dir-615-c1 | \ dir-615-e4 | \ + dir-632-a1 | \ dir-825-c1 | \ dir-835-a1 | \ ew-dorin | \ diff --git a/target/linux/ar71xx/config-3.8 b/target/linux/ar71xx/config-3.8 index 847b9a5..883fe88 100644 --- a/target/linux/ar71xx/config-3.8 +++ b/target/linux/ar71xx/config-3.8 @@ -2,6 +2,25 @@ CONFIG_AG71XX=y CONFIG_AG71XX_AR8216_SUPPORT=y # CONFIG_AG71XX_DEBUG is not set # CONFIG_AG71XX_DEBUG_FS is not set +CONFIG_AG7240=y +# CONFIG_AG7240_GE0_GMII is not set +CONFIG_AG7240_GE0_IS_CONNECTED=y +CONFIG_AG7240_GE0_MII=y +# CONFIG_AG7240_GE0_RGMII is not set +CONFIG_AG7240_GE1_IS_CONNECTED=y +CONFIG_AG7240_GE1_MII=y +CONFIG_AG7240_LEN_PER_TX_DS=1536 +CONFIG_AG7240_MAC_LOCATION=0x1fff0000 +CONFIG_AG7240_NUMBER_RX_PKTS=252 +CONFIG_AG7240_NUMBER_TX_PKTS=80 +CONFIG_AG7240_USE_TRC=y +CONFIG_AR7240_S26_PHY=y +# CONFIG_AR7240_S26_VLAN_IGMP is not set +# CONFIG_AR7242_RGMII_PHY is not set +CONFIG_AR7242_RTL8309G_PHY=y +# CONFIG_AR7242_S16_PHY is not set +# CONFIG_AR7242_VIR_PHY is not set +# CONFIG_AR8021_PHY is not set CONFIG_AR8216_PHY=y CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y CONFIG_ARCH_DISCARD_MEMBLOCK=y @@ -38,6 +57,7 @@ CONFIG_ATH79_MACH_CAP4200AG=y CONFIG_ATH79_MACH_DB120=y CONFIG_ATH79_MACH_DIR_600_A1=y CONFIG_ATH79_MACH_DIR_615_C1=y +CONFIG_ATH79_MACH_DIR_632_A1=y CONFIG_ATH79_MACH_DIR_825_B1=y CONFIG_ATH79_MACH_DIR_825_C1=y CONFIG_ATH79_MACH_EAP7660D=y @@ -97,6 +117,8 @@ CONFIG_ATH79_NVRAM=y CONFIG_ATH79_PCI_ATH9K_FIXUP=y CONFIG_ATH79_ROUTERBOOT=y # CONFIG_ATH79_WDT is not set +# CONFIG_ATHEROS_HEADER_EN is not set +# CONFIG_ATHRS_QOS is not set CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_CEVT_R4K=y CONFIG_CMDLINE="rootfstype=squashfs,yaffs,jffs2 noinitrd" @@ -189,12 +211,9 @@ CONFIG_MIPS_MT_DISABLED=y CONFIG_MODULES_USE_ELF_REL=y CONFIG_MTD_CFI_ADV_OPTIONS=y CONFIG_MTD_CFI_GEOMETRY=y -# CONFIG_MTD_CFI_I2 is not set # CONFIG_MTD_CFI_INTELEXT is not set CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_M25P80=y -# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set CONFIG_MTD_MYLOADER_PARTS=y CONFIG_MTD_PHYSMAP=y CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-2 @@ -224,6 +243,7 @@ CONFIG_RTL8366RB_PHY=y CONFIG_RTL8366S_PHY=y CONFIG_RTL8366_SMI=y CONFIG_RTL8367_PHY=y +# CONFIG_S26_SWITCH_ONLY_MODE is not set # CONFIG_SCSI_DMA is not set CONFIG_SERIAL_8250_NR_UARTS=1 CONFIG_SERIAL_8250_RUNTIME_UARTS=1 diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-dir-632-a1.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-dir-632-a1.c new file mode 100644 index 0000000..8274c84 --- /dev/null +++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-dir-632-a1.c @@ -0,0 +1,403 @@ +/* + * D-Link DIR-632 rev. A1 board support + * + * Copyright (C) 2013 Andrew McDonnell + * Copyright (C) 2010-2012 Gabor Juhos + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include "common.h" +#include "dev-common.h" +#include "dev-eth.h" +#include "dev-m25p80.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-ap9x-pci.h" +#include "dev-usb.h" +#include "machtypes.h" +#include "nvram.h" + +/* Set the following to zero to have a look at actual RAM when trying to find MAC addresses, etc. */ +#define DEBUG_CHECK_MEMORY_CONTENTS 1 + + /* Various in-memory items of interest: + * + * Memory Flash Description + * 1f040000 040000 u-boot environment variables + * 1ffb0004 7b0004 ASCII MAC address matching the case sticker. + * I suspect this is 'fixed' when you first execute + * setenv mac= in u-boot (which I inadvertently did) + * (mtd "MAC" partition) + * 1ffe0000 7e0000 NVRAM (area prefix: "HSLF") + * (partway through factory mtd "LP" partition) + * 1fff0000 7f0000 Hardware MAC address for eth0, according to source + * code of DD-WRT and zRouter, except this MAC address + * is _different_ to the stickered MAC address. + * (mtd "ART" partition) + * 1fff0006 7f0006 Hardware MAC address for eth1, according to source + * code of DD-WRT and zRouter, except this MAC address + * is set to ff:ff:ff:ff:ff:ff in my router. + * 1ff10000 7f1000 PCI EEPROM - "calibration data" + * 1ff1010c 7f110c PCI EEPROM - WLAN mac address reported in DD-WRT + * In my board this is set to 00:00:22:22:22:22 + * Which happens to be the same as the NVRAM value + * ath_hwaddr + */ + +/* Network architecture: + * + * eth0 --> 8309 switch - LAN + * eth1 --> S26 switch - WAN + * wlan0 --> PCI ath9k driver + */ + +#define ADDR_UBOOT 0x1f040000 +#define ADDR_AMAC 0x1ffb0000 +#define ADDR_NVRAM 0x1ffe0000 +#define ADDR_HMAC 0x1fff0000 +#define ADDR_PCIBASE 0x1fff0000 +#define ADDR_EEPROM 0x1fff1000 + +#define EEPROM_WLAN_MAC_OFFSET 0x10c + +#define NVRAM_SIZE 0x10000 + +#define DIR632_MAC0_OFFSET 0 +#define DIR632_MAC1_OFFSET 6 + +#define DIR632_AMAC_OFFSET 4 + +/* + + LED Definitions, derived from + + http://zrouter.org/projects/zrouter/repository/entry/boards/D-Link/DIR-632/GPIO + http://zrouter.org/projects/zrouter/repository/entry/boards/D-Link/DIR-632/board.hints + + and from practical experimentation using the DD-WRT generic_leds. + + Known GPIO Pins: + 0 LED WPS + 1 LED STATUS Yellow + 6 LED STATUS Green - not currently working as expected + 7 LED Internet - not currently working as expected + 8 BUTTON Reset + 9 UART + 10 UART + 11 LED USB + 12 BUTTON WPS + 17 LED Internet Bi + + 32 Wifi + + Active low have a flags of 0x102 otherwise 0x2 in zRouter board.hints + +*/ + +#define DIR_632_A1_GPIO_LED_WPS 0 // Tested +#define DIR_632_A1_GPIO_LED_POWER_AMBER 1 // Tested +#define DIR_632_A1_GPIO_LED_POWER_GREEN 6 +#define DIR_632_A1_GPIO_LED_WAN_AMBER 7 +#define DIR_632_A1_GPIO_LED_USB 11 // Tested +#define DIR_632_A1_GPIO_LED_WAN_GREEN 17 // Tested +#define DIR_632_A1_GPIO_LED_WIRELESS 32 // Tested + +#define DIR_632_A1_WLAN_GPIO_LED 0 // Tested + + +#define DIR_632_A1_GPIO_BTN_RESET 8 +#define DIR_632_A1_GPIO_BTN_WPS 12 + +#define DIR_632_A1_KEYS_POLL_INTERVAL 20 /* msecs */ +#define DIR_632_A1_KEYS_DEBOUNCE_INTERVAL (3 * DIR_632_A1_KEYS_POLL_INTERVAL) + +static struct gpio_led dir_632_a1_leds_gpio[] __initdata = { + { + .name = "d-link:blue:wps", + .gpio = DIR_632_A1_GPIO_LED_WPS, + .active_low = 1, + }, { + .name = "d-link:amber:power", + .gpio = DIR_632_A1_GPIO_LED_POWER_AMBER, + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + }, { + .name = "d-link:green:power", + .gpio = DIR_632_A1_GPIO_LED_POWER_GREEN, + //.active_low = 1, + }, { + .name = "d-link:amber:wan", + .gpio = DIR_632_A1_GPIO_LED_WAN_AMBER, + //.active_low = 1, + }, { + .name = "d-link:green:wan", + .gpio = DIR_632_A1_GPIO_LED_WAN_GREEN, + .active_low = 1, + }, { + .name = "d-link:green:usb", + .gpio = DIR_632_A1_GPIO_LED_USB, + .active_low = 1, + }, +#ifdef FIXME_BROKEN_BEFORE_PORTING_AG7240 + { + .name = "d-link:green:wireless", + .gpio = DIR_632_A1_GPIO_LED_WIRELESS, + .active_low = 1, + }, +#endif +}; + +static struct gpio_keys_button dir_632_a1_gpio_keys[] __initdata = { + { + .desc = "reset", // tested OK, using cat /sys/kernel/debug/gpio/ + .type = EV_KEY, + .code = KEY_RESTART, + .debounce_interval = DIR_632_A1_KEYS_DEBOUNCE_INTERVAL, + .gpio = DIR_632_A1_GPIO_BTN_RESET, /* Note, zrouter flags is 0x4a1, if this doesnt work ... */ + .active_low = 1, + }, { + .desc = "wps", // tested OK, using cat /sys/kernel/debug/gpio/ + .type = EV_KEY, + .code = KEY_WPS_BUTTON, + .debounce_interval = DIR_632_A1_KEYS_DEBOUNCE_INTERVAL, + .gpio = DIR_632_A1_GPIO_BTN_WPS, /* Note, zrouter flags is 0x4a1, if this doesnt work ... */ + .active_low = 1, + } +}; + +static struct ar8327_pad_cfg dir632_ar8327_pad0_cfg = { + .mode = AR8327_PAD_MAC_RGMII, + .txclk_delay_en = true, + .rxclk_delay_en = true, + .txclk_delay_sel = AR8327_CLK_DELAY_SEL1, + .rxclk_delay_sel = AR8327_CLK_DELAY_SEL2, + }; + +static struct ar8327_platform_data dir632_ar8327_data = { + .pad0_cfg = &dir632_ar8327_pad0_cfg, + .port0_cfg = { + .force_link = 1, + + .speed = AR8327_PORT_SPEED_1000, + .duplex = 1, + .txpause = 1, + .rxpause = 1, + }, + }; + +static struct mdio_board_info dir632_mdio0_info[] = { + { + .bus_id = "ag71xx-mdio.0", + .phy_addr = 0, + .platform_data = &dir632_ar8327_data, + }, + }; + +/* At 0x1000 in the upper 64K of memory of my DIR632 is PCI EEPROM information. + * However DD-WRT does a scan at the start of each 0x1000 of the entire 64k, + * so we do just in case. */ +static void *dir_632_a1_get_eeprom_caldata(int slot) +{ + u8 *base; + for (base=(u8 *)KSEG1ADDR(0x1f000000);base<(u8 *)KSEG1ADDR(0x1ffff000);base+=0x1000) { + u32 *cal = (u32 *)base; + if (*cal==0xa55a0000 || *cal==0x5aa50000) { + /* protection bit is always zero on inflash devices, so we can use for match it */ + if (slot) { + base+=0x4000; + } + pr_info("dir632: found PCI EEPROM for slot %d at %p\n",slot,base); + return base; + } + } + return NULL; +} + +static int is_mac_all_ones(u8* mac) { + return !memcmp(mac,"\xff\xff\xff\xff\xff\xff",6); +} + +static int is_mac_all_zeros(u8* mac) { + return !memcmp(mac,"\x0\x0\x0\x0\x0\x0",6); +} + +static int is_mac_ok(u8* mac) { + return !is_mac_all_zeros(mac) && !is_mac_all_ones(mac) && memcmp(mac,"\x08\x0\x0\x0\x0\x0",6); +} + +static struct ag7240_platform_data ath79_ag7240_eth_data; + +static struct platform_device ath79_ag7240_eth_device = { + .name = "ag7240", + .id = 0, + .dev = { + .platform_data = &ath79_ag7240_eth_data, + }, +}; + +static struct flash_platform_data dir_632_a1_flash = { + .type = "mx25l6405d", +}; + +/* + * Platform initialisation for the DIR-632-A1 mainboard. + * + * Here we set platform-specific data for each device that may be loaded later. + * + * This router has: + * (a) AG7240 ethernet + * eth0: connected to RTL8309G, physically to 8-port switch + * eth1: connected vi internal S26 switch to WAN port + * (b) mdio port - bus used to control the switches (I think) + * (c) ath9k wireless - on PCI bus + * (d) USB2 + * (e) serial port + * (f) GPIO - buttons - WPS button and Reset button, of course with OpenWRT + * you can change these to whatever you want + * (g) GPIO - leds - 8 switch LEDS controlled by ag7240 driver + * - power/status - can be green or orange in factory firmware + * - WAN - can be green or orange in factory firmware + * - WLAN - green - controlled by ath9k driver + * - WPS - blue + * - USB - green + * (h) Watchdog timer + * The board also has two extra placeholders for other LEDS so it hopefully + * will be possible to buzz these out and use them as spare GPIO in the future + * (i) Flash - SPI controlled flash, 8MB + * + * MAC addresses: + * + * The factory firmware reads an ASCII MAC address from the MAC partition + * and seems to assign it to all ports. Note that DD-WRT reads the wireless MAC + * 00:00:22:22:22:22 from the ART partition in the Atheros PCI 'eeprom' data + * (see http://wiki.openwrt.org/doc/howto/restore_art_partition ) + * so it appears that D-link just ignore this and replace it with the stickered + * MAC as well. + * + * However a _different_ MAC is binary encoded in the start of the ART partition + * and this us assigned to eth0 and eth1 by uboot! + * + * So I am not really sure what the correct algorithm is for selecting all this! + * + * Looking at other similar boards, whoever ported them to OpenWRT chose to + * do different things... + */ +static void __init dir_632_a1_setup(void) +{ + + const char *nvram = (char *)KSEG1ADDR(ADDR_NVRAM); + u8 *ee = (u8 *)KSEG1ADDR(ADDR_PCIBASE); /* PCI Cal data - will be set by getCalData() */ + u8 mac_buffer[6]; + u8 *wmac = NULL; /* This may end up with a MAC address to pass through to ath9k for it to use instead of PCI calibration data */ + u8 *art = (u8 *)KSEG1ADDR(ADDR_HMAC); /* Early-hardware MAC addresses */ + u8 *hmac = NULL; + +#if DEBUG_CHECK_MEMORY_CONTENTS + print_hex_dump(KERN_DEBUG, "uboot args: ", DUMP_PREFIX_ADDRESS, 16, 1, (u8*)KSEG1ADDR(ADDR_UBOOT), 64, true); + print_hex_dump(KERN_DEBUG, "nvram: ", DUMP_PREFIX_ADDRESS, 16, 1, (u8*)KSEG1ADDR(ADDR_NVRAM), 64, true); + print_hex_dump(KERN_DEBUG, "art: ", DUMP_PREFIX_ADDRESS, 16, 1, (u8*)KSEG1ADDR(ADDR_HMAC), 64, true); + print_hex_dump(KERN_DEBUG, "pci eeprom: ", DUMP_PREFIX_ADDRESS, 16, 1, (u8*)KSEG1ADDR(ADDR_EEPROM), 64, true); + print_hex_dump(KERN_DEBUG, "boxmac : ", DUMP_PREFIX_ADDRESS, 16, 1, (u8*)KSEG1ADDR(ADDR_AMAC), 64, true); +#endif + + /* Depending on the factory firmware prior usage, there may be a MAC address for the WLAN */ + if (ath79_nvram_parse_mac_addr(nvram, NVRAM_SIZE,"ath0_hwaddr=", mac_buffer) == 0) { + wmac = mac_buffer; + pr_debug("dir_632_a1_setup(): NVRAM ath0_hwaddr=%pM", wmac); + } + + if (mac_pton( (char*) ((u8*)KSEG1ADDR(ADDR_AMAC)+DIR632_AMAC_OFFSET), mac_buffer)) { + if (!(is_mac_all_ones(mac_buffer) || is_mac_all_zeros(mac_buffer))) { + // use this instead for WLAN and for Eth? what is the desired behaviour? + // for now apply to WLAN + wmac = mac_buffer; + pr_debug("dir_632_a1_setup(): ASCII (uboot-set) mac=%pM", wmac); + } + } + + /* If we use the ar71xx_ag7240 driver that came with Attitude Adjustment, + * the network just doesnt work, cant find PHY, etc. + * Instead, we ported the ag7240 driver from DD-WRT, which does work. + * But it turns out we still need the following settings. + */ + ath79_register_mdio(0, 0); + mdiobus_register_board_info(dir632_mdio0_info, ARRAY_SIZE(dir632_mdio0_info)); + + /* GMAC0 is connected to the RTL8309G switch (8-port switch) */ + ath79_init_mac(ath79_ag7240_eth_data.mac_addr1, art + DIR632_MAC0_OFFSET, 0); + pr_info("DIR-632-A1 using eth0 mac=%pM\n", ath79_ag7240_eth_data.mac_addr1); + + /* GMAC1 is connected to the internal S26 switch */ + /* If a mac address is in the ART, use it, otherwise, offset by one */ + hmac = art + DIR632_MAC1_OFFSET; + if (is_mac_all_ones(hmac)) { + ath79_init_mac(ath79_ag7240_eth_data.mac_addr2, art + DIR632_MAC0_OFFSET, 1); + } else { + ath79_init_mac(ath79_ag7240_eth_data.mac_addr2, art + DIR632_MAC1_OFFSET, 0); + } + pr_info("DIR-632-A1 using eth1 mac=%pM\n", ath79_ag7240_eth_data.mac_addr2); + + platform_device_register(&ath79_ag7240_eth_device); + + ath79_register_m25p80(&dir_632_a1_flash); + + ath79_gpio_function_disable( + AR71XX_GPIO_FUNC_STEREO_EN | + AR724X_GPIO_FUNC_ETH_SWITCH_LED0_EN | + AR724X_GPIO_FUNC_ETH_SWITCH_LED1_EN | + AR724X_GPIO_FUNC_ETH_SWITCH_LED2_EN | + AR724X_GPIO_FUNC_ETH_SWITCH_LED3_EN | + AR724X_GPIO_FUNC_ETH_SWITCH_LED4_EN); + + ath79_register_leds_gpio(-1, ARRAY_SIZE(dir_632_a1_leds_gpio), + dir_632_a1_leds_gpio); + + ath79_register_gpio_keys_polled(-1, DIR_632_A1_KEYS_POLL_INTERVAL, + ARRAY_SIZE(dir_632_a1_gpio_keys), + dir_632_a1_gpio_keys); + + /* We have usb */ + ath79_register_usb(); + + /* Pass on data for the WLAN PCI device: */ + ee = dir_632_a1_get_eeprom_caldata(0); + if (ee && !wmac) { + /* If no value in NVRAM for wireless, check if there is a valid MAC address + * in the PCI EEPROM. If so, and it is valid we do nothing. + * If not valid, then look in the ART instead for an address to pass along. */ + if (!is_mac_ok(ee+EEPROM_WLAN_MAC_OFFSET)) { + pr_warn("Found empty wireless MAC address in calibration dataset, leave the responsibility to the driver to use the correct one.\n"); + wmac = ee - 0x1000; /* This implies the possibility in this architecture that there could be a second PCI slot and the MAC is 0x1000 below it... */ + } + /* Check the value in the ART is valid before allowing it to be used. For some reason we don't check 08:00:00:00:00:00 */ + if (wmac && (is_mac_all_ones(wmac) || is_mac_all_zeros(wmac))) { + pr_warn("Found empty wireless MAC address in ART, leave the responsibility to the driver to use the correct one\n"); + wmac = NULL; + } + } + if (wmac) { + pr_info("DIR-632-A1 user set wmac=%pM", wmac); + } + + /* inform the ath9k which of its GPIO the WLAN LED is on */ + ap9x_pci_setup_wmac_led_pin(0, DIR_632_A1_WLAN_GPIO_LED); // if this doesnt work, go back and traceit through again - reg values match, so only base? c/f set_wl0_led DDWRT + + // ap9x_pci_setup_wmac_gpio + + /* inform the ath9k what wmac to use and where to find its radio cal data */ + /* also initialises PCI bus */ + ap91_pci_init(ee, wmac); + + /* uart, gpio base and wdt are init from ath79/setup.c */ +} + +MIPS_MACHINE(ATH79_MACH_DIR_632_A1, "DIR-632-A1", "D-Link DIR-632 rev. A1", + dir_632_a1_setup); + diff --git a/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h b/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h index 656a6ef..3002601 100644 --- a/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h +++ b/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h @@ -22,6 +22,12 @@ struct ag71xx_switch_platform_data { u8 phy_poll_mask; }; +struct ag7240_platform_data { + u8 mac_addr1[ETH_ALEN]; + u8 mac_addr2[ETH_ALEN]; +}; + + struct ag71xx_platform_data { phy_interface_t phy_if_mode; u32 phy_mask; diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/Kconfig b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/Kconfig new file mode 100644 index 0000000..c4cfcd3 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/Kconfig @@ -0,0 +1,124 @@ +config AG7240 + tristate "Atheros AR7240 GigE" + depends on ATH79_MACH_DIR_632_A1 + help + This enables the building of Atheros AR7240 gigE driver module as + used by the D-link DIR-632-A1router. + +config AR7240_S26_VLAN_IGMP + bool "Support vlan and igmp functions in AR7240-S26 switch" + depends on AG7240 + +config ATHEROS_HEADER_EN + bool "Add two bytes atheros header before the ethernet packet." + depends on AG7240 + +config AG7240_GE0_IS_CONNECTED + bool "port 0 is connected to a PHY" + depends on AG7240 + +choice + prompt "port 0 interface" + depends on AG7240 + +config AG7240_GE0_MII + bool "MII mode" + +config AG7240_GE0_GMII + bool "GMII mode" + +config AG7240_GE0_RGMII + bool "RGMII mode" + +endchoice + +choice + prompt "PHY or switch used" + depends on AG7240_GE0_IS_CONNECTED + +config AR7242_RGMII_PHY + bool "AR7242 RGMII Phy" + +config AR7242_S16_PHY + bool "AR7242 S16 Switch" + +config WZRG450 + bool "WZRG450 Vlan Mapping" + depends on AR7242_S16_PHY + +config AR7242_VIR_PHY + bool "AR7242 Vitual Switch" + +config AR7242_RTL8309G_PHY + bool "Realtek RTL8309G Switch" + +endchoice + +config ATHRS_QOS + bool "Enable MAC 0 QOS " + depends on AG7240 + +config AG7240_GE1_IS_CONNECTED + bool "port 1 is connected to a PHY" + depends on AG7240 + +choice + prompt "port 1 interface (depends on port0)" + depends on AG7240_GE1_IS_CONNECTED + +config AG7240_GE1_MII + bool "MII mode" + +endchoice + +choice + prompt "PHY or switch used" + depends on AG7240 + +config AR7240_S26_PHY + bool "Atheros S26 Phy" + depends on AG7240 + +config S26_SWITCH_ONLY_MODE + bool "Enable switch only mode" + depends on AR7240_S26_PHY + +config AR8021_PHY + bool "Atheros AR8021 Phy" + +endchoice + +config AG7240_LEN_PER_TX_DS + int "Len per tx descriptor" + default "1536" + depends on (REALTEK_PHY || ADM6996FC_PHY || ICPLUS_PHY) && AG7240 + +config AG7240_LEN_PER_TX_DS + int "Len per tx descriptor" + default "512" + depends on !(REALTEK_PHY || ADM6996FC_PHY || ICPLUS_PHY) && AG7240 + +config AG7240_NUMBER_TX_PKTS + int "Number tx pkts" + default "100" + depends on AG7240 + +config AG7240_NUMBER_RX_PKTS + int "Number rx pkts" + default "252" + depends on AG7240 + +config AG7240_MAC_LOCATION + hex "Memory location of MAC addresses (in flash)" + default "0xbfff0000" + help + This is the physical address in memory that contains + the MAC addresses for the board Ethernet interfaces. They + are stored sequentially in 6 byte groups, for the total number + of interfaces supported on the board. + depends on AG7240 + +config AG7240_USE_TRC + bool "Print realtime trace on asserts, this will negatively affect performance" + default 0 + depends on AG7240 diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/Makefile b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/Makefile new file mode 100644 index 0000000..20e423b --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Ar7240 gigabit MAC and Phys +# +obj-$(CONFIG_AG7240) += ag7240_mod.o + +obj-phy-$(CONFIG_AR7240_S26_PHY) += ar7240_s26_phy.o +obj-phy-$(CONFIG_AR7240_S26_VLAN_IGMP) += python_vlan_igmp.o +obj-phy-$(CONFIG_AR7242_RGMII_PHY) += athrf1_phy.o +obj-phy-$(CONFIG_AR7242_S16_PHY) += athrs16_phy.o +obj-phy-$(CONFIG_AR7242_RTL8309G_PHY) += rtl8309g_phy.o +obj-phy-$(CONFIG_AR7242_VIR_PHY) += athr_vir_phy.o + +ag7240_mod-objs := $(obj-phy-y) ag7240.o + diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240.c new file mode 100644 index 0000000..37469b4 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240.c @@ -0,0 +1,2584 @@ +/* + + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ag7240.h" +#include "ag7240_phy.h" +#include "ag7240_trc.h" + +/* Set the following to zero to have a look at actual RAM when trying to find MAC addresses, etc. */ +#define DEBUG_CHECK_MEMORY_CONTENTS 1 +/* Set to one for now to stop this crashing on module load, unitl I get back to working out why it gets probed twice and how to ignore it */ +#define DEBUG_ENABLE_PLATFORM_PROBE_CRASH_PREVENTION 1 + +ag7240_mac_t *ag7240_macs[2]; +static void ag7240_hw_setup(ag7240_mac_t *mac); +static void ag7240_hw_stop(ag7240_mac_t *mac); +static void ag7240_oom_timer(unsigned long data); +static int check_for_dma_status(ag7240_mac_t *mac,int ac); +static int ag7240_tx_alloc(ag7240_mac_t *mac); +static int ag7240_rx_alloc(ag7240_mac_t *mac); +static void ag7240_rx_free(ag7240_mac_t *mac); +static void ag7240_tx_free(ag7240_mac_t *mac); +static int ag7240_ring_alloc(ag7240_ring_t *r, int count); +static int ag7240_rx_replenish(ag7240_mac_t *mac); +static void ag7240_get_default_macaddr(ag7240_mac_t *mac, u8 *mac_addr); +static int ag7240_tx_reap(ag7240_mac_t *mac,int ac); +static void ag7240_ring_release(ag7240_mac_t *mac, ag7240_ring_t *r); +static void ag7240_ring_free(ag7240_ring_t *r); +static void ag7240_tx_timeout_task(struct work_struct *work); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) +static int ag7240_poll(struct napi_struct *napi, int budget); +#else +static int ag7240_poll(struct net_device *dev, int *budget); +#endif +#define ETH_VLAN_HLEN 18 +#ifdef CONFIG_AR7240_S26_VLAN_IGMP +int athr_ioctl(struct net_device *dev,uint32_t *args, int cmd); +#else +int athr_ioctl(uint32_t *args, int cmd); +#endif +void ar7240_s26_intr(void); +void ag7240_dma_reset(ag7240_mac_t *mac); + +int ag7240_recv_packets(struct net_device *dev, ag7240_mac_t *mac, + int max_work, int *work_done); +static irqreturn_t ag7240_intr(int cpl, void *dev_id); +static irqreturn_t ag7240_link_intr(int cpl, void *dev_id); +static struct sk_buff * ag7240_buffer_alloc(void); +#ifdef ETH_SOFT_LED +ATH_LED_CONTROL PLedCtrl; +atomic_t Ledstatus; +#endif + +extern uint32_t ar7240_ahb_freq; +extern void athrs26_reg_dev(ag7240_mac_t **mac); +extern void athrs26_enable_linkIntrs(int ethUnit); +extern void athrs26_disable_linkIntrs(int ethUnit); +extern int athrs26_phy_is_link_alive(int phyUnit); +extern void athrs26_phy_stab_wr(int phy_id, int phy_up, int phy_speed); +extern uint32_t athrs26_reg_read(unsigned int s26_addr); +extern void athrs26_reg_write(unsigned int s26_addr, unsigned int s26_write_data); +extern void s26_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data); + +char *mii_str[2][4] = { + {"GMii", "Mii", "RGMii", "RMii"}, + {"GMii","Mii","RGMii", "RMii"} +}; +int rg_phy_speed = -1 , rg_phy_duplex = -1; +char *spd_str[] = {"10Mbps", "100Mbps", "1000Mbps"}; +char *dup_str[] = {"half duplex", "full duplex"}; + +#define MODULE_NAME "AG7240" +MODULE_LICENSE("Dual BSD/GPL"); + +static uint32_t prev_dma_chk_ts; +/* if 0 compute in init */ +int tx_len_per_ds = 0; +int phy_in_reset = 0; +int enet_ac = 0; +module_param(tx_len_per_ds, int, 0); +MODULE_PARM_DESC(tx_len_per_ds, "Size of DMA chunk"); + +/* if 0 compute in init */ +int tx_max_desc_per_ds_pkt=0; + +/* if 0 compute in init */ +int fifo_3 = 0x1f00140; +module_param(fifo_3, int, 0); +MODULE_PARM_DESC(fifo_3, "fifo cfg 3 settings"); + +int mii0_if = AG7240_MII0_INTERFACE; +module_param(mii0_if, int, 0); +MODULE_PARM_DESC(mii0_if, "mii0 connect"); + +int mii1_if = AG7240_MII1_INTERFACE; +module_param(mii1_if, int, 0); +MODULE_PARM_DESC(mii1_if, "mii1 connect"); + +#define SW_PLL 0x1f000000ul +int gige_pll = 0x1a000000; +module_param(gige_pll, int, 0); +MODULE_PARM_DESC(gige_pll, "Pll for (R)GMII if"); + +int fifo_5 = 0xbefff; +module_param(fifo_5, int, 0); +MODULE_PARM_DESC(fifo_5, "fifo cfg 5 settings"); + +int xmii_val = 0x16000000; + +int ignore_packet_inspection = 0; + +void set_packet_inspection_flag(int flag) +{ + if(flag==0) + ignore_packet_inspection = 0; + else + ignore_packet_inspection = 1; +} + +#define addr_to_words(addr, w1, w2) { \ + w1 = (addr[5] << 24) | (addr[4] << 16) | (addr[3] << 8) | addr[2]; \ + w2 = (addr[1] << 24) | (addr[0] << 16) | 0; \ +} + +/* + * Defines specific to this implemention + */ + +#ifndef CONFIG_AG7240_LEN_PER_TX_DS +#error Please run menuconfig and define CONFIG_AG7240_LEN_PER_TX_DS +#endif + +#ifndef CONFIG_AG7240_NUMBER_TX_PKTS +#error Please run menuconfig and define CONFIG_AG7240_NUMBER_TX_PKTS +#endif + +#ifndef CONFIG_AG7240_NUMBER_RX_PKTS +#error Please run menuconfig and define CONFIG_AG7240_NUMBER_RX_PKTS +#endif +#define AG7240_TX_FIFO_LEN 2048 +#define AG7240_TX_MIN_DS_LEN 128 +#define AG7240_TX_MAX_DS_LEN AG7240_TX_FIFO_LEN + +#define AG7240_TX_MTU_LEN AG71XX_TX_MTU_LEN + +#define AG7240_TX_DESC_CNT CONFIG_AG7240_NUMBER_TX_PKTS*tx_max_desc_per_ds_pkt +#define AG7240_TX_REAP_THRESH AG7240_TX_DESC_CNT/2 +#define AG7240_TX_QSTART_THRESH 4*tx_max_desc_per_ds_pkt + +#define AG7240_RX_DESC_CNT CONFIG_AG7240_NUMBER_RX_PKTS + +#define AG7240_NAPI_WEIGHT 64 +#define AG7240_PHY_POLL_SECONDS 2 +#define AG7240_LED_POLL_SECONDS (HZ/10) + +#define ENET_UNIT_LAN 1 +#define ENET_UNIT_WAN 0 + + +static inline int ag7240_tx_reap_thresh(ag7240_mac_t *mac,int ac) +{ + ag7240_ring_t *r = &mac->mac_txring[ac]; + + return (ag7240_ndesc_unused(mac, r) < AG7240_TX_REAP_THRESH); +} + +static inline int ag7240_tx_ring_full(ag7240_mac_t *mac,int ac) +{ + ag7240_ring_t *r = &mac->mac_txring[ac]; + + ag7240_trc_new(ag7240_ndesc_unused(mac, r),"tx ring full"); + return (ag7240_ndesc_unused(mac, r) < tx_max_desc_per_ds_pkt + 2); +} +static int +ag7240_open(struct net_device *dev) +{ + unsigned int w1 = 0, w2 = 0; + ag7240_mac_t *mac = (ag7240_mac_t *)netdev_priv(dev); + int st,flags; + uint32_t mask; + +#ifdef SWITCH_AHB_FREQ + u32 tmp_pll, pll; +#endif + + assert(mac); + if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) + mac->mac_noacs = 4; + else + mac->mac_noacs = 1; + + st = request_irq(mac->mac_irq, ag7240_intr, 0, dev->name, dev); + if (st < 0) + { + printk(MODULE_NAME ": request irq %d failed %d\n", mac->mac_irq, st); + return 1; + } + if (ag7240_tx_alloc(mac)) goto tx_failed; + if (ag7240_rx_alloc(mac)) goto rx_failed; + + udelay(20); + mask = ag7240_reset_mask(mac->mac_unit); + + /* + * put into reset, hold, pull out. + */ + spin_lock_irqsave(&mac->mac_lock, flags); + ar7240_reg_rmw_set(AR7240_RESET, mask); + udelay(10); + ar7240_reg_rmw_clear(AR7240_RESET, mask); + udelay(10); + spin_unlock_irqrestore(&mac->mac_lock, flags); + + ag7240_hw_setup(mac); +#ifdef CONFIG_AR7242_VIR_PHY + #ifndef SWITCH_AHB_FREQ + u32 tmp_pll ; + #endif + tmp_pll = 0x62000000 ; + ar7240_reg_wr_nf(AR7242_ETH_XMII_CONFIG, tmp_pll); + udelay(100*1000); +#endif + + mac->mac_speed = -1; + printk(KERN_INFO "reg init for %d\n",mac->mac_unit); + ag7240_phy_reg_init(mac->mac_unit); + + + printk("Setting PHY...\n"); + + if (mac_has_flag(mac,ETH_SOFT_LED)) { + /* Resetting PHY will disable MDIO access needed by soft LED task. + * Hence, Do not reset PHY until Soft LED task get completed. + */ + while(atomic_read(&Ledstatus) == 1); + } + phy_in_reset = 1; +#ifndef CONFIG_AR7242_VIR_PHY + ag7240_phy_setup(mac->mac_unit); +#else + athr_vir_phy_setup(mac->mac_unit); +#endif + phy_in_reset = 0; + +#ifdef SWITCH_AHB_FREQ + ar7240_reg_wr_nf(AR7240_PLL_CONFIG, pll); + udelay(100*1000); +#endif + /* + * set the mac addr + */ + addr_to_words(dev->dev_addr, w1, w2); + ag7240_reg_wr(mac, AG7240_GE_MAC_ADDR1, w1); + ag7240_reg_wr(mac, AG7240_GE_MAC_ADDR2, w2); + + dev->trans_start = jiffies; + + /* + * Keep carrier off while initialization and switch it once the link is up. + */ + netif_carrier_off(dev); + napi_enable(&mac->mac_napi); + + + mac->mac_ifup = 1; + ag7240_int_enable(mac); + + ag7240_intr_enable_rxovf(mac); + +#if defined(CONFIG_AR7242_RGMII_PHY)||defined(CONFIG_AR7242_S16_PHY)||defined(CONFIG_AR7242_VIR_PHY) || defined(CONFIG_AR7242_RTL8309G_PHY) + + if(is_ar7242() && mac->mac_unit == 0) { + + init_timer(&mac->mac_phy_timer); + mac->mac_phy_timer.data = (unsigned long)mac; + mac->mac_phy_timer.function = (void *)ag7242_check_link; + ag7242_check_link(mac); + + } +#endif + + if (is_ar7240() || is_ar7241() || is_ar933x() || (is_ar7242() && mac->mac_unit == 1)) + athrs26_enable_linkIntrs(mac->mac_unit); + + ag7240_rx_start(mac); + netif_start_queue(dev); + + return 0; + +rx_failed: + ag7240_tx_free(mac); +tx_failed: + free_irq(mac->mac_irq, dev); + return 1; +} + +static int +ag7240_stop(struct net_device *dev) +{ + ag7240_mac_t *mac = (ag7240_mac_t *)netdev_priv(dev); + int flags,i; + + spin_lock_irqsave(&mac->mac_lock, flags); + mac->mac_ifup = 0; + napi_disable(&mac->mac_napi); + netif_carrier_off(dev); + netif_stop_queue(dev); + + ag7240_hw_stop(mac); + free_irq(mac->mac_irq, dev); + + + ag7240_tx_free(mac); + ag7240_rx_free(mac); + +/* During interface up on PHY reset the link status will be updated. + * Clearing the software link status while bringing the interface down + * will avoid the race condition during PHY RESET. + */ + if (mac_has_flag(mac,ETH_SOFT_LED)) { + if (mac->mac_unit == ENET_UNIT_LAN) { + for(i = 0;i < 4; i++) + PLedCtrl.ledlink[i] = 0; + } + else { + PLedCtrl.ledlink[4] = 0; + } + } + if (is_ar7242()) + del_timer(&mac->mac_phy_timer); + spin_unlock_irqrestore(&mac->mac_lock, flags); + + /*ag7240_trc_dump();*/ + + return 0; +} + +#define FIFO_CFG0_WTM BIT(0) /* Watermark Module */ +#define FIFO_CFG0_RXS BIT(1) /* Rx System Module */ +#define FIFO_CFG0_RXF BIT(2) /* Rx Fabric Module */ +#define FIFO_CFG0_TXS BIT(3) /* Tx System Module */ +#define FIFO_CFG0_TXF BIT(4) /* Tx Fabric Module */ +#define FIFO_CFG0_ALL (FIFO_CFG0_WTM | FIFO_CFG0_RXS | FIFO_CFG0_RXF \ + | FIFO_CFG0_TXS | FIFO_CFG0_TXF) + +#define FIFO_CFG0_ENABLE_SHIFT 8 + +#define FIFO_CFG4_DE BIT(0) /* Drop Event */ +#define FIFO_CFG4_DV BIT(1) /* RX_DV Event */ +#define FIFO_CFG4_FC BIT(2) /* False Carrier */ +#define FIFO_CFG4_CE BIT(3) /* Code Error */ +#define FIFO_CFG4_CR BIT(4) /* CRC error */ +#define FIFO_CFG4_LM BIT(5) /* Length Mismatch */ +#define FIFO_CFG4_LO BIT(6) /* Length out of range */ +#define FIFO_CFG4_OK BIT(7) /* Packet is OK */ +#define FIFO_CFG4_MC BIT(8) /* Multicast Packet */ +#define FIFO_CFG4_BC BIT(9) /* Broadcast Packet */ +#define FIFO_CFG4_DR BIT(10) /* Dribble */ +#define FIFO_CFG4_LE BIT(11) /* Long Event */ +#define FIFO_CFG4_CF BIT(12) /* Control Frame */ +#define FIFO_CFG4_PF BIT(13) /* Pause Frame */ +#define FIFO_CFG4_UO BIT(14) /* Unsupported Opcode */ +#define FIFO_CFG4_VT BIT(15) /* VLAN tag detected */ +#define FIFO_CFG4_FT BIT(16) /* Frame Truncated */ +#define FIFO_CFG4_UC BIT(17) /* Unicast Packet */ + +#define FIFO_CFG5_DE BIT(0) /* Drop Event */ +#define FIFO_CFG5_DV BIT(1) /* RX_DV Event */ +#define FIFO_CFG5_FC BIT(2) /* False Carrier */ +#define FIFO_CFG5_CE BIT(3) /* Code Error */ +#define FIFO_CFG5_LM BIT(4) /* Length Mismatch */ +#define FIFO_CFG5_LO BIT(5) /* Length Out of Range */ +#define FIFO_CFG5_OK BIT(6) /* Packet is OK */ +#define FIFO_CFG5_MC BIT(7) /* Multicast Packet */ +#define FIFO_CFG5_BC BIT(8) /* Broadcast Packet */ +#define FIFO_CFG5_DR BIT(9) /* Dribble */ +#define FIFO_CFG5_CF BIT(10) /* Control Frame */ +#define FIFO_CFG5_PF BIT(11) /* Pause Frame */ +#define FIFO_CFG5_UO BIT(12) /* Unsupported Opcode */ +#define FIFO_CFG5_VT BIT(13) /* VLAN tag detected */ +#define FIFO_CFG5_LE BIT(14) /* Long Event */ +#define FIFO_CFG5_FT BIT(15) /* Frame Truncated */ +#define FIFO_CFG5_16 BIT(16) /* unknown */ +#define FIFO_CFG5_17 BIT(17) /* unknown */ +#define FIFO_CFG5_SF BIT(18) /* Short Frame */ +#define FIFO_CFG5_BM BIT(19) /* Byte Mode */ + + +#define FIFO_CFG0_INIT (FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT) + +#define FIFO_CFG4_INIT (FIFO_CFG4_DE | FIFO_CFG4_DV | FIFO_CFG4_FC | \ + FIFO_CFG4_CE | FIFO_CFG4_CR | FIFO_CFG4_LM | \ + FIFO_CFG4_LO | FIFO_CFG4_OK | FIFO_CFG4_MC | \ + FIFO_CFG4_BC | FIFO_CFG4_DR | FIFO_CFG4_LE | \ + FIFO_CFG4_CF | FIFO_CFG4_PF | FIFO_CFG4_UO | \ + FIFO_CFG4_VT) + +#define FIFO_CFG5_INIT (FIFO_CFG5_DE | FIFO_CFG5_DV | FIFO_CFG5_FC | \ + FIFO_CFG5_CE | FIFO_CFG5_LO | FIFO_CFG5_OK | \ + FIFO_CFG5_MC | FIFO_CFG5_BC | FIFO_CFG5_DR | \ + FIFO_CFG5_CF | FIFO_CFG5_PF | FIFO_CFG5_VT | \ + FIFO_CFG5_LE | FIFO_CFG5_FT | FIFO_CFG5_16 | \ + FIFO_CFG5_17 | FIFO_CFG5_SF) + + + +static void +ag7240_hw_setup(ag7240_mac_t *mac) +{ + ag7240_ring_t *tx, *rx = &mac->mac_rxring; + ag7240_desc_t *r0, *t0; + uint32_t mgmt_cfg_val,ac; + u32 check_cnt=0; + +#ifdef CONFIG_AR7242_RTL8309G_PHY + ag7240_reg_wr(mac, AG7240_MAC_CFG1, (AG7240_MAC_CFG1_RX_EN | AG7240_MAC_CFG1_TX_EN)); + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG2, (AG7240_MAC_CFG2_PAD_CRC_EN | AG7240_MAC_CFG2_LEN_CHECK | AG7240_MAC_CFG2_IF_10_100)); +#else + if(mac->mac_unit) + { + ag7240_reg_wr(mac, AG7240_MAC_CFG1, (AG7240_MAC_CFG1_RX_EN | AG7240_MAC_CFG1_TX_EN)); + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG2, (AG7240_MAC_CFG2_PAD_CRC_EN | AG7240_MAC_CFG2_IF_1000 | AG7240_MAC_CFG2_LEN_CHECK)); + } + else + { + ag7240_reg_wr(mac, AG7240_MAC_CFG1, (AG7240_MAC_CFG1_RX_EN | AG7240_MAC_CFG1_TX_EN)); +// ag7240_reg_wr(mac, AG7240_MAC_CFG1, (AG7240_MAC_CFG1_RX_EN | AG7240_MAC_CFG1_TX_EN)); + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG2, (AG7240_MAC_CFG2_PAD_CRC_EN | AG7240_MAC_CFG2_LEN_CHECK)); + } +#endif + ag7240_reg_wr(mac, AG71XX_REG_MAC_MFL, AG71XX_TX_MTU_LEN); + + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_0, 0x1f00); +// ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_0, FIFO_CFG0_INIT); + + if (mac_has_flag(mac,ATHR_S26_HEADER) || mac_has_flag(mac,ATHR_S16_HEADER)) + ag7240_reg_rmw_clear(mac, AG7240_MAC_CFG2, AG7240_MAC_CFG2_LEN_CHECK) + /* + * set the mii if type - NB reg not in the gigE space + */ + if ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR7240_REV_1_2) { + mgmt_cfg_val = 0x2; + if (mac->mac_unit == 0) { + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val); + } + } + else { + switch (ar7240_ahb_freq/1000000) { + case 150: + mgmt_cfg_val = 0x7; + break; + case 175: + mgmt_cfg_val = 0x5; + break; + case 200: + mgmt_cfg_val = 0x4; + break; + case 210: + mgmt_cfg_val = 0x9; + break; + case 220: + mgmt_cfg_val = 0x9; + break; + case 40: + case 24: + /* hornet emulation...ahb is either 24 or 40Mhz */ + mgmt_cfg_val = 0x6; + default: + mgmt_cfg_val = 0x7; + } + +#ifdef CONFIG_AR7242_RTL8309G_PHY + mgmt_cfg_val = 0x4; + if ( is_ar7242() && (mac->mac_unit == 0)) { + ar7240_reg_rmw_set(AG7240_ETH_CFG, AG7240_ETH_CFG_MII_GE0 | AG7240_ETH_CFG_MII_GE0_SLAVE); + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val); + } else if(is_ar7242() && mac->mac_unit == 1) { + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val); + } +#else + + if ((is_ar7241() || is_ar7242())) { + + /* External MII mode */ + if (mac->mac_unit == 0 && is_ar7242()) { + mgmt_cfg_val = 0x6; + ar7240_reg_rmw_set(AG7240_ETH_CFG, AG7240_ETH_CFG_RGMII_GE0); + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val); + } + /* Virian */ + mgmt_cfg_val = 0x4; + + if (mac_has_flag(mac,ETH_SWONLY_MODE)) { + ar7240_reg_rmw_set(AG7240_ETH_CFG, AG7240_ETH_CFG_SW_ONLY_MODE); + ag7240_reg_rmw_set(ag7240_macs[0], AG7240_MAC_CFG1, AG7240_MAC_CFG1_SOFT_RST);; + } + ag7240_reg_wr(ag7240_macs[1], AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); + ag7240_reg_wr(ag7240_macs[1], AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val); + printk("Virian MDC CFG Value ==> %x\n",mgmt_cfg_val); + } +#endif + else + if(is_ar933x()){ + ar7240_reg_wr(AG7240_ETH_CFG, AG7240_ETH_CFG_MII_GE0_SLAVE); + mgmt_cfg_val = 0xF; + if (mac->mac_unit == 1) { + while (check_cnt++ < 10) { + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val); + break; + } + if(check_cnt == 11) + printk("%s: MDC check failed (Hornet)\n", __func__); + } + } + else { /* Python 1.0 & 1.1 */ + if (mac->mac_unit == 0) { + check_cnt = 0; + while (check_cnt++ < 10) { + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); + ag7240_reg_wr(mac, AG7240_MAC_MII_MGMT_CFG, mgmt_cfg_val); + if(athrs26_mdc_check() == 0) + break; + } + if(check_cnt == 11) + printk("%s: MDC check failed\n", __func__); + } + } + } + + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_1, 0x10ffff); + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_2, 0x015500aa); + + /* + * Weed out junk frames (CRC errored, short collision'ed frames etc.) + */ + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_4, 0x3ffff); + + /* + * Drop CRC Errors, Pause Frames ,Length Error frames, Truncated Frames + * dribble nibble and rxdv error frames. + */ + DPRINTF("Setting Drop CRC Errors, Pause Frames and Length Error frames \n"); + +#ifdef CONFIG_AR7242_RTL8309G_PHY + if(is_ar7242() && (mac->mac_unit == 0)) { + ag7240_reg_rmw_clear(mac, AG7240_MAC_FIFO_CFG_5, (1 << 18)); + ag7240_reg_rmw_clear(mac, AG7240_MAC_FIFO_CFG_5, (1 << 19)); + } else { + ag7240_reg_rmw_clear(mac, AG7240_MAC_FIFO_CFG_5, (1 << 18)); + + } +#else + if(mac_has_flag(mac,ATHR_S26_HEADER)){ + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_5, 0xe6bc0); + } else if (mac->mac_unit == 0 && is_ar7242()){ + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_5, 0xe6be2); + } else{ + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_5, FIFO_CFG5_INIT); + } + if (mac->mac_unit == 0 && is_ar7242()){ + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_5, 0xe6be2); + } +#endif + if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) { + /* Enable Fixed priority */ +#if 1 + ag7240_reg_wr(mac,AG7240_DMA_TX_ARB_CFG,AG7240_TX_QOS_MODE_WEIGHTED + | AG7240_TX_QOS_WGT_0(0x7) + | AG7240_TX_QOS_WGT_1(0x5) + | AG7240_TX_QOS_WGT_2(0x3) + | AG7240_TX_QOS_WGT_3(0x1)); +#else + ag7240_reg_wr(mac,AG7240_DMA_TX_ARB_CFG,AG7240_TX_QOS_MODE_FIXED); +#endif + for(ac = 0;ac < mac->mac_noacs; ac++) { + tx = &mac->mac_txring[ac]; + t0 = &tx->ring_desc[0]; + switch(ac) { + case ENET_AC_VO: + ag7240_reg_wr(mac, AG7240_DMA_TX_DESC_Q0, ag7240_desc_dma_addr(tx, t0)); + break; + case ENET_AC_VI: + ag7240_reg_wr(mac, AG7240_DMA_TX_DESC_Q1, ag7240_desc_dma_addr(tx, t0)); + break; + case ENET_AC_BK: + ag7240_reg_wr(mac, AG7240_DMA_TX_DESC_Q2, ag7240_desc_dma_addr(tx, t0)); + break; + case ENET_AC_BE: + ag7240_reg_wr(mac, AG7240_DMA_TX_DESC_Q3, ag7240_desc_dma_addr(tx, t0)); + break; + } + } + } + else { + tx = &mac->mac_txring[0]; + t0 = &tx->ring_desc[0]; + ag7240_reg_wr(mac, AG7240_DMA_TX_DESC_Q0, ag7240_desc_dma_addr(tx, t0)); + } + r0 = &rx->ring_desc[0]; + ag7240_reg_wr(mac, AG7240_DMA_RX_DESC, ag7240_desc_dma_addr(rx, r0)); + + DPRINTF(MODULE_NAME ": cfg1 %#x cfg2 %#x\n", ag7240_reg_rd(mac, AG7240_MAC_CFG1), + ag7240_reg_rd(mac, AG7240_MAC_CFG2)); +} + +static void +ag7240_hw_stop(ag7240_mac_t *mac) +{ + ag7240_rx_stop(mac); + ag7240_tx_stop(mac); + ag7240_int_disable(mac); + athrs26_disable_linkIntrs(mac->mac_unit); + /* + * put everything into reset. + * Dont Reset WAN MAC as we are using eth0 MDIO to access S26 Registers. + */ + if(mac->mac_unit == 1 || is_ar7241() || is_ar7242()) + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG1, AG7240_MAC_CFG1_SOFT_RST); +} + +/* + * Several fields need to be programmed based on what the PHY negotiated + * Ideally we should quiesce everything before touching the pll, but: + * 1. If its a linkup/linkdown, we dont care about quiescing the traffic. + * 2. If its a single gigE PHY, this can only happen on lup/ldown. + * 3. If its a 100Mpbs switch, the link will always remain at 100 (or nothing) + * 4. If its a gigE switch then the speed should always be set at 1000Mpbs, + * and the switch should provide buffering for slower devices. + * + * XXX Only gigE PLL can be changed as a parameter for now. 100/10 is hardcoded. + * XXX Need defines for them - + * XXX FIFO settings based on the mode + */ +extern void ar7100_set_gpio(int gpio, int val); +extern int ar7100_get_gpio(int gpio); + + +static void +ag7240_set_mac_from_link(ag7240_mac_t *mac, ag7240_phy_speed_t speed, int fdx) +{ + if(mac->mac_unit == 1) + speed = AG7240_PHY_SPEED_1000T; + + mac->mac_speed = speed; + mac->mac_fdx = fdx; + + ag7240_set_mac_duplex(mac, fdx); + ag7240_reg_wr(mac, AG7240_MAC_FIFO_CFG_3, fifo_3); + +#ifdef CONFIG_AR7242_RTL8309G_PHY + switch (speed) + { + case AG7240_PHY_SPEED_1000T: + case AG7240_PHY_SPEED_100TX: +// printf("Speed is 100 or 100M \n"); + if(mac->mac_unit == 0) { //RTL8309G 100M only + ag7240_reg_rmw_clear(mac, AG7240_MAC_FIFO_CFG_5, (1 << 19)); + ar7240_reg_wr(AR7242_ETH_XMII_CONFIG,0x0101); + } else { + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG2, AG7240_MAC_CFG2_IF_1000); + } + break; + case AG7240_PHY_SPEED_10T: +// printf("Spped is 10M \n"); + ag7240_reg_rmw_clear(mac, AG7240_MAC_FIFO_CFG_5, (1 << 19)); + if(mac->mac_unit == 0) { //RTL8309G 10M only + ag7240_reg_rmw_clear(mac, AG7240_MAC_FIFO_CFG_5, (1 << 19)); + ar7240_reg_wr(AR7242_ETH_XMII_CONFIG,0x1616); + } + break; + default: + printk(KERN_INFO "Invalid speed detected\n"); + return 0; + + } +#else + switch (speed) + { + case AG7240_PHY_SPEED_1000T: + ag7240_set_mac_if(mac, 1); + ag7240_reg_rmw_set(mac, AG7240_MAC_FIFO_CFG_5, (1 << 19)); + if (is_ar7242() &&( mac->mac_unit == 0)) + ar7240_reg_wr(AR7242_ETH_XMII_CONFIG,xmii_val); + break; + + case AG7240_PHY_SPEED_100TX: + ag7240_set_mac_if(mac, 0); + ag7240_set_mac_speed(mac, 1); + ag7240_reg_rmw_clear(mac, AG7240_MAC_FIFO_CFG_5, (1 << 19)); + if (is_ar7242() &&( mac->mac_unit == 0)) + ar7240_reg_wr(AR7242_ETH_XMII_CONFIG,0x0101); + break; + + case AG7240_PHY_SPEED_10T: + ag7240_set_mac_if(mac, 0); + ag7240_set_mac_speed(mac, 0); + ag7240_reg_rmw_clear(mac, AG7240_MAC_FIFO_CFG_5, (1 << 19)); + if (is_ar7242() &&( mac->mac_unit == 0)) + ar7240_reg_wr(AR7242_ETH_XMII_CONFIG,0x1616); + break; + + default: + assert(0); + } +#endif + DPRINTF(MODULE_NAME ": cfg_1: %#x\n", ag7240_reg_rd(mac, AG7240_MAC_FIFO_CFG_1)); + DPRINTF(MODULE_NAME ": cfg_2: %#x\n", ag7240_reg_rd(mac, AG7240_MAC_FIFO_CFG_2)); + DPRINTF(MODULE_NAME ": cfg_3: %#x\n", ag7240_reg_rd(mac, AG7240_MAC_FIFO_CFG_3)); + DPRINTF(MODULE_NAME ": cfg_4: %#x\n", ag7240_reg_rd(mac, AG7240_MAC_FIFO_CFG_4)); + DPRINTF(MODULE_NAME ": cfg_5: %#x\n", ag7240_reg_rd(mac, AG7240_MAC_FIFO_CFG_5)); +} + +static int +led_control_func(ATH_LED_CONTROL *pledctrl) +{ + uint32_t i=0,cnt,reg_addr; + const LED_BLINK_RATES *bRateTab; + static uint32_t pkt_count; + ag7240_mac_t *mac; + mac = ag7240_macs[1]; + + if (mac_has_flag(mac,ETH_SOFT_LED)) { + atomic_inc(&Ledstatus); + + atomic_inc(&Ledstatus); + + /* + * MDIO access will fail While PHY is in RESET phase. + */ + if(phy_in_reset) + goto done; + + if (mac->mac_ifup) { + for (i = 0 ; i < 4 ; i ++) { + bRateTab = BlinkRateTable_100M; + if (pledctrl->ledlink[i]) { + reg_addr = 0x2003c + ((i + 1) << 8); + /* Rx good byte count */ + cnt = athrs26_reg_read(reg_addr); + + reg_addr = 0x20084 + ((i + 1) << 8); + /* Tx good byte count */ + cnt = cnt + athrs26_reg_read(reg_addr); + + if (cnt == 0) { + #ifdef CONFIG_DIR615E + ar7100_set_gpio(13+i,0); + #endif + if(is_ar933x()) { + s26_wr_phy(i,0x19, 0x3c0); + s26_wr_phy(i,0x18,(0x4)); + } else { + s26_wr_phy(i,0x19,(s26_rd_phy( i,0x19) | (0x3c0))); + } + continue; + } + if (pledctrl->speed[i] == AG7240_PHY_SPEED_10T) { + bRateTab = BlinkRateTable_10M; + } + while (bRateTab++) { + if (cnt <= bRateTab->rate) { + #ifdef CONFIG_DIR615E + ar7100_set_gpio(13+i,1); + #endif + if(is_ar933x()&& bRateTab->rate <= MB(5)){ + s26_wr_phy(i,0x18, 0); + } else { + s26_wr_phy(i,0x18,((bRateTab->timeOn << 12)|(bRateTab->timeOff << 8))); + } + if(is_ar933x()) { + s26_wr_phy(i,0x19, 0x140); + } else { + s26_wr_phy(i,0x19,(s26_rd_phy(i,0x19) & ~(0x280))); + } + break; + } + } + } else { + #ifdef CONFIG_DIR615E + ar7100_set_gpio(13+i,1); + #endif + s26_wr_phy(i,0x19,0x0); + } + } + if(!is_ar933x()) { + /* Flush all LAN MIB counters */ + athrs26_reg_write(0x80,((1 << 17) | (1 << 24))); + while ((athrs26_reg_read(0x80) & 0x20000) == 0x20000); + athrs26_reg_write(0x80,0); + } + } + + mac = ag7240_macs[0]; + bRateTab = BlinkRateTable_100M; + cnt = 0; + if (mac->mac_ifup) { + if (pledctrl->ledlink[4]) { + cnt += ag7240_reg_rd(mac,AG7240_RX_BYTES_CNTR) + + ag7240_reg_rd(mac,AG7240_TX_BYTES_CNTR); + + if (ag7240_get_diff(pkt_count,cnt) == 0) { +// #ifdef CONFIG_DIR615E +// ar7100_set_gpio(17,0); +// #endif + if(is_ar933x()) { + s26_wr_phy(4,0x19,((0x3c0))); + + }else + { + s26_wr_phy(4,0x19,(s26_rd_phy(4,0x19) | (0x3c0))); + } + goto done; + } + if (pledctrl->speed[4] == AG7240_PHY_SPEED_10T) { + bRateTab = BlinkRateTable_10M; + } + while (bRateTab++) { + if (ag7240_get_diff(pkt_count,cnt) <= bRateTab->rate) { +// #ifdef CONFIG_DIR615E +// ar7100_set_gpio(17,1); +// #endif + s26_wr_phy(4,0x18,((bRateTab->timeOn << 12)|(bRateTab->timeOff << 8))); + if(is_ar933x()) { + s26_wr_phy(4,0x19,0x140); + } else { + s26_wr_phy(4,0x19,(s26_rd_phy(4,0x19) & ~(0x280))); + } + + break; + } + } + pkt_count = cnt; + } else { +// #ifdef CONFIG_DIR615E +// ar7100_set_gpio(17,1); +// #endif + s26_wr_phy(4,0x19,0x0); + } + } +} +done: + if (mac_has_flag(mac,CHECK_DMA_STATUS)) { + if(ag7240_get_diff(prev_dma_chk_ts,jiffies) >= (1*HZ / 2)) { + if (ag7240_macs[0]->mac_ifup) check_for_dma_status(ag7240_macs[0],0); + if (ag7240_macs[1]->mac_ifup) check_for_dma_status(ag7240_macs[1],0); + prev_dma_chk_ts = jiffies; + } + } + mod_timer(&PLedCtrl.led_timer,(jiffies + AG7240_LED_POLL_SECONDS)); + atomic_dec(&Ledstatus); + return 0; +} + +static int check_dma_status_pause(ag7240_mac_t *mac) { + + int RxFsm,TxFsm,RxFD,RxCtrl,TxCtrl; + + /* + * If get called by tx timeout for other chips we assume + * the DMA is in pause state and update the watchdog + * timer to avoid MAC reset. + */ + + if(!is_ar7240()) + return 1; + + RxFsm = ag7240_reg_rd(mac,AG7240_DMA_RXFSM); + TxFsm = ag7240_reg_rd(mac,AG7240_DMA_TXFSM); + RxFD = ag7240_reg_rd(mac,AG7240_DMA_XFIFO_DEPTH); + RxCtrl = ag7240_reg_rd(mac,AG7240_DMA_RX_CTRL); + TxCtrl = ag7240_reg_rd(mac,AG7240_DMA_TX_CTRL); + + + if ((RxFsm & AG7240_DMA_DMA_STATE) == 0x3 + && ((RxFsm >> 4) & AG7240_DMA_AHB_STATE) == 0x6) { + return 0; + } + else if ((((TxFsm >> 4) & AG7240_DMA_AHB_STATE) <= 0x0) && + ((RxFsm & AG7240_DMA_DMA_STATE) == 0x0) && + (((RxFsm >> 4) & AG7240_DMA_AHB_STATE) == 0x0) && + (RxFD == 0x0) && (RxCtrl == 1) && (TxCtrl == 1)) { + return 0; + } + else if (((((TxFsm >> 4) & AG7240_DMA_AHB_STATE) <= 0x4) && + ((RxFsm & AG7240_DMA_DMA_STATE) == 0x0) && + (((RxFsm >> 4) & AG7240_DMA_AHB_STATE) == 0x0)) || + (((RxFD >> 16) <= 0x20) && (RxCtrl == 1)) ) { + return 1; + } + else { + DPRINTF(" FIFO DEPTH = %x",RxFD); + DPRINTF(" RX DMA CTRL = %x",RxCtrl); + DPRINTF("mac:%d RxFsm:%x TxFsm:%x\n",mac->mac_unit,RxFsm,TxFsm); + return 2; + } +} + +static int check_for_dma_status(ag7240_mac_t *mac,int ac) { + + ag7240_ring_t *r = &mac->mac_txring[ac]; + int head = r->ring_head, tail = r->ring_tail; + ag7240_desc_t *ds; + uint32_t rx_ds; + ag7240_buffer_t *bp; + int flags,mask,int_mask; + unsigned int w1 = 0, w2 = 0; + + /* If Tx hang is asserted reset the MAC and restore the descriptors + * and interrupt state. + */ + while (tail != head) + { + ds = &r->ring_desc[tail]; + bp = &r->ring_buffer[tail]; + + if(ag7240_tx_owned_by_dma(ds)) { + if ((ag7240_get_diff(bp->trans_start,jiffies)) > ((1 * HZ/10))) { + + /* + * If the DMA is in pause state reset kernel watchdog timer + */ + + if(check_dma_status_pause(mac)) { + mac->mac_dev->trans_start = jiffies; + return 0; + } + printk(MODULE_NAME ": Tx Dma status eth%d : %s\n",mac->mac_unit, + ag7240_tx_stopped(mac) ? "inactive" : "active"); + + spin_lock_irqsave(&mac->mac_lock, flags); + + int_mask = ag7240_reg_rd(mac,AG7240_DMA_INTR_MASK); + + ag7240_tx_stop(mac); + ag7240_rx_stop(mac); + + rx_ds = ag7240_reg_rd(mac,AG7240_DMA_RX_DESC); + mask = ag7240_reset_mask(mac->mac_unit); + + /* + * put into reset, hold, pull out. + */ + ar7240_reg_rmw_set(AR7240_RESET, mask); + udelay(10); + ar7240_reg_rmw_clear(AR7240_RESET, mask); + udelay(10); + + ag7240_hw_setup(mac); + + ag7240_reg_wr(mac,AG7240_DMA_TX_DESC,ag7240_desc_dma_addr(r,ds)); + ag7240_reg_wr(mac,AG7240_DMA_RX_DESC,rx_ds); + /* + * set the mac addr + */ + + addr_to_words(mac->mac_dev->dev_addr, w1, w2); + ag7240_reg_wr(mac, AG7240_GE_MAC_ADDR1, w1); + ag7240_reg_wr(mac, AG7240_GE_MAC_ADDR2, w2); + + ag7240_set_mac_from_link(mac, mac->mac_speed, mac->mac_fdx); + + + if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) { + ag7240_tx_start_qos(mac,ac); + } + else { + ag7240_tx_start(mac); + } + + ag7240_rx_start(mac); + + /* + * Restore interrupts + */ + ag7240_reg_wr(mac,AG7240_DMA_INTR_MASK,int_mask); + + spin_unlock_irqrestore(&mac->mac_lock,flags); + break; + } + } + ag7240_ring_incr(tail); + } + return 0; +} + + +/* + * + * phy link state management + */ +int +ag7240_check_link(ag7240_mac_t *mac,int phyUnit) +{ + struct net_device *dev = mac->mac_dev; + int carrier = netif_carrier_ok(dev), fdx=0, phy_up=0; + ag7240_phy_speed_t speed = 0; + int rc; + + /* The vitesse switch uses an indirect method to communicate phy status + * so it is best to limit the number of calls to what is necessary. + * However a single call returns all three pieces of status information. + * + * This is a trivial change to the other PHYs ergo this change. + * + */ + + rc = ag7240_get_link_status(mac->mac_unit, &phy_up, &fdx, &speed, phyUnit); + + athrs26_phy_stab_wr(phyUnit,phy_up,speed); + + if (rc < 0) + goto done; + + if (!phy_up) + { + if (carrier) + { + printk(MODULE_NAME ":unit %d: phy %0d not up carrier %d\n", mac->mac_unit, phyUnit, carrier); + + /* A race condition is hit when the queue is switched on while tx interrupts are enabled. + * To avoid that disable tx interrupts when phy is down. + */ + ag7240_intr_disable_tx(mac); + + netif_carrier_off(dev); + netif_stop_queue(dev); + if (mac_has_flag(mac,ETH_SOFT_LED)) { + PLedCtrl.ledlink[phyUnit] = 0; + s26_wr_phy(phyUnit,0x19,0x0); + } + } + goto done; + } + + if(!mac->mac_ifup) + goto done; + /* + * phy is up. Either nothing changed or phy setttings changed while we + * were sleeping. + */ + + if ((fdx < 0) || (speed < 0)) + { + printk(MODULE_NAME ": phy not connected?\n"); + return 0; + } + + if (carrier && (speed == mac->mac_speed) && (fdx == mac->mac_fdx)) + goto done; + + if (athrs26_phy_is_link_alive(phyUnit)) + { + printk(MODULE_NAME ": enet unit:%d phy:%d is up...", mac->mac_unit,phyUnit); + printk("%s %s %s\n", mii_str[mac->mac_unit][mii_if(mac)], + spd_str[speed], dup_str[fdx]); + + ag7240_set_mac_from_link(mac, speed, fdx); + + printk(MODULE_NAME ": done cfg2 %#x ifctl %#x miictrl \n", + ag7240_reg_rd(mac, AG7240_MAC_CFG2), + ag7240_reg_rd(mac, AG7240_MAC_IFCTL)); + /* + * in business + */ + netif_carrier_on(dev); + netif_start_queue(dev); + /* + * WAR: Enable link LED to glow if speed is negotiated as 10 Mbps + */ + if (mac_has_flag(mac,ETH_SOFT_LED)) { + PLedCtrl.ledlink[phyUnit] = 1; + PLedCtrl.speed[phyUnit] = speed; + + s26_wr_phy(phyUnit,0x19,0x3c0); + } + } + else { + if (mac_has_flag(mac,ETH_SOFT_LED)) { + PLedCtrl.ledlink[phyUnit] = 0; + s26_wr_phy(phyUnit,0x19,0x0); + } + printk(MODULE_NAME ": enet unit:%d phy:%d is down...\n", mac->mac_unit,phyUnit); + } + +done: + return 0; +} + +#if defined(CONFIG_AR7242_RGMII_PHY)||defined(CONFIG_AR7242_S16_PHY)||defined(CONFIG_AR7242_VIR_PHY) || defined(CONFIG_AR7242_RTL8309G_PHY) +/* + * phy link state management + */ +int +ag7242_check_link(ag7240_mac_t *mac) +{ + struct net_device *dev = mac->mac_dev; + int carrier = netif_carrier_ok(dev), fdx = 0, phy_up = 0; + ag7240_phy_speed_t speed = 0; + int rc,phyUnit = 0; + + + rc = ag7240_get_link_status(mac->mac_unit, &phy_up, &fdx, &speed, phyUnit); + + if (rc < 0) + goto done; + + if (!phy_up) + { + if (carrier) + { + printk(MODULE_NAME ": unit %d: phy %0d not up carrier %d\n", mac->mac_unit, phyUnit, carrier); + + /* A race condition is hit when the queue is switched on while tx interrupts are enabled. + * To avoid that disable tx interrupts when phy is down. + */ + ag7240_intr_disable_tx(mac); + + netif_carrier_off(dev); + netif_stop_queue(dev); + } + goto done; + } + + if(!mac->mac_ifup) { + goto done; + } + + if ((fdx < 0) || (speed < 0)) + { + printk(MODULE_NAME ": phy not connected?\n"); + return 0; + } + + if (carrier && (speed == rg_phy_speed ) && (fdx == rg_phy_duplex)) { + goto done; + } +#ifdef CONFIG_AR7242_RGMII_PHY + if (athr_phy_is_link_alive(phyUnit)) +#endif + { + printk(MODULE_NAME ": enet unit:%d is up...\n", mac->mac_unit); + printk("%s %s %s\n", mii_str[mac->mac_unit][mii_if(mac)], + spd_str[speed], dup_str[fdx]); + + rg_phy_speed = speed; + rg_phy_duplex = fdx; + /* Set GEO to be always at Giga Bit */ + speed = AG7240_PHY_SPEED_1000T; + ag7240_set_mac_from_link(mac, speed, fdx); + + printk(MODULE_NAME ": done cfg2 %#x ifctl %#x miictrl \n", + ag7240_reg_rd(mac, AG7240_MAC_CFG2), + ag7240_reg_rd(mac, AG7240_MAC_IFCTL)); + /* + * in business + */ + netif_carrier_on(dev); + netif_start_queue(dev); + } + +done: + mod_timer(&mac->mac_phy_timer, jiffies + AG7240_PHY_POLL_SECONDS*HZ); + return 0; +} + +#endif + +uint16_t +ag7240_mii_read(int unit, uint32_t phy_addr, uint8_t reg) +{ + ag7240_mac_t *mac = ag7240_unit2mac(unit); + uint16_t addr = (phy_addr << AG7240_ADDR_SHIFT) | reg, val; + volatile int rddata; + uint16_t ii = 0x1000; + + + ag7240_reg_wr(mac, AG7240_MII_MGMT_CMD, 0x0); + ag7240_reg_wr(mac, AG7240_MII_MGMT_ADDRESS, addr); + ag7240_reg_wr(mac, AG7240_MII_MGMT_CMD, AG7240_MGMT_CMD_READ); + + do + { + udelay(5); + rddata = ag7240_reg_rd(mac, AG7240_MII_MGMT_IND) & 0x1; + }while(rddata && --ii); + + val = ag7240_reg_rd(mac, AG7240_MII_MGMT_STATUS); + ag7240_reg_wr(mac, AG7240_MII_MGMT_CMD, 0x0); + + return val; +} + +void +ag7240_mii_write(int unit, uint32_t phy_addr, uint8_t reg, uint16_t data) +{ + ag7240_mac_t *mac = ag7240_unit2mac(unit); + uint16_t addr = (phy_addr << AG7240_ADDR_SHIFT) | reg; + volatile int rddata; + uint16_t ii = 0x1000; + + + ag7240_reg_wr(mac, AG7240_MII_MGMT_ADDRESS, addr); + ag7240_reg_wr(mac, AG7240_MII_MGMT_CTRL, data); + + do + { + rddata = ag7240_reg_rd(mac, AG7240_MII_MGMT_IND) & 0x1; + }while(rddata && --ii); + +} + +/* + * Tx operation: + * We do lazy reaping - only when the ring is "thresh" full. If the ring is + * full and the hardware is not even done with the first pkt we q'd, we turn + * on the tx interrupt, stop all q's and wait for h/w to + * tell us when its done with a "few" pkts, and then turn the Qs on again. + * + * Locking: + * The interrupt only touches the ring when Q's stopped => Tx is lockless, + * except when handling ring full. + * + * Desc Flushing: Flushing needs to be handled at various levels, broadly: + * - The DDr FIFOs for desc reads. + * - WB's for desc writes. + */ +static void +ag7240_handle_tx_full(ag7240_mac_t *mac) +{ + u32 flags; + + assert(!netif_queue_stopped(mac->mac_dev)); + + mac->mac_net_stats.tx_fifo_errors ++; + netif_stop_queue(mac->mac_dev); + + + spin_lock_irqsave(&mac->mac_lock, flags); + ag7240_intr_enable_tx(mac); + spin_unlock_irqrestore(&mac->mac_lock, flags); +} + +/* ****************************** + * + * Code under test - do not use + * + * ****************************** + */ +static ag7240_desc_t * +ag7240_get_tx_ds(ag7240_mac_t *mac, int *len, unsigned char **start,int ac) +{ + ag7240_desc_t *ds; + int len_this_ds; + ag7240_ring_t *r = &mac->mac_txring[ac]; + ag7240_buffer_t *bp; + + + + /* force extra pkt if remainder less than 4 bytes */ + if (*len > tx_len_per_ds) + if (*len < (tx_len_per_ds + 4)) + len_this_ds = tx_len_per_ds - 4; + else + len_this_ds = tx_len_per_ds; + else + len_this_ds = *len; + + ds = &r->ring_desc[r->ring_head]; + + ag7240_trc_new(ds,"ds addr"); + ag7240_trc_new(ds,"ds len"); + if (ag7240_tx_owned_by_dma(ds)) + ag7240_dma_reset(mac); + + ds->pkt_size = len_this_ds; + ds->pkt_start_addr = virt_to_phys(*start); + ds->more = 1; + + *len -= len_this_ds; + *start += len_this_ds; + + if (mac_has_flag(mac,CHECK_DMA_STATUS)) { + bp = &r->ring_buffer[r->ring_head]; + bp->trans_start = jiffies; + } + + ag7240_ring_incr(r->ring_head); + + return ds; +} + +int +ag7240_hard_start(struct sk_buff *skb, struct net_device *dev) +{ + ag7240_mac_t *mac = (ag7240_mac_t *)netdev_priv(dev); + ag7240_ring_t *r; + struct ethhdr *eh = (struct ethhdr *) skb->data; + int ac; + ag7240_buffer_t *bp; + ag7240_desc_t *ds, *fds; + unsigned char *start; + int len; + int nds_this_pkt; +//#ifdef CONFIG_AR7240_S26_VLAN_IGMP + if(unlikely((skb->len <= 0) || (skb->len > (dev->mtu + ETH_VLAN_HLEN +6 )))) +//#else +// if(unlikely((skb->len <= 0) || (skb->len > (dev->mtu + ETH_HLEN + 4)))) +//#endif + { + printk(MODULE_NAME ": bad skb, len %d\n", skb->len); + goto dropit; + } + + for (ac = 0;ac < mac->mac_noacs; ac++) { + if (ag7240_tx_reap_thresh(mac,ac)) + ag7240_tx_reap(mac,ac); + } + + /* + * Select the TX based on access category + */ + ac = ENET_AC_BE; + if ( (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) || (mac_has_flag(mac,ATHR_S26_HEADER)) + || (mac_has_flag(mac,ATHR_S16_HEADER))) { + /* Default priority */ + eh = (struct ethhdr *) skb->data; + + if (eh->h_proto == __constant_htons(ETHERTYPE_IP)) + { + const struct iphdr *ip = (struct iphdr *) + (skb->data + sizeof (struct ethhdr)); + /* + * IP frame: exclude ECN bits 0-1 and map DSCP bits 2-7 + * from TOS byte. + */ + ac = TOS_TO_ENET_AC ((ip->tos >> TOS_ECN_SHIFT) & 0x3F); + } + } + skb->priority=ac; + /* add header to normal frames sent to LAN*/ + if (mac_has_flag(mac,ATHR_S26_HEADER)) + { + skb_push(skb, ATHR_HEADER_LEN); + skb->data[0] = 0x30; /* broadcast = 0; from_cpu = 0; reserved = 1; port_num = 0 */ + skb->data[1] = (0x40 | (ac << HDR_PRIORITY_SHIFT)); /* reserved = 0b10; priority = 0; type = 0 (normal) */ + skb->priority=ENET_AC_BE; + } + + if (mac_has_flag(mac,ATHR_S16_HEADER)) + { + skb_push(skb, ATHR_HEADER_LEN); + memcpy(skb->data,skb->data + ATHR_HEADER_LEN, 12); + + skb->data[12] = 0x30; /* broadcast = 0; from_cpu = 0; reserved = 1; port_num = 0 */ + skb->data[13] = 0x40 | ((ac << HDR_PRIORITY_SHIFT)); /* reserved = 0b10; priority = 0; type = 0 (normal) */ + skb->priority=ENET_AC_BE; + } + /*hdr_dump("Tx",mac->mac_unit,skb->data,ac,0);*/ + r = &mac->mac_txring[skb->priority]; + + assert(r); + ag7240_trc_new(r->ring_head,"hard-stop hd"); + ag7240_trc_new(r->ring_tail,"hard-stop tl"); + + ag7240_trc_new(skb->len, "len this pkt"); + ag7240_trc_new(skb->data, "ptr 2 pkt"); + + dma_cache_sync(NULL, (void *)skb->data, skb->len, DMA_TO_DEVICE); + + bp = &r->ring_buffer[r->ring_head]; + bp->buf_pkt = skb; + len = skb->len; + start = skb->data; + + assert(len>4); + + nds_this_pkt = 1; + fds = ds = ag7240_get_tx_ds(mac, &len, &start,skb->priority); + + while (len>0) + { + ds = ag7240_get_tx_ds(mac, &len, &start,skb->priority); + nds_this_pkt++; + ag7240_tx_give_to_dma(ds); + } + + ds->res1 = 0; + ds->res2 = 0; + ds->ftpp_override = 0; + ds->res3 = 0; + + ds->more = 0; + ag7240_tx_give_to_dma(fds); + + bp->buf_lastds = ds; + bp->buf_nds = nds_this_pkt; + + ag7240_trc_new(ds,"last ds"); + ag7240_trc_new(nds_this_pkt,"nmbr ds for this pkt"); + + wmb(); + + mac->net_tx_packets ++; + mac->net_tx_bytes += skb->len; + + ag7240_trc(ag7240_reg_rd(mac, AG7240_DMA_TX_CTRL),"dma idle"); + + if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) { + ag7240_tx_start_qos(mac,skb->priority); + } + else { + ag7240_tx_start(mac); + } + for ( ac = 0;ac < mac->mac_noacs; ac++) { + if (unlikely(ag7240_tx_ring_full(mac,ac))) + ag7240_handle_tx_full(mac); + } + + + dev->trans_start = jiffies; + + return NETDEV_TX_OK; + +dropit: + printk(MODULE_NAME ": dropping skb %08x\n", (unsigned int)skb); + kfree_skb(skb); + return NETDEV_TX_OK; +} + +/* + * Interrupt handling: + * - Recv NAPI style (refer to Documentation/networking/NAPI) + * + * 2 Rx interrupts: RX and Overflow (OVF). + * - If we get RX and/or OVF, schedule a poll. Turn off _both_ interurpts. + * + * - When our poll's called, we + * a) Have one or more packets to process and replenish + * b) The hardware may have stopped because of an OVF. + * + * - We process and replenish as much as we can. For every rcvd pkt + * indicated up the stack, the head moves. For every such slot that we + * replenish with an skb, the tail moves. If head catches up with the tail + * we're OOM. When all's done, we consider where we're at: + * + * if no OOM: + * - if we're out of quota, let the ints be disabled and poll scheduled. + * - If we've processed everything, enable ints and cancel poll. + * + * If OOM: + * - Start a timer. Cancel poll. Ints still disabled. + * If the hardware's stopped, no point in restarting yet. + * + * Note that in general, whether we're OOM or not, we still try to + * indicate everything recvd, up. + * + * Locking: + * The interrupt doesnt touch the ring => Rx is lockless + * + */ +static irqreturn_t +ag7240_intr(int cpl, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + ag7240_mac_t *mac = (ag7240_mac_t *)netdev_priv(dev); + int isr, imr, handled = 0,ac; + + isr = ag7240_get_isr(mac); + imr = ag7240_reg_rd(mac, AG7240_DMA_INTR_MASK); + + ag7240_trc(isr,"isr"); + ag7240_trc(imr,"imr"); + + assert(isr == (isr & imr)); + if (isr & (AG7240_INTR_RX_OVF)) + { + handled = 1; + + ag7240_reg_wr(mac,AG7240_MAC_CFG1,(ag7240_reg_rd(mac,AG7240_MAC_CFG1)&0xfffffff3)); + + ag7240_intr_ack_rxovf(mac); + } + if (likely(isr & AG7240_INTR_RX)) + { + handled = 1; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if (napi_schedule_prep(&mac->mac_napi)) +#else + if (netif_rx_schedule_prep(dev)) +#endif + { + ag7240_intr_disable_recv(mac); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + __napi_schedule(&mac->mac_napi); +#else + __netif_rx_schedule(dev); +#endif + } + else + { + printk(MODULE_NAME ": driver bug! interrupt while in poll\n"); +// assert(0); +// ag7240_intr_disable_recv(mac); + } + /*ag7240_recv_packets(dev, mac, 200, &budget);*/ + } + if (likely(isr & AG7240_INTR_TX)) + { + handled = 1; + ag7240_intr_ack_tx(mac); + /* Which queue to reap ??????????? */ + for(ac = 0; ac < mac->mac_noacs;ac++) + ag7240_tx_reap(mac,ac); + } + if (unlikely(isr & AG7240_INTR_RX_BUS_ERROR)) + { + assert(0); + handled = 1; + ag7240_intr_ack_rxbe(mac); + } + if (unlikely(isr & AG7240_INTR_TX_BUS_ERROR)) + { + assert(0); + handled = 1; + ag7240_intr_ack_txbe(mac); + } + + if (!handled) + { + assert(0); + printk(MODULE_NAME ": unhandled intr isr %#x\n", isr); + } + + return IRQ_HANDLED; +} + +static irqreturn_t +ag7240_link_intr(int cpl, void *dev_id) { + + ar7240_s26_intr(); + return IRQ_HANDLED; +} + +void ag7240_dma_reset(ag7240_mac_t *mac) +{ + uint32_t mask; + + if(mac->mac_unit) + mask = AR7240_RESET_GE1_MAC; + else + mask = AR7240_RESET_GE0_MAC; + + ar7240_reg_rmw_set(AR7240_RESET, mask); + mdelay(100); + ar7240_reg_rmw_clear(AR7240_RESET, mask); + mdelay(100); + + ag7240_intr_disable_recv(mac); + schedule_work(&mac->mac_tx_timeout); +} + + +static int +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) +ag7240_poll(struct napi_struct *napi, int budget) +#else +ag7240_poll(struct net_device *dev, int *budget) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + ag7240_mac_t *mac = container_of(napi, ag7240_mac_t, mac_napi); + struct net_device *dev = mac->mac_dev; + int work_done=0, max_work = budget, status = 0; +#else + ag7240_mac_t *mac = (ag7240_mac_t *)netdev_priv(dev); + int work_done=0, max_work = min(*budget, dev->quota), status = 0; +#endif + + + ag7240_rx_status_t ret; + u32 flags; + spin_lock_irqsave(&mac->mac_lock, flags); + + ret = ag7240_recv_packets(dev, mac, max_work, &work_done); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if (likely(ret == AG7240_RX_STATUS_DONE) && work_done < budget ) + { + napi_complete(napi); + ag7240_intr_enable_recv(mac); + } +#else + dev->quota -= work_done; + *budget -= work_done; + if (likely(ret == AG7240_RX_STATUS_DONE)) + { + netif_rx_complete(dev); + } +#endif + if(ret == AG7240_RX_DMA_HANG) + { + status = 0; + ag7240_dma_reset(mac); + } + if (likely(ret == AG7240_RX_STATUS_NOT_DONE)) + { + /* + * We have work left + */ + status = 1; + napi_complete(napi); + napi_reschedule(napi); + } + else if (ret == AG7240_RX_STATUS_OOM) + { + printk(MODULE_NAME ": oom..?\n"); + /* + * Start timer, stop polling, but do not enable rx interrupts. + */ + mod_timer(&mac->mac_oom_timer, jiffies+1); + } +spin_unlock_irqrestore(&mac->mac_lock, flags); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + return work_done; +#else + return status; +#endif + +} + +int ag7240_recv_packets(struct net_device *dev, ag7240_mac_t *mac, + int quota, int *work_done) +{ + ag7240_ring_t *r = &mac->mac_rxring; + ag7240_desc_t *ds; + ag7240_buffer_t *bp; + struct sk_buff *skb; + ag7240_rx_status_t ret = AG7240_RX_STATUS_DONE; + int head = r->ring_head, len, status, iquota = quota, more_pkts, rep; + + ag7240_trc(iquota,"iquota"); + status = ag7240_reg_rd(mac, AG7240_DMA_RX_STATUS); + +process_pkts: + + ag7240_trc(status,"status"); + + /* + * Flush the DDR FIFOs for our gmac + */ + ar7240_flush_ge(mac->mac_unit); + + assert(quota > 0); /* WCL */ + + while(quota) + { + ds = &r->ring_desc[head]; + + ag7240_trc(head,"hd"); + ag7240_trc(ds, "ds"); + + if (ag7240_rx_owned_by_dma(ds)) + { + break; +/* if(quota == iquota) + { + *work_done = quota = 0; + return AG7240_RX_DMA_HANG; + } + break;*/ + } + ag7240_intr_ack_rx(mac); + + bp = &r->ring_buffer[head]; + len = ds->pkt_size; + skb = bp->buf_pkt; + + assert(skb); + skb_put(skb, len - ETHERNET_FCS_SIZE); + +#if defined(CONFIG_ATHEROS_HEADER_EN) + //01:00:5e:X:X:X is a multicast MAC address. + if(mac->mac_unit == 1){ + int offset = 14; + if(ignore_packet_inspection){ + if((skb->data[2]==0x01)&&(skb->data[3]==0x00)&&(skb->data[4]==0x5e)){ + + //If vlan, calculate the offset of the vlan header. + if((skb->data[14]==0x81)&&(skb->data[15]==0x00)){ + offset +=4; + } + + //If pppoe exists, calculate the offset of the pppoe header. + if((skb->data[offset]==0x88)&&((skb->data[offset+1]==0x63)|(skb->data[offset+1]==0x64))){ + offset += sizeof(struct pppoe_hdr); + } + + //Parse IP layer. + if((skb->data[offset]==0x08)&&(skb->data[offset+1]==0x00)){ + struct iphdr * iph; + struct igmphdr *igh; + + offset +=2; + iph = skb->data+offset; + offset += (iph->ihl<<2); + igh = skb->data + offset; + + //Sanity Check and insert port information into igmp checksum. + if((iph->ihl>4)&&(iph->version==4)&&(iph->protocol==2)){ + igh->csum = 0x7d50|(skb->data[0]&0xf); + printk("[eth1] Type %x port %x.\n",igh->type,skb->data[0]&0xf); + } + + } + } + } + skb_pull(skb,2); + } + +#endif + +#ifdef CONFIG_AR7240_S26_VLAN_IGMP + //For swith S26, if vlan_id == 0, we will remove the tag header. + if(mac->mac_unit==1) + { + if((skb->data[12]==0x81)&&(skb->data[13]==0x00)&&((skb->data[14]&0xf)==0x00)&&(skb->data[15]==0x00)) + { + memmove(&skb->data[4],&skb->data[0],12); + skb_pull(skb,4); + } + } +#endif + dma_cache_sync(NULL, (void *)skb->data, skb->len, DMA_FROM_DEVICE); + + mac->net_rx_packets ++; + mac->net_rx_bytes += skb->len; + /* + * also pulls the ether header + */ + skb->dev = dev; + bp->buf_pkt = NULL; + dev->last_rx = jiffies; + + work_done[0]++; + quota--; + skb->protocol = eth_type_trans(skb, dev); + netif_receive_skb(skb); + ag7240_ring_incr(head); + } + + r->ring_head = head; + rep = ag7240_rx_replenish(mac); + + /* + * let's see what changed while we were slogging. + * ack Rx in the loop above is no flush version. It will get flushed now. + */ + status = ag7240_reg_rd(mac, AG7240_DMA_RX_STATUS); + more_pkts = (status & AG7240_RX_STATUS_PKT_RCVD); + + ag7240_trc(more_pkts,"more_pkts"); + + if (!more_pkts) goto done; + /* + * more pkts arrived; if we have quota left, get rolling again + */ + /* + * out of quota + */ + ret = AG7240_RX_STATUS_NOT_DONE; + +done: + + if (unlikely(ag7240_rx_ring_full(mac))) + return AG7240_RX_STATUS_OOM; + /* + * !oom; if stopped, restart h/w + */ + + if (unlikely(status & AG7240_RX_STATUS_OVF)) + { + mac->net_rx_over_errors ++; + ag7240_intr_ack_rxovf(mac); + ag7240_rx_start(mac); + } + + return ret; +} + +static struct sk_buff * + ag7240_buffer_alloc(void) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(AG7240_RX_BUF_SIZE + AG7240_RX_RESERVE); + if (unlikely(!skb)) + return NULL; + skb_reserve(skb, AG7240_RX_RESERVE); + + return skb; +} + +static void +ag7240_buffer_free(struct sk_buff *skb) +{ + if (in_irq()) + dev_kfree_skb_irq(skb); + else + dev_kfree_skb(skb); +} + +/* + * Head is the first slot with a valid buffer. Tail is the last slot + * replenished. Tries to refill buffers from tail to head. + */ +static int +ag7240_rx_replenish(ag7240_mac_t *mac) +{ + ag7240_ring_t *r = &mac->mac_rxring; + int head = r->ring_head, tail = r->ring_tail, refilled = 0; + ag7240_desc_t *ds; + ag7240_buffer_t *bf; + + ag7240_trc(head,"hd"); + ag7240_trc(tail,"tl"); + + do + { + bf = &r->ring_buffer[tail]; + ds = &r->ring_desc[tail]; + + ag7240_trc(ds,"ds"); + + if(ag7240_rx_owned_by_dma(ds)) + { + return -1; + } + assert(!bf->buf_pkt); + + bf->buf_pkt = ag7240_buffer_alloc(); + if (!bf->buf_pkt) + { + printk(MODULE_NAME ": outta skbs!\n"); + break; + } + dma_cache_sync(NULL, (void *)bf->buf_pkt->data, AG7240_RX_BUF_SIZE, DMA_FROM_DEVICE); + ds->pkt_start_addr = virt_to_phys(bf->buf_pkt->data); + + ag7240_rx_give_to_dma(ds); + refilled ++; + + ag7240_ring_incr(tail); + + } while(tail != head); + /* + * Flush descriptors + */ + wmb(); + + ag7240_reg_wr(mac,AG7240_MAC_CFG1,(ag7240_reg_rd(mac,AG7240_MAC_CFG1)|0xc)); + ag7240_rx_start(mac); + + r->ring_tail = tail; + ag7240_trc(refilled,"refilled"); + + return refilled; +} + +/* + * Reap from tail till the head or whenever we encounter an unxmited packet. + */ +static int +ag7240_tx_reap(ag7240_mac_t *mac,int ac) +{ + ag7240_ring_t *r; + int head, tail, reaped = 0, i; + ag7240_desc_t *ds; + ag7240_buffer_t *bf; + uint32_t flags; + + r = &mac->mac_txring[ac]; + head = r->ring_head; + tail = r->ring_tail; + ag7240_trc_new(head,"hd"); + ag7240_trc_new(tail,"tl"); + + ar7240_flush_ge(mac->mac_unit); + spin_lock_irqsave(&mac->mac_lock, flags); + while(tail != head) + { + ds = &r->ring_desc[tail]; + + ag7240_trc_new(ds,"ds"); + + if(ag7240_tx_owned_by_dma(ds)) + break; + + bf = &r->ring_buffer[tail]; + assert(bf->buf_pkt); + + ag7240_trc_new(bf->buf_lastds,"lastds"); + + if(ag7240_tx_owned_by_dma(bf->buf_lastds)) + break; + + for(i = 0; i < bf->buf_nds; i++) + { + ag7240_intr_ack_tx(mac); + ag7240_ring_incr(tail); + } + + ag7240_buffer_free(bf->buf_pkt); + bf->buf_pkt = NULL; + + reaped ++; + } + spin_unlock_irqrestore(&mac->mac_lock, flags); + + r->ring_tail = tail; + + if (netif_queue_stopped(mac->mac_dev) && + (ag7240_ndesc_unused(mac, r) >= AG7240_TX_QSTART_THRESH) && + netif_carrier_ok(mac->mac_dev)) + { + if (ag7240_reg_rd(mac, AG7240_DMA_INTR_MASK) & AG7240_INTR_TX) + { + spin_lock_irqsave(&mac->mac_lock, flags); + ag7240_intr_disable_tx(mac); + spin_unlock_irqrestore(&mac->mac_lock, flags); + } + netif_wake_queue(mac->mac_dev); + } + + return reaped; +} + +/* + * allocate and init rings, descriptors etc. + */ +static int +ag7240_tx_alloc(ag7240_mac_t *mac) +{ + ag7240_ring_t *r ; + int ac; + ag7240_desc_t *ds; + int i, next; + for(ac = 0;ac < mac->mac_noacs; ac++) { + + r = &mac->mac_txring[ac]; + if (ag7240_ring_alloc(r, AG7240_TX_DESC_CNT)) + return 1; + + ag7240_trc(r->ring_desc,"ring_desc"); + + ds = r->ring_desc; + for(i = 0; i < r->ring_nelem; i++ ) + { + ag7240_trc_new(ds,"tx alloc ds"); + next = (i == (r->ring_nelem - 1)) ? 0 : (i + 1); + ds[i].next_desc = ag7240_desc_dma_addr(r, &ds[next]); + ag7240_tx_own(&ds[i]); + } + } + return 0; +} + +static int +ag7240_rx_alloc(ag7240_mac_t *mac) +{ + ag7240_ring_t *r = &mac->mac_rxring; + ag7240_desc_t *ds; + int i, next, tail = r->ring_tail; + ag7240_buffer_t *bf; + + if (ag7240_ring_alloc(r, AG7240_RX_DESC_CNT)) + return 1; + + ag7240_trc(r->ring_desc,"ring_desc"); + + ds = r->ring_desc; + for(i = 0; i < r->ring_nelem; i++ ) + { + next = (i == (r->ring_nelem - 1)) ? 0 : (i + 1); + ds[i].next_desc = ag7240_desc_dma_addr(r, &ds[next]); + } + + for (i = 0; i < AG7240_RX_DESC_CNT; i++) + { + bf = &r->ring_buffer[tail]; + ds = &r->ring_desc[tail]; + + bf->buf_pkt = ag7240_buffer_alloc(); + if (!bf->buf_pkt) + goto error; + + dma_cache_sync(NULL, (void *)bf->buf_pkt->data, AG7240_RX_BUF_SIZE, DMA_FROM_DEVICE); + ds->pkt_start_addr = virt_to_phys(bf->buf_pkt->data); + + ds->res1 = 0; + ds->res2 = 0; + ds->ftpp_override = 0; + ds->res3 = 0; + ds->more = 0; + ag7240_rx_give_to_dma(ds); + ag7240_ring_incr(tail); + } + + return 0; +error: + printk(MODULE_NAME ": unable to allocate rx\n"); + ag7240_rx_free(mac); + return 1; +} + +static void +ag7240_tx_free(ag7240_mac_t *mac) +{ + int ac; + + for(ac = 0;ac < mac->mac_noacs; ac++) { + ag7240_ring_release(mac, &mac->mac_txring[ac]); + ag7240_ring_free(&mac->mac_txring[ac]); + } +} + +static void +ag7240_rx_free(ag7240_mac_t *mac) +{ + ag7240_ring_release(mac, &mac->mac_rxring); + ag7240_ring_free(&mac->mac_rxring); +} + +static int +ag7240_ring_alloc(ag7240_ring_t *r, int count) +{ + int desc_alloc_size, buf_alloc_size; + + desc_alloc_size = sizeof(ag7240_desc_t) * count; + buf_alloc_size = sizeof(ag7240_buffer_t) * count; + + memset(r, 0, sizeof(ag7240_ring_t)); + + r->ring_buffer = (ag7240_buffer_t *)kmalloc(buf_alloc_size, GFP_KERNEL); + printk("%s Allocated %d at 0x%lx\n",__func__,buf_alloc_size,(unsigned long) r->ring_buffer); + if (!r->ring_buffer) + { + printk(MODULE_NAME ": unable to allocate buffers\n"); + return 1; + } + + r->ring_desc = (ag7240_desc_t *)dma_alloc_coherent(NULL, + desc_alloc_size, + &r->ring_desc_dma, + GFP_DMA); + if (! r->ring_desc) + { + printk(MODULE_NAME ": unable to allocate coherent descs\n"); + kfree(r->ring_buffer); + printk("%s Freeing at 0x%lx\n",__func__,(unsigned long) r->ring_buffer); + return 1; + } + + memset(r->ring_buffer, 0, buf_alloc_size); + memset(r->ring_desc, 0, desc_alloc_size); + r->ring_nelem = count; + + return 0; +} + +static void +ag7240_ring_release(ag7240_mac_t *mac, ag7240_ring_t *r) +{ + int i; + + for(i = 0; i < r->ring_nelem; i++) + if (r->ring_buffer[i].buf_pkt) + ag7240_buffer_free(r->ring_buffer[i].buf_pkt); +} + +static void +ag7240_ring_free(ag7240_ring_t *r) +{ + dma_free_coherent(NULL, sizeof(ag7240_desc_t)*r->ring_nelem, r->ring_desc, + r->ring_desc_dma); + kfree(r->ring_buffer); + printk("%s Freeing at 0x%lx\n",__func__,(unsigned long) r->ring_buffer); +} + +/* + * Error timers + */ +static void +ag7240_oom_timer(unsigned long data) +{ + ag7240_mac_t *mac = (ag7240_mac_t *)data; + int val; + + ag7240_trc(data,"data"); + ag7240_rx_replenish(mac); + if (ag7240_rx_ring_full(mac)) + { + val = mod_timer(&mac->mac_oom_timer, jiffies+1); + assert(!val); + } + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + napi_schedule(&mac->mac_napi); +#else + netif_rx_schedule(mac->mac_dev); +#endif +} + +static void +ag7240_tx_timeout(struct net_device *dev) +{ + ag7240_mac_t *mac = (ag7240_mac_t *)netdev_priv(dev); + ag7240_trc(dev,"dev"); + printk("%s\n",__func__); + /* + * Do the reset outside of interrupt context + */ + schedule_work(&mac->mac_tx_timeout); +} + +static void +ag7240_tx_timeout_task(struct work_struct *work) +{ + ag7240_mac_t *mac = container_of(work, ag7240_mac_t, mac_tx_timeout); + ag7240_trc(mac,"mac"); + check_for_dma_status(mac,0); + ag7240_stop(mac->mac_dev); + ag7240_open(mac->mac_dev); +} + +static void +ag7240_get_default_macaddr(ag7240_mac_t *mac, u8 *mac_addr) +{ + /* Use MAC address stored in Flash */ + +#ifdef CONFIG_AG7240_MAC_LOCATION + u8 *eep_mac_addr = (u8 *)( KSEG1ADDR(CONFIG_AG7240_MAC_LOCATION) + (mac->mac_unit)*6); +#else + u8 *eep_mac_addr = (u8 *)(KSEG1ADDR((mac->mac_unit) ? AR7240_EEPROM_GE1_MAC_ADDR:AR7240_EEPROM_GE0_MAC_ADDR)); +#endif + + if ((mac->mac_unit>0) && (!memcmp(eep_mac_addr,"\xff\xff\xff\xff\xff\xff",6) || !memcmp(eep_mac_addr,"\x00\x00\x00\x00\x00\x00",6))) { +#ifdef CONFIG_AG7240_MAC_LOCATION + eep_mac_addr = (u8 *)(KSEG1ADDR(CONFIG_AG7240_MAC_LOCATION)); +#else + eep_mac_addr = (u8 *)(KSEG1ADDR(AR7240_EEPROM_GE0_MAC_ADDR)); +#endif + } + + + printk(MODULE_NAME "CHH: Mac address for unit %d mac=%pM\n",mac->mac_unit, eep_mac_addr); + memcpy(mac_addr,eep_mac_addr,6); +} + +static int +ag7240_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + return athrs_do_ioctl(dev,ifr, cmd); +} +static struct net_device_stats + *ag7240_get_stats(struct net_device *dev) +{ + + ag7240_mac_t *mac = netdev_priv(dev); + int carrier = netif_carrier_ok(dev); + + if (mac->mac_speed < 1) { + return &mac->mac_net_stats; + } + + /* + * MIB registers reads will fail if link is lost while resetting PHY. + */ + if (carrier && !phy_in_reset) + { + + mac->mac_net_stats.rx_packets = ag7240_reg_rd(mac,AG7240_RX_PKT_CNTR); + mac->mac_net_stats.tx_packets = ag7240_reg_rd(mac,AG7240_TX_PKT_CNTR); + mac->mac_net_stats.rx_bytes = ag7240_reg_rd(mac,AG7240_RX_BYTES_CNTR); + mac->mac_net_stats.tx_bytes = ag7240_reg_rd(mac,AG7240_TX_BYTES_CNTR); + mac->mac_net_stats.tx_errors = ag7240_reg_rd(mac,AG7240_TX_CRC_ERR_CNTR); + mac->mac_net_stats.rx_dropped = ag7240_reg_rd(mac,AG7240_RX_DROP_CNTR); + mac->mac_net_stats.tx_dropped = ag7240_reg_rd(mac,AG7240_TX_DROP_CNTR); + mac->mac_net_stats.collisions = ag7240_reg_rd(mac,AG7240_TOTAL_COL_CNTR); + + /* detailed rx_errors: */ + mac->mac_net_stats.rx_length_errors = ag7240_reg_rd(mac,AG7240_RX_LEN_ERR_CNTR); + mac->mac_net_stats.rx_over_errors = ag7240_reg_rd(mac,AG7240_RX_OVL_ERR_CNTR); + mac->mac_net_stats.rx_crc_errors = ag7240_reg_rd(mac,AG7240_RX_CRC_ERR_CNTR); + mac->mac_net_stats.rx_frame_errors = ag7240_reg_rd(mac,AG7240_RX_FRM_ERR_CNTR); + + mac->mac_net_stats.rx_errors = ag7240_reg_rd(mac,AG7240_RX_CODE_ERR_CNTR) + + ag7240_reg_rd(mac,AG7240_RX_CRS_ERR_CNTR) + + mac->mac_net_stats.rx_length_errors + + mac->mac_net_stats.rx_over_errors + + mac->mac_net_stats.rx_crc_errors + + mac->mac_net_stats.rx_frame_errors; + + mac->mac_net_stats.multicast = ag7240_reg_rd(mac,AG7240_RX_MULT_CNTR) + + ag7240_reg_rd(mac,AG7240_TX_MULT_CNTR); + + } + return &mac->mac_net_stats; +} + +static void +ag7240_vet_tx_len_per_pkt(unsigned int *len) +{ + unsigned int l; + + /* make it into words */ + l = *len & ~3; + + /* + * Not too small + */ + if (l < AG7240_TX_MIN_DS_LEN) { + l = AG7240_TX_MIN_DS_LEN; + } + else { + /* Avoid len where we know we will deadlock, that + * is the range between fif_len/2 and the MTU size + */ + if (l > AG7240_TX_FIFO_LEN/2) { + if (l < AG7240_TX_MTU_LEN) + l = AG7240_TX_MTU_LEN; + else if (l > AG7240_TX_MAX_DS_LEN) + l = AG7240_TX_MAX_DS_LEN; + *len = l; + } + } +} +static struct net_device_ops mac_net_ops; + +static int ag7240_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu<=(AG71XX_TX_MTU_LEN-18)) + dev->mtu = new_mtu; + return 0; +} + +#if DEBUG_ENABLE_PLATFORM_PROBE_CRASH_PREVENTION +static int crash_prevention_hack = 0; +#endif + +/* + * All allocations (except irq and rings). + */ +static int __init +ag7240_probe(struct platform_device *pdev) +{ + int i,st; + struct net_device *dev; + ag7240_mac_t *mac; + uint32_t mask; + struct ag71xx *ag; + struct ag7240_platform_data *pdata; + +#if DEBUG_ENABLE_PLATFORM_PROBE_CRASH_PREVENTION + /* For some reason this is getting called twice which obviously causes no end of pain... */ + pr_debug("ag7240_init() pdev=%p dev=%p hack=%d\n", pdev, pdev?(void*)&pdev->dev:(void*)-1, crash_prevention_hack); + if (crash_prevention_hack) { + pr_err("Whoops! probed again...\n"); + WARN_ON(1); + return 1; + } + crash_prevention_hack++; +#endif + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data specified\n"); + return -ENXIO; + } +#if DEBUG_CHECK_MEMORY_CONTENTS + print_hex_dump(KERN_DEBUG, "test ", DUMP_PREFIX_ADDRESS, 16, 1, (u8*)KSEG1ADDR(0x1fff0000), 64, true); +#ifdef CONFIG_AG7240_MAC_LOCATION + print_hex_dump(KERN_DEBUG, "test ", DUMP_PREFIX_ADDRESS, 16, 1, (u8*)KSEG1ADDR(CONFIG_AG7240_MAC_LOCATION), 64, true); +#endif +#endif + + /* + * tx_len_per_ds is the number of bytes per data transfer in word increments. + * + * If the value is 0 than we default the value to a known good value based + * on benchmarks. Otherwise we use the value specified - within some + * cosntraints of course. + * + * Tested working values are 256, 512, 768, 1024 & 1536. + * + * A value of 256 worked best in all benchmarks. That is the default. + * + */ + + /* Tested 256, 512, 768, 1024, 1536 OK, 1152 and 1280 failed*/ + if (0 == tx_len_per_ds) + tx_len_per_ds = CONFIG_AG7240_LEN_PER_TX_DS; + + ag7240_vet_tx_len_per_pkt( &tx_len_per_ds); + + printk(MODULE_NAME ": Length per segment %d\n", tx_len_per_ds); + + /* + * Compute the number of descriptors for an MTU + */ + tx_max_desc_per_ds_pkt =1; + + + printk(MODULE_NAME ": Max segments per packet %d\n", tx_max_desc_per_ds_pkt); + printk(MODULE_NAME ": Max tx descriptor count %d\n", AG7240_TX_DESC_CNT); + printk(MODULE_NAME ": Max rx descriptor count %d\n", AG7240_RX_DESC_CNT); + + /* + * Let hydra know how much to put into the fifo in words (for tx) + */ + if (0 == fifo_3) + fifo_3 = 0x000001ff | ((AG7240_TX_FIFO_LEN-tx_len_per_ds)/4)<<16; + + printk(MODULE_NAME ": fifo cfg 3 %08x\n", fifo_3); + + /* + ** Do the rest of the initializations + */ + + for(i = 0; i < AG7240_NMACS; i++) + { + pr_debug("alloc eth unit %d\n", i); + dev = alloc_etherdev(sizeof(ag7240_mac_t)); + mac = (ag7240_mac_t *)netdev_priv(dev); + if (!mac) + { + printk(MODULE_NAME ": unable to allocate mac\n"); + return 1; + } + memset(mac, 0, sizeof(ag7240_mac_t)); + + mac->mac_unit = i; + mac->mac_base = ag7240_mac_base(i); + mac->mac_irq = ag7240_mac_irq(i); + mac->mac_noacs = 1; + ag7240_macs[i] = mac; +#ifdef CONFIG_ATHEROS_HEADER_EN + if (mac->mac_unit == ENET_UNIT_LAN) + mac_set_flag(mac,ATHR_S26_HEADER); +#endif +#ifdef CONFIG_ETH_SOFT_LED + if (is_ar7240() || is_ar933x()) + mac_set_flag(mac,ETH_SOFT_LED); +#endif +#ifdef CONFIG_CHECK_DMA_STATUS + mac_set_flag(mac,CHECK_DMA_STATUS); +#endif +#ifdef CONFIG_S26_SWITCH_ONLY_MODE + if (is_ar7241()) { + if(i) { + mac_set_flag(mac,ETH_SWONLY_MODE); + } + else { + continue; + } + } +#endif + spin_lock_init(&mac->mac_lock); + /* + * out of memory timer + */ + init_timer(&mac->mac_oom_timer); + mac->mac_oom_timer.data = (unsigned long)mac; + mac->mac_oom_timer.function = (void *)ag7240_oom_timer; + /* + * watchdog task + */ + + INIT_WORK(&mac->mac_tx_timeout, ag7240_tx_timeout_task); + + if (!dev) + { + kfree(mac); + printk("%s Freeing at 0x%lx\n",__func__,(unsigned long) mac); + printk(MODULE_NAME ": unable to allocate etherdev\n"); + return 1; + } + platform_set_drvdata(pdev, dev); + + mac_net_ops.ndo_open = ag7240_open; + mac_net_ops.ndo_stop = ag7240_stop; + mac_net_ops.ndo_start_xmit= ag7240_hard_start; + mac_net_ops.ndo_get_stats = ag7240_get_stats; + mac_net_ops.ndo_tx_timeout= ag7240_tx_timeout; + mac_net_ops.ndo_do_ioctl = ag7240_do_ioctl; + mac_net_ops.ndo_change_mtu = ag7240_change_mtu; + mac_net_ops.ndo_set_mac_address = eth_mac_addr; + mac_net_ops.ndo_validate_addr = eth_validate_addr; + netif_napi_add(dev, &mac->mac_napi, ag7240_poll, AG7240_NAPI_WEIGHT); + dev->netdev_ops = (const struct net_device_ops *)&mac_net_ops; + mac->mac_dev = dev; + +// if (0) { +// ag7240_get_default_macaddr(mac, dev->dev_addr); +// } else { + if (i==0) { + memcpy(dev->dev_addr, pdata->mac_addr1, ETH_ALEN); + } else { + memcpy(dev->dev_addr, pdata->mac_addr2, ETH_ALEN); + } + printk(MODULE_NAME "CHH: Mac address for unit %d\n",mac->mac_unit); + printk(MODULE_NAME "CHH: %02x:%02x:%02x:%02x:%02x:%02x \n", + dev->dev_addr[0],dev->dev_addr[1],dev->dev_addr[2], + dev->dev_addr[3],dev->dev_addr[4],dev->dev_addr[5]); + + if (register_netdev(dev)) + { + printk(MODULE_NAME ": register netdev failed\n"); + goto failed; + } + netif_carrier_off(dev); + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG1, AG7240_MAC_CFG1_SOFT_RST + | AG7240_MAC_CFG1_RX_RST | AG7240_MAC_CFG1_TX_RST); + + udelay(20); + mask = ag7240_reset_mask(mac->mac_unit); + + /* + * put into reset, hold, pull out. + */ + ar7240_reg_rmw_set(AR7240_RESET, mask); + mdelay(100); + ar7240_reg_rmw_clear(AR7240_RESET, mask); + mdelay(100); + } + + /* + * Enable link interrupts for PHYs + */ + dev = ag7240_macs[0]->mac_dev; + st = request_irq(AR7240_MISC_IRQ_ENET_LINK, ag7240_link_intr, 0, dev->name, dev); + + if (st < 0) + { + printk(MODULE_NAME ": request irq %d failed %d\n", AR7240_MISC_IRQ_ENET_LINK, st); + goto failed; + } + + ag7240_trc_init(); + + athrs_reg_dev(ag7240_macs); + if (mac_has_flag(mac,CHECK_DMA_STATUS)) + prev_dma_chk_ts = jiffies; + + if (mac_has_flag(mac,ETH_SOFT_LED)) { + init_timer(&PLedCtrl.led_timer); + PLedCtrl.led_timer.data = (unsigned long)(&PLedCtrl); + PLedCtrl.led_timer.function = (void *)led_control_func; + mod_timer(&PLedCtrl.led_timer,(jiffies + AG7240_LED_POLL_SECONDS)); + } + platform_set_drvdata(pdev, ag7240_macs[0]); + + return 0; +failed: + free_irq(AR7240_MISC_IRQ_ENET_LINK, ag7240_macs[0]->mac_dev); + for(i = 0; i < AG7240_NMACS; i++) + { +#ifdef CONFIG_S26_SWITCH_ONLY_MODE + if (is_ar7241() && i == 0) + continue; +#endif + if (!ag7240_macs[i]) + continue; + if (ag7240_macs[i]->mac_dev) + free_netdev(ag7240_macs[i]->mac_dev); + kfree(ag7240_macs[i]); + printk("%s Freeing at 0x%lx\n",__func__,(unsigned long) ag7240_macs[i]); + } + return 1; +} + +static int __exit +ag7240_cleanup(struct platform_device *pdev) +{ + int i; + struct net_device *dev = platform_get_drvdata(pdev); + +#if DEBUG_ENABLE_PLATFORM_PROBE_CRASH_PREVENTION + pr_debug("ag7240_cleanup() pdev=%p dev=%p hack=%d\n", pdev, dev, crash_prevention_hack); + crash_prevention_hack--; +#endif + + if (!dev) return 0; + WARN_ON(dev != ag7240_macs[0]); + + free_irq(AR7240_MISC_IRQ_ENET_LINK, ag7240_macs[0]->mac_dev); + for(i = 0; i < AG7240_NMACS; i++) + { + pr_debug(MODULE_NAME "cleanup %d\n", i); + if (mac_has_flag(ag7240_macs[1],ETH_SWONLY_MODE) && i == 0) { + pr_debug(MODULE_NAME "kfree %d.\n", i); + kfree(ag7240_macs[i]); + continue; + } + pr_debug(MODULE_NAME "unregister_netdev %d\n", i); + unregister_netdev(ag7240_macs[i]->mac_dev); + pr_debug(MODULE_NAME "free_netdev %d\n", i); + free_netdev(ag7240_macs[i]->mac_dev); + pr_debug(MODULE_NAME "kfree %d\n", i); + kfree(ag7240_macs[i]); + printk("%s Freeing at 0x%lx\n",__func__,(unsigned long) ag7240_macs[i]); + } + if (mac_has_flag(ag7240_macs[0],ETH_SOFT_LED)) + del_timer(&PLedCtrl.led_timer); + printk(MODULE_NAME ": cleanup done\n"); + return 0; +} + +static struct platform_driver ag71xx_driver = { + .probe = ag7240_probe, + .remove = ag7240_cleanup, + .driver = { + .name = "ag7240", + } +}; + +static int __init ag7240_module_init(void) +{ + pr_debug("ag7240_module_init()"); + int ret = platform_driver_register(&ag71xx_driver); + return ret; +} + +static void __exit ag7240_module_exit(void) +{ + pr_debug("ag7240_module_exit()"); + platform_driver_unregister(&ag71xx_driver); +} + +module_init(ag7240_module_init); +module_exit(ag7240_module_exit); diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240.h new file mode 100644 index 0000000..10aec84 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240.h @@ -0,0 +1,706 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _AG7240_H +#define _AG7240_H + +#include +#include +#include +//#include +#include +#include +#include "ar7240.h" +#include "ag7240_trc.h" + +#ifdef CONFIG_AR7240_EMULATION +#undef CONFIG_AR9100 +#endif + + +#include /* XXX for TOS */ +#include + +#ifndef ETHERTYPE_IP +#define ETHERTYPE_IP 0x0800 /* IP protocol */ +#endif + +#define IP_PRI_SHIFT 5 + +#define ENET_NUM_AC 4 /* 4 AC categories */ +/* QOS stream classes */ +#define ENET_AC_BE 0 /* best effort */ +#define ENET_AC_BK 1 /* background */ +#define ENET_AC_VI 2 /* video */ +#define ENET_AC_VO 3 /* voice */ + +#define HDR_PACKET_TYPE_MASK 0x0F +#define HDR_PRIORITY_SHIFT 0x4 +#define HDR_PRIORITY_MASK 0x3 +#define TOS_ECN_SHIFT 0x2 +#define TOS_ECN_MASK 0xFC + +#define TOS_TO_ENET_AC(_tos) ( \ + (((_tos) == 0) || ((_tos) == 3)) ? ENET_AC_BE : \ + (((_tos) == 1) || ((_tos) == 2)) ? ENET_AC_BK : \ + (((_tos) == 4) || ((_tos) == 5)) ? ENET_AC_VI : \ + ENET_AC_VO) + +#define CONFIG_CHECK_DMA_STATUS 1 +#define CONFIG_ETH_SOFT_LED 1 + +#ifdef AG7240_DEBUG +#define DPRINTF(_fmt,...) do { \ + printk(MODULE_NAME":"_fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(_fmt,...) +#endif + +/* + * h/w descriptor + */ +typedef struct { + uint32_t pkt_start_addr; + + uint32_t is_empty : 1; + uint32_t res1 : 6; + uint32_t more : 1; + uint32_t res2 : 3; + uint32_t ftpp_override : 5; + uint32_t res3 : 4; + uint32_t pkt_size : 12; + + uint32_t next_desc ; + uint32_t pad; +}ag7240_desc_t; + +/* + * s/w descriptor + */ +typedef struct { + struct sk_buff *buf_pkt; /*ptr to skb*/ + int buf_nds; /*no. of desc for this skb*/ + ag7240_desc_t *buf_lastds; /*the last desc. (for convenience)*/ + unsigned long trans_start; /* descriptor time stamp */ +}ag7240_buffer_t; + +/* + * Tx and Rx descriptor rings; + */ +typedef struct { + ag7240_desc_t *ring_desc; /* hardware descriptors */ + dma_addr_t ring_desc_dma; /* dma addresses of desc*/ + ag7240_buffer_t *ring_buffer; /* OS buffer info */ + int ring_head; /* producer index */ + int ring_tail; /* consumer index */ + int ring_nelem; /* nelements */ +}ag7240_ring_t; + +typedef struct { + int stats; +}ag7240_stats_t; + +/* + * 0, 1, 2: based on hardware values for mii ctrl bits [5,4] + */ +typedef enum { + AG7240_PHY_SPEED_10T, + AG7240_PHY_SPEED_100TX, + AG7240_PHY_SPEED_1000T, +}ag7240_phy_speed_t; + +/* + * Represents an ethernet MAC. Contains ethernet devices (LAN and WAN) + */ +#define AG7240_NVDEVS 2 + +struct eth_led_control; + +typedef struct { + struct net_device *mac_dev; + uint32_t mac_unit; + uint32_t mac_base; + int mac_irq; + uint8_t mac_ifup; + uint8_t mac_noacs; + ag7240_ring_t mac_txring[4]; + ag7240_ring_t mac_rxring; + ag7240_stats_t mac_stats; + spinlock_t mac_lock; + struct timer_list mac_oom_timer; + struct work_struct mac_tx_timeout; + struct net_device_stats mac_net_stats; + ag7240_phy_speed_t mac_speed; + int mac_fdx; + struct timer_list mac_phy_timer; + ag7240_trc_t tb; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + struct napi_struct mac_napi; +#endif + uint16_t mac_flags; +}ag7240_mac_t; + +#define net_rx_packets mac_net_stats.rx_packets +#define net_rx_fifo_errors mac_net_stats.rx_fifo_errors +#define net_tx_packets mac_net_stats.tx_packets +#define net_rx_bytes mac_net_stats.rx_bytes +#define net_tx_bytes mac_net_stats.tx_bytes +#define net_rx_over_errors mac_net_stats.rx_over_errors +#define net_tx_dropped mac_net_stats.tx_dropped; + +#define ag7240_dev_up(_dev) \ + (((_dev)->flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP)) + +typedef enum { + AG7240_RX_STATUS_DONE, + AG7240_RX_STATUS_NOT_DONE, + AG7240_RX_STATUS_OOM, + AG7240_RX_DMA_HANG +}ag7240_rx_status_t; + +/* + * This defines the interconnects between MAC and PHY at compile time + * There are several constraints - the Kconfig largely takes care of them + * at compile time. + */ +#if defined (CONFIG_AG7240_GE0_GMII) + #define AG7240_MII0_INTERFACE 0 +#elif defined (CONFIG_AG7240_GE0_MII) + #define AG7240_MII0_INTERFACE 1 +#elif defined (CONFIG_AG7240_GE0_RGMII) + #define AG7240_MII0_INTERFACE 2 +#elif defined (CONFIG_AG7240_GE0_RMII) + #define AG7240_MII0_INTERFACE 3 +#endif /*defined (AG7240_GE0_GMII)*/ + +/* + * Port 1 may or may not be connected + */ +#if defined(CONFIG_AG7240_GE1_IS_CONNECTED) + + #define AG7240_NMACS 2 + + #if defined (CONFIG_AG7240_GE1_GMII) + #define AG7240_MII1_INTERFACE 0 + #elif defined (CONFIG_AG7240_GE1_MII) + #define AG7240_MII1_INTERFACE 1 + #elif defined (CONFIG_AG7240_GE1_RGMII) + #define AG7240_MII1_INTERFACE 2 + #elif defined (CONFIG_AG7240_GE1_RMII) + #define AG7240_MII1_INTERFACE 3 + #endif /*AG7240_GE1_RGMII*/ +#else + #define AG7240_NMACS 1 + #define AG7240_MII1_INTERFACE 0xff /*not connected*/ +#endif /*AG7240_GE1_IS_CONNECTED*/ + +#define mii_reg(_mac) (AR7240_MII0_CTRL + ((_mac)->mac_unit * 4)) +#define mii_if(_mac) (((_mac)->mac_unit == 0) ? mii0_if : mii1_if) +#define phy_reg_read ag7240_mii_read +#define phy_reg_write ag7240_mii_write + +#define ag7240_set_mii_ctrl_speed(_mac, _spd) do { \ + ar7240_reg_rmw_clear(mii_reg(_mac), (3 << 4)); \ + ar7240_reg_rmw_set(mii_reg(_mac), ((_spd) << 4)); \ +}while(0); + +/* + * IP needs 16 bit alignment. But RX DMA needs 4 bit alignment. We sacrifice IP + * Plus Reserve extra head room for wmac + */ +#define ETHERNET_FCS_SIZE 4 +#define AG71XX_TX_FIFO_LEN 2048 +#define AG71XX_TX_MTU_LEN 1544 +#define AG7240_RX_RESERVE (64) +#define AG7240_RX_BUF_SIZE \ + (AG7240_RX_RESERVE + ETH_HLEN + ETH_FRAME_LEN + ETHERNET_FCS_SIZE) + + + +#define ag7240_mac_base(_no) (_no) ? AR7240_GE1_BASE : AR7240_GE0_BASE +#define ag7240_mac_irq(_no) (_no) ? AR7240_CPU_IRQ_GE1 : AR7240_CPU_IRQ_GE0 + +#define ag7240_reset_mask(_no) (_no) ? (AR7240_RESET_GE1_MAC) \ + : (AR7240_RESET_GE0_MAC) + +#define ag7240_unit2mac(_unit) ag7240_macs[(_unit)] + +#define assert(_cond) do { \ + if(!(_cond)) { \ + ag7240_trc_dump(); \ + printk("%s:%d: assertion failed\n", __func__, __LINE__); \ + BUG(); \ + } \ +}while(0); + + +/* + * Config/Mac Register definitions + */ +#define AG7240_MAC_CFG1 0x00 +#define AG7240_MAC_CFG2 0x04 +#define AG7240_MAC_IFCTL 0x38 +#define AG71XX_REG_MAC_IPG 0x0008 +#define AG71XX_REG_MAC_HDX 0x000c +#define AG71XX_REG_MAC_MFL 0x0010 + +/* + * fifo control registers + */ +#define AG7240_MAC_FIFO_CFG_0 0x48 +#define AG7240_MAC_FIFO_CFG_1 0x4c +#define AG7240_MAC_FIFO_CFG_2 0x50 +#define AG7240_MAC_FIFO_CFG_3 0x54 +#define AG7240_MAC_FIFO_CFG_4 0x58 + +#define AG7240_MAC_FIFO_CFG_5 0x5c +#define AG7240_BYTE_PER_CLK_EN (1 << 19) + +#define AG7240_MAC_FIFO_RAM_0 0x60 +#define AG7240_MAC_FIFO_RAM_1 0x64 +#define AG7240_MAC_FIFO_RAM_2 0x68 +#define AG7240_MAC_FIFO_RAM_3 0x6c +#define AG7240_MAC_FIFO_RAM_4 0x70 +#define AG7240_MAC_FIFO_RAM_5 0x74 +#define AG7240_MAC_FIFO_RAM_6 0x78 +#define AG7240_MAC_FIFO_RAM_7 0x7c + +/* + * fields + */ +#define AG7240_MAC_CFG1_SOFT_RST (1 << 31) +#define AG7240_MAC_CFG1_RX_RST (1 << 19) +#define AG7240_MAC_CFG1_TX_RST (1 << 18) +#define AG7240_MAC_CFG1_LOOPBACK (1 << 8) +#define AG7240_MAC_CFG1_RX_EN (1 << 2) +#define AG7240_MAC_CFG1_TX_EN (1 << 0) +#define AG7240_MAC_CFG1_RX_FCTL (1 << 5) +#define AG7240_MAC_CFG1_TX_FCTL (1 << 4) + + +#define AG7240_MAC_CFG2_FDX (1 << 0) +#define AG7240_MAC_CFG2_CRC_EN (1 << 1) +#define AG7240_MAC_CFG2_PAD_CRC_EN (1 << 2) +#define AG7240_MAC_CFG2_LEN_CHECK (1 << 4) +#define AG7240_MAC_CFG2_HUGE_FRAME_EN (1 << 5) +#define AG7240_MAC_CFG2_IF_1000 (1 << 9) +#define AG7240_MAC_CFG2_IF_10_100 (1 << 8) + +#define AG7240_MAC_IFCTL_SPEED (1 << 16) + +/* + * DMA (tx/rx) register defines + */ +#define AG7240_DMA_TX_CTRL 0x180 +#define AG7240_DMA_TX_DESC 0x184 +#define AG7240_DMA_TX_STATUS 0x188 +#define AG7240_DMA_RX_CTRL 0x18c +#define AG7240_DMA_RX_DESC 0x190 +#define AG7240_DMA_RX_STATUS 0x194 +#define AG7240_DMA_INTR_MASK 0x198 +#define AG7240_DMA_INTR 0x19c +#define AG7240_DMA_RXFSM 0x1b0 +#define AG7240_DMA_TXFSM 0x1b4 +#define AG7240_DMA_XFIFO_DEPTH 0x1a8 + + +/* + * DMA status mask. + */ + +#define AG7240_DMA_DMA_STATE 0x3 +#define AG7240_DMA_AHB_STATE 0x7 + +/* + * QOS register Defines + */ + +#define AG7240_DMA_TX_CTRL_Q0 0x180 +#define AG7240_DMA_TX_CTRL_Q1 0x1C0 +#define AG7240_DMA_TX_CTRL_Q2 0x1C8 +#define AG7240_DMA_TX_CTRL_Q3 0x1D0 + +#define AG7240_DMA_TX_DESC_Q0 0x184 +#define AG7240_DMA_TX_DESC_Q1 0x1C4 +#define AG7240_DMA_TX_DESC_Q2 0x1CC +#define AG7240_DMA_TX_DESC_Q3 0x1D4 + +#define AG7240_DMA_TX_ARB_CFG 0x1D8 +#define AG7240_TX_QOS_MODE_FIXED 0x0 +#define AG7240_TX_QOS_MODE_WEIGHTED 0x1 +#define AG7240_TX_QOS_WGT_0(x) ((x & 0x3F) << 8) +#define AG7240_TX_QOS_WGT_1(x) ((x & 0x3F) << 14) +#define AG7240_TX_QOS_WGT_2(x) ((x & 0x3F) << 20) +#define AG7240_TX_QOS_WGT_3(x) ((x & 0x3F) << 26) + +/* + * tx/rx ctrl and status bits + */ +#define AG7240_TXE (1 << 0) +#define AG7240_TX_STATUS_PKTCNT_SHIFT 16 +#define AG7240_TX_STATUS_PKT_SENT 0x1 +#define AG7240_TX_STATUS_URN 0x2 +#define AG7240_TX_STATUS_BUS_ERROR 0x8 + +#define AG7240_RXE (1 << 0) + +#define AG7240_RX_STATUS_PKTCNT_MASK 0xff0000 +#define AG7240_RX_STATUS_PKT_RCVD (1 << 0) +#define AG7240_RX_STATUS_OVF (1 << 2) +#define AG7240_RX_STATUS_BUS_ERROR (1 << 3) + +/* + * Int and int mask + */ +#define AG7240_INTR_TX (1 << 0) +#define AG7240_INTR_TX_URN (1 << 1) +#define AG7240_INTR_TX_BUS_ERROR (1 << 3) +#define AG7240_INTR_RX (1 << 4) +#define AG7240_INTR_RX_OVF (1 << 6) +#define AG7240_INTR_RX_BUS_ERROR (1 << 7) + +/* + * MII registers + */ +#define AG7240_MAC_MII_MGMT_CFG 0x20 +#define AG7240_MGMT_CFG_CLK_DIV_20 0x06 +#define AG7240_MII_MGMT_CMD 0x24 +#define AG7240_MGMT_CMD_READ 0x1 +#define AG7240_MII_MGMT_ADDRESS 0x28 +#define AG7240_ADDR_SHIFT 8 +#define AG7240_MII_MGMT_CTRL 0x2c +#define AG7240_MII_MGMT_STATUS 0x30 +#define AG7240_MII_MGMT_IND 0x34 +#define AG7240_MGMT_IND_BUSY (1 << 0) +#define AG7240_MGMT_IND_INVALID (1 << 2) +#define AG7240_GE_MAC_ADDR1 0x40 +#define AG7240_GE_MAC_ADDR2 0x44 +#define AG7240_MII0_CONTROL 0x18070000 + +/* + * MIB Registers + */ +#define AG7240_RX_PKT_CNTR 0xa0 +#define AG7240_TX_PKT_CNTR 0xe4 +#define AG7240_RX_BYTES_CNTR 0x9c +#define AG7240_TX_BYTES_CNTR 0xe0 +#define AG7240_RX_LEN_ERR_CNTR 0xc0 +#define AG7240_RX_OVL_ERR_CNTR 0xd0 +#define AG7240_RX_CRC_ERR_CNTR 0xa4 +#define AG7240_RX_FRM_ERR_CNTR 0xbc +#define AG7240_RX_CODE_ERR_CNTR 0xc4 +#define AG7240_RX_CRS_ERR_CNTR 0xc8 +#define AG7240_RX_DROP_CNTR 0xdc +#define AG7240_TX_DROP_CNTR 0x114 +#define AG7240_RX_MULT_CNTR 0xa8 +#define AG7240_TX_MULT_CNTR 0xe8 +#define AG7240_TOTAL_COL_CNTR 0x10c +#define AG7240_TX_CRC_ERR_CNTR 0x11c + + +#define AG7240_ETH_CFG 0x18070000 +#define AG7240_ETH_CFG_RGMII_GE0 (1<<0) +#define AG7240_ETH_CFG_MII_GE0 (1<<1) +#define AG7240_ETH_CFG_GMII_GE0 (1<<2) +#define AG7240_ETH_CFG_MII_GE0_MASTER (1<<3) +#define AG7240_ETH_CFG_MII_GE0_SLAVE (1<<4) +#define AG7240_ETH_CFG_GE0_ERR_EN (1<<5) +#define AG7240_ETH_CFG_SW_ONLY_MODE (1<<6) +#define AG7240_ETH_CFG_SW_PHY_SWAP (1<<7) +#define AG7240_ETH_CFG_SW_PHY_ADDR_SWAP (1<<8) + +/* + * Everything but TX + */ +#define AG7240_INTR_MASK (AG7240_INTR_RX | \ + AG7240_INTR_RX_BUS_ERROR | \ + AG7240_INTR_TX_BUS_ERROR \ + /*| AG7240_INTR_TX_URN | AG7240_INTR_TX*/) + +#define ag7240_reg_rd(_mac, _reg) \ + (ar7240_reg_rd((_mac)->mac_base + (_reg))) + +#define ag7240_reg_wr(_mac, _reg, _val) \ + ar7240_reg_wr((_mac)->mac_base + (_reg), (_val)); + +/* + * The no flush version + */ +#define ag7240_reg_wr_nf(_mac, _reg, _val) \ + ar7240_reg_wr_nf((_mac)->mac_base + (_reg), (_val)); + +#define ag7240_reg_rmw_set(_mac, _reg, _mask) \ + ar7240_reg_rmw_set((_mac)->mac_base + (_reg), (_mask)); + +#define ag7240_reg_rmw_clear(_mac, _reg, _mask) \ + ar7240_reg_rmw_clear((_mac)->mac_base + (_reg), (_mask)); + + +#define ag7240_desc_dma_addr(_r, _ds) \ + (u32)((ag7240_desc_t *)(_r)->ring_desc_dma + ((_ds) - ((_r)->ring_desc))) + + +/* + * tx/rx stop start + */ +#define ag7240_tx_stopped(_mac) \ + (!(ag7240_reg_rd((_mac), AG7240_DMA_TX_CTRL) & AG7240_TXE)) + +#define ag7240_rx_start(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_RX_CTRL, AG7240_RXE) + +#define ag7240_rx_stop(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_RX_CTRL, 0) + + +#define ag7240_tx_start_qos(_mac,ac) \ +switch(ac) { \ + case ENET_AC_VO: \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_CTRL_Q0, AG7240_TXE); \ + break; \ + case ENET_AC_VI: \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_CTRL_Q1, AG7240_TXE); \ + break; \ + case ENET_AC_BK: \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_CTRL_Q2, AG7240_TXE); \ + break; \ + case ENET_AC_BE: \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_CTRL_Q3, AG7240_TXE); \ + break; \ +} + + +#define ag7240_tx_start(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_CTRL, AG7240_TXE) + + +#define ag7240_tx_stop(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_CTRL, 0) + +static inline int +ag7240_ndesc_unused(ag7240_mac_t *mac, ag7240_ring_t *ring) +{ + int head = ring->ring_head, tail = ring->ring_tail; + + return ((tail > head ? 0 : ring->ring_nelem) + tail - head); +} + +static inline uint32_t +ag7240_get_diff(uint32_t t1,uint32_t t2) +{ + return (t1 > t2 ? (0xffffffff - (t1 - t2)) : t2 - t1); +} + +static inline int ag7240_rx_ring_full(ag7240_mac_t *mac) +{ + ag7240_ring_t *r = &mac->mac_rxring; + int tail = r->ring_tail; + + return ((r->ring_head == tail) && !r->ring_buffer[tail].buf_pkt); +} + +#define ag7240_ring_incr(_idx) \ + if(unlikely(++(_idx) == r->ring_nelem)) (_idx) = 0; + +/* + * ownership of descriptors between DMA and cpu + */ +#define ag7240_rx_owned_by_dma(_ds) ((_ds)->is_empty == 1) +#define ag7240_rx_give_to_dma(_ds) ((_ds)->is_empty = 1) +#define ag7240_tx_owned_by_dma(_ds) ((_ds)->is_empty == 0) +#define ag7240_tx_give_to_dma(_ds) ((_ds)->is_empty = 0) +#define ag7240_tx_own(_ds) ((_ds)->is_empty = 1) + +/* + * Interrupts + * ---------- + */ +#define ag7240_get_isr(_mac) ag7240_reg_rd((_mac), AG7240_DMA_INTR); +#define ag7240_int_enable(_mac) \ + ag7240_reg_wr(_mac, AG7240_DMA_INTR_MASK, AG7240_INTR_MASK) + +#define ag7240_int_disable(_mac) \ + ag7240_reg_wr(_mac, AG7240_DMA_INTR_MASK, 0) +/* + * ACKS: + * - We just write our bit - its write 1 to clear. + * - These are not rmw's so we dont need locking around these. + * - Txurn and rxovf are not fastpath and need consistency, so we use the flush + * version of reg write. + * - ack_rx is done every packet, and is largely only for statistical purposes; + * so we use the no flush version and later cause an explicite flush. + */ +#define ag7240_intr_ack_txurn(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_STATUS, AG7240_TX_STATUS_URN); +#define ag7240_intr_ack_rx(_mac) \ + ag7240_reg_wr_nf((_mac), AG7240_DMA_RX_STATUS, AG7240_RX_STATUS_PKT_RCVD); +#define ag7240_intr_ack_rxovf(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_RX_STATUS, AG7240_RX_STATUS_OVF); + +/* + * Not used currently + */ +#define ag7240_intr_ack_tx(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_STATUS, AG7240_TX_STATUS_PKT_SENT); +#define ag7240_intr_ack_txbe(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_TX_STATUS, AG7240_TX_STATUS_BUS_ERROR); +#define ag7240_intr_ack_rxbe(_mac) \ + ag7240_reg_wr((_mac), AG7240_DMA_RX_STATUS, AG7240_RX_STATUS_BUS_ERROR); + +/* + * Enable Disable. These are Read-Modify-Writes. Sometimes called from ISR + * sometimes not. So the caller explicitely handles locking. + */ +#define ag7240_intr_disable_txurn(_mac) \ + ag7240_reg_rmw_clear((_mac), AG7240_DMA_INTR_MASK, AG7240_INTR_TX_URN); + +#define ag7240_intr_enable_txurn(_mac) \ + ag7240_reg_rmw_set((_mac), AG7240_DMA_INTR_MASK, AG7240_INTR_TX_URN); + +#define ag7240_intr_enable_tx(_mac) \ + ag7240_reg_rmw_set((_mac), AG7240_DMA_INTR_MASK, AG7240_INTR_TX); + +#define ag7240_intr_disable_tx(_mac) \ + ag7240_reg_rmw_clear((_mac), AG7240_DMA_INTR_MASK, AG7240_INTR_TX); + +#define ag7240_intr_disable_recv(_mac) \ + ag7240_reg_rmw_clear(mac, AG7240_DMA_INTR_MASK, \ + (AG7240_INTR_RX )); + +#define ag7240_intr_enable_rxovf(_mac) \ + ag7240_reg_rmw_set((_mac), AG7240_DMA_INTR_MASK, AG7240_INTR_RX_OVF); + +#define ag7240_intr_disable_rxovf(_mac) \ + ag7240_reg_rmw_clear(mac, AG7240_DMA_INTR_MASK, \ + (AG7240_INTR_RX_OVF)); + + +#define ag7240_intr_enable_recv(_mac) \ + ag7240_reg_rmw_set(mac, AG7240_DMA_INTR_MASK, \ + (AG7240_INTR_RX | AG7240_INTR_RX_OVF)); + +/* + * link settings + */ +static inline void ag7240_set_mac_duplex(ag7240_mac_t *mac, int fdx) +{ + if (fdx) { + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG2, AG7240_MAC_CFG2_FDX); + } + else { + ag7240_reg_rmw_clear(mac, AG7240_MAC_CFG2, AG7240_MAC_CFG2_FDX); + } +} + +static inline void ag7240_set_mac_if(ag7240_mac_t *mac, int is_1000) +{ + ag7240_reg_rmw_clear(mac, AG7240_MAC_CFG2, (AG7240_MAC_CFG2_IF_1000| + AG7240_MAC_CFG2_IF_10_100)); + if (is_1000) { + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG2, AG7240_MAC_CFG2_IF_1000); + ag7240_reg_rmw_set(mac, AG7240_MAC_FIFO_CFG_5, AG7240_BYTE_PER_CLK_EN); + } + else { + ag7240_reg_rmw_set(mac, AG7240_MAC_CFG2, AG7240_MAC_CFG2_IF_10_100); + ag7240_reg_rmw_clear(mac,AG7240_MAC_FIFO_CFG_5, AG7240_BYTE_PER_CLK_EN); + } +} + +static inline void ag7240_set_mac_speed(ag7240_mac_t *mac, int is100) +{ + if (is100) { + ag7240_reg_rmw_set(mac, AG7240_MAC_IFCTL, AG7240_MAC_IFCTL_SPEED); + } + else { + ag7240_reg_rmw_clear(mac, AG7240_MAC_IFCTL, AG7240_MAC_IFCTL_SPEED); + } +} + +uint16_t ag7240_mii_read(int unit, uint32_t phy_addr, uint8_t reg); +void ag7240_mii_write(int unit, uint32_t phy_addr, uint8_t reg, uint16_t data); +unsigned int s26_rd_phy(unsigned int phy_addr, unsigned int reg_addr); +int ag7240_check_link(ag7240_mac_t *mac,int phyUnit); +int ag7242_check_link(ag7240_mac_t *mac); + +#define CHECK_DMA_STATUS 0x0001 +#define ETH_SOFT_LED 0x0002 +#define WAN_QOS_SOFT_CLASS 0x0004 +#define ETH_SWONLY_MODE 0x0008 +#define ATHR_S26_HEADER 0x0010 +#define ATHR_S16_HEADER 0x0020 +#define ETH_PKT_INSPECT 0x0040 + +static inline int +mac_has_flag(ag7240_mac_t *mac, u_int16_t flag) +{ + return ((mac->mac_flags & flag) != 0); +} + +static inline void +mac_set_flag(ag7240_mac_t *mac, u_int16_t flag) +{ + mac->mac_flags |= flag; + return; + +} + +static inline void +mac_clear_flag(ag7240_mac_t *mac, u_int16_t flag) +{ + mac->mac_flags &= ~flag; + return; +} + +/** + * Added for customizing LED control operations + */ + +typedef struct { + u_int32_t rate; // Rate per second + u_int32_t timeOn; // LED ON time in ms + u_int32_t timeOff; // LED OFF time in ms +} LED_BLINK_RATES; + +typedef struct eth_led_control { + uint8_t ledlink[5]; // current link state + ag7240_phy_speed_t speed[5]; // current link speed + struct timer_list led_timer; +} ATH_LED_CONTROL; + + +#define MB(x) ((x * 100000) / 8) /* Mbits/sec */ +static const +LED_BLINK_RATES BlinkRateTable_100M[] = { + { MB(1), 5, 5 }, /* on:168ms off:670ms */ + { MB(5), 4, 3 }, /* on:84ms off:168ms */ + { MB(10), 2, 2 }, /* on:21ms off:84ms */ + { MB(50), 2, 1 }, /* on:21ms off:42ms */ + { MB(100), 1, 1 }, /* on:10ms off:42ms */ + { 0xffffffff, 0 , 1 } +}; + +static const +LED_BLINK_RATES BlinkRateTable_10M[] = { + { MB(1), 5, 5 }, /* on:168ms off:670ms */ + { MB(3), 4, 3 }, /* on:84ms off:168ms */ + { MB(5), 2, 2 }, /* on:21ms off:84ms */ + { MB(7), 2, 1 }, /* on:21ms off:42ms */ + { MB(10), 1, 1 }, /* on:10ms off:42ms */ + { 0xffffffff, 0 , 1 } +}; + +#endif diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240_phy.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240_phy.h new file mode 100644 index 0000000..1ab022a --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240_phy.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _AG7240_PHY_H +#define _AG7240_PHY_H + +#include "ag7240.h" +#include "athrs_ioctl.h" + +#ifdef CONFIG_AR7240_S26_PHY + +#include "ar7240_s26_phy.h" +#ifdef CONFIG_AR7242_RGMII_PHY +#include "athrf1_phy.h" +#endif +#ifdef CONFIG_AR7242_S16_PHY +#include "athrs16_phy.h" +#endif + +#ifdef CONFIG_AR7242_RTL8309G_PHY +#include "rtl8309g_phy.h" +#endif +extern ag7240_mac_t *ag7240_macs[2]; +#define ag7240_phy_is_up(unit) athrs26_phy_is_up (unit) +#define ag7240_phy_speed(unit,phyUnit) athrs26_phy_speed (unit,phyUnit) +#define ag7240_phy_is_fdx(unit,phyUnit) athrs26_phy_is_fdx(unit,phyUnit) +#define ag7240_phy_is_lan_pkt athr_is_lan_pkt +#define ag7240_phy_set_pkt_port athr_set_pkt_port +#define ag7240_phy_tag_len ATHR_VLAN_TAG_SIZE +#define ag7240_phy_get_counters athrs26_get_counters + +static inline void athrs_reg_dev(ag7240_mac_t **ag7240_macs) +{ +#if defined(CONFIG_AR7242_S16_PHY) + if (is_ar7242()) + athrs16_reg_dev(ag7240_macs); +#endif + athrs26_reg_dev(ag7240_macs); + + return ; + +} + +static inline int athrs_do_ioctl(struct net_device *dev,struct ifreq *ifr, int cmd) +{ + ag7240_mac_t *mac = (ag7240_mac_t *)netdev_priv(dev); + int ret = -1; + + if (is_ar7240() || mac->mac_unit == 1) + ret = athrs26_ioctl(dev,ifr, cmd); +#ifdef CONFIG_AR7242_S16_PHY + else if(is_ar7242()) + ret = athrs16_ioctl(ifr->ifr_data, cmd); +#endif +#ifdef CONFIG_ATHRS_QOS + if(ret < 0) + ret = athrs_config_qos(ifr->ifr_data,cmd); +#endif + return ret; +} + +static inline void ag7240_phy_reg_init(int unit) +{ +#ifdef CONFIG_AR7242_RTL8309G_PHY + if (unit == 0 && is_ar7242()) + rtl8309g_reg_init(unit); + else +#else +#ifndef CONFIG_AR7242_S16_PHY + if (unit == 0) + athrs26_reg_init(unit); +#else + if (unit == 0 && is_ar7242()) + athrs16_reg_init(unit); +#endif + else +#endif + athrs26_reg_init_lan(unit); +} + +static inline void ag7240_phy_setup(int unit) +{ + +#ifdef CONFIG_AR7242_RTL8309G_PHY + if (is_ar7242() && (unit==0)) { + rtl8309g_phy_setup(unit); + } else +#endif + if (is_ar7241() || is_ar7240() || is_ar933x()) + athrs26_phy_setup (unit); + else if (is_ar7242() && unit == 1) + athrs26_phy_setup (unit); +#ifdef CONFIG_AR7242_RGMII_PHY + else if (is_ar7242() && unit == 0) + athr_phy_setup(unit); +#endif +#ifdef CONFIG_AR7242_S16_PHY + else if (is_ar7242() && unit == 0) + athrs16_phy_setup(unit); +#endif +} + +static inline unsigned int +ag7240_get_link_status(int unit, int *link, int *fdx, ag7240_phy_speed_t *speed,int phyUnit) +{ + +#ifdef CONFIG_AR7242_RTL8309G_PHY + if (is_ar7242() && (unit==0)) { + *link=rtl8309g_phy_is_up(unit); + *fdx=rtl8309g_phy_is_fdx(unit); + *speed=rtl8309g_phy_speed(unit); + } + else +#endif + + if (is_ar7240() || is_ar7241() || (is_ar7242() && unit == 1) || is_ar933x()) { + *link=ag7240_phy_is_up(unit); + *fdx=ag7240_phy_is_fdx(unit, phyUnit); + *speed=ag7240_phy_speed(unit, phyUnit); + } +#ifdef CONFIG_AR7242_RGMII_PHY + else if(is_ar7242() && unit == 0){ + *link=athr_phy_is_up(unit); + *fdx=athr_phy_is_fdx(unit,phyUnit); + *speed=athr_phy_speed(unit,phyUnit); + } +#endif +#ifdef CONFIG_AR7242_VIR_PHY + else if(is_ar7242() && unit == 0){ + *link=athr_vir_phy_is_up(unit); + *fdx=athr_vir_phy_is_fdx(unit); + *speed=athr_vir_phy_speed(unit); + } +#endif + +#ifdef CONFIG_AR7242_S16_PHY + else if(is_ar7242() && unit == 0){ + *link=athrs16_phy_is_up(unit); + *fdx=athrs16_phy_is_fdx(unit); + *speed=athrs16_phy_speed(unit); + } +#endif + return 0; +} + +static inline int +ag7240_print_link_status(int unit) +{ + return -1; +} +#else +#error unknown PHY type PHY not configured in config.h +#endif + +#endif + diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240_trc.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240_trc.h new file mode 100644 index 0000000..f636af5 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ag7240_trc.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _AG7240_TRC_h +#define _AG7240_TRC_h + +#define TRC_SIZE 256 + +typedef struct { + u32 unit; + int line; + u32 val; + char comment[32]; + +}ag7240_trc_entry_t; + +typedef struct { + int cur; + ag7240_trc_entry_t entry[TRC_SIZE]; +}ag7240_trc_t; + +#ifdef CONFIG_AG7240_USE_TRC + +#define ag7240_trc_init() do { \ + int i; \ + printk(MODULE_NAME": TRACE ENABLED\n"); \ + mac->tb.cur = 0; \ + for(i = 0; i < TRC_SIZE; i++) { \ + mac->tb.entry[i].line = 0; \ + mac->tb.entry[i].val = 0x7f; \ + mac->tb.entry[i].comment[0] = '\0'; \ + } \ +}while(0); + +#define ag7240_trc_new(_x,_y) do { \ + unsigned long flags; \ + spin_lock_irqsave(&mac->mac_lock, flags); \ + mac->tb.entry[mac->tb.cur].unit = mac->mac_unit; \ + mac->tb.entry[mac->tb.cur].line = __LINE__; \ + mac->tb.entry[mac->tb.cur].val = (u32)(_x); \ + if (_y) \ + strncpy(mac->tb.entry[mac->tb.cur].comment, (_y), sizeof(mac->tb.entry[mac->tb.cur].comment)); \ + else \ + mac->tb.entry[mac->tb.cur].comment[0] = '\0'; \ + if (mac->tb.cur == (TRC_SIZE - 1)) \ + mac->tb.cur = 0; \ + else \ + mac->tb.cur ++; \ + spin_unlock_irqrestore(&mac->mac_lock, flags); \ +}while(0); + +#define ag7240_trc ag7240_trc_new + +#define ag7240_trc_dump() do { \ + unsigned long flags; \ + spin_lock_irqsave(&mac->mac_lock, flags); \ + int i, cur = mac->tb.cur; \ + printk(MODULE_NAME": head %d tail %d\n", mac->mac_txring[0].ring_head, mac->mac_txring[0].ring_tail);\ + for(i = 0; i < TRC_SIZE; i++) { \ + if (mac->tb.entry[cur].line) { \ + printk("%d %d %08x %s\n", \ + mac->tb.entry[cur].unit, \ + mac->tb.entry[cur].line, \ + mac->tb.entry[cur].val, \ + mac->tb.entry[cur].comment); \ + mac->tb.entry[cur].line = 0; \ + } \ + if (cur == (TRC_SIZE - 1)) \ + cur = 0; \ + else \ + cur++ ; \ + } \ + spin_unlock_irqrestore(&mac->mac_lock, flags); \ +}while(0); + +#else + +#define ag7240_trc_init() +#define ag7240_trc_new(_x,_y) +#define ag7240_trc(_x, _y) +#define ag7240_trc_dump() + +#endif + +#endif + diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240.h new file mode 100644 index 0000000..ad84511 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240.h @@ -0,0 +1,984 @@ +#ifndef _AR7240_H +#define _AR7240_H + +#include + +#ifdef CONFIG_MACH_HORNET +#include "ar933x.h" +#endif + +typedef unsigned int ar7240_reg_t; + +#define ar7240_reg_rd(_phys) (*(volatile ar7240_reg_t *)KSEG1ADDR(_phys)) +#define ar7240_reg_wr_nf(_phys, _val) \ + ((*(volatile ar7240_reg_t *)KSEG1ADDR(_phys)) = (_val)) + +#define ar7240_reg_wr(_phys, _val) do { \ + ar7240_reg_wr_nf(_phys, _val); \ + ar7240_reg_rd(_phys); \ +}while(0); + +#define ar7240_reg_rmw_set(_reg, _mask) do { \ + ar7240_reg_wr((_reg), (ar7240_reg_rd((_reg)) | (_mask))); \ + ar7240_reg_rd((_reg)); \ +}while(0); + +#define ar7240_reg_rmw_clear(_reg, _mask) do { \ + ar7240_reg_wr((_reg), (ar7240_reg_rd((_reg)) & ~(_mask))); \ + ar7240_reg_rd((_reg)); \ +}while(0); + +/* + * Address map + */ +#define AR7240_PCI_MEM_BASE 0x10000000 /* 128M */ +#define AR7240_APB_BASE 0x18000000 /* 384M */ +#define AR7240_GE0_BASE 0x19000000 /* 16M */ +#define AR7240_GE1_BASE 0x1a000000 /* 16M */ +#define AR7240_USB_OHCI_BASE 0x1b000000 +#define AR7240_USB_EHCI_BASE 0x1b000000 +#define AR7240_SPI_BASE 0x1f000000 + +/* + * Added the PCI LCL RESET register from u-boot + * ar7240_soc.h so that we can query the PCI LCL RESET + * register for the presence of WLAN H/W. + */ +#define AR7240_PCI_LCL_BASE (AR7240_APB_BASE+0x000f0000) +#define AR7240_PCI_LCL_APP (AR7240_PCI_LCL_BASE+0x00) +#define AR7240_PCI_LCL_RESET (AR7240_PCI_LCL_BASE+0x18) + +/* + * APB block + */ +#define AR7240_DDR_CTL_BASE AR7240_APB_BASE+0x00000000 +#define AR7240_CPU_BASE AR7240_APB_BASE+0x00010000 +#define AR7240_UART_BASE AR7240_APB_BASE+0x00020000 +#define AR7240_USB_CONFIG_BASE AR7240_APB_BASE+0x00030000 +#define AR7240_GPIO_BASE AR7240_APB_BASE+0x00040000 +#define AR7240_PLL_BASE AR7240_APB_BASE+0x00050000 +#define AR7240_RESET_BASE AR7240_APB_BASE+0x00060000 +#ifdef CONFIG_WASP_SUPPORT +#define AR7240_SLIC_BASE AR7240_APB_BASE+0x000A9000 +#else +#define AR7240_SLIC_BASE AR7240_APB_BASE+0x00090000 +#endif +#define AR7240_DMA_BASE AR7240_APB_BASE+0x000A0000 +#define AR7240_STEREO_BASE AR7240_APB_BASE+0x000B0000 +#define AR7240_PCI_CTLR_BASE AR7240_APB_BASE+0x000F0000 + +/* + * DDR block + */ +#define AR7240_DDR_CONFIG AR7240_DDR_CTL_BASE+0 +#define AR7240_DDR_CONFIG2 AR7240_DDR_CTL_BASE+4 +#define AR7240_DDR_MODE AR7240_DDR_CTL_BASE+0x08 +#define AR7240_DDR_EXT_MODE AR7240_DDR_CTL_BASE+0x0c +#define AR7240_DDR_CONTROL AR7240_DDR_CTL_BASE+0x10 +#define AR7240_DDR_REFRESH AR7240_DDR_CTL_BASE+0x14 +#define AR7240_DDR_RD_DATA_THIS_CYCLE AR7240_DDR_CTL_BASE+0x18 +#define AR7240_DDR_TAP_CONTROL0 AR7240_DDR_CTL_BASE+0x1c +#define AR7240_DDR_TAP_CONTROL1 AR7240_DDR_CTL_BASE+0x20 +#define AR7240_DDR_TAP_CONTROL2 AR7240_DDR_CTL_BASE+0x24 +#define AR7240_DDR_TAP_CONTROL3 AR7240_DDR_CTL_BASE+0x28 + +/* + * DDR Config values + */ +#define AR7240_DDR_CONFIG_16BIT (1 << 31) +#define AR7240_DDR_CONFIG_PAGE_OPEN (1 << 30) +#define AR7240_DDR_CONFIG_CAS_LAT_SHIFT 27 +#define AR7240_DDR_CONFIG_TMRD_SHIFT 23 +#define AR7240_DDR_CONFIG_TRFC_SHIFT 17 +#define AR7240_DDR_CONFIG_TRRD_SHIFT 13 +#define AR7240_DDR_CONFIG_TRP_SHIFT 9 +#define AR7240_DDR_CONFIG_TRCD_SHIFT 5 +#define AR7240_DDR_CONFIG_TRAS_SHIFT 0 + +#define AR7240_DDR_CONFIG2_BL2 (2 << 0) +#define AR7240_DDR_CONFIG2_BL4 (4 << 0) +#define AR7240_DDR_CONFIG2_BL8 (8 << 0) + +#define AR7240_DDR_CONFIG2_BT_IL (1 << 4) +#define AR7240_DDR_CONFIG2_CNTL_OE_EN (1 << 5) +#define AR7240_DDR_CONFIG2_PHASE_SEL (1 << 6) +#define AR7240_DDR_CONFIG2_DRAM_CKE (1 << 7) +#define AR7240_DDR_CONFIG2_TWR_SHIFT 8 +#define AR7240_DDR_CONFIG2_TRTW_SHIFT 12 +#define AR7240_DDR_CONFIG2_TRTP_SHIFT 17 +#define AR7240_DDR_CONFIG2_TWTR_SHIFT 21 +#define AR7240_DDR_CONFIG2_HALF_WIDTH_L (1 << 31) + +#define AR7240_DDR_TAP_DEFAULT 0x18 + +/* + * DDR block, gmac flushing + */ +#ifdef CONFIG_WASP_SUPPORT +#define AR7240_DDR_GE0_FLUSH AR7240_DDR_CTL_BASE+0x9c +#define AR7240_DDR_GE1_FLUSH AR7240_DDR_CTL_BASE+0xa0 +#define AR7240_DDR_USB_FLUSH AR7240_DDR_CTL_BASE+0xa4 +#define AR7240_DDR_PCIE_FLUSH AR7240_DDR_CTL_BASE+0xa8 +#else +#define AR7240_DDR_GE0_FLUSH AR7240_DDR_CTL_BASE+0x7c +#define AR7240_DDR_GE1_FLUSH AR7240_DDR_CTL_BASE+0x80 +#define AR7240_DDR_USB_FLUSH AR7240_DDR_CTL_BASE+0xa4 +#define AR7240_DDR_PCIE_FLUSH AR7240_DDR_CTL_BASE+0x88 +#endif + + + +#define AR7240_EEPROM_GE0_MAC_ADDR 0xbfff1000 +#define AR7240_EEPROM_GE1_MAC_ADDR 0xbfff1006 + +/* + * PLL block/CPU + */ + +#define AR7240_PLL_CONFIG AR7240_PLL_BASE+0x0 + + +#define PLL_DIV_SHIFT 0 +#define PLL_DIV_MASK 0x3ff +#define REF_DIV_SHIFT 10 +#define REF_DIV_MASK 0xf +#define AHB_DIV_SHIFT 19 +#define AHB_DIV_MASK 0x1 +#define DDR_DIV_SHIFT 22 +#define DDR_DIV_MASK 0x1 + +#ifdef CONFIG_WASP_SUPPORT +#define AR7240_DDR_PLL_CONFIG AR7240_PLL_BASE+0x4 +#define AR7240_DDR_CLK_CTRL AR7240_PLL_BASE+0x8 +#else +#define AR7240_ETH_PLL_CONFIG AR7240_PLL_BASE+0x4 +#endif +#define AR7242_ETH_XMII_CONFIG AR7240_PLL_BASE+0x2c + +#define AR7240_ETH_INT0_CLK AR7240_PLL_BASE+0x14 +#define AR7240_ETH_INT1_CLK AR7240_PLL_BASE+0x18 + + +/* + * USB block + */ +#define AR7240_USB_FLADJ_VAL AR7240_USB_CONFIG_BASE +#define AR7240_USB_CONFIG AR7240_USB_CONFIG_BASE+0x4 +#define AR7240_USB_WINDOW 0x1000000 +#define AR7240_USB_MODE AR7240_USB_EHCI_BASE+0x1a8 + +/* + * PCI block + */ +#define AR7240_PCI_WINDOW 0x4000000 /* 128MB */ +#define AR7240_PCI_WINDOW0_OFFSET AR7240_DDR_CTL_BASE+0x7c +#define AR7240_PCI_WINDOW1_OFFSET AR7240_DDR_CTL_BASE+0x80 +#define AR7240_PCI_WINDOW2_OFFSET AR7240_DDR_CTL_BASE+0x84 +#define AR7240_PCI_WINDOW3_OFFSET AR7240_DDR_CTL_BASE+0x88 +#define AR7240_PCI_WINDOW4_OFFSET AR7240_DDR_CTL_BASE+0x8c +#define AR7240_PCI_WINDOW5_OFFSET AR7240_DDR_CTL_BASE+0x90 +#define AR7240_PCI_WINDOW6_OFFSET AR7240_DDR_CTL_BASE+0x94 +#define AR7240_PCI_WINDOW7_OFFSET AR7240_DDR_CTL_BASE+0x98 + +#define AR7240_PCI_WINDOW0_VAL 0x10000000 +#define AR7240_PCI_WINDOW1_VAL 0x11000000 +#define AR7240_PCI_WINDOW2_VAL 0x12000000 +#define AR7240_PCI_WINDOW3_VAL 0x13000000 +#define AR7240_PCI_WINDOW4_VAL 0x14000000 +#define AR7240_PCI_WINDOW5_VAL 0x15000000 +#define AR7240_PCI_WINDOW6_VAL 0x16000000 +#define AR7240_PCI_WINDOW7_VAL 0x07000000 + +#define ar7240_write_pci_window(_no) \ + ar7240_reg_wr(AR7240_PCI_WINDOW##_no##_OFFSET, AR7240_PCI_WINDOW##_no##_VAL); + +/* + * CRP. To access the host controller config and status registers + */ +#define AR7240_PCI_CRP 0x180c0000 +#define AR7240_PCI_DEV_CFGBASE 0x14000000 +#define AR7240_PCI_CRP_AD_CBE AR7240_PCI_CRP +#define AR7240_PCI_CRP_WRDATA AR7240_PCI_CRP+0x4 +#define AR7240_PCI_CRP_RDDATA AR7240_PCI_CRP+0x8 +#define AR7240_PCI_ERROR AR7240_PCI_CRP+0x1c +#define AR7240_PCI_ERROR_ADDRESS AR7240_PCI_CRP+0x20 +#define AR7240_PCI_AHB_ERROR AR7240_PCI_CRP+0x24 +#define AR7240_PCI_AHB_ERROR_ADDRESS AR7240_PCI_CRP+0x28 + +#define AR7240_CRP_CMD_WRITE 0x00010000 +#define AR7240_CRP_CMD_READ 0x00000000 + +/* + * PCI CFG. To generate config cycles + */ +#define AR7240_PCI_CFG_AD AR7240_PCI_CRP+0xc +#define AR7240_PCI_CFG_CBE AR7240_PCI_CRP+0x10 +#define AR7240_PCI_CFG_WRDATA AR7240_PCI_CRP+0x14 +#define AR7240_PCI_CFG_RDDATA AR7240_PCI_CRP+0x18 +#define AR7240_CFG_CMD_READ 0x0000000a +#define AR7240_CFG_CMD_WRITE 0x0000000b + +#define AR7240_PCI_IDSEL_ADLINE_START 17 + + +/* + * gpio configs + */ +#define AR7240_GPIO_OE AR7240_GPIO_BASE+0x0 +#define AR7240_GPIO_IN AR7240_GPIO_BASE+0x4 +#define AR7240_GPIO_OUT AR7240_GPIO_BASE+0x8 +#define AR7240_GPIO_SET AR7240_GPIO_BASE+0xc +#define AR7240_GPIO_CLEAR AR7240_GPIO_BASE+0x10 +#define AR7240_GPIO_INT_ENABLE AR7240_GPIO_BASE+0x14 +#define AR7240_GPIO_INT_TYPE AR7240_GPIO_BASE+0x18 +#define AR7240_GPIO_INT_POLARITY AR7240_GPIO_BASE+0x1c +#define AR7240_GPIO_INT_PENDING AR7240_GPIO_BASE+0x20 +#define AR7240_GPIO_INT_MASK AR7240_GPIO_BASE+0x24 +#ifdef CONFIG_WASP_SUPPORT +// bit meanings have changed... +#define AR7240_GPIO_FUNCTIONS AR7240_GPIO_BASE+0x6c +#else +#define AR7240_GPIO_FUNCTIONS AR7240_GPIO_BASE+0x28 +#define AR7240_GPIO_FUNCTION_2 AR7240_GPIO_BASE+0x30 +#define AR7240_GPIO_FUNC_ZERO AR7240_GPIO_BASE+0x30 +#endif + +/* + * IRQ Map. + * There are 4 conceptual ICs in the system. We generally give a block of 16 + * irqs to each IC. + * CPU: 0 - 0xf + * MISC: 0x10 - 0x1f + * GPIO: 0x20 - 0x2f + * PCI : 0x30 - 0x40 + * + */ +#define AR7240_CPU_IRQ_BASE 0x00 +#define AR7240_MISC_IRQ_BASE 8 +#define AR7240_MISC_IRQ_COUNT 13 +#define AR7240_GPIO_IRQ_BASE 40 +#define AR7240_GPIO_IRQ_COUNT 32 +#define AR7240_PCI_IRQ_BASE 72 +#define AR7240_PCI_IRQ_COUNT 6 + + +/* + * The IPs. Connected to CPU (hardware IP's; the first two are software) + */ +#ifdef CONFIG_WASP_SUPPORT +#define ATH_CPU_IRQ_WLAN AR7240_CPU_IRQ_BASE+2 +#define AR7240_CPU_IRQ_PCI AR7240_CPU_IRQ_BASE+8 +#elif defined (CONFIG_MACH_HORNET) +#define ATH_CPU_IRQ_WLAN AR7240_CPU_IRQ_BASE+2 +#else +#define AR7240_CPU_IRQ_PCI AR7240_CPU_IRQ_BASE+2 +#endif +#define AR7240_CPU_IRQ_USB AR7240_CPU_IRQ_BASE+3 +#define AR7240_CPU_IRQ_GE0 AR7240_CPU_IRQ_BASE+4 +#define AR7240_CPU_IRQ_GE1 AR7240_CPU_IRQ_BASE+5 +#define AR7240_CPU_IRQ_MISC AR7240_CPU_IRQ_BASE+6 +#define AR7240_CPU_IRQ_TIMER AR7240_CPU_IRQ_BASE+7 + +/* + * Interrupts connected to the CPU->Misc line. + */ +#define AR7240_MISC_IRQ_TIMER AR7240_MISC_IRQ_BASE+0 +#define AR7240_MISC_IRQ_ERROR AR7240_MISC_IRQ_BASE+1 +#define AR7240_MISC_IRQ_GPIO AR7240_MISC_IRQ_BASE+2 +#define AR7240_MISC_IRQ_UART AR7240_MISC_IRQ_BASE+3 +#define AR7240_MISC_IRQ_WATCHDOG AR7240_MISC_IRQ_BASE+4 +#define AR7240_MISC_IRQ_PERF_COUNTER AR7240_MISC_IRQ_BASE+5 +#define AR7240_MISC_IRQ_USB_OHCI AR7240_MISC_IRQ_BASE+6 +#define AR7240_MISC_IRQ_DMA AR7240_MISC_IRQ_BASE+7 +#define AR7240_MISC_IRQ_ENET_LINK AR7240_MISC_IRQ_BASE+12 +#define AR7240_MISC_IRQ_NAT_AGER AR7240_MISC_IRQ_BASE+13 + +#define MIMR_TIMER 0x01 +#define MIMR_ERROR 0x02 +#define MIMR_GPIO 0x04 +#define MIMR_UART 0x08 +#define MIMR_WATCHDOG 0x10 +#define MIMR_PERF_COUNTER 0x20 +#define MIMR_OHCI_USB 0x40 +#define MIMR_DMA 0x80 +#define MIMR_ENET_LINK 0x1000 +#define MIMR_NAT_AGER 0x2000 + +#define MISR_TIMER MIMR_TIMER +#define MISR_ERROR MIMR_ERROR +#define MISR_GPIO MIMR_GPIO +#define MISR_UART MIMR_UART +#define MISR_WATCHDOG MIMR_WATCHDOG +#define MISR_PERF_COUNTER MIMR_PERF_COUNTER +#define MISR_OHCI_USB MIMR_OHCI_USB +#define MISR_DMA MIMR_DMA + +/* + * Interrupts connected to the Misc->GPIO line + */ +#define AR7240_GPIO_IRQn(_gpio) AR7240_GPIO_IRQ_BASE+(_gpio) + +void ar7240_gpio_irq_init(int irq_base); + +void ar7240_misc_enable_irq (unsigned int mask); +void ar7240_misc_disable_irq (unsigned int mask); + +unsigned int ar7240_misc_get_irq_mask (void); +unsigned int ar7240_misc_get_irq_status (void); + + +/* Interrupts connected to CPU->PCI */ +#ifdef CONFIG_PERICOM +# define AR7240_PRI_BUS_NO 0u +# define AR7240_PORT0_BUS_NO 1u +# define AR7240_PORT1_BUS_NO 2u +# define AR7240_PCI_IRQ_DEV0 (AR7240_PCI_IRQ_BASE + 0) +# define AR7240_PCI_IRQ_DEV1 (AR7240_PCI_IRQ_BASE + 1) +# define AR7240_PCI_IRQ_COUNT 2 +#else +# define AR7240_PCI_IRQ_DEV0 AR7240_PCI_IRQ_BASE+0 +#endif /* CONFIG_PERICOM */ + +/* + * PCI interrupt mask and status + */ +#define PIMR_DEV0 0x01 +#define PIMR_DEV1 0x02 +#define PIMR_DEV2 0x04 +#define PIMR_CORE 0x10 + +#define PISR_DEV0 PIMR_DEV0 +#define PISR_DEV1 PIMR_DEV1 +#define PISR_DEV2 PIMR_DEV2 +#define PISR_CORE PIMR_CORE + +void ar7240_pci_irq_init(int irq_base); /* ??? */ + + +#define AR7240_GPIO_COUNT 32 + +/* + * GPIO Function Enables + */ +#define AR7240_GPIO_FUNCTION_STEREO_EN (1<<17) +#define AR7240_GPIO_FUNCTION_SLIC_EN (1<<16) +#define AR7240_GPIO_FUNCTION_SPI_CS_1_EN (1<<15) +#define AR7240_GPIO_FUNCTION_SPI_CS_0_EN (1<<14) +#define AR7240_GPIO_FUNCTION_UART_EN (1<< 8) +#define AR7240_GPIO_FUNCTION_OVERCURRENT_EN (1<< 4) +#define AR7240_GPIO_FUNCTION_USB_CLK_CORE_EN (1<< 0) +#define AR7240_GPIO_FUNCTION_WMAC_LED (1<<22) + +/* + * GPIO Access & Control + */ +void ar7240_gpio_init(void); +void ar7240_gpio_down(void); +void ar7240_gpio_up(void); + +/* + * GPIO Helper Functions + */ +void ar7240_gpio_enable_slic(void); + +/* enable UART block, takes away GPIO 10 and 9 */ +void ar7240_gpio_enable_uart(void); + +/* enable STEREO block, takes away GPIO 11,8,7, and 6 */ +void ar7240_gpio_enable_stereo(void); + +/* allow CS0/CS1 to be controlled via SPI register, takes away GPIO0/GPIO1 */ +void ar7240_gpio_enable_spi_cs1_cs0(void); + +/* allow GPIO0/GPIO1 to be used as SCL/SDA for software based i2c */ +void ar7240_gpio_enable_i2c_on_gpio_0_1(void); + +/* + * GPIO General Functions + */ +void ar7240_gpio_drive_low(unsigned int mask); +void ar7240_gpio_drive_high(unsigned int mask); + +unsigned int ar7240_gpio_float_high_test(unsigned int mask); + +/* + * Software support of i2c on gpio 0/1 + */ +int ar7240_i2c_raw_write_bytes_to_addr(int addr, unsigned char *buffer, int count); +int ar7240_i2c_raw_read_bytes_from_addr(int addr, unsigned char *buffer, int count); + +/* SPI, SLIC and GPIO are all multiplexed on gpio pins */ +#define AR7240_SPI_FS AR7240_SPI_BASE +#define AR7240_SPI_READ AR7240_SPI_BASE +#define AR7240_SPI_CLOCK AR7240_SPI_BASE+4 +#define AR7240_SPI_WRITE AR7240_SPI_BASE+8 +#define AR7240_SPI_RD_STATUS AR7240_SPI_BASE+12 +#define AR7240_SPI_D0_HIGH (1<<0) /* Pin spi_do */ +#define AR7240_SPI_CLK_HIGH (1<<8) /* Pin spi_clk */ + +#define AR7240_SPI_CS_ENABLE_0 (6<<16) /* Pin gpio/cs0 (active low) */ +#define AR7240_SPI_CS_ENABLE_1 (5<<16) /* Pin gpio/cs1 (active low) */ +#define AR7240_SPI_CS_ENABLE_2 (3<<16) /* Pin gpio/cs2 (active low) */ +//#define AR7240_SPI_CS_DIS (AR7240_SPI_CS_ENABLE_0|AR7240_SPI_CS_ENABLE_1|AR7240_SPI_CS_ENABLE_2) +#define AR7240_SPI_CS_DIS 0x70000 + + +#define AR7240_SPI_RD_STATUS AR7240_SPI_BASE+12 /* spi_di is clocked into register pos 0 every clock */ +/* + * SOC + */ +#define AR7240_SPI_CMD_WREN 0x06 +#define AR7240_SPI_CMD_RD_STATUS 0x05 +#define AR7240_SPI_CMD_FAST_READ 0x0b +#define AR7240_SPI_CMD_PAGE_PROG 0x02 +#define AR7240_SPI_CMD_SECTOR_ERASE 0xd8 + +/* Functions to access SPI through software. Example: + * + * ar7240_spi_down(); ---------------------- disable others from accessing SPI bus taking semaphore + * ar7240_spi_enable_soft_access(); -------- disable HW control of SPI + * + * + * + * + * + * + * + * ar7240_spi_disable_soft_acess(); ------- enable HW control of SPI bus + * ar7240_spi_up(); ----------------------- enable others to access SPI bus releasing semaphore + */ +void ar7240_spi_init(void); +void ar7240_spi_down(void); +void ar7240_spi_up(void); + +static inline void +ar7240_spi_enable_soft_access(void) +{ + ar7240_reg_wr_nf(AR7240_SPI_FS, 1); +} + +static inline void +ar7240_spi_disable_soft_access(void) +{ + ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_DIS); + ar7240_reg_wr_nf(AR7240_SPI_FS, 0); +} + +void ar7240_spi_raw_output_u8(unsigned char val); +void ar7240_spi_raw_output_u32(unsigned int val); +unsigned int ar7240_spi_raw_input_u32(void); + +#define AR7240_SPI_SECTOR_SIZE (1024*64) + +void ar7240_spi_flash_read_page(unsigned int addr, unsigned char *data, int len); +void ar7240_spi_flash_write_page(unsigned int addr, unsigned char *data, int len); +void ar7240_spi_flash_sector_erase(unsigned int addr); + +/* + * Allow access to cs0-2 when GPIO Function enables cs0-2 through SPI register. + */ +static inline void ar7240_spi_enable_cs0(void) +{ + unsigned int cs; + ar7240_spi_down(); + ar7240_spi_enable_soft_access(); + cs = ar7240_reg_rd(AR7240_SPI_WRITE) & ~AR7240_SPI_CS_DIS; + ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_ENABLE_0 | cs); +} + +#ifdef CONFIG_WASP_SUPPORT +static inline void ar7240_spi_enable_cs1(void) +{ + unsigned int cs; + ar7240_spi_down(); + ar7240_spi_init(); + ar7240_spi_enable_soft_access(); + cs = ar7240_reg_rd(AR7240_SPI_WRITE) & AR7240_SPI_CS_DIS; + ar7240_reg_wr_nf(AR7240_SPI_WRITE, cs | AR7240_SPI_CLK_HIGH); + cs = ar7240_reg_rd(AR7240_SPI_WRITE) & ~AR7240_SPI_CS_DIS; + ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_ENABLE_1 | cs | AR7240_SPI_CLK_HIGH); + ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_ENABLE_1 | cs); +} +#else +static inline void ar7240_spi_enable_cs1(void) +{ + unsigned int cs; + ar7240_spi_down(); + ar7240_spi_enable_soft_access(); + cs = ar7240_reg_rd(AR7240_SPI_WRITE) & ~AR7240_SPI_CS_DIS; + ar7240_reg_wr_nf(AR7240_SPI_WRITE, AR7240_SPI_CS_ENABLE_1 | cs); +} +#endif + +static inline void ar7240_spi_disable_cs(void) +{ + unsigned int cs = ar7240_reg_rd(AR7240_SPI_WRITE) | AR7240_SPI_CS_DIS; + ar7240_reg_wr_nf(AR7240_SPI_WRITE, cs); + ar7240_spi_disable_soft_access(); + ar7240_spi_up(); +} + +/* + * Example usage to access BOOT flash + */ +static inline void ar7240_spi_flash_cs0_sector_erase(unsigned int addr) +{ + ar7240_spi_enable_cs0(); + ar7240_spi_flash_sector_erase(addr); + ar7240_spi_disable_cs(); +} + +static inline void ar7240_spi_flash_cs0_write_page(unsigned int addr, unsigned char *data, int len) +{ + ar7240_spi_enable_cs0(); + ar7240_spi_flash_write_page(addr, data, len); + ar7240_spi_disable_cs(); +} + +/* + * Reset block + */ +#define AR7240_GENERAL_TMR AR7240_RESET_BASE+0 +#define AR7240_GENERAL_TMR_RELOAD AR7240_RESET_BASE+4 +#define AR7240_WATCHDOG_TMR_CONTROL AR7240_RESET_BASE+8 +#define AR7240_WATCHDOG_TMR AR7240_RESET_BASE+0xc +#define AR7240_MISC_INT_STATUS AR7240_RESET_BASE+0x10 +#define AR7240_MISC_INT_MASK AR7240_RESET_BASE+0x14 + +#define AR7240_PCI_INT_STATUS AR7240_PCI_CTLR_BASE+0x4c +#define AR7240_PCI_INT_MASK AR7240_PCI_CTLR_BASE+0x50 +#define AR7240_PCI_INT_A_L (1 << 14) /* INTA Level Trigger */ +#define AR7240_PCI_INT_B_L (1 << 15) /* INTB Level Trigger */ +#define AR7240_PCI_INT_C_L (1 << 16) /* INTC Level Trigger */ + +#ifdef CONFIG_WASP_SUPPORT +#define AR7240_GLOBAL_INT_STATUS AR7240_RESET_BASE+0x18 +#else +#define AR7240_GLOBAL_INT_STATUS AR7240_RESET_BASE+0x20 +#endif +#define AR7240_RESET AR7240_RESET_BASE+0x1c +#define AR7240_OBSERVATION_ENABLE AR7240_RESET_BASE+0x28 + +#define AR7240_PCIE_WMAC_INT_STATUS AR7240_RESET_BASE+0xac +# define WMAC_MISC_INT (1 << 0) /* Indicates there is a WMAC Intr */ +# define WMAC_TX_INT (1 << 1) /* Reason of interrupt */ +# define WMAC_RXLP_INT (1 << 2) +# define WMAC_RXHP_INT (1 << 3) + +# define PCIE_RC_INT (1 << 4) +# define PCIE_RC_INT0 (1 << 5) +# define PCIE_RC_INT1 (1 << 6) +# define PCIE_RC_INT2 (1 << 7) +# define PCIE_RC_INT3 (1 << 8) + +# define PCI_WMAC_INTR (PCIE_RC_INT | PCIE_RC_INT0 | PCIE_RC_INT1 | \ + PCIE_RC_INT2 | PCIE_RC_INT3) + +# define WMAC_INTR (WMAC_MISC_INT | WMAC_TX_INT | WMAC_RXLP_INT | \ + WMAC_RXHP_INT) + + +#define AR7240_WD_ACT_MASK 3u +#define AR7240_WD_ACT_NONE 0u /* No Action */ +#define AR7240_WD_ACT_GP_INTR 1u /* General purpose intr */ +#define AR7240_WD_ACT_NMI 2u /* NMI */ +#define AR7240_WD_ACT_RESET 3u /* Full Chip Reset */ + +#define AR7240_WD_LAST_SHIFT 31 +#define AR7240_WD_LAST_MASK ((uint32_t)(1 << AR7240_WD_LAST_SHIFT)) + + + +/* + * Performace counters + */ +#define AR7240_PERF0_COUNTER AR7240_GE0_BASE+0xa0 +#define AR7240_PERF1_COUNTER AR7240_GE1_BASE+0xa0 + +/* + * SLIC/STEREO DMA Size Configurations + */ +#define AR7240_DMA_BUF_SIZE_4X2 0x00 +#define AR7240_DMA_BUF_SIZE_8X2 0x01 +#define AR7240_DMA_BUF_SIZE_16X2 0x02 +#define AR7240_DMA_BUF_SIZE_32X2 0x03 +#define AR7240_DMA_BUF_SIZE_64X2 0x04 +#define AR7240_DMA_BUF_SIZE_128X2 0x05 +#define AR7240_DMA_BUF_SIZE_256X2 0x06 +#define AR7240_DMA_BUF_SIZE_512X2 0x07 + +/* + * SLIC/STEREO DMA Assignments + */ +#define AR7240_DMA_CHAN_SLIC0_RX 0 +#define AR7240_DMA_CHAN_SLIC1_RX 1 +#define AR7240_DMA_CHAN_STEREO_RX 2 +#define AR7240_DMA_CHAN_SLIC0_TX 3 +#define AR7240_DMA_CHAN_SLIC1_TX 4 +#define AR7240_DMA_CHAN_STEREO_TX 5 + +/* Low-level routines */ +void ar7240_dma_addr_wr (int chan, unsigned int val); +void ar7240_dma_config_wr(int chan, unsigned int val); +void ar7240_dma_update_wr(int chan, unsigned int val); + +unsigned int ar7240_dma_addr_rd (int chan); +unsigned int ar7240_dma_config_rd(int chan); + +/* Use this routine to configure DMA access. Example: + * + * ar7240_dma_config_buffer( AR7240_DMA_CHAN_SLIC0_TX, + * < address of buffer >, + * AR7240_DMA_BUF_SIZE_512X2 + */ +void ar7240_dma_config_buffer(int chan, void *buffer, int sizeCfg); + +/* + * SLIC register definitions + */ +#define AR7240_SLIC_STATUS (AR7240_SLIC_BASE+0x00) +#define AR7240_SLIC_CNTRL (AR7240_SLIC_BASE+0x04) +#define AR7240_SLIC_SLOT0_NUM (AR7240_SLIC_BASE+0x08) +#define AR7240_SLIC_SLOT1_NUM (AR7240_SLIC_BASE+0x0c) +#define AR7240_SLIC_SAM_POS (AR7240_SLIC_BASE+0x2c) +#define AR7240_SLIC_FREQ_DIV (AR7240_SLIC_BASE+0x30) + +/* + * SLIC Control bits + */ +#define AR7240_SLIC_CNTRL_ENABLE (1<<0) +#define AR7240_SLIC_CNTRL_SLOT0_ENABLE (1<<1) +#define AR7240_SLIC_CNTRL_SLOT1_ENABLE (1<<2) +#define AR7240_SLIC_CNTRL_IRQ_ENABLE (1<<3) + +/* + * SLIC Helper Functions + */ +unsigned int ar7240_slic_status_rd(void); +unsigned int ar7240_slic_cntrl_rd(void); + +void ar7240_slic_cntrl_wr(unsigned int val); +void ar7240_slic_0_slot_pos_wr(unsigned int val); +void ar7240_slic_1_slot_pos_wr(unsigned int val); +void ar7240_slic_freq_div_wr(unsigned int val); +void ar7240_slic_sample_pos_wr(unsigned int val); + +void ar7240_slic_setup(int _sam, int _s0n, int _s1n); + +/* + * STEREO register definitions + */ +#define AR7240_STEREO_CONFIG (AR7240_STEREO_BASE+0x00) +#define AR7240_STEREO_VOLUME (AR7240_STEREO_BASE+0x04) + +/* + * Stereo Configuration Bits + */ +#define AR7240_STEREO_CONFIG_ENABLE (1<<24) +#define AR7240_STEREO_CONFIG_RESET (1<<23) +#define AR7240_STEREO_CONFIG_DELAY (1<<22) +#define AR7240_STEREO_CONFIG_MIC_WORD_SIZE (1<<20) + +#define AR7240_STEREO_CONFIG_MODE(x) ((3&x)<<18) +#define AR7240_STEREO_MODE_STEREO 0 +#define AR7240_STEREO_MODE_LEFT 1 +#define AR7240_STEREO_MODE_RIGHT 2 + +#define AR7240_STEREO_CONFIG_DATA_WORD_SIZE(x) ((3&x)<<16) + +#define AR7240_STEREO_CONFIG_I2S_32B_WORD (1<<15) +#define AR7240_STEREO_CONFIG_MASTER (1<<8) +#define AR7240_STEREO_CONFIG_PSEDGE(x) (0xff&x) + +/* + * Word sizes to use with common configurations: + */ +#define AR7240_STEREO_WS_8B 0 +#define AR7240_STEREO_WS_16B 1 + +/* + * Audio data is little endian so 16b values must be swapped in the DMA buffers. + */ +static inline int ar7240_stereo_sample_16b_cvt(unsigned int _v) { return (((_v<<8)&0xff00)|((_v>>8)&0xff)) & 0xffff; } + +/* Low level read/write of configuration */ +void ar7240_stereo_config_wr(unsigned int val); +void ar7240_stereo_volume_wr(unsigned int val); + +unsigned int ar7240_stereo_config_rd(void); +unsigned int ar7240_stereo_volume_rd(void); + +/* + * Common configurations for stereo block + */ +#define AR7240_STEREO_CFG_MASTER_STEREO_FS32_48KHZ(ws) ( \ + AR7240_STEREO_CONFIG_DELAY | \ + AR7240_STEREO_CONFIG_RESET | \ + AR7240_STEREO_CONFIG_DATA_WORD_SIZE(ws) | \ + AR7240_STEREO_CONFIG_MODE(AR7240_STEREO_MODE_LEFT) | \ + AR7240_STEREO_CONFIG_MASTER | \ + AR7240_STEREO_CONFIG_PSEDGE(26)) + +#define AR7240_STEREO_CFG_MASTER_STEREO_FS64_48KHZ(ws) ( \ + AR7240_STEREO_CONFIG_DELAY | \ + AR7240_STEREO_CONFIG_RESET | \ + AR7240_STEREO_CONFIG_DATA_WORD_SIZE(ws) | \ + AR7240_STEREO_CONFIG_MODE(AR7240_STEREO_MODE_STEREO) | \ + AR7240_STEREO_CONFIG_I2S_32B_WORD | \ + AR7240_STEREO_CONFIG_MASTER | \ + AR7240_STEREO_CONFIG_PSEDGE(13)) + +#define AR7240_STEREO_CFG_SLAVE_STEREO_FS32_48KHZ(ws) ( \ + AR7240_STEREO_CONFIG_RESET | \ + AR7240_STEREO_CONFIG_DATA_WORD_SIZE(ws) | \ + AR7240_STEREO_CONFIG_MODE(AR7240_STEREO_MODE_STEREO) | \ + AR7240_STEREO_CONFIG_PSEDGE(26)) + +#define AR7240_STEREO_CFG_SLAVE_STEREO_FS64_48KHZ(ws) ( \ + AR7240_STEREO_CONFIG_RESET | \ + AR7240_STEREO_CONFIG_I2S_32B_WORD | \ + AR7240_STEREO_CONFIG_DATA_WORD_SIZE(ws) | \ + AR7240_STEREO_CONFIG_MODE(AR7240_STEREO_MODE_STEREO) | \ + AR7240_STEREO_CONFIG_PSEDGE(13)) + +/* Routine sets up STEREO block for use. Use one of the predefined + * configurations. Example: + * + * ar7240_stereo_config_setup( + * AR7240_STEREO_CFG_MASTER_STEREO_FS32_48KHZ(AR7240_STEREO_WS_16B)) + * + */ +void ar7240_stereo_config_setup(unsigned int cfg); + +/* 48 kHz, 16 bit data & i2s 32fs */ +static inline void ar7240_setup_for_stereo_master(int ws) +{ ar7240_stereo_config_setup(AR7240_STEREO_CFG_MASTER_STEREO_FS32_48KHZ(ws)); } + +/* 48 kHz, 16 bit data & 32fs i2s */ +static inline void ar7240_setup_for_stereo_slave(int ws) +{ ar7240_stereo_config_setup(AR7240_STEREO_CFG_SLAVE_STEREO_FS32_48KHZ(ws)); } + +/* + * PERF CTL bits + */ +#define PERF_CTL_PCI_AHB_0 ( 0) +#define PERF_CTL_PCI_AHB_1 ( 1) +#define PERF_CTL_USB_0 ( 2) +#define PERF_CTL_USB_1 ( 3) +#define PERF_CTL_GE0_PKT_CNT ( 4) +#define PERF_CTL_GEO_AHB_1 ( 5) +#define PERF_CTL_GE1_PKT_CNT ( 6) +#define PERF_CTL_GE1_AHB_1 ( 7) +#define PERF_CTL_PCI_DEV_0_BUSY ( 8) +#define PERF_CTL_PCI_DEV_1_BUSY ( 9) +#define PERF_CTL_PCI_DEV_2_BUSY (10) +#define PERF_CTL_PCI_HOST_BUSY (11) +#define PERF_CTL_PCI_DEV_0_ARB (12) +#define PERF_CTL_PCI_DEV_1_ARB (13) +#define PERF_CTL_PCI_DEV_2_ARB (14) +#define PERF_CTL_PCI_HOST_ARB (15) +#define PERF_CTL_PCI_DEV_0_ACTIVE (16) +#define PERF_CTL_PCI_DEV_1_ACTIVE (17) +#define PERF_CTL_PCI_DEV_2_ACTIVE (18) +#define PERF_CTL_HOST_ACTIVE (19) + +#define ar7240_perf0_ctl(_val) ar7240_reg_wr(AR7240_PERF_CTL, (_val)) +#define ar7240_perf1_ctl(_val) ar7240_reg_rmw_set(AR7240_PERF_CTL, ((_val) << 8)) + + +/* These are values used in platform.inc to select PLL settings */ +#define AR7240_REV_ID (AR7240_RESET_BASE + 0x90) +#define AR7240_REV_ID_MASK 0xffff +#define AR7240_REV_ID_MASK_MAJ 0xfff0 +#define AR7240_REV_ID_AR7130 0xa0 +#define AR7240_REV_ID_AR7141 0xa1 +#define AR7240_REV_ID_AR7161 0xa2 + +#define AR7240_REV_1_0 0xc0 +#define AR7240_REV_1_1 0xc1 +#define AR7240_REV_1_2 0xc2 + +#define AR7241_REV_1_0 0x0100 +#define AR7242_REV_1_0 0x1100 + +#define AR7241_REV_1_1 0x0101 +#define AR7242_REV_1_1 0x1101 + +#define AR9330_REV_1_0 0x0110 +#define AR9331_REV_1_0 0x1110 +#define AR9330_REV_1_1 0x0111 +#define AR9331_REV_1_1 0x1111 +#define AR9330_REV_1_2 0x0112 +#define AR9331_REV_1_2 0x1112 +#define AR9344_REV_1_0 0x2120 +#define AR9342_REV_1_0 0x1120 +#define AR9341_REV_1_0 0x0120 +#define AR9344_REV_1_1 0x2121 +#define AR9342_REV_1_1 0x1121 +#define AR9341_REV_1_1 0x0121 +#define AR9344_REV_1_2 0x2122 +#define AR9342_REV_1_2 0x1122 +#define AR9341_REV_1_2 0x0122 + + + + + +#define is_ar7240() (((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR7240_REV_1_2) || \ + ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR7240_REV_1_1) || \ + ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR7240_REV_1_0)) + +#define is_ar7241() (((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR7241_REV_1_0) || \ + ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR7241_REV_1_1)) + +#define is_ar7242() (((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR7242_REV_1_0) || \ + ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR7242_REV_1_1)) + +#define is_ar9344_10() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9344_REV_1_0) +#define is_ar9342_10() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9342_REV_1_0) +#define is_ar9341_10() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9341_REV_1_0) + +#define is_ar9344_11() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9344_REV_1_1) +#define is_ar9342_11() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9342_REV_1_1) +#define is_ar9341_11() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9341_REV_1_1) + +#define is_ar9344_12() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9344_REV_1_2) +#define is_ar9342_12() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9342_REV_1_2) +#define is_ar9341_12() ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9341_REV_1_2) + +#define is_ar9330() (((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9330_REV_1_0) || \ + ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9330_REV_1_1) || \ + ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9330_REV_1_2)) + + +#define is_ar9331() (((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9331_REV_1_0) || \ + ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9331_REV_1_1) || \ + ((ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK) == AR9331_REV_1_2)) + +#define is_ar934x_10() (is_ar9344_10() || is_ar9342_10() || is_ar9341_10()) +#define is_ar934x_11() (is_ar9344_11() || is_ar9342_11() || is_ar9341_11()) +#define is_ar934x_12() (is_ar9344_12() || is_ar9342_12() || is_ar9341_12()) + + +#define is_ar9341() (is_ar9341_10() || is_ar9341_11() || is_ar9341_12()) +#define is_ar9342() (is_ar9342_10() || is_ar9342_11() || is_ar9342_12()) +#define is_ar9344() (is_ar9344_10() || is_ar9344_11() || is_ar9344_12()) + +#define is_ar934x() (is_ar934x_10() || is_ar934x_11() || is_ar934x_12()) + +#define is_ar933x() (is_ar9330() || is_ar9331()) + + +#define AR7240_PLL_USE_REV_ID 0 +#define AR7240_PLL_200_200_100 1 +#define AR7240_PLL_300_300_150 2 +#define AR7240_PLL_333_333_166 3 +#define AR7240_PLL_266_266_133 4 +#define AR7240_PLL_266_266_66 5 +#define AR7240_PLL_400_400_200 6 +#define AR7240_PLL_600_400_150 7 + + +/* + * AR7240_RESET bit defines + */ +#define AR7240_RESET_GE0_MDIO (1 << 22) +#define AR7240_RESET_GE1_MDIO (1 << 23) +#define AR7240_RESET_EXTERNAL (1 << 28) +#define AR7240_RESET_FULL_CHIP (1 << 24) +#define AR7240_RESET_CPU_NMI (1 << 21) +#define AR7240_RESET_CPU_COLD_RESET_MASK (1 << 20) +#define AR7240_RESET_DMA (1 << 19) +#define AR7240_RESET_SLIC (1 << 18) +#define AR7240_RESET_STEREO (1 << 17) +#define AR7240_RESET_DDR (1 << 16) +#define AR7240_RESET_GE1_MAC (1 << 13) +#define AR7240_RESET_GE1_PHY (1 << 12) +#define AR7240_RESET_GE0_MAC (1 << 9) +#define AR7240_RESET_GE0_PHY (1 << 8) +#ifdef CONFIG_MACH_HORNET +#define AR7240_RESET_WMAC (1 << 11) +#else +#define AR7240_RESET_USB_PHY_ANALOG (1 << 11) +#endif +#define AR7240_RESET_PCIE_PHY_SHIFT (1 << 10) +#define AR7240_RESET_USBSUS_OVRIDE (1 << 3) +#define AR7240_RESET_USB_OHCI_DLL (1 << 3) +#define AR7240_RESET_USB_HOST (1 << 5) +#define AR7240_RESET_USB_PHY (1 << 4) +#define AR7240_RESET_PCI_BUS (1 << 1) +#define AR7240_RESET_PCI_CORE (1 << 0) + + +#ifdef CONFIG_WASP_SUPPORT +#define is_wasp() is_ar934x() +#define ATH_WMAC_BASE AR7240_APB_BASE + 0x100000 +#define ATH_WMAC_LEN 0x1ffff /* XXX:Check this */ +#else +#define is_wasp() (0) +#endif + +#ifdef CONFIG_MACH_HORNET +#define ATH_WMAC_BASE AR7240_APB_BASE + 0x100000 +#define ATH_WMAC_LEN 0x1ffff /* XXX:Check this */ +#endif + +void ar7240_reset(unsigned int mask); + +/* + * Mii block + */ +#define AR7240_MII0_CTRL 0x18070000 +#define AR7240_MII1_CTRL 0x18070004 + + +#define ar7240_get_bit(_reg, _bit) (ar7240_reg_rd((_reg)) & (1 << (_bit))) + +#ifdef CONFIG_WASP_SUPPORT + +#define ar7240_flush_ge(_unit) + + +#else +#define ar7240_flush_ge(_unit) do { \ + u32 reg = (_unit) ? AR7240_DDR_GE1_FLUSH : AR7240_DDR_GE0_FLUSH; \ + ar7240_reg_wr(reg, 1); \ + while((ar7240_reg_rd(reg) & 0x1)); \ + ar7240_reg_wr(reg, 1); \ + while((ar7240_reg_rd(reg) & 0x1)); \ +}while(0); +#endif +#define ar7240_flush_pcie() do { \ + ar7240_reg_wr(AR7240_DDR_PCIE_FLUSH, 1); \ + while((ar7240_reg_rd(AR7240_DDR_PCIE_FLUSH) & 0x1)); \ + ar7240_reg_wr(AR7240_DDR_PCIE_FLUSH, 1); \ + while((ar7240_reg_rd(AR7240_DDR_PCIE_FLUSH) & 0x1)); \ +}while(0); + +#define ar7240_flush_USB() do { \ + ar7240_reg_wr(AR7240_DDR_USB_FLUSH, 1); \ + while((ar7240_reg_rd(AR7240_DDR_USB_FLUSH) & 0x1)); \ + ar7240_reg_wr(AR7240_DDR_USB_FLUSH, 1); \ + while((ar7240_reg_rd(AR7240_DDR_USB_FLUSH) & 0x1)); \ +}while(0); + +int ar7240_local_read_config(int where, int size, u32 *value); +int ar7240_local_write_config(int where, int size, u32 value); +int ar7240_check_error(int verbose); +unsigned char __ar7240_readb(const volatile void __iomem *p); +unsigned short __ar7240_readw(const volatile void __iomem *p); +void ap_usb_led_on(void); +void ap_usb_led_off(void); + +#endif diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.c new file mode 100644 index 0000000..e7dac94 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.c @@ -0,0 +1,1431 @@ +/* Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include "ag7240_phy.h" +#include "ag7240.h" +#include "ar7240_s26_phy.h" +#include "eth_diag.h" + + +/* PHY selections and access functions */ + +typedef enum { + PHY_SRCPORT_INFO, + PHY_PORTINFO_SIZE, +} PHY_CAP_TYPE; + +typedef enum { + PHY_SRCPORT_NONE, + PHY_SRCPORT_VLANTAG, + PHY_SRCPORT_TRAILER, +} PHY_SRCPORT_TYPE; + +#ifdef CONFIG_AR7240_S26_VLAN_IGMP +#define PYTHON_OK 0 +#define PYTHON_ERR 1 +#define PYTHON_FULL 2 +#endif + +#define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6) +#define DRV_MSG(x,a,b,c,d,e,f) +#define DRV_PRINT(DBG_SW,X) + +#define ATHR_LAN_PORT_VLAN 1 +#define ATHR_WAN_PORT_VLAN 2 +#define ENET_UNIT_LAN 1 +#define ENET_UNIT_WAN 0 + +#define TRUE 1 +#define FALSE 0 + +#define ATHR_PHY0_ADDR 0x0 +#define ATHR_PHY1_ADDR 0x1 +#define ATHR_PHY2_ADDR 0x2 +#define ATHR_PHY3_ADDR 0x3 +#define ATHR_PHY4_ADDR 0x4 + +#define MODULE_NAME "ATHRS26" +MODULE_LICENSE("Dual BSD/GPL"); + +#ifdef S26_PHY_DEBUG +#define DPRINTF(_fmt,...) do { \ + printk(MODULE_NAME":"_fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(_fmt,...) +#endif + +#ifdef ETH_SOFT_LED +extern ATH_LED_CONTROL PLedCtrl; +extern atomic_t Ledstatus; +#endif +extern int phy_in_reset; +extern char *dup_str[]; + +/* + * Track per-PHY port information. + */ +typedef struct { + BOOL isEnetPort; /* normal enet port */ + BOOL isPhyAlive; /* last known state of link */ + int ethUnit; /* MAC associated with this phy port */ + uint32_t phyBase; + uint32_t phyAddr; /* PHY registers associated with this phy port */ + uint32_t VLANTableSetting; /* Value to be written to VLAN table */ +} athrPhyInfo_t; + +/* + * Per-PHY information, indexed by PHY unit number. + */ +static athrPhyInfo_t athrPhyInfo[] = { + + {TRUE, /* port 1 -- LAN port 1 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY0_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 2 -- LAN port 2 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY1_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 3 -- LAN port 3 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY2_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 4 -- LAN port 4 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY3_ADDR, + ATHR_LAN_PORT_VLAN /* Send to all ports */ + }, + + {TRUE, /* port 5 -- WAN Port 5 */ + FALSE, + ENET_UNIT_WAN, + 0, + ATHR_PHY4_ADDR, + ATHR_LAN_PORT_VLAN /* Send to all ports */ + }, + + {FALSE, /* port 0 -- cpu port 0 */ + TRUE, + ENET_UNIT_LAN, + 0, + 0x00, + ATHR_LAN_PORT_VLAN + }, + +}; + +static uint8_t athr26_init_flag = 0,athr26_init_flag1 = 0; +static DECLARE_WAIT_QUEUE_HEAD (hd_conf_wait); + +#define ATHR_GLOBALREGBASE 0 + +#define ATHR_PHY_MAX 5 + +/* Range of valid PHY IDs is [MIN..MAX] */ +#define ATHR_ID_MIN 0 +#define ATHR_ID_MAX (ATHR_PHY_MAX-1) + +/* Convenience macros to access myPhyInfo */ +#define ATHR_IS_ENET_PORT(phyUnit) (athrPhyInfo[phyUnit].isEnetPort) +#define ATHR_IS_PHY_ALIVE(phyUnit) (athrPhyInfo[phyUnit].isPhyAlive) +#define ATHR_ETHUNIT(phyUnit) (athrPhyInfo[phyUnit].ethUnit) +#define ATHR_PHYBASE(phyUnit) (athrPhyInfo[phyUnit].phyBase) +#define ATHR_PHYADDR(phyUnit) (athrPhyInfo[phyUnit].phyAddr) +#define ATHR_VLAN_TABLE_SETTING(phyUnit) (athrPhyInfo[phyUnit].VLANTableSetting) + + +#define ATHR_IS_ETHUNIT(phyUnit, ethUnit) \ + (ATHR_IS_ENET_PORT(phyUnit) && \ + ATHR_ETHUNIT(phyUnit) == (ethUnit)) + +#define ATHR_IS_WAN_PORT(phyUnit) (!(ATHR_ETHUNIT(phyUnit)==ENET_UNIT_LAN)) + +/* Forward references */ +BOOL athrs26_phy_is_link_alive(int phyUnit); +uint32_t athrs26_reg_read(uint32_t reg_addr); +void athrs26_reg_write(uint32_t reg_addr, uint32_t reg_val); +unsigned int s26_rd_phy(unsigned int phy_addr, unsigned int reg_addr); +void s26_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data); +extern int ag7240_check_link(ag7240_mac_t *mac,int phyUnit); + +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +static uint16_t port_def_vid[5] = {1, 1, 1, 1, 1}; +static uint8_t cpu_egress_tagged_flag = 0; + +inline uint8_t is_cpu_egress_tagged(void) +{ + return cpu_egress_tagged_flag; +} + +void set_cpu_egress_tagged(uint8_t is_tagged) +{ + cpu_egress_tagged_flag = is_tagged; +} + +inline uint16_t athrs26_defvid_get(uint32_t port_id) +{ + if ((port_id == 0) || (port_id > 5)) + return 0; + + return port_def_vid[port_id - 1]; +} + +BOOL athrs26_defvid_set(uint32_t port_id, uint16_t def_vid) +{ + if ((def_vid == 0) || (def_vid > 4094)) + return FALSE; + + if ((port_id == 0) || (port_id > 5)) + return FALSE; + + port_def_vid[port_id - 1] = def_vid; + return TRUE; +} +#endif + +void athrs26_powersave_off(int phy_addr) +{ + s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_ADDRESS,0x29); + s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_DATA,0x36c0); + +} + +void athrs26_sleep_off(int phy_addr) +{ + s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_ADDRESS,0xb); + s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_DATA,0x3c00); +} + +void athrs26_enable_linkIntrs(int ethUnit) +{ + int phyUnit = 0, phyAddr = 0; + ag7240_mac_t *mac = ag7240_macs[ethUnit]; + + /* Enable global PHY link status interrupt */ + athrs26_reg_write(S26_GLOBAL_INTR_MASK_REG,PHY_LINK_CHANGE_REG); + + if (mac_has_flag(mac,ETH_SWONLY_MODE)) { + if (ethUnit == 1) { + for (phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + phyAddr = ATHR_PHYADDR(phyUnit); + s26_wr_phy(phyAddr,ATHR_PHY_INTR_ENABLE,PHY_LINK_INTRS); + } + } + return ; + } + + if (ethUnit == ENET_UNIT_WAN) { + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_INTR_ENABLE,PHY_LINK_INTRS); + } + else { + for (phyUnit = 0; phyUnit < ATHR_PHY_MAX - 1; phyUnit++) { + phyAddr = ATHR_PHYADDR(phyUnit); + s26_wr_phy(phyAddr,ATHR_PHY_INTR_ENABLE,PHY_LINK_INTRS); + } + } +} + +void athrs26_disable_linkIntrs(int ethUnit) +{ + int phyUnit = 0, phyAddr = 0; + ag7240_mac_t *mac = ag7240_macs[ethUnit]; + if (mac_has_flag(mac,ETH_SWONLY_MODE)) { + if (ethUnit == 1) { + for (phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + phyAddr = ATHR_PHYADDR(phyUnit); + s26_wr_phy(phyAddr,ATHR_PHY_INTR_ENABLE,0x0); + } + } + return ; + } + + if (ethUnit == ENET_UNIT_WAN) { + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_INTR_ENABLE,0x0); + } + else { + for (phyUnit = 0; phyUnit < ATHR_PHY_MAX - 1; phyUnit++) { + phyAddr = ATHR_PHYADDR(phyUnit); + s26_wr_phy(phyAddr,ATHR_PHY_INTR_ENABLE,0x0); + } + } +} + +void athrs26_force_100M(int phyAddr,int duplex) +{ + uint32_t ar7240_revid; + +#ifdef ETH_SOFT_LED + /* Resetting PHY will disable MDIO access needed by soft LED task. + * Hence, Do not reset PHY until Soft LED task get completed. + */ + while(atomic_read(&Ledstatus) == 1); + PLedCtrl.ledlink[phyAddr] = 0; +#endif + phy_in_reset = 1; + /* + * Force MDI and MDX to alternate ports + * Phy 0,2 and 4 -- MDI + * Phy 1 and 3 -- MDX + */ + if(phyAddr%2) { + s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x820); + } + else { + s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x800); + } + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x0); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x2ee); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x3a11); + } + s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,(0xa000|(duplex << 8))); + phy_in_reset = 0; +} + +void athrs26_force_10M(int phyAddr,int duplex) +{ + uint32_t ar7240_revid; +#ifdef ETH_SOFT_LED + /* Resetting PHY will disable MDIO access needed by soft LED task. + * Hence, Do not reset PHY until Soft LED task get completed. + */ + while(atomic_read(&Ledstatus) == 1); + PLedCtrl.ledlink[phyAddr] = 0; +#endif + phy_in_reset = 1; + + athrs26_powersave_off(phyAddr); + athrs26_sleep_off(phyAddr); + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { + s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x800); + s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,0x8100); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x0); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x12ee); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x3af0); + } + s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,(0x8000 |(duplex << 8))); + phy_in_reset = 0; +} + +void athrs26_reg_init(int ethUnit) +{ + uint32_t ar7240_revid; + uint32_t rd_data; + + ag7240_mac_t *mac = ag7240_macs[ethUnit]; + + /* if using header for register configuration, we have to */ + /* configure s26 register after frame transmission is enabled */ + if (athr26_init_flag) + return; + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { +#ifdef S26_FORCE_100M + athrs26_force_100M(ATHR_PHY4_ADDR,1); +#endif +#ifdef S26_FORCE_10M + athrs26_force_10M(ATHR_PHY4_ADDR,1); +#endif + } else { + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL,0x9000); + + /* Class A setting for 100M */ + s26_wr_phy(ATHR_PHY4_ADDR, 29, 5); + s26_wr_phy(ATHR_PHY4_ADDR, 30, (s26_rd_phy(ATHR_PHY4_ADDR, 30)&((~2)&0xffff))); + } + + /* Enable flow control on CPU port */ + athrs26_reg_write(PORT_STATUS_REGISTER0, (athrs26_reg_read(PORT_STATUS_REGISTER0) | 0x30)); + + /* Disable WAN mac inside S26 */ + athrs26_reg_write(PORT_STATUS_REGISTER5,0x0); + + /* Enable WAN mac inside S26 */ + if (mac_has_flag(mac,ETH_SWONLY_MODE) || is_ar933x() ) + athrs26_reg_write(PORT_STATUS_REGISTER5,0x200); + + /* Enable MDIO Access if PHY is Powered-down */ + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_ADDRESS,0x3); + rd_data = s26_rd_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_DATA); + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_DATA,(rd_data & 0xfffffeff) ); + + if (mac_has_flag(mac,ATHR_S26_HEADER)) + athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x4804); + else + athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x6004); + + athrs26_reg_write(0x30,(athrs26_reg_read(0x30)&0xfffff800)|0x6b4); +// athrs26_reg_write(0x30,(athrs26_reg_read(0x30)&AR8216_GCTRL_MTU)|1716); + + athr26_init_flag = 1; +} + +void athrs26_reg_init_lan(int ethUnit) +{ + int i = 60; + int phyUnit; + uint32_t phyBase = 0; + BOOL foundPhy = FALSE; + uint32_t phyAddr = 0; + uint32_t queue_ctrl_reg = 0; + uint32_t ar7240_revid; + uint32_t rd_data; + ag7240_mac_t *mac = ag7240_macs[ethUnit]; + + /* if using header for register configuration, we have to */ + /* configure s26 register after frame transmission is enabled */ + if (athr26_init_flag1) + return; + + /* reset switch */ + printk(MODULE_NAME ": resetting s26\n"); + athrs26_reg_write(0x0, athrs26_reg_read(0x0)|0x80000000); + + while(i--) { + mdelay(100); + if(!(athrs26_reg_read(0x0)&0x80000000)) + break; + } + printk(MODULE_NAME ": s26 reset done\n"); + + /* 100M Half Duplex Throughput setting */ + athrs26_reg_write(0xe4,0x079c1040); + + /* Change HOL settings + * 25: PORT_QUEUE_CTRL_ENABLE. + * 24: PRI_QUEUE_CTRL_EN. + */ + athrs26_reg_write(0x118,0x0032b5555); + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX ; phyUnit++) { + + if ((ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN) && + !mac_has_flag(mac,ETH_SWONLY_MODE)) + continue; + + foundPhy = TRUE; + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { +#ifdef S26_FORCE_100M + athrs26_force_100M(phyAddr,1); +#endif +#ifdef S26_FORCE_10M + athrs26_force_10M(phyAddr,1); +#endif + } + else { + s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,0x9000); + + /* Class A setting for 100M */ + s26_wr_phy(phyAddr, 29, 5); + s26_wr_phy(phyAddr, 30, (s26_rd_phy(phyAddr, 30)&((~2)&0xffff))); + } + + /* Enable MDIO Access if PHY is Powered-down */ + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3); + rd_data = s26_rd_phy(phyAddr,ATHR_DEBUG_PORT_DATA); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,(rd_data & 0xfffffeff) ); + + /* Change HOL settings + * 25: PORT_QUEUE_CTRL_ENABLE. + * 24: PRI_QUEUE_CTRL_EN. + */ + queue_ctrl_reg = (0x100 * phyUnit) + 0x218 ; + athrs26_reg_write(queue_ctrl_reg,0x032b5555); + + DPRINTF("S26 ATHR_PHY_FUNC_CONTROL (%d):%x\n",phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_FUNC_CONTROL)); + DPRINTF("S26 PHY ID (%d) :%x\n",phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_ID1)); + DPRINTF("S26 PHY CTRL (%d) :%x\n",phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_CONTROL)); + DPRINTF("S26 ATHR PHY STATUS (%d) :%x\n",phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_STATUS)); + } + + /* + * CPU port Enable + */ + + /* + * status[1:0]=2'h2; - (0x10 - 1000 Mbps , 0x01 - 100Mbps, 0x0 - 10 Mbps) + * status[2]=1'h1; - Tx Mac En + * status[3]=1'h1; - Rx Mac En + * status[4]=1'h1; - Tx Flow Ctrl En + * status[5]=1'h1; - Rx Flow Ctrl En + * status[6]=1'h1; - Duplex Mode + */ + athrs26_reg_write(PORT_STATUS_REGISTER1, 0x200); /* LAN - 1 */ + athrs26_reg_write(PORT_STATUS_REGISTER2, 0x200); /* LAN - 2 */ + athrs26_reg_write(PORT_STATUS_REGISTER3, 0x200); /* LAN - 3 */ + athrs26_reg_write(PORT_STATUS_REGISTER4, 0x200); /* LAN - 4 */ + + /* QM Control */ + athrs26_reg_write(0x38, 0xc000050e); + + /* + * status[11]=1'h0; - CPU Disable + * status[7] = 1'b1; - Learn One Lock + * status[14] = 1'b0; - Learn Enable + */ + if (mac_has_flag(mac,ATHR_S26_HEADER)) + athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x4804); + else + athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x6004); + + /* Tag Priority Mapping */ +// athrs26_reg_write(0x70, 0x41af); + athrs26_reg_write(0x70, 0xfa50); + + /* Enable ARP packets to CPU port */ + athrs26_reg_write(S26_ARL_TBL_CTRL_REG,(athrs26_reg_read(S26_ARL_TBL_CTRL_REG) | 0x100000)); + + /* Enable Broadcast packets to CPU port */ + athrs26_reg_write(S26_FLD_MASK_REG,(athrs26_reg_read(S26_FLD_MASK_REG) | S26_ENABLE_CPU_BROADCAST )); + + /* Turn on back pressure */ + athrs26_reg_write(PORT_STATUS_REGISTER0, + (athrs26_reg_read(PORT_STATUS_REGISTER0) | 0x80)); + athrs26_reg_write(PORT_STATUS_REGISTER1, + (athrs26_reg_read(PORT_STATUS_REGISTER1) | 0x80)); + athrs26_reg_write(PORT_STATUS_REGISTER2, + (athrs26_reg_read(PORT_STATUS_REGISTER2) | 0x80)); + athrs26_reg_write(PORT_STATUS_REGISTER3, + (athrs26_reg_read(PORT_STATUS_REGISTER3) | 0x80)); + athrs26_reg_write(PORT_STATUS_REGISTER4, + (athrs26_reg_read(PORT_STATUS_REGISTER4) | 0x80)); + + DPRINTF("S26 CPU_PORT_REGISTER :%x\n", athrs26_reg_read ( CPU_PORT_REGISTER )); + DPRINTF("S26 PORT_STATUS_REGISTER0 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER0 )); + DPRINTF("S26 PORT_STATUS_REGISTER1 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER1 )); + DPRINTF("S26 PORT_STATUS_REGISTER2 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER2 )); + DPRINTF("S26 PORT_STATUS_REGISTER3 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER3 )); + DPRINTF("S26 PORT_STATUS_REGISTER4 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER4 )); + + DPRINTF("S26 PORT_CONTROL_REGISTER0 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER0 )); + DPRINTF("S26 PORT_CONTROL_REGISTER1 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER1 )); + DPRINTF("S26 PORT_CONTROL_REGISTER2 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER2 )); + DPRINTF("S26 PORT_CONTROL_REGISTER3 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER3 )); + DPRINTF("S26 PORT_CONTROL_REGISTER4 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER4 )); + if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) { + athrs26_reg_write(ATHR_PRI_CTRL_PORT_2,(athrs26_reg_read(ATHR_PRI_CTRL_PORT_2)|ATHR_TOS_PRI_EN)); + athrs26_reg_write(ATHR_PRI_CTRL_PORT_3,(athrs26_reg_read(ATHR_PRI_CTRL_PORT_3)|ATHR_TOS_PRI_EN)); + athrs26_reg_write(ATHR_PRI_CTRL_PORT_4,(athrs26_reg_read(ATHR_PRI_CTRL_PORT_4)|ATHR_TOS_PRI_EN)); + athrs26_reg_write(ATHR_PRI_CTRL_PORT_5,(athrs26_reg_read(ATHR_PRI_CTRL_PORT_5)|ATHR_TOS_PRI_EN)); + athrs26_reg_write(ATHR_QOS_MODE_REGISTER,ATHR_QOS_FIXED_PRIORITY); + + } + /* Disable WAN mac inside S26 after S26 Reset*/ + + athrs26_reg_write(PORT_STATUS_REGISTER5,0x0); + +//#ifdef CONFIG_AR7240_S26_VLAN_IGMP + // Set Max MTU to 1518+6 for vlan and header space. +// athrs26_reg_write(0x30,(athrs26_reg_read(0x30)&AR8216_GCTRL_MTU)|1716); + athrs26_reg_write(0x30,(athrs26_reg_read(0x30)&0xfffff800)|0x6b4); +//#endif + + /* Set CPU port0 to Atheros Header Enable. */ + if(mac_has_flag(mac,ATHR_S26_HEADER)) + athrs26_reg_write(0x104,athrs26_reg_read(0x104)|(0x1<<11)); + + if (mac_has_flag(mac,ETH_SWONLY_MODE) || is_ar933x()) + athrs26_reg_write(PORT_STATUS_REGISTER5,0x200); + + + athr26_init_flag1 = 1; +} +static unsigned int phy_val_saved = 0; +/****************************************************************************** +* +* athrs26_phy_off - power off the phy to change its speed +* +* Power off the phy +*/ +void athrs26_phy_off(ag7240_mac_t *mac) +{ + struct net_device *dev = mac->mac_dev; + + if (mac->mac_unit == ENET_UNIT_LAN) + return; + + netif_carrier_off(dev); + + phy_val_saved = s26_rd_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL); + s26_wr_phy( ATHR_PHY4_ADDR, ATHR_PHY_CONTROL, phy_val_saved | 0x800); +} + +/****************************************************************************** +* +* athrs26_phy_on - power on the phy after speed changed +* +* Power on the phy +*/ +void athrs26_phy_on(ag7240_mac_t *mac) +{ + if ((mac->mac_unit == ENET_UNIT_LAN) || (phy_val_saved == 0)) + return; + + s26_wr_phy( ATHR_PHY4_ADDR, ATHR_PHY_CONTROL, phy_val_saved & 0xf7ff); + + mdelay(2000); +} + +/****************************************************************************** +* +* athrs26_mac_speed_set - set mac in s26 speed mode (actually RMII mode) +* +* Set mac speed mode +*/ +void athrs26_mac_speed_set(ag7240_mac_t *mac, ag7240_phy_speed_t speed) +{ + if ((mac->mac_unit == ENET_UNIT_LAN)) + return; + + switch (speed) { + case AG7240_PHY_SPEED_100TX: + athrs26_reg_write (0x600, 0x7d); + break; + + case AG7240_PHY_SPEED_10T: + athrs26_reg_write (0x600, 0x7c); + break; + + default: + break; + } +} + +/****************************************************************************** +* +* athrs26_phy_is_link_alive - test to see if the specified link is alive +* +* RETURNS: +* TRUE --> link is alive +* FALSE --> link is down +*/ +BOOL +athrs26_phy_is_link_alive(int phyUnit) +{ + uint16_t phyHwStatus; + uint32_t phyBase; + uint32_t phyAddr; + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + phyHwStatus = s26_rd_phy( phyAddr, ATHR_PHY_SPEC_STATUS); + if (phyHwStatus & ATHR_STATUS_LINK_PASS) + return TRUE; + + return FALSE; +} + +/****************************************************************************** +* +* athrs26_phy_setup - reset and setup the PHY associated with +* the specified MAC unit number. +* +* Resets the associated PHY port. +* +* RETURNS: +* TRUE --> associated PHY is alive +* FALSE --> no LINKs on this ethernet unit +*/ + +BOOL +athrs26_phy_setup(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus; + uint16_t timeout; + int liveLinks = 0; + uint32_t phyBase = 0; + BOOL foundPhy = FALSE; + uint32_t phyAddr = 0; + uint32_t ar7240_revid; + + + /* See if there's any configuration data for this enet */ + /* start auto negogiation on each phy */ + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + + foundPhy = TRUE; + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + s26_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT,ATHR_ADVERTISE_ALL); + DPRINTF("%s ATHR_AUTONEG_ADVERT %d :%x\n",__func__,phyAddr, + s26_rd_phy(phyAddr,ATHR_AUTONEG_ADVERT)); + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + if(ar7240_revid != AR7240_REV_1_0) { + s26_wr_phy( phyAddr, ATHR_PHY_CONTROL,ATHR_CTRL_AUTONEGOTIATION_ENABLE + | ATHR_CTRL_SOFTWARE_RESET); + } + + DPRINTF("%s ATHR_PHY_CONTROL %d :%x\n",__func__,phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_CONTROL)); + } + + if (!foundPhy) { + return FALSE; /* No PHY's configured for this ethUnit */ + } + + /* + * After the phy is reset, it takes a little while before + * it can respond properly. + */ + if (ethUnit == ENET_UNIT_LAN) + mdelay(1000); + else + mdelay(3000); + + /* + * Wait up to 3 seconds for ALL associated PHYs to finish + * autonegotiation. The only way we get out of here sooner is + * if ALL PHYs are connected AND finish autonegotiation. + */ + for (phyUnit=0; (phyUnit < ATHR_PHY_MAX) /*&& (timeout > 0) */; phyUnit++) { + + + if ((ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN) && + !mac_has_flag(ag7240_macs[ENET_UNIT_LAN],ETH_SWONLY_MODE)) + continue; + + timeout=20; + for (;;) { + phyHwStatus = s26_rd_phy(phyAddr, ATHR_PHY_CONTROL); + + if (ATHR_RESET_DONE(phyHwStatus)) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Neg Success\n", phyUnit)); + break; + } + if (timeout == 0) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Negogiation timeout\n", phyUnit)); + break; + } + if (--timeout == 0) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Negogiation timeout\n", phyUnit)); + break; + } + + mdelay(150); + } + + /* fix IOT */ + s26_wr_phy(phyUnit, 29, 0x14); + s26_wr_phy( phyUnit, 30, 0x1352); + +#ifdef S26_VER_1_0 + //turn off power saving + s26_wr_phy(phyUnit, 29, 41); + s26_wr_phy(phyUnit, 30, 0); + printk("def_ S26_VER_1_0\n"); +#endif + } + + /* + * All PHYs have had adequate time to autonegotiate. + * Now initialize software status. + * + * It's possible that some ports may take a bit longer + * to autonegotiate; but we can't wait forever. They'll + * get noticed by mv_phyCheckStatusChange during regular + * polling activities. + */ + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + if (athrs26_phy_is_link_alive(phyUnit)) { + liveLinks++; + ATHR_IS_PHY_ALIVE(phyUnit) = TRUE; + } else { + ATHR_IS_PHY_ALIVE(phyUnit) = FALSE; + } + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("eth%d: Phy Specific Status=%4.4x\n", + ethUnit, + s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS))); + } + + return (liveLinks > 0); +} + +/****************************************************************************** +* +* athrs26_phy_is_fdx - Determines whether the phy ports associated with the +* specified device are FULL or HALF duplex. +* +* RETURNS: +* 1 --> FULL +* 0 --> HALF +*/ +int +athrs26_phy_is_fdx(int ethUnit,int phyUnit) +{ + uint32_t phyBase; + uint32_t phyAddr; + uint16_t phyHwStatus; + int ii = 200; + + if (athrs26_phy_is_link_alive(phyUnit)) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + do { + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + mdelay(10); + } while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii); + + if (phyHwStatus & ATHER_STATUS_FULL_DEPLEX) + return TRUE; + } + + return FALSE; +} + +/****************************************************************************** +* +* athrs26_phy_stab_wr - Function to Address 100Mbps stability issue, as +* suggested by EBU. +* +* RETURNS: none +* +*/ +void athrs26_phy_stab_wr(int phy_id, BOOL phy_up, int phy_speed) +{ + if( phy_up && (phy_speed == AG7240_PHY_SPEED_100TX)) { + s26_wr_phy(ATHR_PHYADDR(phy_id), ATHR_DEBUG_PORT_ADDRESS, 0x18); + s26_wr_phy(ATHR_PHYADDR(phy_id), ATHR_DEBUG_PORT_DATA, 0xBA8); + } else { + s26_wr_phy(ATHR_PHYADDR(phy_id), ATHR_DEBUG_PORT_ADDRESS, 0x18); + s26_wr_phy(ATHR_PHYADDR(phy_id), ATHR_DEBUG_PORT_DATA, 0x2EA); + } +} + +/****************************************************************************** +* +* athrs26_phy_speed - Determines the speed of phy ports associated with the +* specified device. +* +* RETURNS: +* AG7240_PHY_SPEED_10T, AG7240_PHY_SPEED_100TX; +* AG7240_PHY_SPEED_1000T; +*/ + +int +athrs26_phy_speed(int ethUnit,int phyUnit) +{ + uint16_t phyHwStatus; + uint32_t phyBase; + uint32_t phyAddr; + int ii = 200; + + + if (athrs26_phy_is_link_alive(phyUnit)) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + do { + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + mdelay(10); + } while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii); + + phyHwStatus = ((phyHwStatus & ATHER_STATUS_LINK_MASK) >> + ATHER_STATUS_LINK_SHIFT); + + switch(phyHwStatus) { + case 0: + return AG7240_PHY_SPEED_10T; + case 1: + return AG7240_PHY_SPEED_100TX; + case 2: + return AG7240_PHY_SPEED_1000T; + default: + printk("Unkown speed read!\n"); + } + } + + return AG7240_PHY_SPEED_10T; +} + +/***************************************************************************** +* +* athr_phy_is_up -- checks for significant changes in PHY state. +* +* A "significant change" is: +* dropped link (e.g. ethernet cable unplugged) OR +* autonegotiation completed + link (e.g. ethernet cable plugged in) +* +* When a PHY is plugged in, phyLinkGained is called. +* When a PHY is unplugged, phyLinkLost is called. +*/ + +int +athrs26_phy_is_up(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus, phyHwControl; + athrPhyInfo_t *lastStatus; + int linkCount = 0; + int lostLinks = 0; + int gainedLinks = 0; + uint32_t phyBase; + uint32_t phyAddr; + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if(mac_has_flag(ag7240_macs[ENET_UNIT_LAN],ETH_SWONLY_MODE)) + ethUnit = ENET_UNIT_LAN; + + if (!ATHR_IS_ETHUNIT(phyUnit,ethUnit)) + continue; + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + lastStatus = &athrPhyInfo[phyUnit]; + + if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */ + + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + + /* See if we've lost link */ + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { /* check realtime link */ + linkCount++; + } else { + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_STATUS); + /* If realtime failed check link in latch register before + * asserting link down. + */ + if (phyHwStatus & ATHR_LATCH_LINK_PASS) + linkCount++; + else + lostLinks++; + DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d down\n", + ethUnit, phyUnit)); + lastStatus->isPhyAlive = FALSE; + } + } else { /* last known link status was DEAD */ + + /* Check for reset complete */ + + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_STATUS); + + if (!ATHR_RESET_DONE(phyHwStatus)) + continue; + + phyHwControl = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_CONTROL); + + /* Check for AutoNegotiation complete */ + + if ((!(phyHwControl & ATHR_CTRL_AUTONEGOTIATION_ENABLE)) + || ATHR_AUTONEG_DONE(phyHwStatus)) { + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { + gainedLinks++; + linkCount++; + DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d up\n", + ethUnit, phyUnit)); + lastStatus->isPhyAlive = TRUE; + } + } + } + } + return (linkCount); + +} +void athrs26_reg_dev(ag7240_mac_t **mac) +{ + if( mac[0]) { + ag7240_macs[0] = mac[0]; + ag7240_macs[0]->mac_speed = 0xff; + } + else + printk("MAC [0] not registered \n"); + + if( mac[1]) { + ag7240_macs[1] = mac[1]; + ag7240_macs[1]->mac_speed = 0xff; + } + else + printk("MAC [1] not registered \n"); + return; + +} + +uint32_t +athrs26_reg_read(unsigned int s26_addr) +{ + unsigned int addr_temp; + unsigned int s26_rd_csr_low, s26_rd_csr_high, s26_rd_csr; + unsigned int data; + unsigned int phy_address, reg_address, unit = 0; + + addr_temp = (s26_addr & 0xfffffffc) >>2; + data = addr_temp >> 7; + + phy_address = 0x1f; + reg_address = 0x10; + + if (is_ar7240()) { + unit = 0; + } + else if(is_ar7241() || is_ar7242() || is_ar933x()) { + unit = 1; + } + + phy_reg_write(unit,phy_address, reg_address, data); + + phy_address = (0x17 & ((addr_temp >> 4) | 0x10)); + reg_address = ((addr_temp << 1) & 0x1e); + s26_rd_csr_low = (uint32_t) phy_reg_read(unit,phy_address, reg_address); + + reg_address = reg_address | 0x1; + s26_rd_csr_high = (uint32_t) phy_reg_read(unit,phy_address, reg_address); + s26_rd_csr = (s26_rd_csr_high << 16) | s26_rd_csr_low ; + + return(s26_rd_csr); +} + +void +athrs26_reg_write(unsigned int s26_addr, unsigned int s26_write_data) +{ + unsigned int addr_temp; + unsigned int data; + unsigned int phy_address, reg_address, unit = 0; + + + addr_temp = (s26_addr & 0xfffffffc) >>2; + data = addr_temp >> 7; + + phy_address = 0x1f; + reg_address = 0x10; + + if (is_ar7240()) { + unit = 0; + } + else if(is_ar7241() || is_ar7242() || is_ar933x()) { + unit = 1; + } + + if(is_ar933x()) + { + //The writing sequence need special care for register 0x40,0x50,0x98, because the busy bit design + //0x98: L->H (write low address first, then high address), 0x40: H->L, 0x50: H->L + //Other registers: any sequence is working. + if(s26_addr!=0x98) + { + phy_reg_write(unit, phy_address, reg_address, data); + + phy_address = 0x17 & ((addr_temp >> 4) | 0x10); + reg_address = ((addr_temp << 1) & 0x1e) | 0x1; + data = s26_write_data >> 16; + phy_reg_write(unit, phy_address, reg_address, data); + + reg_address = reg_address & 0x1e; + data = s26_write_data & 0xffff; + phy_reg_write(unit, phy_address, reg_address, data); + } + else + { + phy_reg_write(unit, phy_address, reg_address, data); + + phy_address = (0x17 & ((addr_temp >> 4) | 0x10)); + reg_address = ((addr_temp << 1) & 0x1e); + + data = s26_write_data & 0xffff; + phy_reg_write(unit, phy_address, reg_address, data); + + reg_address = (((addr_temp << 1) & 0x1e) | 0x1); + data = s26_write_data >> 16; + phy_reg_write(unit, phy_address, reg_address, data); + + } + } + else + { + phy_reg_write(unit,phy_address, reg_address, data); + + phy_address = (0x17 & ((addr_temp >> 4) | 0x10)); + + reg_address = (((addr_temp << 1) & 0x1e) | 0x1); + data = s26_write_data >> 16; + phy_reg_write(unit,phy_address, reg_address, data); + + reg_address = ((addr_temp << 1) & 0x1e); + data = s26_write_data & 0xffff; + phy_reg_write(unit,phy_address, reg_address, data); + } +} + +void athrs26_reg_rmw(unsigned int s26_addr, unsigned int s26_write_data) +{ + int val = athrs26_reg_read(s26_addr); + athrs26_reg_write(s26_addr,(val | s26_write_data)); +} + +unsigned int s26_rd_phy(unsigned int phy_addr, unsigned int reg_addr) +{ + + unsigned int rddata; + + if(phy_addr >= ATHR_PHY_MAX) { + DPRINTF("%s:Error invalid Phy Address:0x%x\n",__func__,phy_addr); + return -1 ; + } + + // MDIO_CMD is set for read + + rddata = athrs26_reg_read(0x98); + rddata = (rddata & 0x0) | (reg_addr<<16) | (phy_addr<<21) | (1<<27) | (1<<30) | (1<<31) ; + athrs26_reg_write(0x98, rddata); + + rddata = athrs26_reg_read(0x98); + rddata = rddata & (1<<31); + + // Check MDIO_BUSY status + while(rddata){ + rddata = athrs26_reg_read(0x98); + rddata = rddata & (1<<31); + } + + // Read the data from phy + + rddata = athrs26_reg_read(0x98) & 0xffff; + + return(rddata); +} + +void s26_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data) +{ + unsigned int rddata; + + if(phy_addr >= ATHR_PHY_MAX) { + DPRINTF("%s:Error invalid Phy Address:0x%x\n",__func__,phy_addr); + return ; + } + + // MDIO_CMD is set for read + + rddata = athrs26_reg_read(0x98); + rddata = (rddata & 0x0) | (write_data & 0xffff) | (reg_addr<<16) | (phy_addr<<21) | (0<<27) | (1<<30) | (1<<31) ; + athrs26_reg_write(0x98, rddata); + + rddata = athrs26_reg_read(0x98); + rddata = rddata & (1<<31); + + // Check MDIO_BUSY status + while(rddata){ + rddata = athrs26_reg_read(0x98); + rddata = rddata & (1<<31); + } + +} + +int athrs26_ioctl(struct net_device *dev,void *args, int cmd) +{ + struct ifreq * ifr = (struct ifreq *) args; + struct eth_cfg_params *ethcfg; + uint32_t ar7240_revid; + ag7240_mac_t *mac; + +#ifdef CONFIG_AR7240_S26_VLAN_IGMP + struct arl_struct * arl = (struct arl_struct *) (&ifr->ifr_ifru.ifru_mtu); + unsigned int vlan_value = ifr->ifr_ifru.ifru_ivalue; + unsigned short vlan_id = vlan_value >> 16; + unsigned short mode = vlan_value >> 16; + unsigned short vlan_port = vlan_value & 0x1f; + unsigned int flag = 0; + uint32_t ret = 0; +#endif + + ethcfg = (struct eth_cfg_params *)ifr->ifr_data; + + switch(cmd){ +#ifdef CONFIG_AR7240_S26_VLAN_IGMP + case S26_PACKET_FLAG: + printk("ag7240::S26_PACKET_FLAG %d \n",vlan_value); + set_packet_inspection_flag(vlan_value); + break; + + case S26_VLAN_ADDPORTS: + if(vlan_id>4095) return -EINVAL; + printk("ag7240::S26_ADD_PORT vid = %d ports=%x.\n",vlan_id,vlan_port); + ret = python_ioctl_vlan_addports(vlan_id,vlan_port); + break; + + case S26_VLAN_DELPORTS: + if(vlan_id>4095) return -EINVAL; + printk("ag7240::S26_DEL_PORT vid = %d ports=%x.\n",vlan_id,vlan_port); + ret = python_ioctl_vlan_delports(vlan_id,vlan_port); + break; + + case S26_VLAN_SETTAGMODE: + printk("ag7240::S26_VLAN_SETTAGMODE mode=%d portno=%d .\n",mode,vlan_port); + ret = python_port_egvlanmode_set(vlan_port,mode); + break; + + case S26_VLAN_SETDEFAULTID: + if(vlan_id>4095) return -EINVAL; + printk("ag7240::S26_VLAN_SETDEFAULTID vid = %d portno=%d.\n",vlan_id,vlan_port); + ret = python_port_default_vid_set(vlan_port,vlan_id); + break; + + case S26_IGMP_ON_OFF: + { + int tmp = 0; + tmp = vlan_value & (0x1 << 7); + vlan_port &= ~(0x1 << 7); + if(vlan_port>4) return -EINVAL; + if(tmp != 0){ + printk("ag7240::Enable IGMP snooping in port no %x.\n",vlan_port); + ret= python_port_igmps_status_set(vlan_port,1); + }else{ + printk("ag7240::Disable IGMP snooping in port no %x.\n",vlan_port); + ret= python_port_igmps_status_set(vlan_port,0); + } + } + break; + + case S26_LINK_GETSTAT: + if(vlan_port>4){/* if port=WAN */ + int fdx, phy_up; + ag7240_phy_speed_t speed; + ag7240_get_link_status(0, &phy_up, &fdx, &speed, 4); + ifr->ifr_ifru.ifru_ivalue = (speed<<16|fdx<<8|phy_up); + printk("ag7240::S26_LINK_GETSTAT portno WAN is %x.\n",ifr->ifr_ifru.ifru_ivalue); + }else if(vlan_port > 0){ + flag = athrs26_phy_is_link_alive(vlan_port-1); + ifr->ifr_ifru.ifru_ivalue = flag; + printk("ag7240::S26_LINK_GETSTAT portno %d is %s.\n",vlan_port,flag?"up":"down"); + }else{ + ifr->ifr_ifru.ifru_ivalue = 1; + } + /* PHY 0-4 <---> port 1-5 in user space. */ + break; + + case S26_VLAN_ENABLE: + python_ioctl_enable_vlan(); + printk("ag7240::S26_VLAN_ENABLE.\n"); + break; + + case S26_VLAN_DISABLE: + python_ioctl_disable_vlan(); + printk("ag7240::S26_VLAN_DISABLE.\n"); + break; + + case S26_ARL_ADD: + ret = python_fdb_add(arl->mac_addr,arl->port_map,arl->sa_drop); + printk("ag7240::S26_ARL_ADD,mac:[%x.%x.%x.%x.%x.%x] port[%x] drop %d\n", + arl->mac_addr.uc[0],arl->mac_addr.uc[1],arl->mac_addr.uc[2],arl->mac_addr.uc[3], + arl->mac_addr.uc[4],arl->mac_addr.uc[5],arl->port_map,arl->sa_drop); + break; + + case S26_ARL_DEL: + ret = python_fdb_del(arl->mac_addr); + printk("ag7240::S26_ARL_DEL mac:[%x.%x.%x.%x.%x.%x].\n",arl->mac_addr.uc[0],arl->mac_addr.uc[1], + arl->mac_addr.uc[2],arl->mac_addr.uc[3],arl->mac_addr.uc[4],arl->mac_addr.uc[5]); + break; + case S26_MCAST_CLR: + /* 0: switch off the unkown multicast packets over vlan. 1: allow the unknown multicaset packets over vlans. */ + if(!vlan_value) + python_clear_multi(); + else + python_set_multi(); + printk("athr_gmac::S26_MCAST_CLR --- %s.\n", vlan_value?"enable Multicast":"disable Multicast"); + break; +#endif + case S26_RD_PHY: + if(ethcfg->portnum != 0xf) + ethcfg->val = s26_rd_phy(ethcfg->portnum,ethcfg->phy_reg); + else + ethcfg->val = athrs26_reg_read(ethcfg->phy_reg); + break; + + case S26_WR_PHY: + if(ethcfg->portnum != 0xf) + s26_wr_phy(ethcfg->portnum,ethcfg->phy_reg,ethcfg->val); + else + athrs26_reg_write(ethcfg->phy_reg,ethcfg->val); + break; + case S26_FORCE_PHY: + printk("Duplex %d\n",ethcfg->duplex); + if(ethcfg->phy_reg < ATHR_PHY_MAX) { + if(ethcfg->val == 10) { + printk("Forcing 10Mbps %s on port:%d \n", + dup_str[ethcfg->duplex],(ethcfg->phy_reg)); + athrs26_force_10M(ethcfg->phy_reg,ethcfg->duplex); + }else if(ethcfg->val == 100) { + printk("Forcing 100Mbps %s on port:%d \n", + dup_str[ethcfg->duplex],(ethcfg->phy_reg)); + athrs26_force_100M(ethcfg->phy_reg,ethcfg->duplex); + }else if(ethcfg->val == 0) { + printk("Enabling Auto Neg on port:%d \n",(ethcfg->phy_reg)); + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { + s26_wr_phy(ethcfg->phy_reg,ATHR_DEBUG_PORT_ADDRESS,0x0); + s26_wr_phy(ethcfg->phy_reg,ATHR_DEBUG_PORT_DATA,0x2ee); + s26_wr_phy(ethcfg->phy_reg,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(ethcfg->phy_reg,ATHR_DEBUG_PORT_DATA,0x3a11); + } + s26_wr_phy(ethcfg->phy_reg,ATHR_PHY_CONTROL,0x9000); + }else + return -EINVAL; + + if(ATHR_ETHUNIT(ethcfg->phy_reg) == ENET_UNIT_WAN) { + if(mac_has_flag(ag7240_macs[ENET_UNIT_LAN],ETH_SWONLY_MODE)) + ag7240_check_link(ag7240_macs[ENET_UNIT_LAN],ethcfg->phy_reg); + else + ag7240_check_link(ag7240_macs[0],ethcfg->phy_reg); + }else{ + ag7240_check_link(ag7240_macs[1],ethcfg->phy_reg); + } + break; + } + default: + return -EINVAL; + } + return 0; +} + +int athrs26_mdc_check() +{ + int i; + + for (i=0; i<4000; i++) { + if(athrs26_reg_read(0x10c) != 0x18007fff) + return -1; + } + return 0; +} + +void ar7240_s26_intr(void) +{ + int status = 0, intr_reg_val; + uint32_t phyUnit = 0 ,phyBase = 0 ,phyAddr = 0; + uint32_t phymask = 0x0; + uint32_t linkDown = 0x0; + + athrs26_reg_write(S26_GLOBAL_INTR_MASK_REG,0x0); + + intr_reg_val = athrs26_reg_read(S26_GLOBAL_INTR_REG); + + /* clear global link interrupt */ + athrs26_reg_write(S26_GLOBAL_INTR_REG,intr_reg_val); + + if (intr_reg_val & PHY_LINK_CHANGE_REG) { + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + status = s26_rd_phy(phyAddr,ATHR_PHY_INTR_STATUS); + + if(status & PHY_LINK_UP) { + DPRINTF("LINK UP - Port %d:%x\n",phyAddr,status); + phymask = (phymask | (1 << phyUnit)); + } + if(status & PHY_LINK_DOWN) { + DPRINTF("LINK DOWN - Port %d:%x\n",phyAddr,status); + phymask = (phymask | (1 << phyUnit)); + linkDown = (linkDown | (1 << phyUnit)); + } + if(status & PHY_LINK_DUPLEX_CHANGE) { + DPRINTF("LINK DUPLEX CHANGE - Port %d:%x\n",phyAddr,status); + phymask = (phymask | (1 << phyUnit)); + } + if(status & PHY_LINK_SPEED_CHANGE) { + DPRINTF("LINK SPEED CHANGE %d:%x\n",phyAddr,status); + phymask = (phymask | (1 << phyUnit)); + } + } + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if ((phymask >> phyUnit) & 0x1) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + status = s26_rd_phy(phyAddr,ATHR_PHY_INTR_STATUS); + + if (!athrs26_phy_is_link_alive(phyUnit) && !((linkDown >> phyUnit) & 0x1)) + continue; + + if(ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN) { + if (mac_has_flag(ag7240_macs[ENET_UNIT_LAN],ETH_SWONLY_MODE)) + ag7240_check_link(ag7240_macs[ENET_UNIT_LAN],phyUnit); + else + ag7240_check_link(ag7240_macs[0],phyUnit); + } + else { + ag7240_check_link(ag7240_macs[1],phyUnit); + } + } + } + athrs26_reg_write(S26_GLOBAL_INTR_MASK_REG,PHY_LINK_CHANGE_REG); + } + else { + DPRINTF("Spurious link interrupt:%s,status:%x\n",__func__,status); + athrs26_reg_write(S26_GLOBAL_INTR_MASK_REG,PHY_LINK_CHANGE_REG); + } +} diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.c.new b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.c.new new file mode 100644 index 0000000..e1653ce --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.c.new @@ -0,0 +1,1386 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "ag7240_phy.h" +#include "ar7240_s26_phy.h" +#include "eth_diag.h" +/* PHY selections and access functions */ + +typedef enum { + PHY_SRCPORT_INFO, + PHY_PORTINFO_SIZE, +} PHY_CAP_TYPE; + +typedef enum { + PHY_SRCPORT_NONE, + PHY_SRCPORT_VLANTAG, + PHY_SRCPORT_TRAILER, +} PHY_SRCPORT_TYPE; + +#ifdef CONFIG_AR7240_S26_VLAN_IGMP +#define PYTHON_OK 0 +#define PYTHON_ERR 1 +#define PYTHON_FULL 2 +#endif + +#define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6) +#define DRV_MSG(x,a,b,c,d,e,f) +#define DRV_PRINT(DBG_SW,X) + +#define ATHR_LAN_PORT_VLAN 1 +#define ATHR_WAN_PORT_VLAN 2 +#define ENET_UNIT_LAN 1 +#define ENET_UNIT_WAN 0 + +#define TRUE 1 +#define FALSE 0 + +#define ATHR_PHY0_ADDR 0x0 +#define ATHR_PHY1_ADDR 0x1 +#define ATHR_PHY2_ADDR 0x2 +#define ATHR_PHY3_ADDR 0x3 +#define ATHR_PHY4_ADDR 0x4 + +#define MODULE_NAME "ATHRS26" +MODULE_LICENSE("Dual BSD/GPL"); + +#ifdef S26_PHY_DEBUG +#define DPRINTF(_fmt,...) do { \ + printk(MODULE_NAME":"_fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(_fmt,...) +#endif + +#ifdef ETH_SOFT_LED +extern ATH_LED_CONTROL PLedCtrl; +extern atomic_t Ledstatus; +#endif +extern int phy_in_reset; + +/* + * Track per-PHY port information. + */ +typedef struct { + BOOL isEnetPort; /* normal enet port */ + BOOL isPhyAlive; /* last known state of link */ + int ethUnit; /* MAC associated with this phy port */ + uint32_t phyBase; + uint32_t phyAddr; /* PHY registers associated with this phy port */ + uint32_t VLANTableSetting; /* Value to be written to VLAN table */ +} athrPhyInfo_t; + +/* + * Per-PHY information, indexed by PHY unit number. + */ +static athrPhyInfo_t athrPhyInfo[] = { + + {TRUE, /* port 1 -- LAN port 1 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY0_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 2 -- LAN port 2 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY1_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 3 -- LAN port 3 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY2_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 4 -- LAN port 4 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY3_ADDR, + ATHR_LAN_PORT_VLAN /* Send to all ports */ + }, + + {TRUE, /* port 5 -- WAN Port 5 */ + FALSE, + ENET_UNIT_WAN, + 0, + ATHR_PHY4_ADDR, + ATHR_LAN_PORT_VLAN /* Send to all ports */ + }, + + {FALSE, /* port 0 -- cpu port 0 */ + TRUE, + ENET_UNIT_LAN, + 0, + 0x00, + ATHR_LAN_PORT_VLAN + }, + +}; + +static uint8_t athr26_init_flag = 0,athr26_init_flag1 = 0; +static DECLARE_WAIT_QUEUE_HEAD (hd_conf_wait); + +#define ATHR_GLOBALREGBASE 0 + +#define ATHR_PHY_MAX 5 + +/* Range of valid PHY IDs is [MIN..MAX] */ +#define ATHR_ID_MIN 0 +#define ATHR_ID_MAX (ATHR_PHY_MAX-1) + +/* Convenience macros to access myPhyInfo */ +#define ATHR_IS_ENET_PORT(phyUnit) (athrPhyInfo[phyUnit].isEnetPort) +#define ATHR_IS_PHY_ALIVE(phyUnit) (athrPhyInfo[phyUnit].isPhyAlive) +#define ATHR_ETHUNIT(phyUnit) (athrPhyInfo[phyUnit].ethUnit) +#define ATHR_PHYBASE(phyUnit) (athrPhyInfo[phyUnit].phyBase) +#define ATHR_PHYADDR(phyUnit) (athrPhyInfo[phyUnit].phyAddr) +#define ATHR_VLAN_TABLE_SETTING(phyUnit) (athrPhyInfo[phyUnit].VLANTableSetting) + + +#define ATHR_IS_ETHUNIT(phyUnit, ethUnit) \ + (ATHR_IS_ENET_PORT(phyUnit) && \ + ATHR_ETHUNIT(phyUnit) == (ethUnit)) + +#define ATHR_IS_WAN_PORT(phyUnit) (!(ATHR_ETHUNIT(phyUnit)==ENET_UNIT_LAN)) + +/* Forward references */ +BOOL athrs26_phy_is_link_alive(int phyUnit); +unsigned int s26_rd_phy(unsigned int phy_addr, unsigned int reg_addr); +void s26_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data); +extern int ag7240_check_link(ag7240_mac_t *mac,int phyUnit); + +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +static uint16_t port_def_vid[5] = {1, 1, 1, 1, 1}; +static uint8_t cpu_egress_tagged_flag = 0; + +inline uint8_t is_cpu_egress_tagged(void) +{ + return cpu_egress_tagged_flag; +} + +void set_cpu_egress_tagged(uint8_t is_tagged) +{ + cpu_egress_tagged_flag = is_tagged; +} + +inline uint16_t athrs26_defvid_get(uint32_t port_id) +{ + if ((port_id == 0) || (port_id > 5)) + return 0; + + return port_def_vid[port_id - 1]; +} + +BOOL athrs26_defvid_set(uint32_t port_id, uint16_t def_vid) +{ + if ((def_vid == 0) || (def_vid > 4094)) + return FALSE; + + if ((port_id == 0) || (port_id > 5)) + return FALSE; + + port_def_vid[port_id - 1] = def_vid; + return TRUE; +} +#endif + +void athrs26_powersave_off(int phy_addr) +{ + s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_ADDRESS,0x29); + s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_DATA,0x36c0); + +} + +void athrs26_sleep_off(int phy_addr) +{ + s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_ADDRESS,0xb); + s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_DATA,0x3c00); +} + +void athrs26_enable_linkIntrs(int ethUnit) +{ + int phyUnit = 0, phyAddr = 0; + ag7240_mac_t *mac = ag7240_macs[ethUnit]; + + /* Enable global PHY link status interrupt */ + athrs26_reg_write(S26_GLOBAL_INTR_MASK_REG,PHY_LINK_CHANGE_REG); + + if (mac_has_flag(mac,ETH_SWONLY_MODE)) { + if (ethUnit == 1) { + for (phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + phyAddr = ATHR_PHYADDR(phyUnit); + s26_wr_phy(phyAddr,ATHR_PHY_INTR_ENABLE,PHY_LINK_INTRS); + } + } + return ; + } + + if (ethUnit == ENET_UNIT_WAN) { + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_INTR_ENABLE,PHY_LINK_INTRS); + } + else { + for (phyUnit = 0; phyUnit < ATHR_PHY_MAX - 1; phyUnit++) { + phyAddr = ATHR_PHYADDR(phyUnit); + s26_wr_phy(phyAddr,ATHR_PHY_INTR_ENABLE,PHY_LINK_INTRS); + } + } +} + +void athrs26_disable_linkIntrs(int ethUnit) +{ + int phyUnit = 0, phyAddr = 0; + ag7240_mac_t *mac = ag7240_macs[ethUnit]; + + if (mac_has_flag(mac,ETH_SWONLY_MODE)) { + if (ethUnit == 1) { + for (phyUnit = 0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + phyAddr = ATHR_PHYADDR(phyUnit); + s26_wr_phy(phyAddr,ATHR_PHY_INTR_ENABLE,0x0); + } + } + return ; + } + + if (ethUnit == ENET_UNIT_WAN) { + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_INTR_ENABLE,0x0); + } + else { + for (phyUnit = 0; phyUnit < ATHR_PHY_MAX - 1; phyUnit++) { + phyAddr = ATHR_PHYADDR(phyUnit); + s26_wr_phy(phyAddr,ATHR_PHY_INTR_ENABLE,0x0); + } + } +} + +void athrs26_force_100M(int phyAddr,int duplex) +{ + uint32_t ar7240_revid; + +#ifdef ETH_SOFT_LED + /* Resetting PHY will disable MDIO access needed by soft LED task. + * Hence, Do not reset PHY until Soft LED task get completed. + */ + while(atomic_read(&Ledstatus) == 1); + PLedCtrl.ledlink[phyAddr] = 0; +#endif + phy_in_reset = 1; + /* + * Force MDI and MDX to alternate ports + * Phy 0,2 and 4 -- MDI + * Phy 1 and 3 -- MDX + */ + if(phyAddr%2) { + s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x820); + } + else { + s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x800); + } + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x0); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x2ee); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x3a11); + } + s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,(0xa000|(duplex << 8))); + phy_in_reset = 0; +} + +void athrs26_force_10M(int phyAddr,int duplex) +{ + uint32_t ar7240_revid; +#ifdef ETH_SOFT_LED + /* Resetting PHY will disable MDIO access needed by soft LED task. + * Hence, Do not reset PHY until Soft LED task get completed. + */ + while(atomic_read(&Ledstatus) == 1); + PLedCtrl.ledlink[phyAddr] = 0; +#endif + phy_in_reset = 1; + + athrs26_powersave_off(phyAddr); + athrs26_sleep_off(phyAddr); + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { + s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x800); + s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,0x8100); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x0); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x12ee); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x3af0); + } + s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,(0x8000 |(duplex << 8))); + phy_in_reset = 0; +} + +void athrs26_reg_init(int ethUnit) +{ + uint32_t ar7240_revid; + uint32_t rd_data; + ag7240_mac_t *mac = ag7240_macs[ethUnit]; + + /* if using header for register configuration, we have to */ + /* configure s26 register after frame transmission is enabled */ + if (athr26_init_flag) + return; + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { +#ifdef S26_FORCE_100M + athrs26_force_100M(ATHR_PHY4_ADDR,1); +#endif +#ifdef S26_FORCE_10M + athrs26_force_10M(ATHR_PHY4_ADDR,1); +#endif + } else { + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL,0x9000); + + /* Class A setting for 100M */ + s26_wr_phy(ATHR_PHY4_ADDR, 29, 5); + s26_wr_phy(ATHR_PHY4_ADDR, 30, (s26_rd_phy(ATHR_PHY4_ADDR, 30)&((~2)&0xffff))); + } + + /* Enable flow control on CPU port */ + athrs26_reg_write(PORT_STATUS_REGISTER0, + (athrs26_reg_read(PORT_STATUS_REGISTER0) | 0x30)); + + /* Disable WAN mac inside S26 */ + athrs26_reg_write(PORT_STATUS_REGISTER5,0x0); + + /* Enable WAN mac inside S26 */ + if (mac_has_flag(mac,ETH_SWONLY_MODE)) + athrs26_reg_write(PORT_STATUS_REGISTER5,0x200); + + /* Enable MDIO Access if PHY is Powered-down */ + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_ADDRESS,0x3); + rd_data = s26_rd_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_DATA); + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_DATA,(rd_data & 0xfffffeff) ); + + if (mac_has_flag(mac,ATHR_S26_HEADER)) + athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x4804); + else + athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x4004); + + athr26_init_flag = 1; +} + +void athrs26_reg_init_lan(int ethUnit) +{ + int i = 60; + int phyUnit; + uint32_t phyBase = 0; + BOOL foundPhy = FALSE; + uint32_t phyAddr = 0; + uint32_t queue_ctrl_reg = 0; + uint32_t ar7240_revid; + uint32_t rd_data; + ag7240_mac_t *mac = ag7240_macs[ethUnit]; + + /* if using header for register configuration, we have to */ + /* configure s26 register after frame transmission is enabled */ + if (athr26_init_flag1) + return; + + /* reset switch */ + printk(MODULE_NAME ": resetting s26\n"); + athrs26_reg_write(0x0, athrs26_reg_read(0x0)|0x80000000); + + while(i--) { + mdelay(100); + if(!(athrs26_reg_read(0x0)&0x80000000)) + break; + } + printk(MODULE_NAME ": s26 reset done\n"); + + /* 100M Half Duplex Throughput setting */ + athrs26_reg_write(0xe4,0x079c1040); + + /* Change HOL settings + * 25: PORT_QUEUE_CTRL_ENABLE. + * 24: PRI_QUEUE_CTRL_EN. + */ + athrs26_reg_write(0x118,0x0032b5555); + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + + if ((ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN) && + !mac_has_flag(mac,ETH_SWONLY_MODE)) + continue; + + foundPhy = TRUE; + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { +#ifdef S26_FORCE_100M + athrs26_force_100M(phyAddr,1); +#endif +#ifdef S26_FORCE_10M + athrs26_force_10M(phyAddr,1); +#endif + } + else { + s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,0x9000); + + /* Class A setting for 100M */ + s26_wr_phy(phyAddr, 29, 5); + s26_wr_phy(phyAddr, 30, (s26_rd_phy(phyAddr, 30)&((~2)&0xffff))); + } + + /* Enable MDIO Access if PHY is Powered-down */ + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3); + rd_data = s26_rd_phy(phyAddr,ATHR_DEBUG_PORT_DATA); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,(rd_data & 0xfffffeff) ); + + /* Change HOL settings + * 25: PORT_QUEUE_CTRL_ENABLE. + * 24: PRI_QUEUE_CTRL_EN. + */ + queue_ctrl_reg = (0x100 * phyUnit) + 0x218 ; + athrs26_reg_write(queue_ctrl_reg,0x032b5555); + + DPRINTF("S26 ATHR_PHY_FUNC_CONTROL (%d):%x\n",phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_FUNC_CONTROL)); + DPRINTF("S26 PHY ID (%d) :%x\n",phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_ID1)); + DPRINTF("S26 PHY CTRL (%d) :%x\n",phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_CONTROL)); + DPRINTF("S26 ATHR PHY STATUS (%d) :%x\n",phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_STATUS)); + } + + /* + * CPU port Enable + */ + + /* + * status[1:0]=2'h2; - (0x10 - 1000 Mbps , 0x01 - 100Mbps, 0x0 - 10 Mbps) + * status[2]=1'h1; - Tx Mac En + * status[3]=1'h1; - Rx Mac En + * status[4]=1'h1; - Tx Flow Ctrl En + * status[5]=1'h1; - Rx Flow Ctrl En + * status[6]=1'h1; - Duplex Mode + */ + athrs26_reg_write(PORT_STATUS_REGISTER1, 0x200); /* LAN - 1 */ + athrs26_reg_write(PORT_STATUS_REGISTER2, 0x200); /* LAN - 2 */ + athrs26_reg_write(PORT_STATUS_REGISTER3, 0x200); /* LAN - 3 */ + athrs26_reg_write(PORT_STATUS_REGISTER4, 0x200); /* LAN - 4 */ + + /* QM Control */ + athrs26_reg_write(0x38, 0xc000050e); + + /* + * status[11]=1'h0; - CPU Disable + * status[7] = 1'b1; - Learn One Lock + * status[14] = 1'b0; - Learn Enable + */ + if (mac_has_flag(mac,ATHR_S26_HEADER)) + athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x4804); + else + athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x4004); + + /* Tag Priority Mapping */ + athrs26_reg_write(0x70, 0xfa50); + + /* Enable ARP packets to CPU port */ + athrs26_reg_write(S26_ARL_TBL_CTRL_REG,(athrs26_reg_read(S26_ARL_TBL_CTRL_REG) | 0x100000)); + + /* Enable Broadcast packets to CPU port */ + athrs26_reg_write(S26_FLD_MASK_REG,(athrs26_reg_read(S26_FLD_MASK_REG) | S26_ENABLE_CPU_BROADCAST )); + + /* Turn on back pressure */ + athrs26_reg_write(PORT_STATUS_REGISTER0, + (athrs26_reg_read(PORT_STATUS_REGISTER0) | 0x80)); + athrs26_reg_write(PORT_STATUS_REGISTER1, + (athrs26_reg_read(PORT_STATUS_REGISTER1) | 0x80)); + athrs26_reg_write(PORT_STATUS_REGISTER2, + (athrs26_reg_read(PORT_STATUS_REGISTER2) | 0x80)); + athrs26_reg_write(PORT_STATUS_REGISTER3, + (athrs26_reg_read(PORT_STATUS_REGISTER3) | 0x80)); + athrs26_reg_write(PORT_STATUS_REGISTER4, + (athrs26_reg_read(PORT_STATUS_REGISTER4) | 0x80)); + + DPRINTF("S26 CPU_PORT_REGISTER :%x\n", athrs26_reg_read ( CPU_PORT_REGISTER )); + DPRINTF("S26 PORT_STATUS_REGISTER0 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER0 )); + DPRINTF("S26 PORT_STATUS_REGISTER1 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER1 )); + DPRINTF("S26 PORT_STATUS_REGISTER2 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER2 )); + DPRINTF("S26 PORT_STATUS_REGISTER3 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER3 )); + DPRINTF("S26 PORT_STATUS_REGISTER4 :%x\n", athrs26_reg_read ( PORT_STATUS_REGISTER4 )); + + DPRINTF("S26 PORT_CONTROL_REGISTER0 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER0 )); + DPRINTF("S26 PORT_CONTROL_REGISTER1 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER1 )); + DPRINTF("S26 PORT_CONTROL_REGISTER2 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER2 )); + DPRINTF("S26 PORT_CONTROL_REGISTER3 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER3 )); + DPRINTF("S26 PORT_CONTROL_REGISTER4 :%x\n", athrs26_reg_read ( PORT_CONTROL_REGISTER4 )); + + if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) { + athrs26_reg_write(ATHR_PRI_CTRL_PORT_2,(athrs26_reg_read(ATHR_PRI_CTRL_PORT_2)|ATHR_TOS_PRI_EN)); + athrs26_reg_write(ATHR_PRI_CTRL_PORT_3,(athrs26_reg_read(ATHR_PRI_CTRL_PORT_3)|ATHR_TOS_PRI_EN)); + athrs26_reg_write(ATHR_PRI_CTRL_PORT_4,(athrs26_reg_read(ATHR_PRI_CTRL_PORT_4)|ATHR_TOS_PRI_EN)); + athrs26_reg_write(ATHR_PRI_CTRL_PORT_5,(athrs26_reg_read(ATHR_PRI_CTRL_PORT_5)|ATHR_TOS_PRI_EN)); + athrs26_reg_write(ATHR_QOS_MODE_REGISTER,ATHR_QOS_FIXED_PRIORITY); + + } + /* Disable WAN mac inside S26 */ + athrs26_reg_write(PORT_STATUS_REGISTER5,0x0); +#ifdef CONFIG_AR7240_S26_VLAN_IGMP + /* Set Max MTU to 1518+6 for vlan and header space. */ + athrs26_reg_write(0x30,(athrs26_reg_read(0x30)&0xfffff800)|0x5f4); +#endif + if(mac_has_flag(mac,ATHR_S26_HEADER)) + /* Set CPU port0 to Atheros Header Enable. */ + athrs26_reg_write(0x104,athrs26_reg_read(0x104)|(0x1<<11)); + /* Enable WAN mac inside S26 */ + if (mac_has_flag(mac,ETH_SWONLY_MODE)) + athrs26_reg_write(PORT_STATUS_REGISTER5,0x200); + + athr26_init_flag1 = 1; +} +static unsigned int phy_val_saved = 0; +/****************************************************************************** +* +* athrs26_phy_off - power off the phy to change its speed +* +* Power off the phy +*/ +void athrs26_phy_off(ag7240_mac_t *mac) +{ + struct net_device *dev = mac->mac_dev; + + if (mac->mac_unit == ENET_UNIT_LAN) + return; + + netif_carrier_off(dev); + netif_stop_queue(dev); + + phy_val_saved = s26_rd_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL); + s26_wr_phy( ATHR_PHY4_ADDR, ATHR_PHY_CONTROL, phy_val_saved | 0x800); +} + +/****************************************************************************** +* +* athrs26_phy_on - power on the phy after speed changed +* +* Power on the phy +*/ +void athrs26_phy_on(ag7240_mac_t *mac) +{ + if ((mac->mac_unit == ENET_UNIT_LAN) || (phy_val_saved == 0)) + return; + + s26_wr_phy( ATHR_PHY4_ADDR, ATHR_PHY_CONTROL, phy_val_saved & 0xf7ff); + + mdelay(2000); +} + +/****************************************************************************** +* +* athrs26_mac_speed_set - set mac in s26 speed mode (actually RMII mode) +* +* Set mac speed mode +*/ +void athrs26_mac_speed_set(ag7240_mac_t *mac, ag7240_phy_speed_t speed) +{ + if ((mac->mac_unit == ENET_UNIT_LAN)) + return; + + switch (speed) { + case AG7240_PHY_SPEED_100TX: + athrs26_reg_write (0x600, 0x7d); + break; + + case AG7240_PHY_SPEED_10T: + athrs26_reg_write (0x600, 0x7c); + break; + + default: + break; + } +} + +/****************************************************************************** +* +* athrs26_phy_is_link_alive - test to see if the specified link is alive +* +* RETURNS: +* TRUE --> link is alive +* FALSE --> link is down +*/ +BOOL +athrs26_phy_is_link_alive(int phyUnit) +{ + uint16_t phyHwStatus; + uint32_t phyBase; + uint32_t phyAddr; + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + phyHwStatus = s26_rd_phy( phyAddr, ATHR_PHY_SPEC_STATUS); + if (phyHwStatus & ATHR_STATUS_LINK_PASS) + return TRUE; + + return FALSE; +} + +/****************************************************************************** +* +* athrs26_phy_setup - reset and setup the PHY associated with +* the specified MAC unit number. +* +* Resets the associated PHY port. +* +* RETURNS: +* TRUE --> associated PHY is alive +* FALSE --> no LINKs on this ethernet unit +*/ + +BOOL +athrs26_phy_setup(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus; + uint16_t timeout; + int liveLinks = 0; + uint32_t phyBase = 0; + BOOL foundPhy = FALSE; + uint32_t phyAddr = 0; + uint32_t ar7240_revid; + + + /* See if there's any configuration data for this enet */ + /* start auto negogiation on each phy */ + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + + foundPhy = TRUE; + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + s26_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT,ATHR_ADVERTISE_ALL); + DPRINTF("%s ATHR_AUTONEG_ADVERT %d :%x\n",__func__,phyAddr, + s26_rd_phy(phyAddr,ATHR_AUTONEG_ADVERT)); + + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + if(ar7240_revid != AR7240_REV_1_0) { + s26_wr_phy( phyAddr, ATHR_PHY_CONTROL,ATHR_CTRL_AUTONEGOTIATION_ENABLE + | ATHR_CTRL_SOFTWARE_RESET); + } + + DPRINTF("%s ATHR_PHY_CONTROL %d :%x\n",__func__,phyAddr, + s26_rd_phy(phyAddr,ATHR_PHY_CONTROL)); + } + + if (!foundPhy) { + return FALSE; /* No PHY's configured for this ethUnit */ + } + + /* + * After the phy is reset, it takes a little while before + * it can respond properly. + */ + if (ethUnit == ENET_UNIT_LAN) + mdelay(1000); + else + mdelay(3000); + + /* + * Wait up to 3 seconds for ALL associated PHYs to finish + * autonegotiation. The only way we get out of here sooner is + * if ALL PHYs are connected AND finish autonegotiation. + */ + for (phyUnit=0; (phyUnit < ATHR_PHY_MAX) /*&& (timeout > 0) */; phyUnit++) { + + if ((ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN) && + !mac_has_flag(ag7240_macs[ENET_UNIT_LAN],ETH_SWONLY_MODE)) + continue; + + timeout=20; + for (;;) { + phyHwStatus = s26_rd_phy(phyAddr, ATHR_PHY_CONTROL); + + if (ATHR_RESET_DONE(phyHwStatus)) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Neg Success\n", phyUnit)); + break; + } + if (timeout == 0) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Negogiation timeout\n", phyUnit)); + break; + } + if (--timeout == 0) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Negogiation timeout\n", phyUnit)); + break; + } + + mdelay(150); + } + + /* fix IOT */ + s26_wr_phy(phyUnit, 29, 0x14); + s26_wr_phy( phyUnit, 30, 0x1352); + +#ifdef S26_VER_1_0 + /* turn off power saving */ + s26_wr_phy(phyUnit, 29, 41); + s26_wr_phy(phyUnit, 30, 0); + printk("def_ S26_VER_1_0\n"); +#endif + } + + /* + * All PHYs have had adequate time to autonegotiate. + * Now initialize software status. + * + * It's possible that some ports may take a bit longer + * to autonegotiate; but we can't wait forever. They'll + * get noticed by mv_phyCheckStatusChange during regular + * polling activities. + */ + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + if (athrs26_phy_is_link_alive(phyUnit)) { + liveLinks++; + ATHR_IS_PHY_ALIVE(phyUnit) = TRUE; + } else { + ATHR_IS_PHY_ALIVE(phyUnit) = FALSE; + } + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("eth%d: Phy Specific Status=%4.4x\n", + ethUnit, + s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS))); + } + + return (liveLinks > 0); +} + +/****************************************************************************** +* +* athrs26_phy_is_fdx - Determines whether the phy ports associated with the +* specified device are FULL or HALF duplex. +* +* RETURNS: +* 1 --> FULL +* 0 --> HALF +*/ +int +athrs26_phy_is_fdx(int ethUnit,int phyUnit) +{ + uint32_t phyBase; + uint32_t phyAddr; + uint16_t phyHwStatus; + int ii = 200; + + if (athrs26_phy_is_link_alive(phyUnit)) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + do { + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + mdelay(10); + } while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii); + + if (phyHwStatus & ATHER_STATUS_FULL_DUPLEX) + return TRUE; + } + + return FALSE; +} + +/****************************************************************************** +* +* athrs26_phy_stab_wr - Function to Address 100Mbps stability issue, as +* suggested by EBU. +* +* RETURNS: none +* +*/ +void athrs26_phy_stab_wr(int phy_id, BOOL phy_up, int phy_speed) +{ + if( phy_up && (phy_speed == AG7240_PHY_SPEED_100TX)) { + s26_wr_phy(ATHR_PHYADDR(phy_id), ATHR_DEBUG_PORT_ADDRESS, 0x18); + s26_wr_phy(ATHR_PHYADDR(phy_id), ATHR_DEBUG_PORT_DATA, 0xBA8); + } else { + s26_wr_phy(ATHR_PHYADDR(phy_id), ATHR_DEBUG_PORT_ADDRESS, 0x18); + s26_wr_phy(ATHR_PHYADDR(phy_id), ATHR_DEBUG_PORT_DATA, 0x2EA); + } +} + +/****************************************************************************** +* +* athrs26_phy_speed - Determines the speed of phy ports associated with the +* specified device. +* +* RETURNS: +* AG7240_PHY_SPEED_10T, AG7240_PHY_SPEED_100TX; +* AG7240_PHY_SPEED_1000T; +*/ + +int +athrs26_phy_speed(int ethUnit,int phyUnit) +{ + uint16_t phyHwStatus; + uint32_t phyBase; + uint32_t phyAddr; + int ii = 200; + + + if (athrs26_phy_is_link_alive(phyUnit)) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + do { + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + mdelay(10); + } while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii); + + phyHwStatus = ((phyHwStatus & ATHER_STATUS_LINK_MASK) >> + ATHER_STATUS_LINK_SHIFT); + + switch(phyHwStatus) { + case 0: + return AG7240_PHY_SPEED_10T; + case 1: + return AG7240_PHY_SPEED_100TX; + case 2: + return AG7240_PHY_SPEED_1000T; + default: + printk("Unkown speed read!\n"); + } + } + + return AG7240_PHY_SPEED_10T; +} + +/***************************************************************************** +* +* athr_phy_is_up -- checks for significant changes in PHY state. +* +* A "significant change" is: +* dropped link (e.g. ethernet cable unplugged) OR +* autonegotiation completed + link (e.g. ethernet cable plugged in) +* +* When a PHY is plugged in, phyLinkGained is called. +* When a PHY is unplugged, phyLinkLost is called. +*/ + +int +athrs26_phy_is_up(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus, phyHwControl; + athrPhyInfo_t *lastStatus; + int linkCount = 0; + int lostLinks = 0; + int gainedLinks = 0; + uint32_t phyBase; + uint32_t phyAddr; + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + + if(mac_has_flag(ag7240_macs[ENET_UNIT_LAN],ETH_SWONLY_MODE)) + ethUnit = ENET_UNIT_LAN; + + if (!ATHR_IS_ETHUNIT(phyUnit,ethUnit)) + continue; + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + lastStatus = &athrPhyInfo[phyUnit]; + + if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */ + + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + + /* See if we've lost link */ + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { /* check realtime link */ + linkCount++; + } else { + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_STATUS); + /* If realtime failed check link in latch register before + * asserting link down. + */ + if (phyHwStatus & ATHR_LATCH_LINK_PASS) + linkCount++; + else + lostLinks++; + DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d down\n", + ethUnit, phyUnit)); + lastStatus->isPhyAlive = FALSE; + } + } else { /* last known link status was DEAD */ + + /* Check for reset complete */ + + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_STATUS); + + if (!ATHR_RESET_DONE(phyHwStatus)) + continue; + + phyHwControl = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_CONTROL); + + /* Check for AutoNegotiation complete */ + + if ((!(phyHwControl & ATHR_CTRL_AUTONEGOTIATION_ENABLE)) + || ATHR_AUTONEG_DONE(phyHwStatus)) { + phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { + gainedLinks++; + linkCount++; + DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d up\n", + ethUnit, phyUnit)); + lastStatus->isPhyAlive = TRUE; + } + } + } + } + return (linkCount); + +} + +void athrs26_reg_dev(ag7240_mac_t **mac) +{ + if( mac[0]) { + ag7240_macs[0] = mac[0]; + ag7240_macs[0]->mac_speed = 0xff; + } + else + printk("MAC [0] not registered \n"); + + if( mac[1]) { + ag7240_macs[1] = mac[1]; + ag7240_macs[1]->mac_speed = 0xff; + } + else + printk("MAC [1] not registered \n"); + return; + +} + +uint32_t athrs26_reg_read(unsigned int s26_addr) +{ + unsigned int addr_temp; + unsigned int s26_rd_csr_low, s26_rd_csr_high, s26_rd_csr; + unsigned int data,unit = 0; + unsigned int phy_address, reg_address; + + addr_temp = (s26_addr & 0xfffffffc) >>2; + data = addr_temp >> 7; + + phy_address = 0x1f; + reg_address = 0x10; + + if (is_ar7240()) { + unit = 0; + } + else if(is_ar7241() || is_ar7242()) { + unit = 1; + } + + phy_reg_write(unit,phy_address, reg_address, data); + + phy_address = (0x17 & ((addr_temp >> 4) | 0x10)); + reg_address = ((addr_temp << 1) & 0x1e); + s26_rd_csr_low = (uint32_t) phy_reg_read(unit,phy_address, reg_address); + + reg_address = reg_address | 0x1; + s26_rd_csr_high = (uint32_t) phy_reg_read(unit,phy_address, reg_address); + s26_rd_csr = (s26_rd_csr_high << 16) | s26_rd_csr_low ; + + return(s26_rd_csr); +} + +void athrs26_reg_write(unsigned int s26_addr, unsigned int s26_write_data) +{ + unsigned int addr_temp; + unsigned int data; + unsigned int phy_address, reg_address,unit = 0; + + + addr_temp = (s26_addr & 0xfffffffc) >>2; + data = addr_temp >> 7; + + phy_address = 0x1f; + reg_address = 0x10; + + if (is_ar7240()) { + unit = 0; + } + else if(is_ar7241() || is_ar7242()) { + unit = 1; + } + + phy_reg_write(unit,phy_address, reg_address, data); + + phy_address = (0x17 & ((addr_temp >> 4) | 0x10)); + + reg_address = (((addr_temp << 1) & 0x1e) | 0x1); + data = s26_write_data >> 16; + phy_reg_write(unit,phy_address, reg_address, data); + + reg_address = ((addr_temp << 1) & 0x1e); + data = s26_write_data & 0xffff; + phy_reg_write(unit,phy_address, reg_address, data); +} + +void athrs26_reg_rmw(unsigned int s26_addr, unsigned int s26_write_data) +{ + int val = athrs26_reg_read(s26_addr); + athrs26_reg_write(s26_addr,(val | s26_write_data)); +} + +unsigned int s26_rd_phy(unsigned int phy_addr, unsigned int reg_addr) +{ + + unsigned int rddata; + + if(phy_addr >= ATHR_PHY_MAX) { + DPRINTF("%s:Error invalid Phy Address:0x%x\n",__func__,phy_addr); + return -1 ; + } + + /* MDIO_CMD is set for read */ + + rddata = athrs26_reg_read(0x98); + rddata = (rddata & 0x0) | (reg_addr<<16) + | (phy_addr<<21) | (1<<27) + | (1<<30) | (1<<31) ; + + athrs26_reg_write(0x98, rddata); + + rddata = athrs26_reg_read(0x98); + rddata = rddata & (1<<31); + + /* Check MDIO_BUSY status */ + while(rddata){ + rddata = athrs26_reg_read(0x98); + rddata = rddata & (1<<31); + } + + /* Read the data from phy */ + + rddata = athrs26_reg_read(0x98) & 0xffff; + + return(rddata); +} + +void s26_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data) +{ + unsigned int rddata; + + if(phy_addr >= ATHR_PHY_MAX) { + DPRINTF("%s:Error invalid Phy Address:0x%x\n",__func__,phy_addr); + return ; + } + + /* MDIO_CMD is set for read */ + + rddata = athrs26_reg_read(0x98); + rddata = (rddata & 0x0) | (write_data & 0xffff) + | (reg_addr<<16) | (phy_addr<<21) + | (0<<27) | (1<<30) | (1<<31) ; + + athrs26_reg_write(0x98, rddata); + + rddata = athrs26_reg_read(0x98); + rddata = rddata & (1<<31); + + /* Check MDIO_BUSY status */ + while(rddata){ + rddata = athrs26_reg_read(0x98); + rddata = rddata & (1<<31); + } + +} +int athrs26_ioctl(struct net_device *dev,void *args, int cmd) +{ + struct ifreq * ifr = (struct ifreq *) args; + struct eth_cfg_params *ethcfg; + uint32_t ar7240_revid; + ag7240_mac_t *mac; + +#ifdef CONFIG_AR7240_S26_VLAN_IGMP + struct arl_struct * arl = (struct arl_struct *) (&ifr->ifr_ifru.ifru_mtu); + unsigned int vlan_value = ifr->ifr_ifru.ifru_ivalue; + unsigned short vlan_id = vlan_value >> 16; + unsigned short mode = vlan_value >> 16; + unsigned short vlan_port = vlan_value & 0x1f; + unsigned int flag = 0; + uint32_t ret = 0; +#endif + + ethcfg = (struct eth_cfg_params *)ifr->ifr_data; + + switch(cmd){ +#ifdef CONFIG_AR7240_S26_VLAN_IGMP + case S26_PACKET_FLAG: + printk("ag7240::S26_PACKET_FLAG %d \n",vlan_value); + set_packet_inspection_flag(vlan_value); + break; + + case S26_VLAN_ADDPORTS: + if(vlan_id>4095) return -EINVAL; + printk("ag7240::S26_ADD_PORT vid = %d ports=%x.\n",vlan_id,vlan_port); + ret = python_ioctl_vlan_addports(vlan_id,vlan_port); + break; + + case S26_VLAN_DELPORTS: + if(vlan_id>4095) return -EINVAL; + printk("ag7240::S26_DEL_PORT vid = %d ports=%x.\n",vlan_id,vlan_port); + ret = python_ioctl_vlan_delports(vlan_id,vlan_port); + break; + + case S26_VLAN_SETTAGMODE: + printk("ag7240::S26_VLAN_SETTAGMODE mode=%d portno=%d .\n",mode,vlan_port); + ret = python_port_egvlanmode_set(vlan_port,mode); + break; + + case S26_VLAN_SETDEFAULTID: + if(vlan_id>4095) return -EINVAL; + printk("ag7240::S26_VLAN_SETDEFAULTID vid = %d portno=%d.\n",vlan_id,vlan_port); + ret = python_port_default_vid_set(vlan_port,vlan_id); + break; + + case S26_IGMP_ON_OFF: + { + int tmp = 0; + tmp = vlan_value & (0x1 << 7); + vlan_port &= ~(0x1 << 7); + if(vlan_port>4) return -EINVAL; + if(tmp != 0){ + printk("ag7240::Enable IGMP snooping in port no %x.\n",vlan_port); + ret= python_port_igmps_status_set(vlan_port,1); + }else{ + printk("ag7240::Disable IGMP snooping in port no %x.\n",vlan_port); + ret= python_port_igmps_status_set(vlan_port,0); + } + } + break; + + case S26_LINK_GETSTAT: + if(vlan_port>4){/* if port=WAN */ + int fdx, phy_up; + ag7240_phy_speed_t speed; + ag7240_get_link_status(0, &phy_up, &fdx, &speed, 4); + ifr->ifr_ifru.ifru_ivalue = (speed<<16|fdx<<8|phy_up); + printk("ag7240::S26_LINK_GETSTAT portno WAN is %x.\n",ifr->ifr_ifru.ifru_ivalue); + }else if(vlan_port > 0){ + flag = athrs26_phy_is_link_alive(vlan_port-1); + ifr->ifr_ifru.ifru_ivalue = flag; + printk("ag7240::S26_LINK_GETSTAT portno %d is %s.\n",vlan_port,flag?"up":"down"); + }else{ + ifr->ifr_ifru.ifru_ivalue = 1; + } + /* PHY 0-4 <---> port 1-5 in user space. */ + break; + + case S26_VLAN_ENABLE: + python_ioctl_enable_vlan(); + printk("ag7240::S26_VLAN_ENABLE.\n"); + break; + + case S26_VLAN_DISABLE: + python_ioctl_disable_vlan(); + printk("ag7240::S26_VLAN_DISABLE.\n"); + break; + + case S26_ARL_ADD: + ret = python_fdb_add(arl->mac_addr,arl->port_map,arl->sa_drop); + printk("ag7240::S26_ARL_ADD,mac:[%x.%x.%x.%x.%x.%x] port[%x] drop %d\n", + arl->mac_addr.uc[0],arl->mac_addr.uc[1],arl->mac_addr.uc[2],arl->mac_addr.uc[3], + arl->mac_addr.uc[4],arl->mac_addr.uc[5],arl->port_map,arl->sa_drop); + break; + + case S26_ARL_DEL: + ret = python_fdb_del(arl->mac_addr); + printk("ag7240::S26_ARL_DEL mac:[%x.%x.%x.%x.%x.%x].\n",arl->mac_addr.uc[0],arl->mac_addr.uc[1], + arl->mac_addr.uc[2],arl->mac_addr.uc[3],arl->mac_addr.uc[4],arl->mac_addr.uc[5]); + break; + case S26_MCAST_CLR: + /* 0: switch off the unkown multicast packets over vlan. 1: allow the unknown multicaset packets over vlans. */ + if(!vlan_value) + python_clear_multi(); + else + python_set_multi(); + printk("athr_gmac::S26_MCAST_CLR --- %s.\n", vlan_value?"enable Multicast":"disable Multicast"); + break; +#endif + case S26_RD_PHY: + if(ethcfg->portnum != 0xf) + ethcfg->val = s26_rd_phy(ethcfg->portnum,ethcfg->phy_reg); + else + ethcfg->val = athrs26_reg_read(ethcfg->phy_reg); + break; + + case S26_WR_PHY: + if(ethcfg->portnum != 0xf) + s26_wr_phy(ethcfg->portnum,ethcfg->phy_reg,ethcfg->val); + else + athrs26_reg_write(ethcfg->phy_reg,ethcfg->val); + break; + case S26_FORCE_PHY: + printk("Duplex %d\n",ethcfg->duplex); + if(ethcfg->phy_reg < ATHR_PHY_MAX) { + if(ethcfg->val == 10) { + printk("Forcing 10Mbps %s on port:%d \n", + dup_str[ethcfg->duplex],(ethcfg->phy_reg)); + athrs26_force_10M(ethcfg->phy_reg,ethcfg->duplex); + }else if(ethcfg->val == 100) { + printk("Forcing 100Mbps %s on port:%d \n", + dup_str[ethcfg->duplex],(ethcfg->phy_reg)); + athrs26_force_100M(ethcfg->phy_reg,ethcfg->duplex); + }else if(ethcfg->val == 0) { + printk("Enabling Auto Neg on port:%d \n",(ethcfg->phy_reg)); + ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK; + + if(ar7240_revid == AR7240_REV_1_0) { + s26_wr_phy(ethcfg->phy_reg,ATHR_DEBUG_PORT_ADDRESS,0x0); + s26_wr_phy(ethcfg->phy_reg,ATHR_DEBUG_PORT_DATA,0x2ee); + s26_wr_phy(ethcfg->phy_reg,ATHR_DEBUG_PORT_ADDRESS,0x3); + s26_wr_phy(ethcfg->phy_reg,ATHR_DEBUG_PORT_DATA,0x3a11); + } + s26_wr_phy(ethcfg->phy_reg,ATHR_PHY_CONTROL,0x9000); + }else + return -EINVAL; + + if(ATHR_ETHUNIT(ethcfg->phy_reg) == ENET_UNIT_WAN) { + if(mac_has_flag(ag7240_macs[ENET_UNIT_LAN],ETH_SWONLY_MODE)) + ag7240_check_link(ag7240_macs[ENET_UNIT_LAN],ethcfg->phy_reg); + else + ag7240_check_link(ag7240_macs[0],ethcfg->phy_reg); + }else{ + ag7240_check_link(ag7240_macs[1],ethcfg->phy_reg); + } + break; + } + default: + return -EINVAL; + } + return 0; +} + +int athrs26_mdc_check() +{ + int i; + + for (i=0; i<4000; i++) { + if(athrs26_reg_read(0x10c) != 0x18007fff) + return -1; + } + return 0; +} + +void ar7240_s26_intr(void) +{ + int status = 0, intr_reg_val; + uint32_t phyUnit = 0 ,phyBase = 0 ,phyAddr = 0; + uint32_t phymask = 0x0; + uint32_t linkDown = 0x0; + + athrs26_reg_write(S26_GLOBAL_INTR_MASK_REG,0x0); + + intr_reg_val = athrs26_reg_read(S26_GLOBAL_INTR_REG); + + /* clear global link interrupt */ + athrs26_reg_write(S26_GLOBAL_INTR_REG,intr_reg_val); + + if (intr_reg_val & PHY_LINK_CHANGE_REG) { + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + status = s26_rd_phy(phyAddr,ATHR_PHY_INTR_STATUS); + + if(status & PHY_LINK_UP) { + DPRINTF("LINK UP - Port %d:%x\n",phyAddr,status); + phymask = (phymask | (1 << phyUnit)); + } + if(status & PHY_LINK_DOWN) { + DPRINTF("LINK DOWN - Port %d:%x\n",phyAddr,status); + phymask = (phymask | (1 << phyUnit)); + linkDown = (linkDown | (1 << phyUnit)); + } + if(status & PHY_LINK_DUPLEX_CHANGE) { + DPRINTF("LINK DUPLEX CHANGE - Port %d:%x\n",phyAddr,status); + phymask = (phymask | (1 << phyUnit)); + } + if(status & PHY_LINK_SPEED_CHANGE) { + DPRINTF("LINK SPEED CHANGE %d:%x\n",phyAddr,status); + phymask = (phymask | (1 << phyUnit)); + } + } + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if ((phymask >> phyUnit) & 0x1) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + status = s26_rd_phy(phyAddr,ATHR_PHY_INTR_STATUS); + + if (!athrs26_phy_is_link_alive(phyUnit) && !((linkDown >> phyUnit) & 0x1)) + continue; + + if(ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN) { + if (mac_has_flag(ag7240_macs[ENET_UNIT_LAN],ETH_SWONLY_MODE)) + ag7240_check_link(ag7240_macs[ENET_UNIT_LAN],phyUnit); + else + ag7240_check_link(ag7240_macs[0],phyUnit); + } + else { + ag7240_check_link(ag7240_macs[1],phyUnit); + } + } + } + athrs26_reg_write(S26_GLOBAL_INTR_MASK_REG,PHY_LINK_CHANGE_REG); + } + else { + DPRINTF("Spurious link interrupt:%s,status:%x\n",__func__,status); + athrs26_reg_write(S26_GLOBAL_INTR_MASK_REG,PHY_LINK_CHANGE_REG); + } +} diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.h new file mode 100644 index 0000000..499a6f9 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar7240_s26_phy.h @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ATHRS26_PHY_H +#define _ATHRS26_PHY_H + + +/*****************/ +/* PHY Registers */ +/*****************/ +#define ATHR_PHY_CONTROL 0 +#define ATHR_PHY_STATUS 1 +#define ATHR_PHY_ID1 2 +#define ATHR_PHY_ID2 3 +#define ATHR_AUTONEG_ADVERT 4 +#define ATHR_LINK_PARTNER_ABILITY 5 +#define ATHR_AUTONEG_EXPANSION 6 +#define ATHR_NEXT_PAGE_TRANSMIT 7 +#define ATHR_LINK_PARTNER_NEXT_PAGE 8 +#define ATHR_1000BASET_CONTROL 9 +#define ATHR_1000BASET_STATUS 10 +#define ATHR_PHY_FUNC_CONTROL 16 +#define ATHR_PHY_SPEC_STATUS 17 +#define ATHR_DEBUG_PORT_ADDRESS 29 +#define ATHR_DEBUG_PORT_DATA 30 +#define ATHR_PHY_INTR_ENABLE 0x12 +#define ATHR_PHY_INTR_STATUS 0x13 + +/* ATHR_PHY_CONTROL fields */ +#define ATHR_CTRL_SOFTWARE_RESET 0x8000 +#define ATHR_CTRL_SPEED_LSB 0x2000 +#define ATHR_CTRL_AUTONEGOTIATION_ENABLE 0x1000 +#define ATHR_CTRL_RESTART_AUTONEGOTIATION 0x0200 +#define ATHR_CTRL_SPEED_FULL_DUPLEX 0x0100 +#define ATHR_CTRL_SPEED_MSB 0x0040 + +#define BITS(_s, _n) (((1UL << (_n)) - 1) << _s) +#define BIT(nr) (1UL << (nr)) +#define AR8216_REG_GLOBAL_CTRL 0x0030 +#define AR8216_GCTRL_MTU BITS(0, 11) + + +#define ATHR_RESET_DONE(phy_control) \ + (((phy_control) & (ATHR_CTRL_SOFTWARE_RESET)) == 0) + +/* Phy status fields */ +#define ATHR_STATUS_AUTO_NEG_DONE 0x0020 + +#define ATHR_AUTONEG_DONE(ip_phy_status) \ + (((ip_phy_status) & \ + (ATHR_STATUS_AUTO_NEG_DONE)) == \ + (ATHR_STATUS_AUTO_NEG_DONE)) + +/* Link Partner ability */ +#define ATHR_LINK_100BASETX_FULL_DUPLEX 0x0100 +#define ATHR_LINK_100BASETX 0x0080 +#define ATHR_LINK_10BASETX_FULL_DUPLEX 0x0040 +#define ATHR_LINK_10BASETX 0x0020 + +/* Advertisement register. */ +#define ATHR_ADVERTISE_NEXT_PAGE 0x8000 +#define ATHR_ADVERTISE_ASYM_PAUSE 0x0800 +#define ATHR_ADVERTISE_PAUSE 0x0400 +#define ATHR_ADVERTISE_100FULL 0x0100 +#define ATHR_ADVERTISE_100HALF 0x0080 +#define ATHR_ADVERTISE_10FULL 0x0040 +#define ATHR_ADVERTISE_10HALF 0x0020 + +#define ATHR_ADVERTISE_ALL (ATHR_ADVERTISE_ASYM_PAUSE | ATHR_ADVERTISE_PAUSE | \ + ATHR_ADVERTISE_10HALF | ATHR_ADVERTISE_10FULL | \ + ATHR_ADVERTISE_100HALF | ATHR_ADVERTISE_100FULL) + +/* 1000BASET_CONTROL */ +#define ATHR_ADVERTISE_1000FULL 0x0200 + +/* Phy Specific status fields */ +#define ATHER_STATUS_LINK_MASK 0xC000 +#define ATHER_STATUS_LINK_SHIFT 14 +#define ATHER_STATUS_FULL_DEPLEX 0x2000 +#define ATHR_STATUS_LINK_PASS 0x0400 +#define ATHR_LATCH_LINK_PASS 0x0004 +#define ATHR_STATUS_RESOVLED 0x0800 + +/*phy debug port register */ +#define ATHER_DEBUG_SERDES_REG 5 + +/* Serdes debug fields */ +#define ATHER_SERDES_BEACON 0x0100 + +/* S26 CSR Registers */ + +#define PORT_STATUS_REGISTER0 0x0100 +#define PORT_STATUS_REGISTER1 0x0200 +#define PORT_STATUS_REGISTER2 0x0300 +#define PORT_STATUS_REGISTER3 0x0400 +#define PORT_STATUS_REGISTER4 0x0500 +#define PORT_STATUS_REGISTER5 0x0600 + +#define RATE_LIMIT_REGISTER0 0x010C +#define RATE_LIMIT_REGISTER1 0x020C +#define RATE_LIMIT_REGISTER2 0x030C +#define RATE_LIMIT_REGISTER3 0x040C +#define RATE_LIMIT_REGISTER4 0x050C +#define RATE_LIMIT_REGISTER5 0x060C + +#define PORT_CONTROL_REGISTER0 0x0104 +#define PORT_CONTROL_REGISTER1 0x0204 +#define PORT_CONTROL_REGISTER2 0x0204 +#define PORT_CONTROL_REGISTER3 0x0204 +#define PORT_CONTROL_REGISTER4 0x0204 +#define PORT_CONTROL_REGISTER5 0x0204 + +#define CPU_PORT_REGISTER 0x0078 +#define MDIO_CTRL_REGISTER 0x0098 + +#define S26_ARL_TBL_FUNC_REG0 0x0050 +#define S26_ARL_TBL_FUNC_REG1 0x0054 +#define S26_ARL_TBL_FUNC_REG2 0x0058 +#define S26_FLD_MASK_REG 0x002c +#define S26_ARL_TBL_CTRL_REG 0x005c +#define S26_GLOBAL_INTR_REG 0x10 +#define S26_GLOBAL_INTR_MASK_REG 0x14 + + +#define S26_ENABLE_CPU_BROADCAST (1 << 26) + +#define PHY_LINK_CHANGE_REG 0x4 +#define PHY_LINK_UP 0x400 +#define PHY_LINK_DOWN 0x800 +#define PHY_LINK_DUPLEX_CHANGE 0x2000 +#define PHY_LINK_SPEED_CHANGE 0x4000 +#define PHY_LINK_INTRS (PHY_LINK_UP | PHY_LINK_DOWN | PHY_LINK_DUPLEX_CHANGE | PHY_LINK_SPEED_CHANGE) + +/* SWITCH QOS REGISTERS */ + +#define ATHR_PRI_CTRL_PORT_0 0x110 /* CPU PORT */ +#define ATHR_PRI_CTRL_PORT_1 0x210 +#define ATHR_PRI_CTRL_PORT_2 0x310 +#define ATHR_PRI_CTRL_PORT_3 0x410 +#define ATHR_PRI_CTRL_PORT_4 0x510 +#define ATHR_PRI_CTRL_PORT_5 0x610 + +#define ATHR_TOS_PRI_EN (1 << 16) +#define ATHR_VLAN_PRI_EN (1 << 17) +#define ATHR_DA_PRI_EN (1 << 18) +#define ATHR_PORT_PRI_EN (1 << 19) +#define ATHR_HDR_EN (1 << 11) +#define ATHR_CPU_EN (1 << 8 ) + + +#define ATHR_QOS_MODE_REGISTER 0x030 +#define ATHR_QOS_FIXED_PRIORITY ((0 << 31) | (0 << 28)) +#define ATHR_QOS_WEIGHTED ((1 << 31) | (0 << 28)) /* Fixed weight 8,4,2,1 */ +#define ATHR_QOS_MIXED ((1 << 31) | (1 << 28)) /* Q3 for managment; Q2,Q1,Q0 - 4,2,1 */ + + +#ifndef BOOL +#define BOOL int +#endif + +#undef S26_VER_1_0 + +#ifndef _ATH_HEADER_CONF +#define _ATH_HEADER_CONF + +typedef enum { + NORMAL_PACKET, + RESERVED0, + MIB_1ST, + RESERVED1, + RESERVED2, + READ_WRITE_REG, + READ_WRITE_REG_ACK, + RESERVED3 +} AT_HEADER_TYPE; + +typedef struct { + uint16_t reserved0 :2; + uint16_t priority :2; + uint16_t type :4; + uint16_t broadcast :1; + uint16_t from_cpu :1; + uint16_t reserved1 :2; + uint16_t port_num :4; +}at_header_t; + +#define ATHR_HEADER_LEN 2 + +#endif // _ATH_HEADER_CONF + +int ag7240_hard_start(struct sk_buff *skb, struct net_device *dev); + +void athrs26_reg_init(int ethUnit); +void athrs26_reg_init_lan(int ethUnit); +int athrs26_phy_is_up(int unit); +int athrs26_phy_is_fdx(int unit,int phyUnit); +int athrs26_phy_speed(int unit,int phyUnit); +void athrs26_phy_stab_wr(int phy_id, BOOL phy_up, int phy_speed); +BOOL athrs26_phy_setup(int unit); +int athrs26_mdc_check(void); + +void athrs26_phy_off(ag7240_mac_t *mac); +void athrs26_phy_on(ag7240_mac_t *mac); +void athrs26_mac_speed_set(ag7240_mac_t *mac, ag7240_phy_speed_t speed); +void athrs26_reg_dev(ag7240_mac_t **mac); +int athrs26_ioctl(struct net_device *dev,void *args, int cmd); +uint32_t athrs26_reg_read(unsigned int s26_addr); +void athrs26_reg_write(unsigned int s26_addr, unsigned int s26_write_data); + +#define S26_FORCE_100M 1 +#endif diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar8216.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar8216.c new file mode 100644 index 0000000..93c961c --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar8216.c @@ -0,0 +1,843 @@ +/* + * ar8216.c: AR8216 switch driver + * + * Copyright (C) 2009 Felix Fietkau + * + * This program 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 + * of the License, or (at your option) any later version. + * + * This program 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ar8216.h" + +/* size of the vlan table */ +#define AR8X16_MAX_VLANS 128 +#define AR8X16_PROBE_RETRIES 10 + +struct ar8216_priv { + struct switch_dev dev; + struct phy_device *phy; + u32 (*read)(struct ar8216_priv *priv, int reg); + void (*write)(struct ar8216_priv *priv, int reg, u32 val); + const struct net_device_ops *ndo_old; + struct net_device_ops ndo; + struct mutex reg_mutex; + int chip; + + /* all fields below are cleared on reset */ + bool vlan; + u16 vlan_id[AR8X16_MAX_VLANS]; + u8 vlan_table[AR8X16_MAX_VLANS]; + u8 vlan_tagged; + u16 pvid[AR8216_NUM_PORTS]; +}; + +#define to_ar8216(_dev) container_of(_dev, struct ar8216_priv, dev) + +static inline void +split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) +{ + regaddr >>= 1; + *r1 = regaddr & 0x1e; + + regaddr >>= 5; + *r2 = regaddr & 0x7; + + regaddr >>= 3; + *page = regaddr & 0x1ff; +} + +static u32 +ar8216_mii_read(struct ar8216_priv *priv, int reg) +{ + struct phy_device *phy = priv->phy; + u16 r1, r2, page; + u16 lo, hi; + + split_addr((u32) reg, &r1, &r2, &page); + phy->bus->write(phy->bus, 0x18, 0, page); + msleep(1); /* wait for the page switch to propagate */ + lo = phy->bus->read(phy->bus, 0x10 | r2, r1); + hi = phy->bus->read(phy->bus, 0x10 | r2, r1 + 1); + + return (hi << 16) | lo; +} + +static void +ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) +{ + struct phy_device *phy = priv->phy; + u16 r1, r2, r3; + u16 lo, hi; + + split_addr((u32) reg, &r1, &r2, &r3); + phy->bus->write(phy->bus, 0x18, 0, r3); + msleep(1); /* wait for the page switch to propagate */ + + lo = val & 0xffff; + hi = (u16) (val >> 16); + phy->bus->write(phy->bus, 0x10 | r2, r1 + 1, hi); + phy->bus->write(phy->bus, 0x10 | r2, r1, lo); +} + +static u32 +ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) +{ + u32 v; + + v = priv->read(priv, reg); + v &= ~mask; + v |= val; + priv->write(priv, reg, v); + + return v; +} + +static inline int +ar8216_id_chip(struct ar8216_priv *priv) +{ + u32 val; + u16 id; + int i; + + val = ar8216_mii_read(priv, AR8216_REG_CTRL); + if (val == ~0) + return UNKNOWN; + + id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); + for (i = 0; i < AR8X16_PROBE_RETRIES; i++) { + u16 t; + + val = ar8216_mii_read(priv, AR8216_REG_CTRL); + if (val == ~0) + return UNKNOWN; + + t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); + if (t != id) + return UNKNOWN; + } + + switch (id) { + case 0x0101: + return AR8216; + case 0x1001: + return AR8316; + default: + printk(KERN_DEBUG + "ar8216: Unknown Atheros device [ver=%d, rev=%d, phy_id=%04x%04x]\n", + (int)(id >> AR8216_CTRL_VERSION_S), + (int)(id & AR8216_CTRL_REVISION), + priv->phy->bus->read(priv->phy->bus, priv->phy->addr, 2), + priv->phy->bus->read(priv->phy->bus, priv->phy->addr, 3)); + + return UNKNOWN; + } +} + +static int +ar8216_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8216_priv *priv = to_ar8216(dev); + priv->vlan = !!val->value.i; + return 0; +} + +static int +ar8216_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8216_priv *priv = to_ar8216(dev); + val->value.i = priv->vlan; + return 0; +} + + +static int +ar8216_set_pvid(struct switch_dev *dev, int port, int vlan) +{ + struct ar8216_priv *priv = to_ar8216(dev); + + /* make sure no invalid PVIDs get set */ + + if (vlan >= dev->vlans) + return -EINVAL; + + priv->pvid[port] = vlan; + return 0; +} + +static int +ar8216_get_pvid(struct switch_dev *dev, int port, int *vlan) +{ + struct ar8216_priv *priv = to_ar8216(dev); + *vlan = priv->pvid[port]; + return 0; +} + +static int +ar8216_set_vid(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8216_priv *priv = to_ar8216(dev); + priv->vlan_id[val->port_vlan] = val->value.i; + return 0; +} + +static int +ar8216_get_vid(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8216_priv *priv = to_ar8216(dev); + val->value.i = priv->vlan_id[val->port_vlan]; + return 0; +} + + +static int +ar8216_mangle_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct ar8216_priv *priv = dev->phy_ptr; + unsigned char *buf; + + if (unlikely(!priv)) + goto error; + + if (!priv->vlan) + goto send; + + if (unlikely(skb_headroom(skb) < 2)) { + if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0) + goto error; + } + + buf = skb_push(skb, 2); + buf[0] = 0x10; + buf[1] = 0x80; + +send: + return priv->ndo_old->ndo_start_xmit(skb, dev); + +error: + dev_kfree_skb_any(skb); + return 0; +} + +static int +ar8216_mangle_rx(struct sk_buff *skb, int napi) +{ + struct ar8216_priv *priv; + struct net_device *dev; + unsigned char *buf; + int port, vlan; + + dev = skb->dev; + if (!dev) + goto error; + + priv = dev->phy_ptr; + if (!priv) + goto error; + + /* don't strip the header if vlan mode is disabled */ + if (!priv->vlan) + goto recv; + + /* strip header, get vlan id */ + buf = skb->data; + skb_pull(skb, 2); + + /* check for vlan header presence */ + if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00)) + goto recv; + + port = buf[0] & 0xf; + + /* no need to fix up packets coming from a tagged source */ + if (priv->vlan_tagged & (1 << port)) + goto recv; + + /* lookup port vid from local table, the switch passes an invalid vlan id */ + vlan = priv->vlan_id[priv->pvid[port]]; + + buf[14 + 2] &= 0xf0; + buf[14 + 2] |= vlan >> 8; + buf[15 + 2] = vlan & 0xff; + +recv: + skb->protocol = eth_type_trans(skb, skb->dev); + + if (napi) + return netif_receive_skb(skb); + else + return netif_rx(skb); + +error: + /* no vlan? eat the packet! */ + dev_kfree_skb_any(skb); + return NET_RX_DROP; +} + +static int +ar8216_netif_rx(struct sk_buff *skb) +{ + return ar8216_mangle_rx(skb, 0); +} + +static int +ar8216_netif_receive_skb(struct sk_buff *skb) +{ + return ar8216_mangle_rx(skb, 1); +} + + +static struct switch_attr ar8216_globals[] = { + { + .type = SWITCH_TYPE_INT, + .name = "enable_vlan", + .description = "Enable VLAN mode", + .set = ar8216_set_vlan, + .get = ar8216_get_vlan, + .max = 1 + }, +}; + +static struct switch_attr ar8216_port[] = { +}; + +static struct switch_attr ar8216_vlan[] = { + { + .type = SWITCH_TYPE_INT, + .name = "vid", + .description = "VLAN ID (0-4094)", + .set = ar8216_set_vid, + .get = ar8216_get_vid, + .max = 4094, + }, +}; + + +static int +ar8216_get_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct ar8216_priv *priv = to_ar8216(dev); + u8 ports = priv->vlan_table[val->port_vlan]; + int i; + + val->len = 0; + for (i = 0; i < AR8216_NUM_PORTS; i++) { + struct switch_port *p; + + if (!(ports & (1 << i))) + continue; + + p = &val->value.ports[val->len++]; + p->id = i; + if (priv->vlan_tagged & (1 << i)) + p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); + else + p->flags = 0; + } + return 0; +} + +static int +ar8216_set_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct ar8216_priv *priv = to_ar8216(dev); + u8 *vt = &priv->vlan_table[val->port_vlan]; + int i, j; + + *vt = 0; + for (i = 0; i < val->len; i++) { + struct switch_port *p = &val->value.ports[i]; + + if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) + priv->vlan_tagged |= (1 << p->id); + else { + priv->vlan_tagged &= ~(1 << p->id); + priv->pvid[p->id] = val->port_vlan; + + /* make sure that an untagged port does not + * appear in other vlans */ + for (j = 0; j < AR8X16_MAX_VLANS; j++) { + if (j == val->port_vlan) + continue; + priv->vlan_table[j] &= ~(1 << p->id); + } + } + + *vt |= 1 << p->id; + } + return 0; +} + +static int +ar8216_wait_bit(struct ar8216_priv *priv, int reg, u32 mask, u32 val) +{ + int timeout = 20; + + while ((priv->read(priv, reg) & mask) != val) { + if (timeout-- <= 0) { + printk(KERN_ERR "ar8216: timeout waiting for operation to complete\n"); + return 1; + } + } + return 0; +} + +static void +ar8216_vtu_op(struct ar8216_priv *priv, u32 op, u32 val) +{ + if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0)) + return; + if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) { + val &= AR8216_VTUDATA_MEMBER; + val |= AR8216_VTUDATA_VALID; + priv->write(priv, AR8216_REG_VTU_DATA, val); + } + op |= AR8216_VTU_ACTIVE; + priv->write(priv, AR8216_REG_VTU, op); +} + +static int +ar8216_hw_apply(struct switch_dev *dev) +{ + struct ar8216_priv *priv = to_ar8216(dev); + u8 portmask[AR8216_NUM_PORTS]; + int i, j; + + mutex_lock(&priv->reg_mutex); + /* flush all vlan translation unit entries */ + ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); + + memset(portmask, 0, sizeof(portmask)); + if (priv->vlan) { + /* calculate the port destination masks and load vlans + * into the vlan translation unit */ + for (j = 0; j < AR8X16_MAX_VLANS; j++) { + u8 vp = priv->vlan_table[j]; + + if (!vp) + continue; + + for (i = 0; i < AR8216_NUM_PORTS; i++) { + u8 mask = (1 << i); + if (vp & mask) + portmask[i] |= vp & ~mask; + } + + ar8216_vtu_op(priv, + AR8216_VTU_OP_LOAD | + (priv->vlan_id[j] << AR8216_VTU_VID_S), + priv->vlan_table[j]); + } + } else { + /* vlan disabled: + * isolate all ports, but connect them to the cpu port */ + for (i = 0; i < AR8216_NUM_PORTS; i++) { + if (i == AR8216_PORT_CPU) + continue; + + portmask[i] = 1 << AR8216_PORT_CPU; + portmask[AR8216_PORT_CPU] |= (1 << i); + } + } + + /* update the port destination mask registers and tag settings */ + for (i = 0; i < AR8216_NUM_PORTS; i++) { + int egress, ingress; + int pvid; + + if (priv->vlan) { + pvid = priv->vlan_id[priv->pvid[i]]; + } else { + pvid = i; + } + + if (priv->vlan && (priv->vlan_tagged & (1 << i))) { + egress = AR8216_OUT_ADD_VLAN; + } else { + egress = AR8216_OUT_STRIP_VLAN; + } + if (priv->vlan) { + ingress = AR8216_IN_SECURE; + } else { + ingress = AR8216_IN_PORT_ONLY; + } + + ar8216_rmw(priv, AR8216_REG_PORT_CTRL(i), + AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | + AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | + AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK, + AR8216_PORT_CTRL_LEARN | + (priv->vlan && i == AR8216_PORT_CPU && (priv->chip == AR8216) ? + AR8216_PORT_CTRL_HEADER : 0) | + (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | + (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); + + ar8216_rmw(priv, AR8216_REG_PORT_VLAN(i), + AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE | + AR8216_PORT_VLAN_DEFAULT_ID, + (portmask[i] << AR8216_PORT_VLAN_DEST_PORTS_S) | + (ingress << AR8216_PORT_VLAN_MODE_S) | + (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S)); + } + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static int +ar8316_hw_init(struct ar8216_priv *priv) { + static int initialized; + int i; + u32 val; + struct mii_bus *bus; + + if (initialized) + return 0; + + val = priv->read(priv, 0x8); + + if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { + /* value taken from Ubiquiti RouterStation Pro */ + if (val == 0x81461bea) { + /* switch already intialized by bootloader */ + initialized = true; + return 0; + } + priv->write(priv, 0x8, 0x81461bea); + } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) { + /* value taken from AVM Fritz!Box 7390 sources */ + if (val == 0x010e5b71) { + /* switch already initialized by bootloader */ + initialized = true; + return 0; + } + priv->write(priv, 0x8, 0x010e5b71); + } else { + /* no known value for phy interface */ + printk(KERN_ERR "ar8316: unsupported mii mode: %d.\n", + priv->phy->interface); + return -EINVAL; + } + + /* standard atheros magic */ + priv->write(priv, 0x38, 0xc000050e); + + /* Initialize the ports */ + bus = priv->phy->bus; + for (i = 0; i < 5; i++) { + if ((i == 4) && + priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { + /* work around for phy4 rgmii mode */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x12); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x480c); + /* rx delay */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x0); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x824e); + /* tx delay */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x5); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x3d47); + msleep(1000); + } + + /* initialize the port itself */ + bus->write(bus, i, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + bus->write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL); + bus->write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); + msleep(1000); + } + initialized = true; + return 0; +} + +static int +ar8216_reset_switch(struct switch_dev *dev) +{ + struct ar8216_priv *priv = to_ar8216(dev); + int i; + + mutex_lock(&priv->reg_mutex); + memset(&priv->vlan, 0, sizeof(struct ar8216_priv) - + offsetof(struct ar8216_priv, vlan)); + for (i = 0; i < AR8X16_MAX_VLANS; i++) { + priv->vlan_id[i] = i; + } + for (i = 0; i < AR8216_NUM_PORTS; i++) { + /* Enable port learning and tx */ + priv->write(priv, AR8216_REG_PORT_CTRL(i), + AR8216_PORT_CTRL_LEARN | + (4 << AR8216_PORT_CTRL_STATE_S)); + + priv->write(priv, AR8216_REG_PORT_VLAN(i), 0); + + /* Configure all PHYs */ + if (i == AR8216_PORT_CPU) { + priv->write(priv, AR8216_REG_PORT_STATUS(i), + AR8216_PORT_STATUS_LINK_UP | + ((priv->chip == AR8316) ? + AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | + AR8216_PORT_STATUS_TXMAC | + AR8216_PORT_STATUS_RXMAC | + ((priv->chip == AR8316) ? AR8216_PORT_STATUS_RXFLOW : 0) | + ((priv->chip == AR8316) ? AR8216_PORT_STATUS_TXFLOW : 0) | + AR8216_PORT_STATUS_DUPLEX); + } else { + priv->write(priv, AR8216_REG_PORT_STATUS(i), + AR8216_PORT_STATUS_LINK_AUTO); + } + } + /* XXX: undocumented magic from atheros, required! */ + priv->write(priv, 0x38, 0xc000050e); + + if (priv->chip == AR8216) { + ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8216_GCTRL_MTU, 1518 + 8 + 2); + } else if (priv->chip == AR8316) { + /* enable jumbo frames */ + ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8316_GCTRL_MTU, 9018 + 8 + 2); + } + + if (priv->chip == AR8316) { + /* enable cpu port to receive multicast and broadcast frames */ + priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); + } + mutex_unlock(&priv->reg_mutex); + return ar8216_hw_apply(dev); +} + + +static const struct switch_dev_ops ar8216_ops = { + .attr_global = { + .attr = ar8216_globals, + .n_attr = ARRAY_SIZE(ar8216_globals), + }, + .attr_port = { + .attr = ar8216_port, + .n_attr = ARRAY_SIZE(ar8216_port), + }, + .attr_vlan = { + .attr = ar8216_vlan, + .n_attr = ARRAY_SIZE(ar8216_vlan), + }, + .get_port_pvid = ar8216_get_pvid, + .set_port_pvid = ar8216_set_pvid, + .get_vlan_ports = ar8216_get_ports, + .set_vlan_ports = ar8216_set_ports, + .apply_config = ar8216_hw_apply, + .reset_switch = ar8216_reset_switch, +}; + +static int +ar8216_config_init(struct phy_device *pdev) +{ + struct ar8216_priv *priv; + struct net_device *dev = pdev->attached_dev; + struct switch_dev *swdev; + int ret; + + priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->phy = pdev; + + priv->chip = ar8216_id_chip(priv); + + if (pdev->addr == 0) + printk(KERN_INFO "%s: AR%d switch driver attached.\n", + pdev->attached_dev->name, priv->chip); + + + if (pdev->addr != 0) { + if (priv->chip == AR8316) { + pdev->supported |= SUPPORTED_1000baseT_Full; + pdev->advertising |= ADVERTISED_1000baseT_Full; + } + kfree(priv); + return 0; + } + + pdev->supported = priv->chip == AR8316 ? + SUPPORTED_1000baseT_Full : SUPPORTED_100baseT_Full; + pdev->advertising = pdev->supported; + + mutex_init(&priv->reg_mutex); + priv->read = ar8216_mii_read; + priv->write = ar8216_mii_write; + + pdev->priv = priv; + + swdev = &priv->dev; + swdev->cpu_port = AR8216_PORT_CPU; + swdev->ops = &ar8216_ops; + + if (priv->chip == AR8316) { + swdev->name = "Atheros AR8316"; + swdev->vlans = AR8X16_MAX_VLANS; + /* port 5 connected to the other mac, therefore unusable */ + swdev->ports = (AR8216_NUM_PORTS - 1); + } else { + swdev->name = "Atheros AR8216"; + swdev->vlans = AR8216_NUM_VLANS; + swdev->ports = AR8216_NUM_PORTS; + } + + if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) { + kfree(priv); + goto done; + } + + if (priv->chip == AR8316) { + ret = ar8316_hw_init(priv); + if (ret) { + kfree(priv); + goto done; + } + } + + ret = ar8216_reset_switch(&priv->dev); + if (ret) { + kfree(priv); + goto done; + } + + dev->phy_ptr = priv; + + /* VID fixup only needed on ar8216 */ + if (pdev->addr == 0 && priv->chip == AR8216) { + pdev->pkt_align = 2; + pdev->netif_receive_skb = ar8216_netif_receive_skb; + pdev->netif_rx = ar8216_netif_rx; + priv->ndo_old = dev->netdev_ops; + memcpy(&priv->ndo, priv->ndo_old, sizeof(struct net_device_ops)); + priv->ndo.ndo_start_xmit = ar8216_mangle_tx; + dev->netdev_ops = &priv->ndo; + } + +done: + return ret; +} + +static int +ar8216_read_status(struct phy_device *phydev) +{ + struct ar8216_priv *priv = phydev->priv; + int ret; + if (phydev->addr != 0) { + return genphy_read_status(phydev); + } + + phydev->speed = priv->chip == AR8316 ? SPEED_1000 : SPEED_100; + phydev->duplex = DUPLEX_FULL; + phydev->link = 1; + + /* flush the address translation unit */ + mutex_lock(&priv->reg_mutex); + ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); + + if (!ret) + priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + else + ret = -ETIMEDOUT; + mutex_unlock(&priv->reg_mutex); + + phydev->state = PHY_RUNNING; + netif_carrier_on(phydev->attached_dev); + phydev->adjust_link(phydev->attached_dev); + + return ret; +} + +static int +ar8216_config_aneg(struct phy_device *phydev) +{ + if (phydev->addr == 0) + return 0; + + return genphy_config_aneg(phydev); +} + +static int +ar8216_probe(struct phy_device *pdev) +{ + struct ar8216_priv priv; + u16 chip; + + priv.phy = pdev; + chip = ar8216_id_chip(&priv); + if (chip == UNKNOWN) + return -ENODEV; + + return 0; +} + +static void +ar8216_remove(struct phy_device *pdev) +{ + struct ar8216_priv *priv = pdev->priv; + struct net_device *dev = pdev->attached_dev; + + if (!priv) + return; + + if (priv->ndo_old && dev) + dev->netdev_ops = priv->ndo_old; + if (pdev->addr == 0) + unregister_switch(&priv->dev); + kfree(priv); +} + +static struct phy_driver ar8216_driver = { + .phy_id = 0x004d0000, + .name = "Atheros AR8216/AR8316", + .phy_id_mask = 0xffff0000, + .features = PHY_BASIC_FEATURES, + .probe = ar8216_probe, + .remove = ar8216_remove, + .config_init = &ar8216_config_init, + .config_aneg = &ar8216_config_aneg, + .read_status = &ar8216_read_status, + .driver = { .owner = THIS_MODULE }, +}; + +int __init +ar8216_init(void) +{ + return phy_driver_register(&ar8216_driver); +} + +void __exit +ar8216_exit(void) +{ + phy_driver_unregister(&ar8216_driver); +} + +module_init(ar8216_init); +module_exit(ar8216_exit); +MODULE_LICENSE("GPL"); + diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar8216.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar8216.h new file mode 100644 index 0000000..5a8fa3c --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/ar8216.h @@ -0,0 +1,187 @@ +/* + * ar8216.h: AR8216 switch driver + * + * Copyright (C) 2009 Felix Fietkau + * + * This program 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 + * of the License, or (at your option) any later version. + * + * This program 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. + */ + +#ifndef __AR8216_H +#define __AR8216_H + +#define BITS(_s, _n) (((1UL << (_n)) - 1) << _s) + +#define AR8216_PORT_CPU 0 +#define AR8216_NUM_PORTS 6 +#define AR8216_NUM_VLANS 16 +#define AR8316_NUM_VLANS 4096 + +/* Atheros specific MII registers */ +#define MII_ATH_DBG_ADDR 0x1d +#define MII_ATH_DBG_DATA 0x1e + +#define AR8216_REG_CTRL 0x0000 +#define AR8216_CTRL_REVISION BITS(0, 8) +#define AR8216_CTRL_REVISION_S 0 +#define AR8216_CTRL_VERSION BITS(8, 8) +#define AR8216_CTRL_VERSION_S 8 +#define AR8216_CTRL_RESET BIT(31) + +#define AR8216_REG_FLOOD_MASK 0x002C +#define AR8216_FM_UNI_DEST_PORTS BITS(0, 6) +#define AR8216_FM_MULTI_DEST_PORTS BITS(16, 6) + +#define AR8216_REG_GLOBAL_CTRL 0x0030 +#define AR8216_GCTRL_MTU BITS(0, 11) +#define AR8316_GCTRL_MTU BITS(0, 14) + +#define AR8216_REG_VTU 0x0040 +#define AR8216_VTU_OP BITS(0, 3) +#define AR8216_VTU_OP_NOOP 0x0 +#define AR8216_VTU_OP_FLUSH 0x1 +#define AR8216_VTU_OP_LOAD 0x2 +#define AR8216_VTU_OP_PURGE 0x3 +#define AR8216_VTU_OP_REMOVE_PORT 0x4 +#define AR8216_VTU_ACTIVE BIT(3) +#define AR8216_VTU_FULL BIT(4) +#define AR8216_VTU_PORT BITS(8, 4) +#define AR8216_VTU_PORT_S 8 +#define AR8216_VTU_VID BITS(16, 12) +#define AR8216_VTU_VID_S 16 +#define AR8216_VTU_PRIO BITS(28, 3) +#define AR8216_VTU_PRIO_S 28 +#define AR8216_VTU_PRIO_EN BIT(31) + +#define AR8216_REG_VTU_DATA 0x0044 +#define AR8216_VTUDATA_MEMBER BITS(0, 10) +#define AR8216_VTUDATA_VALID BIT(11) + +#define AR8216_REG_ATU 0x0050 +#define AR8216_ATU_OP BITS(0, 3) +#define AR8216_ATU_OP_NOOP 0x0 +#define AR8216_ATU_OP_FLUSH 0x1 +#define AR8216_ATU_OP_LOAD 0x2 +#define AR8216_ATU_OP_PURGE 0x3 +#define AR8216_ATU_OP_FLUSH_LOCKED 0x4 +#define AR8216_ATU_OP_FLUSH_UNICAST 0x5 +#define AR8216_ATU_OP_GET_NEXT 0x6 +#define AR8216_ATU_ACTIVE BIT(3) +#define AR8216_ATU_PORT_NUM BITS(8, 4) +#define AR8216_ATU_FULL_VIO BIT(12) +#define AR8216_ATU_ADDR4 BITS(16, 8) +#define AR8216_ATU_ADDR5 BITS(24, 8) + +#define AR8216_REG_ATU_DATA 0x0054 +#define AR8216_ATU_ADDR3 BITS(0, 8) +#define AR8216_ATU_ADDR2 BITS(8, 8) +#define AR8216_ATU_ADDR1 BITS(16, 8) +#define AR8216_ATU_ADDR0 BITS(24, 8) + +#define AR8216_REG_ATU_CTRL 0x005C +#define AR8216_ATU_CTRL_AGE_EN BIT(17) +#define AR8216_ATU_CTRL_AGE_TIME BITS(0, 16) +#define AR8216_ATU_CTRL_AGE_TIME_S 0 + +#define AR8216_PORT_OFFSET(_i) (0x0100 * (_i + 1)) +#define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000) +#define AR8216_PORT_STATUS_SPEED BITS(0,2) +#define AR8216_PORT_STATUS_SPEED_S 0 +#define AR8216_PORT_STATUS_TXMAC BIT(2) +#define AR8216_PORT_STATUS_RXMAC BIT(3) +#define AR8216_PORT_STATUS_TXFLOW BIT(4) +#define AR8216_PORT_STATUS_RXFLOW BIT(5) +#define AR8216_PORT_STATUS_DUPLEX BIT(6) +#define AR8216_PORT_STATUS_LINK_UP BIT(8) +#define AR8216_PORT_STATUS_LINK_AUTO BIT(9) +#define AR8216_PORT_STATUS_LINK_PAUSE BIT(10) + +#define AR8216_REG_PORT_CTRL(_i) (AR8216_PORT_OFFSET(_i) + 0x0004) + +/* port forwarding state */ +#define AR8216_PORT_CTRL_STATE BITS(0, 3) +#define AR8216_PORT_CTRL_STATE_S 0 + +#define AR8216_PORT_CTRL_LEARN_LOCK BIT(7) + +/* egress 802.1q mode */ +#define AR8216_PORT_CTRL_VLAN_MODE BITS(8, 2) +#define AR8216_PORT_CTRL_VLAN_MODE_S 8 + +#define AR8216_PORT_CTRL_IGMP_SNOOP BIT(10) +#define AR8216_PORT_CTRL_HEADER BIT(11) +#define AR8216_PORT_CTRL_MAC_LOOP BIT(12) +#define AR8216_PORT_CTRL_SINGLE_VLAN BIT(13) +#define AR8216_PORT_CTRL_LEARN BIT(14) +#define AR8216_PORT_CTRL_MIRROR_TX BIT(16) +#define AR8216_PORT_CTRL_MIRROR_RX BIT(17) + +#define AR8216_REG_PORT_VLAN(_i) (AR8216_PORT_OFFSET(_i) + 0x0008) + +#define AR8216_PORT_VLAN_DEFAULT_ID BITS(0, 12) +#define AR8216_PORT_VLAN_DEFAULT_ID_S 0 + +#define AR8216_PORT_VLAN_DEST_PORTS BITS(16, 9) +#define AR8216_PORT_VLAN_DEST_PORTS_S 16 + +/* bit0 added to the priority field of egress frames */ +#define AR8216_PORT_VLAN_TX_PRIO BIT(27) + +/* port default priority */ +#define AR8216_PORT_VLAN_PRIORITY BITS(28, 2) +#define AR8216_PORT_VLAN_PRIORITY_S 28 + +/* ingress 802.1q mode */ +#define AR8216_PORT_VLAN_MODE BITS(30, 2) +#define AR8216_PORT_VLAN_MODE_S 30 + +#define AR8216_REG_PORT_RATE(_i) (AR8216_PORT_OFFSET(_i) + 0x000c) +#define AR8216_REG_PORT_PRIO(_i) (AR8216_PORT_OFFSET(_i) + 0x0010) + +/* port speed */ +enum { + AR8216_PORT_SPEED_10M = 0, + AR8216_PORT_SPEED_100M = 1, + AR8216_PORT_SPEED_1000M = 2, + AR8216_PORT_SPEED_ERR = 3, +}; + +/* ingress 802.1q mode */ +enum { + AR8216_IN_PORT_ONLY = 0, + AR8216_IN_PORT_FALLBACK = 1, + AR8216_IN_VLAN_ONLY = 2, + AR8216_IN_SECURE = 3 +}; + +/* egress 802.1q mode */ +enum { + AR8216_OUT_KEEP = 0, + AR8216_OUT_STRIP_VLAN = 1, + AR8216_OUT_ADD_VLAN = 2 +}; + +/* port forwarding state */ +enum { + AR8216_PORT_STATE_DISABLED = 0, + AR8216_PORT_STATE_BLOCK = 1, + AR8216_PORT_STATE_LISTEN = 2, + AR8216_PORT_STATE_LEARN = 3, + AR8216_PORT_STATE_FORWARD = 4 +}; + +/* device */ +enum { + UNKNOWN = 0, + AR8216 = 8216, + AR8316 = 8316 +}; + +#endif diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrf1_phy.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrf1_phy.c new file mode 100644 index 0000000..6fc3764 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrf1_phy.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include "ag7240_phy.h" +#include "ag7240.h" +#include "eth_diag.h" + +#define MODULE_NAME "ATHRSF1_PHY" + +#define ATHR_LAN_PORT_VLAN 1 +#define ATHR_WAN_PORT_VLAN 2 +#define ENET_UNIT_LAN 1 +#define ENET_UNIT_WAN 0 + +#define TRUE 1 +#define FALSE 0 + +#define ATHR_PHY_MAX 5 +#define ATHR_PHY0_ADDR 0x0 +#define ATHR_PHY1_ADDR 0x1 +#define ATHR_PHY2_ADDR 0x2 +#define ATHR_PHY3_ADDR 0x3 +#define ATHR_PHY4_ADDR 0x4 + +/* + * Track per-PHY port information. + */ +typedef struct { + BOOL isEnetPort; /* normal enet port */ + BOOL isPhyAlive; /* last known state of link */ + int ethUnit; /* MAC associated with this phy port */ + uint32_t phyBase; + uint32_t phyAddr; /* PHY registers associated with this phy port */ + uint32_t VLANTableSetting; /* Value to be written to VLAN table */ +} athrPhyInfo_t; + +/* + * Per-PHY information, indexed by PHY unit number. + */ + +static athrPhyInfo_t athrPhyInfo[] = { + + {TRUE, /* port 1 -- LAN port 1 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY0_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 2 -- LAN port 2 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY1_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 3 -- LAN port 3 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY2_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* port 4 -- LAN port 4 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY3_ADDR, + ATHR_LAN_PORT_VLAN /* Send to all ports */ + }, + + {TRUE, /* port 5 -- WAN Port 5 */ + FALSE, + ENET_UNIT_WAN, + 0, + ATHR_PHY0_ADDR, + ATHR_LAN_PORT_VLAN /* Send to all ports */ + }, + + {FALSE, /* port 0 -- cpu port 0 */ + TRUE, + ENET_UNIT_LAN, + 0, + 0x00, + ATHR_LAN_PORT_VLAN + }, + +}; + +#define ATHR_IS_ENET_PORT(phyUnit) (athrPhyInfo[phyUnit].isEnetPort) +#define ATHR_IS_PHY_ALIVE(phyUnit) (athrPhyInfo[phyUnit].isPhyAlive) +#define ATHR_ETHUNIT(phyUnit) (athrPhyInfo[phyUnit].ethUnit) +#define ATHR_PHYBASE(phyUnit) (athrPhyInfo[phyUnit].phyBase) +#define ATHR_PHYADDR(phyUnit) (athrPhyInfo[phyUnit].phyAddr) +#define ATHR_VLAN_TABLE_SETTING(phyUnit) (athrPhyInfo[phyUnit].VLANTableSetting) + +#define ATHR_IS_ETHUNIT(phyUnit, ethUnit) \ + (ATHR_IS_ENET_PORT(phyUnit) && \ + ATHR_ETHUNIT(phyUnit) == (ethUnit)) + +#define ATHR_IS_WAN_PORT(phyUnit) (!(ATHR_ETHUNIT(phyUnit)==ENET_UNIT_LAN)) + +/* Forward references */ +BOOL athr_phy_is_link_alive(int phyUnit); +//extern int ag7240_check_link(ag7240_mac_t *); + +void athr_enable_linkIntrs(int ethUnit) +{ + return; +} + +void athr_disable_linkIntrs(int ethUnit) +{ + return; +} +void athr_auto_neg(int ethUnit,int phyUnit) +{ + int timeout = 0; + uint16_t phyHwStatus; + + phy_reg_write(ethUnit, phyUnit , ATHR_AUTONEG_ADVERT, ATHR_ADVERTISE_ALL); + phy_reg_write(ethUnit, phyUnit , ATHR_1000BASET_CONTROL, (ATHR_ADVERTISE_1000FULL)); + phy_reg_write(ethUnit, phyUnit , ATHR_PHY_CONTROL, ATHR_CTRL_AUTONEGOTIATION_ENABLE | ATHR_CTRL_SOFTWARE_RESET); + + /* + * Wait up to 3 seconds for ALL associated PHYs to finish + * autonegotiation. The only way we get out of here sooner is + * if ALL PHYs are connected AND finish autonegotiation. + */ + timeout=20; + for (;;) { + phyHwStatus = phy_reg_read(ethUnit, phyUnit, ATHR_PHY_CONTROL); + + if (ATHR_RESET_DONE(phyHwStatus)) { + printk(MODULE_NAME": Port %d, Neg Success\n", phyUnit); + break; + } + if (timeout == 0) { + printk(MODULE_NAME": Port %d, Negogiation timeout\n", phyUnit); + break; + } + if (--timeout == 0) { + printk(MODULE_NAME": Port %d, Negogiation timeout\n", phyUnit); + break; + } + + mdelay(150); + } + + printk(MODULE_NAME": unit %d phy addr %x ", ethUnit, phyUnit); +} + +/****************************************************************************** +* +* athr_phy_is_link_alive - test to see if the specified link is alive +* +* RETURNS: +* TRUE --> link is alive +* FALSE --> link is down +*/ +BOOL +athr_phy_is_link_alive(int phyUnit) +{ + uint16_t phyHwStatus; + uint32_t phyBase; + uint32_t phyAddr; + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + phyHwStatus = phy_reg_read(0, phyAddr, ATHR_PHY_SPEC_STATUS); + + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { + return TRUE; + } + + return FALSE; +} + +/****************************************************************************** +* +* athr_phy_setup - reset and setup the PHY associated with +* the specified MAC unit number. +* +* Resets the associated PHY port. +* +* RETURNS: +* TRUE --> associated PHY is alive +* FALSE --> no LINKs on this ethernet unit +*/ + +BOOL +athr_phy_setup(int ethUnit) +{ + int phyUnit = 0; + int liveLinks = 0; + + athr_auto_neg(ethUnit,phyUnit); + + if (athr_phy_is_link_alive(phyUnit)) { + liveLinks++; + ATHR_IS_PHY_ALIVE(phyUnit) = TRUE; + } else { + ATHR_IS_PHY_ALIVE(phyUnit) = FALSE; + } + return (liveLinks > 0); +} + +/****************************************************************************** +* +* athr_phy_is_fdx - Determines whether the phy ports associated with the +* specified device are FULL or HALF duplex. +* +* RETURNS: +* 1 --> FULL +* 0 --> HALF +*/ +int +athr_phy_is_fdx(int ethUnit,int phyUnit) +{ + uint32_t phyBase; + uint32_t phyAddr; + uint16_t phyHwStatus; + int ii = 200; + + if (athr_phy_is_link_alive(phyUnit)) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + do { + phyHwStatus = phy_reg_read(ethUnit, ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + mdelay(10); + } while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii); + + if (phyHwStatus & ATHER_STATUS_FULL_DUPLEX) { + return TRUE; + } + } + return FALSE; +} + +/****************************************************************************** +* +* athr_phy_speed - Determines the speed of phy ports associated with the +* specified device. +* +* RETURNS: +* AG7240_PHY_SPEED_10T, AG7240_PHY_SPEED_100TX; +* AG7240_PHY_SPEED_1000T; +*/ + +int +athr_phy_speed(int ethUnit,int phyUnit) +{ + uint16_t phyHwStatus; + uint32_t phyBase; + uint32_t phyAddr; + int ii = 200; + + + if (athr_phy_is_link_alive(phyUnit)) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + do { + phyHwStatus = phy_reg_read(0, ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + mdelay(10); + } while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii); + + phyHwStatus = ((phyHwStatus & ATHER_STATUS_LINK_MASK) >> + ATHER_STATUS_LINK_SHIFT); + + switch(phyHwStatus) { + case 0: + return AG7240_PHY_SPEED_10T; + case 1: + return AG7240_PHY_SPEED_100TX; + case 2: + return AG7240_PHY_SPEED_1000T; + default: + printk("Unkown speed read!\n"); + } + } + + //printk("athr_phy_speed: link down, returning 10t\n"); + return AG7240_PHY_SPEED_10T; +} + +/***************************************************************************** +* +* athr_phy_is_up -- checks for significant changes in PHY state. +* +* A "significant change" is: +* dropped link (e.g. ethernet cable unplugged) OR +* autonegotiation completed + link (e.g. ethernet cable plugged in) +* +* When a PHY is plugged in, phyLinkGained is called. +* When a PHY is unplugged, phyLinkLost is called. +*/ + +int +athr_phy_is_up(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus, phyHwControl; + athrPhyInfo_t *lastStatus; + int linkCount = 0; + int lostLinks = 0; + int gainedLinks = 0; + uint32_t phyBase; + uint32_t phyAddr; + + for (phyUnit=0; phyUnit < 1; phyUnit++) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + lastStatus = &athrPhyInfo[phyUnit]; + + if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */ + + phyHwStatus = phy_reg_read(0, ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + + /* See if we've lost link */ + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { /* check realtime link */ + linkCount++; + } else { + phyHwStatus = phy_reg_read(0, ATHR_PHYADDR(phyUnit),ATHR_PHY_STATUS); + /* If realtime failed check link in latch register before + * asserting link down. + */ + if (phyHwStatus & ATHR_LATCH_LINK_PASS) + linkCount++; + else + lostLinks++; + lastStatus->isPhyAlive = FALSE; + } + } else { /* last known link status was DEAD */ + + /* Check for reset complete */ + + phyHwStatus = phy_reg_read(0, ATHR_PHYADDR(phyUnit),ATHR_PHY_STATUS); + + if (!ATHR_RESET_DONE(phyHwStatus)) + continue; + + phyHwControl = phy_reg_read(0, ATHR_PHYADDR(phyUnit),ATHR_PHY_CONTROL); + + /* Check for AutoNegotiation complete */ + + if ((!(phyHwControl & ATHR_CTRL_AUTONEGOTIATION_ENABLE)) + || ATHR_AUTONEG_DONE(phyHwStatus)) { + phyHwStatus = phy_reg_read(0, ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS); + + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { + gainedLinks++; + linkCount++; + lastStatus->isPhyAlive = TRUE; + } + } + } + } + return (linkCount); + +} diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrf1_phy.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrf1_phy.h new file mode 100644 index 0000000..1bc645e --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrf1_phy.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ATHRS26_RGMII_H +#define _ATHRS26_RGMII_H + +int athr_phy_is_up(int unit); +int athr_phy_is_fdx(int unit,int phyUnit); +int athr_phy_speed(int unit,int phyUnit); +BOOL athr_phy_setup(int unit); +BOOL athr_phy_is_link_alive(int phyUnit); + +#endif diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs16_phy.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs16_phy.c new file mode 100644 index 0000000..dec1387 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs16_phy.c @@ -0,0 +1,896 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright © 2007 Atheros Communications, Inc., All Rights Reserved. + */ + +/* + * Manage the atheros ethernet PHY. + * + * All definitions in this file are operating system independent! + */ + +#include +#include +#include +#include +#include "ag7240_phy.h" +/*#include "ag7240.h" +#include "eth_diag.h"*/ +#include "athrs16_phy.h" + +/* PHY selections and access functions */ +typedef enum { + PHY_SRCPORT_INFO, + PHY_PORTINFO_SIZE, +} PHY_CAP_TYPE; + +typedef enum { + PHY_SRCPORT_NONE, + PHY_SRCPORT_VLANTAG, + PHY_SRCPORT_TRAILER, +} PHY_SRCPORT_TYPE; + +#define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6) +#define DRV_MSG(x,a,b,c,d,e,f) +#define DRV_PRINT(DBG_SW,X) + +#define ATHR_LAN_PORT_VLAN 1 +#define ATHR_WAN_PORT_VLAN 2 + + +/*depend on connection between cpu mac and s16 mac*/ +#if defined (CONFIG_PORT0_AS_SWITCH) +#define ENET_UNIT_LAN 0 +#define ENET_UNIT_WAN 1 +#define CFG_BOARD_AP96 1 + +#elif defined (CONFIG_AG7240_GE0_IS_CONNECTED) +#define ENET_UNIT_LAN 0 +#define CFG_BOARD_PB45 0 +#define CFG_BOARD_AP96 1 + +#else +#define ENET_UNIT_LAN 1 +#define ENET_UNIT_WAN 0 +#define CFG_BOARD_PB45 1 +#endif + + +#define TRUE 1 +#define FALSE 0 + +#define ATHR_PHY0_ADDR 0x0 +#define ATHR_PHY1_ADDR 0x1 +#define ATHR_PHY2_ADDR 0x2 +#define ATHR_PHY3_ADDR 0x3 +#define ATHR_PHY4_ADDR 0x4 +#define ATHR_IND_PHY 4 + +#define MODULE_NAME "ATHRS16" +extern int xmii_val; +extern int ag7240_open(struct net_device *dev); +extern int ag7240_stop(struct net_device *dev); +extern ag7240_mac_t *ag7240_macs[2]; + +/* + * Track per-PHY port information. + */ +typedef struct { + BOOL isEnetPort; /* normal enet port */ + BOOL isPhyAlive; /* last known state of link */ + int ethUnit; /* MAC associated with this phy port */ + uint32_t phyBase; + uint32_t phyAddr; /* PHY registers associated with this phy port */ + uint32_t VLANTableSetting; /* Value to be written to VLAN table */ +} athrPhyInfo_t; + +/* + * Per-PHY information, indexed by PHY unit number. + */ +static athrPhyInfo_t athrPhyInfo[] = { + {TRUE, /* phy port 0 -- LAN port 0 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY0_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* phy port 1 -- LAN port 1 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY1_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* phy port 2 -- LAN port 2 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY2_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* phy port 3 -- LAN port 3 */ + FALSE, + ENET_UNIT_LAN, + 0, + ATHR_PHY3_ADDR, + ATHR_LAN_PORT_VLAN + }, + + {TRUE, /* phy port 4 -- WAN port or LAN port 4 */ + FALSE, + ENET_UNIT_LAN,//ENET_UNIT_WAN, + 0, + ATHR_PHY4_ADDR, + ATHR_LAN_PORT_VLAN /* Send to all ports */ + }, + + {FALSE, /* phy port 5 -- CPU port (no RJ45 connector) */ + TRUE, + ENET_UNIT_LAN, + 0, + 0x00, + ATHR_LAN_PORT_VLAN /* Send to all ports */ + }, +}; + +static uint8_t athr16_init_flag = 0; + +//#define ATHR_PHY_MAX (sizeof(ipPhyInfo) / sizeof(ipPhyInfo[0])) +#define ATHR_PHY_MAX 5 + +/* Range of valid PHY IDs is [MIN..MAX] */ +#define ATHR_ID_MIN 0 +#define ATHR_ID_MAX (ATHR_PHY_MAX-1) + +/* Convenience macros to access myPhyInfo */ +#define ATHR_IS_ENET_PORT(phyUnit) (athrPhyInfo[phyUnit].isEnetPort) +#define ATHR_IS_PHY_ALIVE(phyUnit) (athrPhyInfo[phyUnit].isPhyAlive) +#define ATHR_ETHUNIT(phyUnit) (athrPhyInfo[phyUnit].ethUnit) +#define ATHR_PHYBASE(phyUnit) (athrPhyInfo[phyUnit].phyBase) +#define ATHR_PHYADDR(phyUnit) (athrPhyInfo[phyUnit].phyAddr) +#define ATHR_VLAN_TABLE_SETTING(phyUnit) (athrPhyInfo[phyUnit].VLANTableSetting) + + +#define ATHR_IS_ETHUNIT(phyUnit, ethUnit) \ + (ATHR_IS_ENET_PORT(phyUnit) && \ + ATHR_ETHUNIT(phyUnit) == (ethUnit)) + +#define ATHR_IS_WAN_PORT(phyUnit) (!(ATHR_ETHUNIT(phyUnit)==ENET_UNIT_LAN)) + +/* Forward references */ +BOOL athrs16_phy_is_link_alive(int phyUnit); +static void phy_mode_setup(void); + +static void phy_mode_setup(void) +{ +#ifdef ATHRS16_VER_1_0 + printk("phy_mode_setup\n"); + + /*work around for phy4 rgmii mode*/ + phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 29, 18); + phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 30, 0x480c); + + /*rx delay*/ + phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 29, 0); + phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 30, 0x824e); + + /*tx delay*/ + phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 29, 5); + phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 30, 0x3d47); +#endif +} + +void athrs16_reg_init(int ethUinit) +{ + /* if using header for register configuration, we have to */ + /* configure s16 register after frame transmission is enabled */ + if (athr16_init_flag) + return; + + /*Power on strip mode setup*/ +#if CFG_BOARD_PB45 + athrs16_reg_write(0x208, 0x2fd0001); /*tx delay*/ + athrs16_reg_write(0x108, 0x2be0001); /*mac0 rgmii mode*/ +#elif CFG_BOARD_AP96 + //athrs16_reg_write(0x8, 0x012e1bea); + athrs16_reg_write(0x8, 0x01261be2); +#endif +#if 0 + athrs16_reg_write(S16_PORT_STATUS_REGISTER0, /* 0x7e */ + S16_PORT_STATUS_SPEED_1000MBPS + | S16_PORT_STATUS_TXMAC_EN + | S16_PORT_STATUS_RXMAC_EN + | S16_PORT_STATUS_RXFLOW_EN + | S16_PORT_STATUS_TXFLOW_EN + | S16_PORT_STATUS_DUPLEX_FULL); + + athrs16_reg_write(S16_PORT_STATUS_REGISTER1, /* 0x230 */ + S16_PORT_STATUS_LINK_EN + | S16_PORT_STATUS_RXFLOW_EN + | S16_PORT_STATUS_TXFLOW_EN); + + athrs16_reg_write(S16_PORT_STATUS_REGISTER2, /*0x230*/ + S16_PORT_STATUS_LINK_EN + | S16_PORT_STATUS_RXFLOW_EN + | S16_PORT_STATUS_TXFLOW_EN); + + athrs16_reg_write(S16_PORT_STATUS_REGISTER3, /*0x230*/ + S16_PORT_STATUS_LINK_EN + | S16_PORT_STATUS_RXFLOW_EN + | S16_PORT_STATUS_TXFLOW_EN); + athrs16_reg_write(S16_PORT_STATUS_REGISTER4, /*0x230*/ + S16_PORT_STATUS_LINK_EN + | S16_PORT_STATUS_RXFLOW_EN + | S16_PORT_STATUS_TXFLOW_EN); +#endif + athrs16_reg_write(S16_PORT_STATUS_REGISTER0, 0x7e); + athrs16_reg_write(S16_PORT_STATUS_REGISTER1, 0x200); + athrs16_reg_write(S16_PORT_STATUS_REGISTER2, 0x200); + athrs16_reg_write(S16_PORT_STATUS_REGISTER3, 0x200); + athrs16_reg_write(S16_PORT_STATUS_REGISTER4, 0x200); +#if CFG_BOARD_PB45 + athrs16_reg_write(0x600, 0x200); + printk("CFG Board PB45 \n"); +#elif CFG_BOARD_AP96 + //athrs16_reg_write(0x600, 0x0); + printk("CFG Board AP96 \n"); +// athrs16_reg_write(0x600, 0x200); + athrs16_reg_write(S16_PORT_STATUS_REGISTER5, /*0x230*/ + S16_PORT_STATUS_LINK_EN + | S16_PORT_STATUS_RXFLOW_EN + | S16_PORT_STATUS_TXFLOW_EN); +#endif + + athrs16_reg_write(S16_FLD_MASK_REG, 0x003f003f); + +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +#ifdef HEADER_EN + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0, 0x6804); +#else + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0, 0x6004); +#endif + + athrs16_reg_write(S16_PORT_CONTROL_REGISTER1, 0x6004); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER2, 0x6004); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER3, 0x6004); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER4, 0x6004); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER5, 0x6004); +#else +#ifdef HEADER_EN + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0, 0x4804); +#else + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0, 0x4004); +#endif +#endif + +#ifdef FULL_FEATURE + hsl_dev_init(0, 2); +#endif + +#if 1 + /* Enable ARP packets to CPU port */ + athrs16_reg_write(S16_ARL_TBL_CTRL_REG,(athrs16_reg_read(S16_ARL_TBL_CTRL_REG) | 0x100000)); + /* Enable Broadcast packets to CPU port */ +// athrs16_reg_write(S16_FLD_MASK_REG,(athrs16_reg_read(S16_FLD_MASK_REG) | S16_ENABLE_CPU_BROADCAST )); + athrs16_reg_write(S16_FLD_MASK_REG,0x003f003f | S16_ENABLE_CPU_BROADCAST); // enable multicast and unicast on all ports, in case that bootloader did not initialize it in correct way + //jumbo, 8316 only + athrs16_reg_write(0x30,(athrs16_reg_read(0x30)&AR8316_GCTRL_MTU)|(9018 + 8 + 2)); + +#endif +#ifndef CONFIG_S26_SWITCH_ONLY_MODE + athrs16_reg_write(S16_CPU_PORT_REGISTER,0x000001f0); + + +#ifndef CONFIG_WZRG450 +// config wan = port 4, lan = 0 - 3 + /* Recognize tag packet from CPU */ + +#ifdef CONFIG_WZRG300NH2 +//totally screwed up vlan mapping. wan port is 1, lan is 2 3 4 0 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER0,0xc03e0001); // 1111100000000000000001 + /* Insert PVID 1 to LAN ports */ + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER1,0x00390001); // 1110010000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER3,0x00330001); // 1100110000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER4,0x002b0001); // 1010110000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER5,0x001b0001); // 0110110000000000000001 + /* Insert PVID 2 to WAN port */ + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER2,0x00010002); // 0000010000000000000010 + + /* Egress tag packet to CPU and untagged packet to LAN port */ + + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0,0x00006204); + + athrs16_reg_write(S16_PORT_CONTROL_REGISTER1,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER2,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER3,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER4,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER5,0x00006104); + + /* Group port - 0,1,2,3 to VID 1 */ + athrs16_reg_write(S16_VLAN_FUNC_REGISTER1,0x0000083b); // 100000111011 + athrs16_reg_write(S16_VLAN_FUNC_REGISTER0,0x0001000a); + + /* port - 4 to VID 2 */ + athrs16_reg_write(S16_VLAN_FUNC_REGISTER1,0x00000805); // 100000000101 + athrs16_reg_write(S16_VLAN_FUNC_REGISTER0,0x0002000a); +#else + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER0,0xc03e0001); // 1111100000000000000001 + /* Insert PVID 1 to LAN ports */ + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER1,0x001d0001); // 111010000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER2,0x001b0001); // 110110000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER3,0x00170001); // 101110000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER4,0x000f0001); // 011110000000000000001 + /* Insert PVID 2 to WAN port */ + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER5,0x00010002); // 0000010000000000000010 + + /* Egress tag packet to CPU and untagged packet to LAN port */ + + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0,0x00006204); + + athrs16_reg_write(S16_PORT_CONTROL_REGISTER1,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER2,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER3,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER4,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER5,0x00006104); + + /* Group port - 0,1,2,3 to VID 1 */ + athrs16_reg_write(S16_VLAN_FUNC_REGISTER1,0x0000081f); // 100000011111 + athrs16_reg_write(S16_VLAN_FUNC_REGISTER0,0x0001000a); + + /* port - 4 to VID 2 */ + athrs16_reg_write(S16_VLAN_FUNC_REGISTER1,0x00000821); // 100000100001 + athrs16_reg_write(S16_VLAN_FUNC_REGISTER0,0x0002000a); +#endif + + + + + +#else +#if 0 // double vlan tag + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER0,0x403e0001); // 1111100000000000000001 + + /* Insert PVID 1 to LAN ports */ + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER1,0x40010002); // 0000010000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER2,0x40390001); // 1110010000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER3,0x40350001); // 1101010000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER4,0x402d0001); // 1011010000000000000001 + + /* Insert PVID 2 to WAN port */ + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER5,0x401d0001); // 0111010000000000000010 +#else + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER0,0xc03e0001); // 1111100000000000000001 + + /* Insert PVID 1 to LAN ports */ + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER1,0x00010002); // 0000010000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER2,0x00390001); // 1110010000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER3,0x00350001); // 1101010000000000000001 + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER4,0x002d0001); // 1011010000000000000001 + + /* Insert PVID 2 to WAN port */ + athrs16_reg_write(S16_PORT_BASE_VLAN_REGISTER5,0x001d0001); // 0111010000000000000010 + + +#endif + + /* Egress tag packet to CPU and untagged packet to LAN port */ + +#if 0 // double vlan tag + + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0,0x00004204); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER1,0x00004104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER2,0x00004104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER3,0x00004104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER4,0x00004104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER5,0x00004104); +#else + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0,0x00006204); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER1,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER2,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER3,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER4,0x00006104); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER5,0x00006104); + + +#endif + /* Group port - 0,1,2,3 to VID 1 */ + athrs16_reg_write(S16_VLAN_FUNC_REGISTER1,0x0000083d); // 100000111101 + athrs16_reg_write(S16_VLAN_FUNC_REGISTER0,0x0001000a); + + /* port - 4 to VID 2 */ + athrs16_reg_write(S16_VLAN_FUNC_REGISTER1,0x00000803); // 100000000011 + athrs16_reg_write(S16_VLAN_FUNC_REGISTER0,0x0002000a); + +#endif + +#if 0 + printk("Disable switch learning function.\n"); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER0, athrs16_reg_read(S16_PORT_CONTROL_REGISTER0)&(~(0x1<<14))); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER1, athrs16_reg_read(S16_PORT_CONTROL_REGISTER1)&(~(0x1<<14))); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER2, athrs16_reg_read(S16_PORT_CONTROL_REGISTER2)&(~(0x1<<14))); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER3, athrs16_reg_read(S16_PORT_CONTROL_REGISTER3)&(~(0x1<<14))); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER4, athrs16_reg_read(S16_PORT_CONTROL_REGISTER4)&(~(0x1<<14))); + athrs16_reg_write(S16_PORT_CONTROL_REGISTER5, athrs16_reg_read(S16_PORT_CONTROL_REGISTER5)&(~(0x1<<14))); +#endif + printk("athrs16_reg_init complete.\n"); + +#endif + athr16_init_flag = 1; +} + +/****************************************************************************** +* +* athrs16_phy_is_link_alive - test to see if the specified link is alive +* +* RETURNS: +* TRUE --> link is alive +* FALSE --> link is down +*/ +BOOL +athrs16_phy_is_link_alive(int phyUnit) +{ + uint16_t phyHwStatus; + uint32_t phyBase; + uint32_t phyAddr; + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + phyHwStatus = phy_reg_read(phyBase, phyAddr, ATHR_PHY_SPEC_STATUS); + + if (phyHwStatus & ATHR_STATUS_LINK_PASS) + return TRUE; + + return FALSE; +} + +/****************************************************************************** +* +* athrs16_phy_setup - reset and setup the PHY associated with +* the specified MAC unit number. +* +* Resets the associated PHY port. +* +* RETURNS: +* TRUE --> associated PHY is alive +* FALSE --> no LINKs on this ethernet unit +*/ + +BOOL +athrs16_phy_setup(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus; + uint16_t timeout; + int liveLinks = 0; + uint32_t phyBase = 0; + BOOL foundPhy = FALSE; + uint32_t phyAddr = 0; + + + /* See if there's any configuration data for this enet */ + /* start auto negogiation on each phy */ + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + foundPhy = TRUE; + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + phy_reg_write(phyBase, phyAddr, ATHR_AUTONEG_ADVERT, + ATHR_ADVERTISE_ALL); + + phy_reg_write(phyBase, phyAddr, ATHR_1000BASET_CONTROL, + ATHR_ADVERTISE_1000FULL); + + /* Reset PHYs*/ + phy_reg_write(phyBase, phyAddr, ATHR_PHY_CONTROL, + ATHR_CTRL_AUTONEGOTIATION_ENABLE + | ATHR_CTRL_SOFTWARE_RESET); + + } + + if (!foundPhy) { + return FALSE; /* No PHY's configured for this ethUnit */ + } + + /* + * After the phy is reset, it takes a little while before + * it can respond properly. + */ + mdelay(1000); + + + /* + * Wait up to 3 seconds for ALL associated PHYs to finish + * autonegotiation. The only way we get out of here sooner is + * if ALL PHYs are connected AND finish autonegotiation. + */ + for (phyUnit=0; (phyUnit < ATHR_PHY_MAX) /*&& (timeout > 0) */; phyUnit++) { + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + timeout=20; + for (;;) { + phyHwStatus = phy_reg_read(phyBase, phyAddr, ATHR_PHY_CONTROL); + + if (ATHR_RESET_DONE(phyHwStatus)) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Neg Success\n", phyUnit)); + break; + } + if (timeout == 0) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Negogiation timeout\n", phyUnit)); + break; + } + if (--timeout == 0) { + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("Port %d, Negogiation timeout\n", phyUnit)); + break; + } + + mdelay(150); + } + } + + /* + * All PHYs have had adequate time to autonegotiate. + * Now initialize software status. + * + * It's possible that some ports may take a bit longer + * to autonegotiate; but we can't wait forever. They'll + * get noticed by mv_phyCheckStatusChange during regular + * polling activities. + */ + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } +#if 0 + /* Enable RGMII */ + phy_reg_write(0,phyUnit,0x1d,0x12); + phy_reg_write(0,phyUnit,0x1e,0x8); + /* Tx delay on PHY */ + phy_reg_write(0,phyUnit,0x1d,0x5); + phy_reg_write(0,phyUnit,0x1e,0x100); + + /* Rx delay on PHY */ + phy_reg_write(0,phyUnit,0x1d,0x0); + phy_reg_write(0,phyUnit,0x1e,0x8000); +#endif + if (athrs16_phy_is_link_alive(phyUnit)) { + liveLinks++; + ATHR_IS_PHY_ALIVE(phyUnit) = TRUE; + } else { + ATHR_IS_PHY_ALIVE(phyUnit) = FALSE; + } + + DRV_PRINT(DRV_DEBUG_PHYSETUP, + ("eth%d: Phy Specific Status=%4.4x\n", + ethUnit, + phy_reg_read(ATHR_PHYBASE(phyUnit), + ATHR_PHYADDR(phyUnit), + ATHR_PHY_SPEC_STATUS))); + } + phy_mode_setup(); + return (liveLinks > 0); +} + +/****************************************************************************** +* +* athrs16_phy_is_fdx - Determines whether the phy ports associated with the +* specified device are FULL or HALF duplex. +* +* RETURNS: +* 1 --> FULL +* 0 --> HALF +*/ +int +athrs16_phy_is_fdx(int ethUnit) +{ + int phyUnit; + uint32_t phyBase; + uint32_t phyAddr; + uint16_t phyHwStatus; + int ii = 200; + + if (ethUnit == ENET_UNIT_LAN) + return TRUE; + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + if (athrs16_phy_is_link_alive(phyUnit)) { + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + do { + phyHwStatus = phy_reg_read (phyBase, phyAddr, + ATHR_PHY_SPEC_STATUS); + if(phyHwStatus & ATHR_STATUS_RESOVLED) + break; + mdelay(10); + } while(--ii); + + if (phyHwStatus & ATHER_STATUS_FULL_DEPLEX) + return TRUE; + } + } + + return FALSE; +} + +/****************************************************************************** +* +* athrs16_phy_speed - Determines the speed of phy ports associated with the +* specified device. +* +* RETURNS: +* AG7240_PHY_SPEED_10T, AG7240_PHY_SPEED_100TX; +* AG7240_PHY_SPEED_1000T; +*/ + +int +athrs16_phy_speed(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus; + uint32_t phyBase; + uint32_t phyAddr; + int ii = 200; + ag7240_phy_speed_t phySpeed = 0; + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + phySpeed = AG7240_PHY_SPEED_10T; + + if (athrs16_phy_is_link_alive(phyUnit)) { + + do { + phyHwStatus = phy_reg_read(phyBase, phyAddr, + ATHR_PHY_SPEC_STATUS); + if(phyHwStatus & ATHR_STATUS_RESOVLED) + break; + mdelay(10); + }while(--ii); + + phyHwStatus = ((phyHwStatus & ATHER_STATUS_LINK_MASK) >> + ATHER_STATUS_LINK_SHIFT); + + switch(phyHwStatus) { + case 0: + phySpeed = AG7240_PHY_SPEED_10T; + break; + case 1: + phySpeed = AG7240_PHY_SPEED_100TX; + break; + case 2: + phySpeed = AG7240_PHY_SPEED_1000T; + break; + default: + printk("Unkown speed read!\n"); + } + } + + phy_reg_write(phyBase, phyAddr, ATHR_DEBUG_PORT_ADDRESS, 0x18); + + if(phySpeed == AG7240_PHY_SPEED_100TX) { + phy_reg_write(phyBase, phyAddr, ATHR_DEBUG_PORT_DATA, 0xba8); + } else { + phy_reg_write(phyBase, phyAddr, ATHR_DEBUG_PORT_DATA, 0x2ea); + } + } + + if (ethUnit == ENET_UNIT_LAN) + phySpeed = AG7240_PHY_SPEED_1000T; + + return phySpeed; +} + +/***************************************************************************** +* +* athr_phy_is_up -- checks for significant changes in PHY state. +* +* A "significant change" is: +* dropped link (e.g. ethernet cable unplugged) OR +* autonegotiation completed + link (e.g. ethernet cable plugged in) +* +* When a PHY is plugged in, phyLinkGained is called. +* When a PHY is unplugged, phyLinkLost is called. +*/ + +int +athrs16_phy_is_up(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus, phyHwControl; + athrPhyInfo_t *lastStatus; + int linkCount = 0; + int lostLinks = 0; + int gainedLinks = 0; + uint32_t phyBase; + uint32_t phyAddr; + + for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { + if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + phyBase = ATHR_PHYBASE(phyUnit); + phyAddr = ATHR_PHYADDR(phyUnit); + + lastStatus = &athrPhyInfo[phyUnit]; + + if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */ + phyHwStatus = phy_reg_read(phyBase, phyAddr, ATHR_PHY_SPEC_STATUS); + + /* See if we've lost link */ + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { + linkCount++; + } else { + lostLinks++; + DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d down\n", + ethUnit, phyUnit)); + lastStatus->isPhyAlive = FALSE; + } + } else { /* last known link status was DEAD */ + /* Check for reset complete */ + phyHwStatus = phy_reg_read(phyBase, phyAddr, ATHR_PHY_STATUS); + if (!ATHR_RESET_DONE(phyHwStatus)) + continue; + + phyHwControl = phy_reg_read(phyBase, phyAddr, ATHR_PHY_CONTROL); + /* Check for AutoNegotiation complete */ + if ((!(phyHwControl & ATHR_CTRL_AUTONEGOTIATION_ENABLE)) + || ATHR_AUTONEG_DONE(phyHwStatus)) { + phyHwStatus = phy_reg_read(phyBase, phyAddr, + ATHR_PHY_SPEC_STATUS); + + if (phyHwStatus & ATHR_STATUS_LINK_PASS) { + gainedLinks++; + linkCount++; + DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d up\n", + ethUnit, phyUnit)); + printk("\nenet%d port%d up\n",ethUnit, phyUnit); + lastStatus->isPhyAlive = TRUE; + } + } + } + } + + return (linkCount); + +} + +uint32_t +athrs16_reg_read(uint32_t reg_addr) +{ + uint32_t reg_word_addr; + uint32_t phy_addr, tmp_val, reg_val; + uint16_t phy_val; + uint8_t phy_reg; + + /* change reg_addr to 16-bit word address, 32-bit aligned */ + reg_word_addr = (reg_addr & 0xfffffffc) >> 1; + + /* configure register high address */ + phy_addr = 0x18; + phy_reg = 0x0; + phy_val = (uint16_t) ((reg_word_addr >> 8) & 0x1ff); /* bit16-8 of reg address */ + phy_reg_write(0, phy_addr, phy_reg, phy_val); + + /* For some registers such as MIBs, since it is read/clear, we should */ + /* read the lower 16-bit register then the higher one */ + + /* read register in lower address */ + phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ + phy_reg = (uint8_t) (reg_word_addr & 0x1f); /* bit4-0 of reg address */ + reg_val = (uint32_t) phy_reg_read(0, phy_addr, phy_reg); + + /* read register in higher address */ + reg_word_addr++; + phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ + phy_reg = (uint8_t) (reg_word_addr & 0x1f); /* bit4-0 of reg address */ + tmp_val = (uint32_t) phy_reg_read(0, phy_addr, phy_reg); + reg_val |= (tmp_val << 16); + + return reg_val; +} + +void +athrs16_reg_write(uint32_t reg_addr, uint32_t reg_val) +{ + uint32_t reg_word_addr; + uint32_t phy_addr; + uint16_t phy_val; + uint8_t phy_reg; + + /* change reg_addr to 16-bit word address, 32-bit aligned */ + reg_word_addr = (reg_addr & 0xfffffffc) >> 1; + + /* configure register high address */ + phy_addr = 0x18; + phy_reg = 0x0; + phy_val = (uint16_t) ((reg_word_addr >> 8) & 0x1ff); /* bit16-8 of reg address */ + phy_reg_write(0, phy_addr, phy_reg, phy_val); + + /* For some registers such as ARL and VLAN, since they include BUSY bit */ + /* in lower address, we should write the higher 16-bit register then the */ + /* lower one */ + + /* read register in higher address */ + reg_word_addr++; + phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ + phy_reg = (uint8_t) (reg_word_addr & 0x1f); /* bit4-0 of reg address */ + phy_val = (uint16_t) ((reg_val >> 16) & 0xffff); + phy_reg_write(0, phy_addr, phy_reg, phy_val); + + /* write register in lower address */ + reg_word_addr--; + phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ + phy_reg = (uint8_t) (reg_word_addr & 0x1f); /* bit4-0 of reg address */ + phy_val = (uint16_t) (reg_val & 0xffff); + phy_reg_write(0, phy_addr, phy_reg, phy_val); +} + +void athrs16_reg_dev(ag7240_mac_t **mac) +{ + if( mac[0]) { + ag7240_macs[0] = mac[0]; + ag7240_macs[0]->mac_speed = 0xff; + } + else + printk("MAC [0] not registered \n"); + + if( mac[1]) { + ag7240_macs[1] = mac[1]; + ag7240_macs[1]->mac_speed = 0xff; + } + else + printk("MAC [1] not registered \n"); + return; + +} + + +int athrs16_ioctl(struct eth_cfg_params *etd, int cmd) +{ +// uint32_t ar7240_revid; + if(cmd == S26_RD_PHY) { + if(etd->portnum != 0xf) + etd->val = phy_reg_read(0,etd->portnum,etd->phy_reg); + else + etd->val = athrs16_reg_read(etd->phy_reg); + } + else if(cmd == S26_WR_PHY) { + if(etd->portnum != 0xf) + phy_reg_write(0,etd->portnum,etd->phy_reg,etd->val); + else + athrs16_reg_write(etd->phy_reg,etd->val); + } + else + return -EINVAL; + + return 0; +} + + diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs16_phy.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs16_phy.h new file mode 100644 index 0000000..a2641a0 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs16_phy.h @@ -0,0 +1,245 @@ +#ifndef _ATHRS16_PHY_H +#define _ATHRS16_PHY_H + +#define BITS(_s, _n) (((1UL << (_n)) - 1) << _s) + +#define AR8216_REG_GLOBAL_CTRL 0x0030 +#define AR8316_GCTRL_MTU BITS(0, 14) + +/*****************/ +/* PHY Registers */ +/*****************/ +#define ATHR_PHY_CONTROL 0 +#define ATHR_PHY_STATUS 1 +#define ATHR_PHY_ID1 2 +#define ATHR_PHY_ID2 3 +#define ATHR_AUTONEG_ADVERT 4 +#define ATHR_LINK_PARTNER_ABILITY 5 +#define ATHR_AUTONEG_EXPANSION 6 +#define ATHR_NEXT_PAGE_TRANSMIT 7 +#define ATHR_LINK_PARTNER_NEXT_PAGE 8 +#define ATHR_1000BASET_CONTROL 9 +#define ATHR_1000BASET_STATUS 10 +#define ATHR_PHY_SPEC_CONTROL 16 +#define ATHR_PHY_SPEC_STATUS 17 +#define ATHR_DEBUG_PORT_ADDRESS 29 +#define ATHR_DEBUG_PORT_DATA 30 + +/* ATHR_PHY_CONTROL fields */ +#define ATHR_CTRL_SOFTWARE_RESET 0x8000 +#define ATHR_CTRL_SPEED_LSB 0x2000 +#define ATHR_CTRL_AUTONEGOTIATION_ENABLE 0x1000 +#define ATHR_CTRL_RESTART_AUTONEGOTIATION 0x0200 +#define ATHR_CTRL_SPEED_FULL_DUPLEX 0x0100 +#define ATHR_CTRL_SPEED_MSB 0x0040 + +#define ATHR_RESET_DONE(phy_control) \ + (((phy_control) & (ATHR_CTRL_SOFTWARE_RESET)) == 0) + +/* Phy status fields */ +#define ATHR_STATUS_AUTO_NEG_DONE 0x0020 + +#define ATHR_AUTONEG_DONE(ip_phy_status) \ + (((ip_phy_status) & \ + (ATHR_STATUS_AUTO_NEG_DONE)) == \ + (ATHR_STATUS_AUTO_NEG_DONE)) + +/* Link Partner ability */ +#define ATHR_LINK_100BASETX_FULL_DUPLEX 0x0100 +#define ATHR_LINK_100BASETX 0x0080 +#define ATHR_LINK_10BASETX_FULL_DUPLEX 0x0040 +#define ATHR_LINK_10BASETX 0x0020 + +/* Advertisement register. */ +#define ATHR_ADVERTISE_NEXT_PAGE 0x8000 +#define ATHR_ADVERTISE_ASYM_PAUSE 0x0800 +#define ATHR_ADVERTISE_PAUSE 0x0400 +#define ATHR_ADVERTISE_100FULL 0x0100 +#define ATHR_ADVERTISE_100HALF 0x0080 +#define ATHR_ADVERTISE_10FULL 0x0040 +#define ATHR_ADVERTISE_10HALF 0x0020 + +#define ATHR_ADVERTISE_ALL (ATHR_ADVERTISE_ASYM_PAUSE | ATHR_ADVERTISE_PAUSE | \ + ATHR_ADVERTISE_10HALF | ATHR_ADVERTISE_10FULL | \ + ATHR_ADVERTISE_100HALF | ATHR_ADVERTISE_100FULL) + +/* 1000BASET_CONTROL */ +#define ATHR_ADVERTISE_1000FULL 0x0200 + +/* Phy Specific status fields */ +#define ATHER_STATUS_LINK_MASK 0xC000 +#define ATHER_STATUS_LINK_SHIFT 14 +#define ATHER_STATUS_FULL_DEPLEX 0x2000 +#define ATHR_STATUS_LINK_PASS 0x0400 +#define ATHR_STATUS_RESOVLED 0x0800 + +/*phy debug port register */ +#define ATHER_DEBUG_SERDES_REG 5 + +/* Serdes debug fields */ +#define ATHER_SERDES_BEACON 0x0100 + +/* S16 CSR Registers */ + +#define S16_PORT_STATUS_REGISTER0 0x0100 +#define S16_PORT_STATUS_REGISTER1 0x0200 +#define S16_PORT_STATUS_REGISTER2 0x0300 +#define S16_PORT_STATUS_REGISTER3 0x0400 +#define S16_PORT_STATUS_REGISTER4 0x0500 +#define S16_PORT_STATUS_REGISTER5 0x0600 + + +/* bit definition for port status register + * 31:13 : RO : : Reserved + * 12 : RW : 1: Reserved + * 11 : RW : 0: Reserved + * 10 : RW : 0: Reserved + * 9 : RW : 1: LIN_EN + * 8 : RO : 1: 0 - phy link down. 1 - phy link up + * 7 : RW : 1; TX_HALF_FLOW_EN + * 6 : RW : 0: DUPLEX_MODE, 0-half, 1-full + * 5 : RW : 0: RxFlow control Enable + * 4 : RW : 0: TxFlow control Enable + * 3 : RW : 0: Rx Mac Enable + * 2 : RW : 0: Tx Mac Enable + * 1:0 : RW : 00: Speed, 00-10, 01-100, 10-1000, 11 -err mbps. +*/ +#define S16_PORT_STATUS_LINK_EN (1<<9) +#define S16_PORT_STATUS_LINK_STATUS (1<<8) +#define S16_PORT_STATUS_TXHALF_FLOW_EN (1<<7) +#define S16_PORT_STATUS_DUPLEX_HALF (0<<6) +#define S16_PORT_STATUS_DUPLEX_FULL (1<<6) +#define S16_PORT_STATUS_RXFLOW_EN (1<<5) +#define S16_PORT_STATUS_TXFLOW_EN (1<<4) +#define S16_PORT_STATUS_RXMAC_EN (1<<3) +#define S16_PORT_STATUS_TXMAC_EN (1<<2) +#define S16_PORT_STATUS_SPEED_10MBPS 0x0 +#define S16_PORT_STATUS_SPEED_100MBPS 0x1 +#define S16_PORT_STATUS_SPEED_1000MBPS 0x2 +#define S16_PORT_STATUS_SPEED_ERROR 0x3 + + + +#define S16_PORT_BASE_VLAN_REGISTER0 0x0108 +#define S16_PORT_BASE_VLAN_REGISTER1 0x0208 +#define S16_PORT_BASE_VLAN_REGISTER2 0x0308 +#define S16_PORT_BASE_VLAN_REGISTER3 0x0408 +#define S16_PORT_BASE_VLAN_REGISTER4 0x0508 +#define S16_PORT_BASE_VLAN_REGISTER5 0x0608 + +#define S16_VLAN_FUNC_REGISTER0 0x0040 +#define S16_VLAN_FUNC_REGISTER1 0x0044 + + +#define S16_RATE_LIMIT_REGISTER0 0x010C +#define S16_RATE_LIMIT_REGISTER1 0x020C +#define S16_RATE_LIMIT_REGISTER2 0x030C +#define S16_RATE_LIMIT_REGISTER3 0x040C +#define S16_RATE_LIMIT_REGISTER4 0x050C +#define S16_RATE_LIMIT_REGISTER5 0x060C + +#define S16_PORT_CONTROL_REGISTER0 0x0104 +#define S16_PORT_CONTROL_REGISTER1 0x0204 +#define S16_PORT_CONTROL_REGISTER2 0x0304 +#define S16_PORT_CONTROL_REGISTER3 0x0404 +#define S16_PORT_CONTROL_REGISTER4 0x0504 +#define S16_PORT_CONTROL_REGISTER5 0x0604 + +#define S16_CPU_PORT_REGISTER 0x0078 +#define S16_MDIO_CTRL_REGISTER 0x0098 + +#define S16_ARL_TBL_FUNC_REG0 0x0050 +#define S16_ARL_TBL_FUNC_REG1 0x0054 +#define S16_ARL_TBL_FUNC_REG2 0x0058 +#define S16_FLD_MASK_REG 0x002c +#define S16_ARL_TBL_CTRL_REG 0x005c +#define S16_GLOBAL_INTR_REG 0x10 +#define S16_GLOBAL_INTR_MASK_REG 0x14 +#define S16_PWR_ON_STRAP_REG 0x8 + + +#define S16_ENABLE_CPU_BROADCAST (1 << 26) + +#define S16_PHY_LINK_CHANGE_REG 0x4 +#define S16_PHY_LINK_UP 0x400 +#define S16_PHY_LINK_DOWN 0x800 +#define S16_PHY_LINK_DUPLEX_CHANGE 0x2000 +#define S16_PHY_LINK_SPEED_CHANGE 0x4000 +#define S16_PHY_LINK_INTRS (PHY_LINK_UP | PHY_LINK_DOWN | PHY_LINK_DUPLEX_CHANGE | PHY_LINK_SPEED_CHANGE) + +/* SWITCH QOS REGISTERS */ + +#define ATHR_PRI_CTRL_PORT_0 0x110 /* CPU PORT */ +#define ATHR_PRI_CTRL_PORT_1 0x210 +#define ATHR_PRI_CTRL_PORT_2 0x310 +#define ATHR_PRI_CTRL_PORT_3 0x410 +#define ATHR_PRI_CTRL_PORT_4 0x510 +#define ATHR_PRI_CTRL_PORT_5 0x610 + +#define ATHR_TOS_PRI_EN (1 << 16) +#define ATHR_VLAN_PRI_EN (1 << 17) +#define ATHR_DA_PRI_EN (1 << 18) +#define ATHR_PORT_PRI_EN (1 << 19) + +#define ATHR_QOS_MODE_REGISTER 0x030 +#define ATHR_QOS_FIXED_PRIORITY ((0 << 31) | (0 << 28)) +#define ATHR_QOS_WEIGHTED ((1 << 31) | (0 << 28)) /* Fixed weight 8,4,2,1 */ +#define ATHR_QOS_MIXED ((1 << 31) | (1 << 28)) /* Q3 for managment; Q2,Q1,Q0 - 4,2,1 */ + +#ifndef BOOL +#define BOOL int +#endif + +/*add feature define here*/ +//#define FULL_FEATURE + +#ifdef CONFIG_AR7242_S16_PHY +#undef HEADER_REG_CONF +#undef HEADER_EN +#endif + +void athrs16_reg_init(int ethUnit); +int athrs16_phy_is_up(int unit); +int athrs16_phy_is_fdx(int unit); +int athrs16_phy_speed(int unit); +BOOL athrs16_phy_setup(int unit); +int athrs16_phy_is_link_alive(int phyUnit); +void athrs16_reg_dev(ag7240_mac_t **mac); +uint32_t athrs16_reg_read(uint32_t reg_addr); +void athrs16_reg_write(uint32_t reg_addr, uint32_t reg_val); +int athrs16_ioctl(struct eth_cfg_params *ethcfg, int cmd); + +/* + * Atheros header defines + */ +#ifndef _ATH_HEADER_CONF +#define _ATH_HEADER_CONF + +typedef enum { + NORMAL_PACKET, + RESERVED0, + MIB_1ST, + RESERVED1, + RESERVED2, + READ_WRITE_REG, + READ_WRITE_REG_ACK, + RESERVED3 +} AT_HEADER_TYPE; + +typedef struct { + uint16_t reserved0; + uint16_t priority; + uint16_t type ; + uint16_t broadcast; + uint16_t from_cpu; + uint16_t reserved1; + uint16_t port_num; +}at_header_t; + +#define ATHR_HEADER_LEN 2 + +#endif // _ATH_HEADER_CONF + +#endif + + diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_ioctl.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_ioctl.h new file mode 100644 index 0000000..96b1fd2 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_ioctl.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ATHRS_IOCTL_H +#define _ATHRS_IOCTL_H 1 + +#ifndef ETHREG_TOOL_BUILD +#include +#include +#include +//#include +#include +#include /* XXX for TOS */ +#include +#else +#include +#endif +#define S26_RD_PHY (SIOCDEVPRIVATE | 0x1) +#define S26_WR_PHY (SIOCDEVPRIVATE | 0x2) +#define S26_FORCE_PHY (SIOCDEVPRIVATE | 0x3) + + +struct eth_cfg_params { + uint16_t cmd; + char ad_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + uint16_t vlanid; + uint16_t portnum; /* pack to fit, yech */ + uint32_t phy_reg; + uint32_t tos; + uint32_t val; + uint8_t duplex; + uint8_t mac_addr[6]; +}; + + +#ifdef CONFIG_ATHRS_QOS + +#define ETH_SOFT_CLASS (SIOCDEVPRIVATE | 0x4) +#define ETH_PORT_QOS (SIOCDEVPRIVATE | 0x5) +#define ETH_VLAN_QOS (SIOCDEVPRIVATE | 0x6) +#define ETH_DA_QOS (SIOCDEVPRIVATE | 0x7) +#define ETH_IP_QOS (SIOCDEVPRIVATE | 0x8) +#define ETH_PORT_ILIMIT (SIOCDEVPRIVATE | 0x9) +#define ETH_PORT_ELIMIT (SIOCDEVPRIVATE | 0xa) +#define ETH_PORT_EQLIMIT (SIOCDEVPRIVATE | 0xb) +struct ath_qops { + uint32_t (*reg_read)(char *ad_name,uint32_t Reg); + void (*reg_write)(char *ad_name,uint32_t Reg, uint32_t Val); + void (*reg_rmw_set)(char *ad_name,uint32_t Reg, uint32_t Val); + void (*reg_rmw_clear)(char *ad_name,uint32_t Reg, uint32_t Val); + int (*enable_qos)(char *ad_name); + void (*disable_qos)(char *ad_name); + int qos_cap; + int qos_flag; +}; + +int athrs_config_qos(struct eth_cfg_params *ethcfg, int cmd); +int athr_register_qos(void *mac); + +#endif + +#ifdef CONFIG_MACH_AR7240 +#define SW_ONLY_MODE (SIOCDEVPRIVATE | 0x8) +#define SOFT_LED_BLINK (SIOCDEVPRIVATE | 0x9) +#define ETH_DMA_CHECK (SIOCDEVPRIVATE | 0xa) +#endif + +#ifdef CONFIG_ATHRS_QOS +#define S26_QOS_CTL (SIOCDEVPRIVATE | 0x9) +#endif + +#ifdef CONFIG_AR7240_S26_VLAN_IGMP +// Add or remove ports to the device +// bit0--->port0;bit1--->port1. +#define S26_VLAN_ADDPORTS (SIOCDEVPRIVATE | 0x4) +#define S26_VLAN_DELPORTS (SIOCDEVPRIVATE | 0x5) + +// Set the tag mode to the port. +#define S26_VLAN_SETTAGMODE (SIOCDEVPRIVATE | 0x6) + +// Set default vlan id to the port +#define S26_VLAN_SETDEFAULTID (SIOCDEVPRIVATE | 0x7) + +// Enable or disable IGMP snooping based on a vlanid +#define S26_IGMP_ON_OFF (SIOCDEVPRIVATE | 0x8) +//#define S26_IGMP_OFF (SIOCDEVPRIVATE | 0x9) + +// Get a link status from the specified port. +#define S26_LINK_GETSTAT (SIOCDEVPRIVATE | 0xA) + +#define S26_VLAN_ENABLE (SIOCDEVPRIVATE | 0xB) +#define S26_VLAN_DISABLE (SIOCDEVPRIVATE | 0xC) + +#define S26_ARL_ADD (SIOCDEVPRIVATE | 0xD) +#define S26_ARL_DEL (SIOCDEVPRIVATE | 0xE) + +#define S26_MCAST_CLR (SIOCDEVPRIVATE | 0xF) +#define S26_PACKET_FLAG (SIOCDEVPRIVATE | 0x0) + +#define VLAN_DEV_INFO(x) ((struct eth_vlan_dev_info *)x->priv) + +struct eth_vlan_dev_info { + unsigned long inmap[8]; + char * outmap[16]; + unsigned short vlan_id; +}; + +#endif + +typedef struct { + u_int8_t uc[6]; +} mac_addr_t; + +struct arl_struct { + mac_addr_t mac_addr; + int port_map; + int sa_drop; +}; + + + +#endif //_ATHRS_IOCTL_H diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_qos.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_qos.c new file mode 100644 index 0000000..d97e263 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_qos.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include "athrs_qos.h" + +int athrs_config_qos(struct eth_cfg_params *ethcfg, int cmd) +{ + struct ath_qops *qmac; + struct net_device *dev; + + if(cmd != S26_QOS_CTL) + return -EINVAL; + + cmd = ethcfg->cmd; + qmac = get_qmac(ethcfg->ad_name); + dev = get_ndev(ethcfg->ad_name); + if (((cmd != ETH_SOFT_CLASS) && (qmac->qos_cap == 0)) + && !(cmd == ETH_PORT_ILIMIT + || cmd == ETH_PORT_ELIMIT + || cmd == ETH_PORT_EQLIMIT)) { + + printk("QOS capability not enabled\n"); + return -EINVAL; + } + + netif_carrier_off(dev); + netif_stop_queue(dev); + printk(KERN_INFO "Qos Config %x,%x,%s,%x,%x,%x\n",cmd,ethcfg->val,ethcfg->ad_name,ethcfg->portnum, + ethcfg->vlanid,ethcfg->mac_addr[0]); + switch(cmd){ + case ETH_SOFT_CLASS: + if( (ethcfg->val > 0) && (qmac->qos_cap == 0) ) { + qmac->enable_qos(ethcfg->ad_name); + }else if((ethcfg->val == 0) && (qmac->qos_cap != 0)){ + qmac->disable_qos(ethcfg->ad_name); + } + break; + case ETH_PORT_QOS: + if(ethcfg->val >= 0 && ethcfg->val < 4 ) { + qmac->reg_rmw_clear(ethcfg->ad_name,ATHR_PRI_CTRL_PORT_0 + (ethcfg->portnum * 256), + (ATHR_DA_PRI_EN|ATHR_TOS_PRI_EN|ATHR_VLAN_PRI_EN)); + + qmac->reg_rmw_set(ethcfg->ad_name,ATHR_PRI_CTRL_PORT_0 + + (ethcfg->portnum * 256),ATHR_PORT_PRI_EN); + + qmac->reg_rmw_clear(ethcfg->ad_name,PORT_BASE_VLAN_REGISTER0 + + (ethcfg->portnum * 256),(0x3 << 28)); + + qmac->reg_rmw_set(ethcfg->ad_name,PORT_BASE_VLAN_REGISTER0 + + (ethcfg->portnum * 256),((ethcfg->val & 0xf) << 28)); + } + break; + case ETH_VLAN_QOS: + if (ethcfg->val >= 0 && ethcfg->val < 8) { + int portmap = 0; + int i; + python_ioctl_vlan_qos(ethcfg->vlanid, ethcfg->val, &portmap); + for(i=1; i<5; i++){ + if((portmap & (0x1 << i)) != 0){ + qmac->reg_rmw_clear(ethcfg->ad_name,ATHR_PRI_CTRL_PORT_0 + (i * 256), + (ATHR_DA_PRI_EN|ATHR_TOS_PRI_EN|ATHR_PORT_PRI_EN)); + + qmac->reg_rmw_set(ethcfg->ad_name,ATHR_PRI_CTRL_PORT_0 + + (i * 256),ATHR_VLAN_PRI_EN); + } + } + } + break; + case ETH_IP_QOS: + { + int i; + for(i=1; i<5; i++){ + qmac->reg_rmw_clear(ethcfg->ad_name,ATHR_PRI_CTRL_PORT_0 + (i * 256), + (ATHR_DA_PRI_EN|ATHR_VLAN_PRI_EN|ATHR_PORT_PRI_EN)); + + qmac->reg_rmw_set(ethcfg->ad_name,ATHR_PRI_CTRL_PORT_0 + + (i * 256),ATHR_TOS_PRI_EN); + } + if(ethcfg->tos != 0 || ethcfg->val != 0){ + qmac->reg_rmw_clear(ethcfg->ad_name, ATHR_IP_PRI_MAP0 + (((ethcfg->tos >> 1)/32)<<2), + 0x3 << ((ethcfg->tos%64) >> 1)); + + qmac->reg_rmw_set(ethcfg->ad_name, ATHR_IP_PRI_MAP0 + (((ethcfg->tos >> 1)/32)<<2), + ethcfg->val << ((ethcfg->tos%64) >> 1)); + } + } + break; + case ETH_DA_QOS: + if (ethcfg->val >= 0 && ethcfg->val < 4) { + int i=0; + for(i=1; i<5; i++){ + qmac->reg_rmw_clear(ethcfg->ad_name,ATHR_PRI_CTRL_PORT_0 + (i * 256), + (ATHR_TOS_PRI_EN|ATHR_VLAN_PRI_EN|ATHR_PORT_PRI_EN)); + qmac->reg_rmw_set(ethcfg->ad_name,ATHR_PRI_CTRL_PORT_0 + + (i * 256),ATHR_DA_PRI_EN); + } + python_fdb_add_qos(ethcfg->mac_addr, (ethcfg->val & 0x3),(ethcfg->portnum)); + } + break; + case ETH_PORT_ILIMIT: + { + int val; + val = ethcfg->val & 0x7fff; + qmac->reg_rmw_clear(ethcfg->ad_name, RATE_LIMIT_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff); + qmac->reg_rmw_set(ethcfg->ad_name, RATE_LIMIT_REGISTER0 + + (ethcfg->portnum * 256), val); + } + break; + case ETH_PORT_ELIMIT: + { + int val; + + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT1_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff7fff); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT1_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff7fff); + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff7fff); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff7fff); + + val = ethcfg->val & 0x7fff; + if(val != 0x7fff){ + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT_REGISTER0 + + (ethcfg->portnum * 256), (0x1 << 23)); + + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff<<16); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), val<<16); + }else{ + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT_REGISTER0 + + (ethcfg->portnum * 256), (0x1 << 23)); + + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff<<16); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), val<<16); + } + } + break; + case ETH_PORT_EQLIMIT: + { + int val; + int queue_id; + val = ethcfg->val & 0x7fff; + queue_id = ethcfg->phy_reg; + + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT_REGISTER0 + + (ethcfg->portnum * 256), (0x1 << 23)); + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff<<16); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff); + + if (0 == queue_id) { + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT1_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT1_REGISTER0 + + (ethcfg->portnum * 256), val); + } else if (1 == queue_id) { + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT1_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff<<16); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT1_REGISTER0 + + (ethcfg->portnum * 256), val<<16); + } else if (2 == queue_id) { + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), val); + } else if (3 == queue_id) { + qmac->reg_rmw_clear(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), 0x7fff<<16); + qmac->reg_rmw_set(ethcfg->ad_name,RATE_LIMIT2_REGISTER0 + + (ethcfg->portnum * 256), val<<16); + } + } + break; + default: + break; + } + + if(!netif_carrier_ok(dev)) { + netif_carrier_on(dev); + netif_start_queue(dev); + } + return 0; +} + +int athr_register_qos(void *mac) +{ + set_mac_qops(mac); + return 0; +} diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_qos.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_qos.h new file mode 100644 index 0000000..2318cbd --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/athrs_qos.h @@ -0,0 +1,289 @@ +#ifndef _ATHRS_QOPS_H +#define _ATHRS_QOPS_H 1 + +#include "athrs_ioctl.h" + +#if defined(CONFIG_MACH_AR7240) + +#define ENET_UNIT_LAN 1 +#define ENET_UNIT_WAN 0 + +#include "ag7240_mac.h" +extern ag7240_mac_t *ag7240_macs[2]; + +#ifdef CONFIG_AR7240_S26_PHY +#include "ar7240_s26_phy.h" +#endif + +#ifdef CONFIG_AR7242_RGMII_PHY +#include "athrf1_phy.h" +#endif + +#ifdef CONFIG_AR7242_S16_PHY +#include "athrs16_phy.h" +#endif + +static uint32_t +ag7240_mac_reg_read(char *ad_name,uint32_t Reg) { + +#ifdef CONFIG_AR7242_S16_PHY + ag7240_mac_t *mac = ag7240_name2mac(ad_name); + + if (mac->mac_unit == 1 && is_ar7242()) + return (athrs16_reg_read(Reg)); + else +#endif + return (athrs26_reg_read(Reg)); + +} + +static void +ag7240_mac_reg_write(char *ad_name,uint32_t Reg, uint32_t Val) +{ + + ag7240_mac_t *mac = ag7240_name2mac(ad_name); +#ifdef CONFIG_AR7242_S16_PHY + + if (mac->mac_unit == 1 && is_ar7242()) + athrs16_reg_write(Reg,Val); + else +#endif + printk(KERN_INFO "[%x] QoS Reg write %x=%x\n",mac->mac_unit,Reg,Val); + athrs26_reg_write(Reg,Val); + return; + +} + +static void +ag7240_mac_reg_rmw_set(char *ad_name,uint32_t Reg, uint32_t Val) +{ + int tval = ag7240_mac_reg_read(ad_name,Reg); + + ag7240_mac_reg_write(ad_name,Reg,(tval | Val)); +} + +static void +ag7240_mac_reg_rmw_clear(char *ad_name,uint32_t Reg, uint32_t Val) +{ + int tval = ag7240_mac_reg_read(ad_name,Reg); + + ag7240_mac_reg_write(ad_name,Reg,(tval & ~Val)); +} + +static int +ag7240_enable_mac_qos(char *ad_name) { + + ag7240_mac_t *mac = ag7240_name2mac(ad_name); + struct net_device *dev = mac->mac_dev; + int val = 0; + + dev->stop(dev); + if(mac_has_flag(mac,ETH_SWONLY_MODE)) + return -1; + + mac_set_flag(mac,WAN_QOS_SOFT_CLASS); + + if (mac->mac_unit == ENET_UNIT_LAN) { + val = ag7240_mac_reg_read(ad_name, ATHR_CTRL_PORT_0); + if( (val & ATHR_HDR_EN) == 0){ + ag7240_mac_reg_rmw_set(ad_name,ATHR_CTRL_PORT_0, ATHR_HDR_EN); + mac->qos->qos_flag |= 0x1 << 0; + } + + val = ag7240_mac_reg_read(ad_name, ATHR_CPU_PORT); + if( (val & ATHR_CPU_EN) == 0){ + ag7240_mac_reg_rmw_set(ad_name,ATHR_CPU_PORT, ATHR_CPU_EN); + mac->qos->qos_flag |= 0x1 << 1; + } + ag7240_mac_reg_rmw_set(ad_name,ATHR_QOS_MODE_REGISTER,ATHR_QOS_WEIGHTED); + +#ifdef CONFIG_AR7242_S16_PHY + if (is_ar7242() && mac->mac_unit == 0) + mac_set_flag(mac,ATHR_S16_HEADER); + else +#endif +#ifndef CONFIG_ATHEROS_HEADER_EN + mac_set_flag(mac,ATHR_S26_HEADER); +#endif + for (val = 1; val <= 4; val++){ + ag7240_mac_reg_rmw_clear(ad_name,ATHR_PRI_CTRL_PORT_0 + + (val * 256) , (ATHR_DA_PRI_EN|ATHR_TOS_PRI_EN|ATHR_VLAN_PRI_EN|ATHR_PORT_PRI_EN)); + } + } + else { + /* WAN MAC setting */ + ag7240_mac_reg_rmw_clear(ad_name,ATHR_PRI_CTRL_PORT_0 + + (5 * 256) , (ATHR_DA_PRI_EN|ATHR_TOS_PRI_EN|ATHR_VLAN_PRI_EN|ATHR_PORT_PRI_EN)); + } + mac->qos->qos_cap = 1; + dev->open(dev); + return 0; +} + +static void +ag7240_disable_mac_qos(char *ad_name) { + + ag7240_mac_t *mac = ag7240_name2mac(ad_name); + struct net_device *dev = mac->mac_dev; + int val = 0; + + dev->stop(dev); + + mac_clear_flag(mac,WAN_QOS_SOFT_CLASS); + + if (mac->mac_unit == ENET_UNIT_LAN) { + if(mac->qos->qos_flag != 0){ + if( (0x1 & mac->qos->qos_flag) != 0) + ag7240_mac_reg_rmw_clear(ad_name,ATHR_CTRL_PORT_0 ,ATHR_HDR_EN); + if( ( (0x1 << 1) & mac->qos->qos_flag) != 0) + ag7240_mac_reg_rmw_clear(ad_name,ATHR_CPU_PORT, ATHR_CPU_EN); + } + ag7240_mac_reg_rmw_clear(ad_name,ATHR_QOS_MODE_REGISTER,ATHR_QOS_WEIGHTED); + +#ifdef CONFIG_AR7242_S16_PHY + if (is_ar7242() && mac->mac_unit == 0) + mac_clear_flag(mac,ATHR_S16_HEADER); + else +#endif +#ifndef CONFIG_ATHEROS_HEADER_EN + mac_clear_flag(mac,ATHR_S26_HEADER); +#endif + for (val = 1; val <= 4; val++){ + ag7240_mac_reg_rmw_clear(ad_name,ATHR_PRI_CTRL_PORT_0 + + (val * 256) , (ATHR_DA_PRI_EN|ATHR_TOS_PRI_EN|ATHR_VLAN_PRI_EN|ATHR_PORT_PRI_EN)); + } + } + else { + /* WAN MAC setting */ + ag7240_mac_reg_rmw_clear(ad_name,ATHR_PRI_CTRL_PORT_0 + + (5 * 256) , (ATHR_DA_PRI_EN|ATHR_TOS_PRI_EN|ATHR_VLAN_PRI_EN|ATHR_PORT_PRI_EN)); + + } + mac->qos->qos_cap = 0; + mac->qos->qos_flag = 0; + dev->open(dev); +} + +struct ath_qops ath_qos_ops = { + ag7240_mac_reg_read, + ag7240_mac_reg_write, + ag7240_mac_reg_rmw_set, + ag7240_mac_reg_rmw_clear, + ag7240_enable_mac_qos, + ag7240_disable_mac_qos, + 0, +}; + +static inline struct ath_qops +*get_qmac(char *ad_name) +{ + ag7240_mac_t *mac = ag7240_name2mac(ad_name); + + if (mac_has_flag(mac,ETH_SWONLY_MODE)) + mac = ag7240_unit2mac(1); + + return (mac->qos); +} + +static inline struct net_device +*get_ndev(char *ad_name) +{ + ag7240_mac_t *mac = ag7240_name2mac(ad_name); + + if (mac_has_flag(mac,ETH_SWONLY_MODE)) + mac = ag7240_unit2mac(1); + + return (mac->mac_dev); +} + +static inline void +set_mac_qops(void *mac) +{ + ag7240_mac_t *qmac = (ag7240_mac_t *)mac; + + qmac->qos = &ath_qos_ops; + printk("%s : %p\n",__func__,qmac->mac_dev); +} + +#elif defined(CONFIG_MACH_AR7100) + +#include "ar7100.h" + +#define #define ag7100_name2mac(name) \ + strcmp(name,"eth0") ? ag7100_unit2mac(1) : ag7100_unit2mac(0) + +#if defined(CONFIG_ATHRS26_PHY) +#include "athrs26_phy.h" +#elif defined(CONFIG_ATHRS16_PHY) +#include "athrs16_phy.h" +#endif + +static uint32_t +ag7100_mac_reg_read(char *ad_name,uint32_t Reg) { + +#if defined(CONFIG_ATHRS26_PHY) + return (athrs26_reg_read(Reg)); +#elif defined(CONFIG_ATHRS16_PHY) + return (athrs16_reg_read(Reg)); +#endif + +} + +static void +ag7100_mac_reg_write(char *ad_name,uint32_t Reg, uint32_t Val) { + +#if defined(CONFIG_ATHRS26_PHY) + return (athrs26_reg_write(Reg,Val)); +#elif defined(CONFIG_ATHRS16_PHY) + return (athrs16_reg_write(Reg,Val)); +#endif + +} + +static void +ag7100_mac_reg_rmw_set(char *ad_name,uint32_t Reg, uint32_t Val) { + + int tval = ag7100_mac_reg_read(ad_name,Reg); + ag7100_mac_reg_write(ad_name,Reg,(tval | Val)); + return; +} + +static void +ag7100_mac_reg_rmw_clear(char *ad_name,uint32_t Reg, uint32_t Val) { + + int tval = ag7100_mac_reg_read(ad_name,Reg); + ag7100_mac_reg_write(ad_name,Reg,(tval & ~Val)); + return; +} + +struct ath_qops ath_qos_ops = { + ag7100_mac_reg_read, + ag7100_mac_reg_write, + ag7100_mac_reg_rmw_set, + ag7100_mac_reg_rmw_clear, +}; + +static inline struct ath_qops +*get_qmac(char *ad_name) +{ + ag7100_mac_t *mac = ag7100_name2mac(ad_name); + return (mac->qos); +} + +static inline void +set_mac_qops(void *mac) +{ + ag7100_mac_t *qmac = (ag7100_mac_t *)mac; + qmac->qos = &ath_qos_ops; +} + +static inline struct net_device +*get_ndev(char *ad_name) +{ + ag7100_mac_t *mac = ag7100_name2mac(ad_name); + return (mac->mac_dev); +} + +#endif + +#endif //_ATHRS_QOS_H diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/eth_diag.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/eth_diag.h new file mode 100644 index 0000000..35f3536 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/eth_diag.h @@ -0,0 +1,65 @@ +#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */ +#define S26_RD_PHY (SIOCDEVPRIVATE | 0x1) +#define S26_WR_PHY (SIOCDEVPRIVATE | 0x2) +#define S26_FORCE_PHY (SIOCDEVPRIVATE | 0x3) + +#ifdef CONFIG_AR7240_S26_VLAN_IGMP +// Add or remove ports to the device +// bit0--->port0;bit1--->port1. +#define S26_VLAN_ADDPORTS (SIOCDEVPRIVATE | 0x4) +#define S26_VLAN_DELPORTS (SIOCDEVPRIVATE | 0x5) + +// Set the tag mode to the port. +#define S26_VLAN_SETTAGMODE (SIOCDEVPRIVATE | 0x6) + +// Set default vlan id to the port +#define S26_VLAN_SETDEFAULTID (SIOCDEVPRIVATE | 0x7) + +// Enable or disable IGMP snooping based on a vlanid +#define S26_IGMP_ON (SIOCDEVPRIVATE | 0x8) +#define S26_IGMP_OFF (SIOCDEVPRIVATE | 0x9) + +// Get a link status from the specified port. +#define S26_LINK_GETSTAT (SIOCDEVPRIVATE | 0xA) + +#define S26_VLAN_ENABLE (SIOCDEVPRIVATE | 0xB) +#define S26_VLAN_DISABLE (SIOCDEVPRIVATE | 0xC) + +#define S26_ARL_ADD (SIOCDEVPRIVATE | 0xD) +#define S26_ARL_DEL (SIOCDEVPRIVATE | 0xE) + +#define S26_MCAST_CLR (SIOCDEVPRIVATE | 0xF) +#define S26_PACKET_FLAG (SIOCDEVPRIVATE | 0x0) + +#define VLAN_DEV_INFO(x) ((struct eth_vlan_dev_info *)x->priv) + +struct eth_vlan_dev_info { + unsigned long inmap[8]; + char * outmap[16]; + unsigned short vlan_id; +}; + +typedef struct { + u_int8_t uc[6]; +} mac_addr_t; + +struct arl_struct { + mac_addr_t mac_addr; + int port_map; + int sa_drop; +}; + +#endif + +struct eth_diag { + char ad_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + union { + u_int16_t portnum; /* pack to fit, yech */ + u_int8_t duplex; + }ed_u; + u_int32_t phy_reg; + u_int val; + caddr_t ad_in_data; + caddr_t ad_out_data; +}; + diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/python_vlan_igmp.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/python_vlan_igmp.c new file mode 100644 index 0000000..0e183dc --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/python_vlan_igmp.c @@ -0,0 +1,686 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include "ag7240_phy.h" +#include "ag7240.h" +#include "ar7240_s26_phy.h" +#include "eth_diag.h" + +#define TRUE 1 +#define FALSE 0 + +#define PYTHON_OK 0 +#define PYTHON_ERR 1 +#define PYTHON_FULL 2 + +#define PYTHON_MAX_PORT_ID 4 /* lan ports and cpu port, exclude cpu port */ +#define PYTHON_MAX_PORTS_ID 31 /* The maxsize is 0x11111 */ + +#define PYTHON_PORT_ID_CHECK(port_id) \ +do { \ + if (PYTHON_MAX_PORT_ID < port_id) \ + return PYTHON_ERR; \ +} while (0) + +#define PYTHON_PORTS_ID_CHECK(port_id) \ +do { \ + if (PYTHON_MAX_PORTS_ID < port_id) \ + return PYTHON_ERR; \ +} while (0) + + +#define PORT_CONTROL_REG_ADDR(port_id) (0x104 + 0x100 * port_id) + +static void +athrs26_reg_write(unsigned int s26_addr, unsigned int s26_write_data); + +uint32_t +python_port_igmps_status_set(uint32_t port_id, BOOL enable) +{ + uint32_t data; + uint32_t addr; + + PYTHON_PORT_ID_CHECK(port_id); + + addr = PORT_CONTROL_REG_ADDR(port_id); + + data = athrs26_reg_read(addr); + if (TRUE == enable) { + data |= (0x1 << 10); + } else if (FALSE == enable) { + data &= (~(0x1 << 10)); + } else { + return PYTHON_ERR; + } + athrs26_reg_write(addr, data); + + return PYTHON_OK; +} + + + + +#define PYTHON_VLAN_ID_CHECK(vlan_id) \ +do { \ + if ((4095 < vlan_id) || (0 > vlan_id)) \ + return PYTHON_ERR; \ +} while (0) + +#define VLAN_LOAD_ENTRY 2 +#define VLAN_PURGE_ENTRY 3 +#define VLAN_FIND_ENTRY 6 + +#define PYTHON_VTF0_ADDR 0x40 +#define PYTHON_VTF1_ADDR 0x44 + +static uint32_t +_python_vlan_commit(uint32_t op) +{ + uint32_t vt_busy = 1, i = 0x1000, vt_full, reg0; + + reg0 = athrs26_reg_read(PYTHON_VTF0_ADDR); + reg0 |= ((0x7 & op) | 0x8); + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + + while (vt_busy && --i) { + reg0 = athrs26_reg_read(PYTHON_VTF0_ADDR); + vt_busy = reg0 & 0x8; + udelay(5); + } + + /* hardware can't complete the operation */ + if (0 == i) { + return PYTHON_ERR; + } + + vt_full = reg0 & 0x10; + if (vt_full) { + /* write one to clear full voilation bit */ + reg0 = 0x10; + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + return PYTHON_FULL; + } + + return PYTHON_OK; +} + +static uint32_t +_python_vlan_read(uint32_t vlan_id, uint32_t * port_bmap) +{ + uint32_t reg0 = 0, reg1 = 0, ret; + + PYTHON_VLAN_ID_CHECK(vlan_id); + + reg0 |= ((0xfff & vlan_id) << 16); + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + + ret = _python_vlan_commit(VLAN_FIND_ENTRY); + if (PYTHON_OK != ret) { + return PYTHON_ERR; + } + + reg0 = athrs26_reg_read(PYTHON_VTF0_ADDR); + reg1 = athrs26_reg_read(PYTHON_VTF1_ADDR); + + if (reg1 & 0x800) { + *port_bmap = reg1 & 0x3f; + return PYTHON_OK; + } else { + return PYTHON_ERR; + } +} + +uint32_t +python_vlan_create(uint32_t vlan_id) +{ + uint32_t reg0 = 0, reg1 = 0; + uint32_t ret; + + PYTHON_VLAN_ID_CHECK(vlan_id); + + reg0 |= (vlan_id << 16); + reg1 |= (0x1 << 11); + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + athrs26_reg_write(PYTHON_VTF1_ADDR, reg1); + + ret = _python_vlan_commit(VLAN_LOAD_ENTRY); + if (PYTHON_OK != ret) { + return PYTHON_ERR; + } + + return PYTHON_OK; +} + +uint32_t +python_vlan_delete(uint32_t vlan_id) +{ + uint32_t reg0 = 0; + uint32_t ret; + + PYTHON_VLAN_ID_CHECK(vlan_id); + + reg0 |= (vlan_id << 16); + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + + ret = _python_vlan_commit(VLAN_PURGE_ENTRY); + if (PYTHON_OK != ret) { + return PYTHON_ERR; + } + + return PYTHON_OK; +} + +uint32_t +python_vlan_query(uint32_t vlan_id, uint32_t * port_bmap) +{ + PYTHON_VLAN_ID_CHECK(vlan_id); + + if (!port_bmap) { + return PYTHON_ERR; + } + + return _python_vlan_read(vlan_id, port_bmap); +} + +uint32_t +python_vlan_member_add(uint32_t vlan_id, uint32_t port_id) +{ + uint32_t port_bmap, reg0 = 0, reg1 = 0; + uint32_t ret; + + PYTHON_VLAN_ID_CHECK(vlan_id); + PYTHON_PORT_ID_CHECK(port_id); + + ret = _python_vlan_read(vlan_id, &port_bmap); + if (PYTHON_OK != ret) { + /* the vlan entry has not been created */ + return PYTHON_ERR; + } + + port_bmap |= (0x1 << port_id); + reg0 |= (vlan_id << 16); + reg1 |= ((0x1 << 11) | port_bmap); + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + athrs26_reg_write(PYTHON_VTF1_ADDR, reg1); + + ret = _python_vlan_commit(VLAN_LOAD_ENTRY); + if (PYTHON_FULL == ret) { + /* hardware will set full bit when update vlan entry */ + return PYTHON_OK; + } + + return ret; +} + +uint32_t +python_vlan_member_del(uint32_t vlan_id, uint32_t port_id) +{ + uint32_t port_bmap, reg0 = 0, reg1 = 0; + uint32_t ret; + + PYTHON_VLAN_ID_CHECK(vlan_id); + PYTHON_PORT_ID_CHECK(port_id); + + ret = _python_vlan_read(vlan_id, &port_bmap); + if (PYTHON_OK != ret) { + /* the vlan entry has not been created */ + return PYTHON_ERR; + } + + port_bmap &= (~(0x1 << port_id)); + reg0 |= (vlan_id << 16); + reg1 |= ((0x1 << 11) | port_bmap); + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + athrs26_reg_write(PYTHON_VTF1_ADDR, reg1); + + ret = _python_vlan_commit(VLAN_LOAD_ENTRY); + if (PYTHON_FULL == ret) { + /* hardware will set full bit when update vlan entry */ + return PYTHON_OK; + } + + return ret; +} + +#define PORT_BASE_VLAN_ADDR(port_id) (0x108 + 0x100 * port_id) + +uint32_t +python_port_default_vid_set(uint32_t port_id, uint32_t vlan_id) +{ + uint32_t addr, data; + + PYTHON_PORT_ID_CHECK(port_id); + PYTHON_VLAN_ID_CHECK(vlan_id); + + addr = PORT_BASE_VLAN_ADDR(port_id); + data = athrs26_reg_read(addr); + data &= 0xfffff000; + data |= vlan_id; + athrs26_reg_write(addr, data); + + return PYTHON_OK; +} + +typedef enum { + PORT_EG_UNMODIFIED = 0, /**< egress transmit packets unmodified */ + PORT_EG_UNTAGGED, /**< egress transmit packets without vlan tag */ + PORT_EG_TAGGED, /**< egress transmit packets with vlan tag */ +} port_1q_egmode_t; + +uint32_t +python_port_egvlanmode_set(uint32_t port_id, port_1q_egmode_t mode) +{ + uint32_t addr, data; + + PYTHON_PORT_ID_CHECK(port_id); + + addr = PORT_CONTROL_REG_ADDR(port_id); + + data = athrs26_reg_read(addr); + data &= 0xfffffcff; + if (PORT_EG_UNMODIFIED == mode) { + /* need't set value */ + } else if (PORT_EG_UNTAGGED == mode) { + data |= (0x1 << 8); + } else if (PORT_EG_TAGGED == mode) { + data |= (0x2 << 8); + } else { + /* hardware not support value */ + return PYTHON_ERR; + } + athrs26_reg_write(addr, data); + + return PYTHON_OK; +} + +/* Don't forget to enable the ports once the driver has completed the startup.*/ +uint32_t +python_port_1qmode_set(uint32_t port_id, BOOL enable) +{ + uint32_t addr, data; + + PYTHON_PORT_ID_CHECK(port_id); + + addr = PORT_BASE_VLAN_ADDR(port_id); + + data = athrs26_reg_read(addr); + data &= 0x3fffffff; + if (TRUE == enable) { + data |= (0x3 << 30); + } else if (FALSE == enable) { + /* needn't set value */ + } else { + return PYTHON_ERR; + } + athrs26_reg_write(addr, data); + + return PYTHON_OK; +} + + + + +#define PYTHON_ATF0_ADDR 0x50 +#define PYTHON_ATF1_ADDR 0x54 +#define PYTHON_ATF2_ADDR 0x58 + +#define ARL_FLUSH_ALL 1 +#define ARL_LOAD_ENTRY 2 +#define ARL_PURGE_ENTRY 3 +#define ARL_FLUSH_ALL_UNLOCK 4 +#define ARL_LOOKUP_ENTRY 7 + +static uint32_t +_python_fdb_commit(uint32_t op) +{ + uint32_t busy = 1, i = 0x1000; + uint32_t reg0, full_vio; + + reg0 = athrs26_reg_read(PYTHON_ATF0_ADDR); + reg0 |= ((op & 0x7) | 0x8); + athrs26_reg_write(PYTHON_ATF0_ADDR, reg0); + + while (busy && --i) { + reg0 = athrs26_reg_read(PYTHON_ATF0_ADDR); + busy = reg0 & 0x8; + udelay(5); + } + + if (0 == i) { + return PYTHON_ERR; + } + + full_vio = reg0 & 0x1000; + if (full_vio) { + /* must clear AT_FULL_VOI bit */ + reg0 = 0x1000; + athrs26_reg_write(PYTHON_ATF0_ADDR, reg0); + + if (ARL_LOAD_ENTRY == op) { + return PYTHON_FULL; + } else { + return PYTHON_ERR; + } + } + + return PYTHON_OK; +} + +static void +_python_fdb_fill_addr(mac_addr_t addr, uint32_t * reg0, uint32_t * reg1) +{ + *reg1 |= (addr.uc[0] << 24); + *reg1 |= (addr.uc[1] << 16); + *reg1 |= (addr.uc[2] << 8); + *reg1 |= addr.uc[3]; + + *reg0 |= (addr.uc[4] << 24); + *reg0 |= (addr.uc[5] << 16); +} + +static BOOL +_python_fdb_is_zeroaddr(mac_addr_t addr) +{ + uint32_t i; + + for (i = 0; i < 6; i++) { + if (addr.uc[i]) { + return FALSE; + } + } + + return TRUE; +} + +uint32_t +python_fdb_add(mac_addr_t addr, uint32_t port_bmap, BOOL sa_drop) +{ + uint32_t reg0 = 0, reg1 = 0, reg2 = 0; + + if (TRUE == _python_fdb_is_zeroaddr(addr)) { + return PYTHON_ERR; + } + + _python_fdb_fill_addr(addr, ®0, ®1); + + reg2 |= (0xf << 16); + if (TRUE == sa_drop) { + reg2 |= (0x1 << 14); + } + reg2 |= (port_bmap & 0x3f); + + athrs26_reg_write(PYTHON_ATF0_ADDR, reg0); + athrs26_reg_write(PYTHON_ATF1_ADDR, reg1); + athrs26_reg_write(PYTHON_ATF2_ADDR, reg2); + + return _python_fdb_commit(ARL_LOAD_ENTRY); +} + +uint32_t +python_fdb_del(mac_addr_t addr) +{ + uint32_t reg0 = 0, reg1 = 0, reg2 = 0; + + if (TRUE == _python_fdb_is_zeroaddr(addr)) { + return PYTHON_ERR; + } + + _python_fdb_fill_addr(addr, ®0, ®1); + + athrs26_reg_write(PYTHON_ATF0_ADDR, reg0); + athrs26_reg_write(PYTHON_ATF1_ADDR, reg1); + athrs26_reg_write(PYTHON_ATF2_ADDR, reg2); + + return _python_fdb_commit(ARL_PURGE_ENTRY); +} + +uint32_t +python_fdb_flush(BOOL f_static) +{ + if (f_static) { + return _python_fdb_commit(ARL_FLUSH_ALL); + } else { + return _python_fdb_commit(ARL_FLUSH_ALL_UNLOCK); + } +} + +uint32_t +python_fdb_getentry(mac_addr_t addr) +{ + uint32_t reg0 = 0, reg1 = 0, reg2 = 0,ret = 0; + int drop=0,valid=0,port= -1; + + if (TRUE == _python_fdb_is_zeroaddr(addr)) { + printk("ret = %d .\n",ret); + return 0; + } + + _python_fdb_fill_addr(addr, ®0, ®1); + + athrs26_reg_write(PYTHON_ATF0_ADDR, reg0); + athrs26_reg_write(PYTHON_ATF1_ADDR, reg1); + athrs26_reg_write(PYTHON_ATF2_ADDR, reg2); + + ret = _python_fdb_commit(ARL_LOOKUP_ENTRY); + + if(ret != PYTHON_OK){ + printk("ret = %d .\n",ret); + return 0; + } + + reg0 = athrs26_reg_read(PYTHON_ATF0_ADDR); + reg1 = athrs26_reg_read(PYTHON_ATF1_ADDR); + reg2 = athrs26_reg_read(PYTHON_ATF2_ADDR); + + addr.uc[0] = reg1 >> 24; + addr.uc[1] = (reg1 >> 16)&0xff; + addr.uc[2] = (reg1 >> 8)&0xff; + addr.uc[3] = reg1 & 0xff; + + addr.uc[4] = reg0 >> 24; + addr.uc[5] = (reg0 >> 16)&0xff; + + port = reg2&0x3f; + drop = (reg2 >> 14)&1; + valid = (reg2 >> 16)&0xf; + + printk("Lookup reg %8x %8x %8x mac %x.%x.%x.%x.%x.%x,port %d,drop %d,valid %d.\n",reg0,reg1,reg2,addr.uc[0],addr.uc[1],addr.uc[2], + addr.uc[3],addr.uc[4],addr.uc[5],port,drop,valid); + + if(reg1==0&&(reg0&0xffff0000)==0&&port==0) + return 0; + else + return 1; +} + +/* We should write high 16bits to phy then low 16bits + The reason is that the trigger bit about vlan is located in the low 16bits. + So we adjust the sequence of writing registers here. Normally we still use the + adverse sequence.*/ + +static void +athrs26_reg_write(unsigned int s26_addr, unsigned int s26_write_data) +{ + unsigned int addr_temp; + unsigned int data; + unsigned int phy_address, reg_address; + + + addr_temp = (s26_addr & 0xfffffffc) >>2; + data = addr_temp >> 7; + + phy_address = 0x1f; + reg_address = 0x10; + + phy_reg_write(0,phy_address, reg_address, data); + + phy_address = (0x17 & ((addr_temp >> 4) | 0x10)); + + reg_address = (((addr_temp << 1) & 0x1e) | 0x1); + data = s26_write_data >> 16; + phy_reg_write(0,phy_address, reg_address, data); + + reg_address = ((addr_temp << 1) & 0x1e); + data = s26_write_data & 0xffff; + phy_reg_write(0,phy_address, reg_address, data); +} +/*============================================================================================= + * The following functions are designed for the ioctl calls. + *============================================================================================*/ + +/* [python_ioctl_vlan_addports or python_ioctl_vlan_addports] port_id: bit0--->port0,bit1--->port1.*/ +uint32_t +python_ioctl_vlan_addports(uint32_t vlan_id, uint32_t port_id) +{ + uint32_t port_bmap, reg0 = 0, reg1 = 0; + uint32_t ret; + + PYTHON_VLAN_ID_CHECK(vlan_id); + PYTHON_PORTS_ID_CHECK(port_id); + + ret = _python_vlan_read(vlan_id, &port_bmap); + if (PYTHON_OK != ret) { + /* the vlan entry has not been created */ + ret = python_vlan_create(vlan_id); + if (PYTHON_OK != ret) return PYTHON_ERR; + } + + port_bmap |= port_id; + reg0 |= (vlan_id << 16); + reg1 |= ((0x1 << 11) | port_bmap); + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + athrs26_reg_write(PYTHON_VTF1_ADDR, reg1); + + ret = _python_vlan_commit(VLAN_LOAD_ENTRY); + if (PYTHON_FULL == ret) { + /* hardware will set full bit when update vlan entry */ + return PYTHON_OK; + } + + return ret; + +} + +// port_id: bit0--->port0,bit1--->port1. +uint32_t +python_ioctl_vlan_delports(uint32_t vlan_id, uint32_t port_id) +{ + uint32_t port_bmap, reg0 = 0, reg1 = 0; + uint32_t ret; + + PYTHON_VLAN_ID_CHECK(vlan_id); + PYTHON_PORTS_ID_CHECK(port_id); + + ret = _python_vlan_read(vlan_id, &port_bmap); + if (PYTHON_OK != ret) { + /* the vlan entry has not been created */ + return PYTHON_ERR; + } + + port_bmap &= (~port_id); + reg0 |= (vlan_id << 16); + reg1 |= ((0x1 << 11) | port_bmap); + athrs26_reg_write(PYTHON_VTF0_ADDR, reg0); + athrs26_reg_write(PYTHON_VTF1_ADDR, reg1); + + ret = _python_vlan_commit(VLAN_LOAD_ENTRY); + if (PYTHON_FULL == ret) { + /* hardware will set full bit when update vlan entry */ + return PYTHON_OK; + } + + // Delete the vlan id if no ports exists in the vlan. + if(port_bmap == 0) + { + ret = python_vlan_delete(vlan_id); + if (PYTHON_OK != ret) return PYTHON_ERR; + } + + return ret; + +} + +uint32_t +python_ioctl_set_igmp(uint32_t vlan_id, BOOL enable) +{ + uint32_t port_bmap; + uint32_t ret; + uint32_t i = 0; + uint32_t port = 0; + + PYTHON_VLAN_ID_CHECK(vlan_id); + + ret = _python_vlan_read(vlan_id, &port_bmap); + if (PYTHON_OK != ret) { + /* the vlan entry has not been created */ + return PYTHON_ERR; + } + + for(i=1;i<5;i++) + { + port = (port_bmap >>i)&0x1; + if(port) + { + ret = python_port_igmps_status_set(i,enable); + if (PYTHON_OK != ret) return PYTHON_ERR; + } + } + return PYTHON_OK; +} + +uint32_t +python_ioctl_enable_vlan() +{ + int i; + /* Enable ports [0-4](phy0-3 and cpu port0) */ + for(i=0;i<5;i++) + python_port_1qmode_set(i,1); + + return PYTHON_OK; +} + +uint32_t +python_ioctl_disable_vlan() +{ + int i; + /* Disable ports [0-4](phy0-3 and cpu port0) */ + for(i=0;i<5;i++) + python_port_1qmode_set(i,0); + + return PYTHON_OK; +} + +void +python_clear_multi() +{ + athrs26_reg_write(0x2c, athrs26_reg_read(0x2c)&0xffc0ffff); +} + +void +python_set_multi() +{ + athrs26_reg_write(0x2c,athrs26_reg_read(0x2c)|0x3f0000); +} + + + + + + diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/rtl8309g_phy.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/rtl8309g_phy.c new file mode 100644 index 0000000..b40bac0 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/rtl8309g_phy.c @@ -0,0 +1,245 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright © 2007 Atheros Communications, Inc., All Rights Reserved. + */ + +/* + * Manage the atheros ethernet PHY. + * + * All definitions in this file are operating system independent! + */ + +#include +#include +#include +#include +#include "ag7240_phy.h" +#include "rtl8309g_phy.h" + +static int rtl8309g_init_flag = 0; + +#define RTK_8309G_PHY0_ADDR 0x0 +#define RTK_8309G_PHY1_ADDR 0x1 +#define RTK_8309G_PHY2_ADDR 0x2 +#define RTK_8309G_PHY3_ADDR 0x3 +#define RTK_8309G_PHY4_ADDR 0x4 +#define RTK_8309G_PHY5_ADDR 0x5 +#define RTK_8309G_PHY6_ADDR 0x6 +#define RTK_8309G_PHY7_ADDR 0x7 +#define RTK_8309G_UNIT_LAN 1 +#define RTK_LAN_PORT_VLAN 1 +#define RTK_8309G_PHY_MAX 8 + +#define TRUE 1 +#define FALSE 0 + +/* Convenience macros to access myPhyInfo */ +#define RTK_8309G_IS_ENET_PORT(phyUnit) (RTKPhyInfo[phyUnit].isEnetPort) +#define RTK_8309G_IS_PHY_ALIVE(phyUnit) (RTKPhyInfo[phyUnit].isPhyAlive) +#define RTK_8309G_ETHUNIT(phyUnit) (RTKPhyInfo[phyUnit].ethUnit) +#define RTK_8309G_PHYBASE(phyUnit) (RTKPhyInfo[phyUnit].phyBase) +#define RTK_8309G_PHYADDR(phyUnit) (RTKPhyInfo[phyUnit].phyAddr) +#define RTK_8309G_VLAN_TABLE_SETTING(phyUnit) (RTKPhyInfo[phyUnit].VLANTableSetting) + +/* + * Track per-PHY port information. + */ +typedef struct { + BOOL isEnetPort; /* normal enet port */ + BOOL isPhyAlive; /* last known state of link */ + int ethUnit; /* MAC associated with this phy port */ + uint32_t phyBase; + uint32_t phyAddr; /* PHY registers associated with this phy port */ + uint32_t VLANTableSetting; /* Value to be written to VLAN table */ +} RTK8309PhyInfo_t; + +/* + * Per-PHY information, indexed by PHY unit number. + */ +static RTK8309PhyInfo_t RTKPhyInfo[] = { + {TRUE, /* phy port 0 -- LAN port 0 */ + FALSE, + RTK_8309G_UNIT_LAN, + 0, + RTK_8309G_PHY0_ADDR, + RTK_LAN_PORT_VLAN}, + {TRUE, /* phy port 0 -- LAN port 1 */ + FALSE, + RTK_8309G_UNIT_LAN, + 0, + RTK_8309G_PHY1_ADDR, + RTK_LAN_PORT_VLAN}, + {TRUE, /* phy port 0 -- LAN port 2 */ + FALSE, + RTK_8309G_UNIT_LAN, + 0, + RTK_8309G_PHY2_ADDR, + RTK_LAN_PORT_VLAN}, + {TRUE, /* phy port 0 -- LAN port 3 */ + FALSE, + RTK_8309G_UNIT_LAN, + 0, + RTK_8309G_PHY3_ADDR, + RTK_LAN_PORT_VLAN}, + {TRUE, /* phy port 0 -- LAN port 4 */ + FALSE, + RTK_8309G_UNIT_LAN, + 0, + RTK_8309G_PHY4_ADDR, + RTK_LAN_PORT_VLAN}, + {TRUE, /* phy port 0 -- LAN port 5 */ + FALSE, + RTK_8309G_UNIT_LAN, + 0, + RTK_8309G_PHY5_ADDR, + RTK_LAN_PORT_VLAN}, + {TRUE, /* phy port 0 -- LAN port 6 */ + FALSE, + RTK_8309G_UNIT_LAN, + 0, + RTK_8309G_PHY6_ADDR, + RTK_LAN_PORT_VLAN}, + {TRUE, /* phy port 0 -- LAN port 7 */ + FALSE, + RTK_8309G_UNIT_LAN, + 0, + RTK_8309G_PHY7_ADDR, + RTK_LAN_PORT_VLAN}, +}; + +int rtl8309g_phy_is_fdx(int ethUnit) +{ + return 1; //Full Duplex. +} + +int rtl8309g_phy_speed(int ethUnit) +{ + return AG7240_PHY_SPEED_100TX; +} + +int rtl8309g_phy_is_up(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus; + RTK8309PhyInfo_t *lastStatus; + int linkCount = 0; + uint32_t phyBase; + uint32_t phyAddr; + + if (!rtl8309g_init_flag) { + return 0; + } + if (ethUnit) { + return 0; + } + for (phyUnit = 0; phyUnit < RTK_8309G_PHY_MAX; phyUnit++) { + phyBase = RTK_8309G_PHYBASE(phyUnit); + phyAddr = RTK_8309G_PHYADDR(phyUnit); + lastStatus = &RTKPhyInfo[phyUnit]; + if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */ + phyHwStatus = + phy_reg_read(phyBase, phyAddr, + RTL_8309G_PHY_SPEC_STATUS); + /* See if we've lost link */ + if ((phyHwStatus & RTL_AUTO_NEG_CHECK) + && (phyHwStatus & RTL_8309G_STATUS_LINK_PASS)) { + linkCount++; + } else { + lastStatus->isPhyAlive = FALSE; + } + } else { /* last known link status was DEAD */ + phyHwStatus = + phy_reg_read(phyBase, phyAddr, + RTL_8309G_PHY_SPEC_STATUS); + if ((phyHwStatus & RTL_AUTO_NEG_CHECK) + && (phyHwStatus & RTL_8309G_STATUS_LINK_PASS)) { + linkCount++; + lastStatus->isPhyAlive = TRUE; + } + } + } + return (linkCount); +} + +BOOL rtl8309g_phy_is_link_alive(int phyUnit) +{ + return FALSE; +} + +BOOL rtl8309g_phy_setup(int ethUnit) +{ + int phyUnit; + uint16_t phyHwStatus; + uint16_t timeout; + uint32_t phyBase = 0; + BOOL foundPhy = FALSE; + uint32_t phyAddr = 0; + uint16_t id1,id2; + + if (!rtl8309g_init_flag) { + return FALSE; + } + if (ethUnit) { + return 0; + } + /* See if there's any configuration data for this enet */ + /* start auto negogiation on each phy */ + for (phyUnit = 0; phyUnit < RTK_8309G_PHY_MAX; phyUnit++) { + foundPhy = TRUE; + phyBase = RTK_8309G_PHYBASE(phyUnit); + phyAddr = RTK_8309G_PHYADDR(phyUnit); + /* Reset PHYs */ + phy_reg_write(phyBase, phyAddr, RTL_8309G_PHY_CTRL_REG, + 1 << 15); + } + /* + * After the phy is reset, it takes a little while before + * it can respond properly. + */ + mdelay(2000); + for (phyUnit = 0; phyUnit < RTK_8309G_PHY_MAX; phyUnit++) { + timeout = 20; + for (;;) { + phyHwStatus = + phy_reg_read(phyBase, phyAddr, + RTL_8309G_PHY_SPEC_STATUS); + if (RTL_RESET_DONE(phyHwStatus)) { + break; + } + if (timeout == 0) { + break; + } + if (--timeout == 0) { + break; + } + mdelay(150); + } + } + + for (phyUnit = 0; phyUnit < RTK_8309G_PHY_MAX; phyUnit++) { + foundPhy = TRUE; + phyBase = RTK_8309G_PHYBASE(phyUnit); + phyAddr = RTK_8309G_PHYADDR(phyUnit); + id1 = phy_reg_read(phyBase, phyAddr ,RTL_8309G_PHY_ID1); + id2 = phy_reg_read(phyBase, phyAddr ,RTL_8309G_PHY_ID2); + printk(KERN_INFO "rtl phy%d id %X:%X\n",phyUnit,id1,id2); + } + + + return TRUE; +} + +void rtl8309g_reg_init(int ethUnit) +{ + if (ethUnit) { + return; + } + if (rtl8309g_init_flag) { + return; + } + rtl8309g_init_flag = 1; + return; +} diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/rtl8309g_phy.h b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/rtl8309g_phy.h new file mode 100644 index 0000000..7ca8b2d --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/rtl8309g_phy.h @@ -0,0 +1,35 @@ +#ifndef _ATHRS16_PHY_H +#define _ATHRS16_PHY_H + +#define BITS(_s, _n) (((1UL << (_n)) - 1) << _s) + +#ifndef BOOL +#define BOOL int +#endif + +#define RTL_8309G_PHY_CTRL_REG 0x00 +#define RTL_8309G_PHY_SPEC_STATUS 0x01 +#define RTL_8309G_PHY_ID1 0x02 +#define RTL_8309G_PHY_ID2 0x03 +#define RTL_8309G_PHY_AUTO_NEG 0x04 + +#define RTL_8309G_PHY_AUTO_NEG_VALUE 0x05e1 +#define RTL_8309G_STATUS_LINK_PASS 0x0004 +#define RTL_8309G_PHY_CTRL_VALE 0x3100 +#define RTL_AUTO_NEG_CHECK 0x0020 +#define RTL_CTRL_SOFTWARE_RESET 0x8000 + +#define RTL_AUTO_NEG_DONE(phy_control) (((phy_control) & (RTL_AUTO_NEG_CHECK)) == 0) +#define RTL_AUTO_LINK_UP(phy_control) (((phy_control) & (RTL_AUTO_LINK_CHECK)) == 0) +#define RTL_RESET_DONE(phy_control) (((phy_control) & (RTL_CTRL_SOFTWARE_RESET)) == 0) + +#define RTL_AUTO_LINK_CHECK 0x0004 + +BOOL rtl8309g_phy_is_link_alive(int phyUnit); +BOOL rtl8309g_phy_setup(int ethUnit); +void rtl8309g_reg_init(int ethUnit); +int rtl8309g_phy_is_fdx(int ethUnit); +int rtl8309g_phy_speed(int ethUnit); +int rtl8309g_phy_is_up(int ethUnit); + +#endif diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/Makefile b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/Makefile new file mode 100644 index 0000000..6c8c8d5 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/Makefile @@ -0,0 +1,47 @@ +# +# Copyright (c) 2002-2005 Sam Leffler, Errno Consulting +# Copyright (c) 2002-2005 Atheros Communications, Inc. +# All rights reserved. +# +ifeq ($(obj),) +obj := . +endif + +DEPTH := ../../../../.. + +BINDIR= /usr/local/bin + +include Makefile.inc + +INSTALL_ROOT = ${DEPTH}/rootfs-${BOARD_TYPE}.optbuild/ +INSTALL_ROOT2 = ${DEPTH}/rootfs-${BOARD_TYPE}.build/ +INCS+= -I${HAL} -I${HAL}/${OS} -I${ATH_HAL} -I${obj}/${DEPTH} -I${COMMON} -I${ATH_RATE} -I${ATH} -I${WLAN} +CFLAGS= ${INCS} -g -O2 -Wall + +ifeq (${BUILD_STATIC}, y) +LDFLAGS= -static +endif + +all: ${ALL} + + +ALL= ethreg + +all: clean ${ALL} + +ethreg: ethreg.c + ${CC} -o ethreg ${CFLAGS} ethreg.c + +strip_all: + ${STRIP} ethreg + +install: all strip_all + for i in ${ALL}; do \ + rm -f $(INSTALL_ROOT)/usr/bin/$$i; \ + rm -f $(INSTALL_ROOT2)/usr/bin/$$i; \ + cp $$i $(INSTALL_ROOT)/usr/bin/$$i; \ + cp $$i $(INSTALL_ROOT2)/usr/bin/$$i; \ + done + +clean: + rm -f ${ALL} diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/Makefile.inc b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/Makefile.inc new file mode 100644 index 0000000..2743df0 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/Makefile.inc @@ -0,0 +1,84 @@ +ifeq ($(obj),) +obj := $(shell pwd) +endif + +# other locales sometimes cause trouble +export LC_ALL = POSIX + + +# If we are AHB bus type, then we need the kernel path for the target +# kernel sources. + +ifeq ($(strip ${BUS}),AHB) +TARGET := mipsisa32-be-elf + +ifndef KERNELPATH +$(error KERNELPATH must be defined for bus type AHB.) +endif + +ifndef TOOLPATH +$(error TOOLPATH must be defined for bus type AHB.) +endif + +KERNELCONF := ${KERNELPATH}/.config + +ifndef KERNELRELEASE +VERSION := $(strip $(shell head -n 1 ${KERNELPATH}/Makefile | cut -f 2 -d'=')) +PATCHLEVEL := $(strip $(shell head -n 2 ${KERNELPATH}/Makefile | tail -1 | cut -f 2 -d'=')) +SUBVERSION := $(strip $(shell head -n 3 ${KERNELPATH}/Makefile | tail -1 | cut -f 2 -d'=')) +KERNELRELEASE := $(strip ${VERSION}.${PATCHLEVEL}.${SUBVERSION}${EXTRAVERSION}) +endif + +ifndef MODULEPATH +MODULEPATH := ${KERNELPATH}/arch/mips/ar531x/ROOTDISK/rootdir/lib/modules/${KERNELRELEASE}/net +endif + +endif + +# +NM := nm +AWK := awk + +# Path to the Linux kernel, include files, etc. +# +# KERNELRELEASE is the target kernel's version. If KERNELRELEASE +# is not set in the environment then it is taken from the running +# system. +# +# KERNELPATH is the path to the target kernel's build/source area. +# This is used to obtain the kernel configuration and include files. +# If KERNELPATH is not set in the environment then it is derived +# from KERNELRELEASE. +# +ifeq ($(KERNELRELEASE),) +KERNELRELEASE := $(shell uname -r) +endif + +ifneq ($(findstring 2.6,$(KERNELRELEASE)),) +export-objs := +list-multi := +KMODSUF := ko +else +KMODSUF := o +endif + +ifeq ($(KERNELPATH),) +KERNELPATH := /lib/modules/${KERNELRELEASE}/build +endif + +ifeq ($(DESTDIR),) +DESTDIR := +endif + +ifeq ($(MODULEPATH),) +MODULEPATH := /lib/modules/${KERNELRELEASE}/net +endif + +ifeq ($(strip $(BUS)),AHB) +#INCS += -isystem ${TOOLPATH}/include +endif + +TARGET = mips-linux- + +CC = ${TOOLPATH}/bin/${TARGET}gcc +STRIP = ${TOOLPATH}/bin/${TARGET}strip diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/ethreg.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/ethreg.c new file mode 100644 index 0000000..db88979 --- /dev/null +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag7240/tools/ethreg.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2002-2005 Atheros Communications, Inc. + * All rights reserved. + * + * $Id: //depot/sw/src3/linux/drivers/net/ag7240/tools/ethreg.c#2 $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../eth_diag.h" + +struct eth_diag etd; +int s,opt_force = 0,duplex = 1; +const char *progname; + +u_int32_t +regread(u_int32_t phy_reg,u_int16_t portno) +{ + etd.phy_reg = phy_reg; + etd.ed_u.portnum = portno; + + if (ioctl(s,S26_RD_PHY, &etd) < 0) + err(1, etd.ad_name); + + return etd.val; +} + +static void +regwrite(u_int32_t phy_reg,u_int32_t val,u_int16_t portno) +{ + + etd.val = val; + etd.phy_reg = phy_reg; + + if(opt_force) { + etd.ed_u.duplex = duplex; + if (ioctl(s,S26_FORCE_PHY, &etd) < 0) + err(1, etd.ad_name); + opt_force = 0; + } + else { + etd.ed_u.portnum = portno; + if (ioctl(s,S26_WR_PHY, &etd) < 0) + err(1, etd.ad_name); + } +} + +static void +usage() +{ + fprintf(stderr, "usage: %s [-i ifname] [-p portnum] offset[=value]\n", progname); + fprintf(stderr, "usage: %s [-f] portnum=10/100/0 [-d duplex]\n", progname); + exit(-1); +} + +int +main(int argc, char *argv[]) +{ + const char *ifname = "eth0"; + int c,portnum = 0xf; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + err(1, "socket"); + + opt_force = 0; + progname = argv[0]; + + while ((c = getopt(argc, argv, "fi:p:d:")) != -1) { + switch (c) { + case 'i': + ifname = optarg; + break; + case 'p': + portnum = strtoul(optarg,0,0); + break; + case 'f': + opt_force = 1; + break; + case 'd': + duplex = strtoul(optarg,0,0); + break; + default: + usage(); + /*NOTREACHED*/ + } + + } + + argc -= optind; + argv += optind; + + strncpy(etd.ad_name, ifname, sizeof (etd.ad_name)); + + for (; argc > 0; argc--, argv++) { + u_int32_t off; + u_int32_t val, oval; + char *cp; + + cp = strchr(argv[0], '='); + + if (cp != NULL) + *cp = '\0'; + + off = (u_int) strtoul(argv[0], 0, 0); + + if (off == 0 && errno == EINVAL) + errx(1, "%s: invalid reg offset %s", + progname, argv[0]); + + if (cp == NULL) { + val = regread(off,portnum); + printf("Read Reg: 0x%08x = 0x%08x\n",off, val); + return 0; + } else { + val = (u_int32_t) strtoul(cp+1, 0, 0); + if (val == 0 && errno == EINVAL) { + errx(1, "%s: invalid reg value %s", + progname, cp+1); + } + else { + + oval = regread(off,portnum); + if(opt_force == 0) + printf("Write Reg: 0x%08x: Oldval = 0x%08x Newval = 0x%08x\n", off, oval, val); + regwrite(off,val,portnum); + } + } + } + return 0; +} + diff --git a/target/linux/ar71xx/generic/profiles/d-link.mk b/target/linux/ar71xx/generic/profiles/d-link.mk index f30e9b1..8c371a3e 100644 --- a/target/linux/ar71xx/generic/profiles/d-link.mk +++ b/target/linux/ar71xx/generic/profiles/d-link.mk @@ -50,6 +50,17 @@ endef $(eval $(call Profile,DIR615E4)) +define Profile/DIR632A1 + NAME:=D-Link DIR-632 rev. A1 + PACKAGES:=kmod-usb-core kmod-usb2 kmod-ledtrig-usbdev +endef + +define Profile/DIR632A1/Description + Package set optimized for the D-Link DIR-632 rev. A1 +endef + +$(eval $(call Profile,DIR632A1)) + define Profile/DIR825B1 NAME:=D-Link DIR-825 rev. B1 diff --git a/target/linux/ar71xx/image/Makefile b/target/linux/ar71xx/image/Makefile index 7522926..eeaec51 100644 --- a/target/linux/ar71xx/image/Makefile +++ b/target/linux/ar71xx/image/Makefile @@ -174,6 +174,7 @@ cameo933x_mtdlayout=mtdparts=spi0.0:64k(u-boot)ro,64k(art)ro,64k(mac)ro,64k(nvra cameo934x_mtdlayout=mtdparts=spi0.0:64k(uboot)ro,64k(nvram)ro,1280k(kernel),14656k(rootfs),192k(lang)ro,64k(mac)ro,64k(art)ro,15936k@0x20000(firmware) cap4200ag_mtdlayout=mtdparts=spi0.0:256k(u-boot),64k(u-boot-env),320k(custom)ro,1536k(kernel),12096k(rootfs),2048k(failsafe),64k(art),13632k@0xa0000(firmware) db120_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,6336k(rootfs),1408k(kernel),64k(nvram),64k(art)ro,7744k@0x50000(firmware) +dir632a1_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(nvram),1984k(linux),5568k(rootfs),64k(MAC),192k(LP),64k(ART)ro,7488k@0x50000(firmware) debug loglevel=8 dir825b1_mtdlayout=mtdparts=spi0.0:256k(uboot)ro,64k(config)ro,1024k(kernel),5184k(rootfs),64k(caldata)ro,1600k(unknown)ro,6208k@0x50000(firmware),64k@0x7f0000(caldata_copy) dir825b1_mtdlayout_fat=mtdparts=spi0.0:256k(uboot)ro,64k(config)ro,1024k(kernel),6784k(rootfs),64k(caldata)ro,7808k@0x50000(firmware),64k@0x660000(caldata_orig),6208k@0x50000(firmware_orig) ew-dorin_mtdlayout_4M=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env),1024k(kernel),2688k(rootfs),64k(art),3712k@0x50000(firmware) @@ -358,6 +359,15 @@ define Image/Build/Cameo933x/initramfs $(call MkuImageLzma/initramfs,$(2),$(3) $(cameo933x_mtdlayout)) endef +define Image/Build/DIR632A1 + $(call Image/Build/Cameo,$(1),$(2),$(3),$(dir632a1_mtdlayout),2031616,5636096,$(4)) +endef + +define Image/Build/DIR632A1/initramfs + $(call MkuImageLzma/initramfs,$(2),$(3) $(dir632a1_mtdlayout)) +endef + + define Image/Build/Cameo934x $(call Image/Build/Cameo,$(1),$(2),$(3),$(cameo934x_mtdlayout),1310720,15007718,$(4)) endef @@ -863,6 +873,8 @@ $(eval $(call SingleProfile,Cameo934x,$(fs_64k),DIR835A1,dir-835-a1,DIR-835-A1,t $(eval $(call SingleProfile,CyberTAN,$(fs_64k),WRT160NL,wrt160nl,WRT160NL,ttyS0,115200,1.00.01)) +$(eval $(call SingleProfile,DIR632A1,$(fs_squash),DIR632A1,dir-632-a1,DIR-632-A1,ttyS0,115200,"A101-AR7242-RT-100324-02")) + $(eval $(call SingleProfile,DIR825B1,$(fs_64k),DIR825B1,dir-825-b1,DIR-825-B1,ttyS0,115200,01AP94-AR7161-RT-080619-00,00AP94-AR7161-RT-080619-00)) $(eval $(call SingleProfile,DIR825B1,$(fs_64k),TEW673GRU,tew-673gru,TEW-673GRU,ttyS0,115200,01AP94-AR7161-RT-080619-01,00AP94-AR7161-RT-080619-01)) diff --git a/target/linux/ar71xx/patches-3.8/617-MIPS-ath79-add-DIR-632-A1-support.patch b/target/linux/ar71xx/patches-3.8/617-MIPS-ath79-add-DIR-632-A1-support.patch new file mode 100644 index 0000000..b4c5bf2 --- /dev/null +++ b/target/linux/ar71xx/patches-3.8/617-MIPS-ath79-add-DIR-632-A1-support.patch @@ -0,0 +1,39 @@ +--- a/arch/mips/ath79/Kconfig ++++ b/arch/mips/ath79/Kconfig +@@ -273,6 +273,16 @@ config ATH79_MACH_DIR_615_C1 + select ATH79_DEV_WMAC + select ATH79_NVRAM + ++config ATH79_MACH_DIR_632_A1 ++ bool "D-Link DIR-632 rev. A1 support" ++ select SOC_AR724X ++ select ATH79_DEV_ETH ++ select ATH79_DEV_GPIO_BUTTONS ++ select ATH79_DEV_LEDS_GPIO ++ select ATH79_DEV_M25P80 ++ select ATH79_DEV_USB ++ select ATH79_NVRAM ++ + config ATH79_MACH_DIR_825_B1 + bool "D-Link DIR-825 rev. B1 board support" + select SOC_AR71XX +--- a/arch/mips/ath79/machtypes.h ++++ b/arch/mips/ath79/machtypes.h +@@ -38,6 +38,7 @@ enum ath79_mach_type { + ATH79_MACH_DIR_600_A1, /* D-Link DIR-600 rev. A1 */ + ATH79_MACH_DIR_615_C1, /* D-Link DIR-615 rev. C1 */ + ATH79_MACH_DIR_615_E4, /* D-Link DIR-615 rev. E4 */ ++ ATH79_MACH_DIR_632_A1, /* D-Link DIR-632 rev. A1 */ + ATH79_MACH_DIR_825_B1, /* D-Link DIR-825 rev. B1 */ + ATH79_MACH_DIR_825_C1, /* D-Link DIR-825 rev. C1 */ + ATH79_MACH_DIR_835_A1, /* D-Link DIR-835 rev. A1 */ +--- a/arch/mips/ath79/Makefile ++++ b/arch/mips/ath79/Makefile +@@ -54,6 +54,7 @@ obj-$(CONFIG_ATH79_MACH_CAP4200AG) += ma + obj-$(CONFIG_ATH79_MACH_DB120) += mach-db120.o + obj-$(CONFIG_ATH79_MACH_DIR_600_A1) += mach-dir-600-a1.o + obj-$(CONFIG_ATH79_MACH_DIR_615_C1) += mach-dir-615-c1.o ++obj-$(CONFIG_ATH79_MACH_DIR_632_A1) += mach-dir-632-a1.o + obj-$(CONFIG_ATH79_MACH_DIR_825_B1) += mach-dir-825-b1.o + obj-$(CONFIG_ATH79_MACH_DIR_825_C1) += mach-dir-825-c1.o + obj-$(CONFIG_ATH79_MACH_EW_DORIN) += mach-ew-dorin.o diff --git a/target/linux/ar71xx/patches-3.8/620-net-add-ag7240-driver-for-DIR-632-A1.patch b/target/linux/ar71xx/patches-3.8/620-net-add-ag7240-driver-for-DIR-632-A1.patch new file mode 100644 index 0000000..5c69157 --- /dev/null +++ b/target/linux/ar71xx/patches-3.8/620-net-add-ag7240-driver-for-DIR-632-A1.patch @@ -0,0 +1,19 @@ +--- a/drivers/net/ethernet/atheros/Makefile ++++ b/drivers/net/ethernet/atheros/Makefile +@@ -3,6 +3,7 @@ + # + + obj-$(CONFIG_AG71XX) += ag71xx/ ++obj-$(CONFIG_AG7240) += ag7240/ + obj-$(CONFIG_ATL1) += atlx/ + obj-$(CONFIG_ATL2) += atlx/ + obj-$(CONFIG_ATL1E) += atl1e/ +--- a/drivers/net/ethernet/atheros/Kconfig ++++ b/drivers/net/ethernet/atheros/Kconfig +@@ -68,5 +68,6 @@ config ATL1C + will be called atl1c. + + source drivers/net/ethernet/atheros/ag71xx/Kconfig ++source drivers/net/ethernet/atheros/ag7240/Kconfig + + endif # NET_VENDOR_ATHEROS