/* * Copyright 2013 Freescale Semiconductor, Inc. * * SPDX-License-Identifier: GPL-2.0+ */ /* * QorIQ P1 Tower boards configuration file */ #ifndef __CONFIG_H #define __CONFIG_H #if defined(CONFIG_TWR_P1025) #define CONFIG_BOARDNAME "TWR-P1025" #define CONFIG_P1025 #define CONFIG_PHY_ATHEROS #define CONFIG_QE #define CONFIG_SYS_LBC_LBCR 0x00080000 /* Conversion of LBC addr */ #define CONFIG_SYS_LBC_LCRR 0x80000002 /* LB clock ratio reg */ #endif #ifdef CONFIG_SDCARD #define CONFIG_RAMBOOT_SDCARD #define CONFIG_SYS_RAMBOOT #define CONFIG_SYS_EXTRA_ENV_RELOC #define CONFIG_SYS_TEXT_BASE 0x11000000 #define CONFIG_RESET_VECTOR_ADDRESS 0x110bfffc #endif #ifndef CONFIG_SYS_TEXT_BASE #define CONFIG_SYS_TEXT_BASE 0xeff40000 #endif #ifndef CONFIG_RESET_VECTOR_ADDRESS #define CONFIG_RESET_VECTOR_ADDRESS 0xeffffffc #endif #ifndef CONFIG_SYS_MONITOR_BASE #define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE /* start of monitor */ #endif /* High Level Configuration Options */ #define CONFIG_BOOKE #define CONFIG_E500 #define CONFIG_MP #define CONFIG_FSL_ELBC #define CONFIG_PCI #define CONFIG_PCIE1 /* PCIE controler 1 (slot 1) */ #define CONFIG_PCIE2 /* PCIE controler 2 (slot 2) */ #define CONFIG_FSL_PCI_INIT /* Use common FSL init code */ #define CONFIG_PCI_INDIRECT_BRIDGE /* indirect PCI bridge support */ #define CONFIG_FSL_PCIE_RESET /* need PCIe reset errata */ #define CONFIG_SYS_PCI_64BIT /* enable 64-bit PCI resources */ #define CONFIG_FSL_LAW #define CONFIG_TSEC_ENET /* tsec ethernet support */ #define CONFIG_ENV_OVERWRITE #define CONFIG_CMD_SATA #define CONFIG_SATA_SIL3114 #define CONFIG_SYS_SATA_MAX_DEVICE 2 #define CONFIG_LIBATA #define CONFIG_LBA48 #ifndef __ASSEMBLY__ extern unsigned long get_board_sys_clk(unsigned long dummy); #endif #define CONFIG_SYS_CLK_FREQ get_board_sys_clk(0) /*sysclk for TWR-P1025 */ #define CONFIG_DDR_CLK_FREQ 66666666 #define CONFIG_HWCONFIG /* * These can be toggled for performance analysis, otherwise use default. */ #define CONFIG_L2_CACHE #define CONFIG_BTB #define CONFIG_BOARD_EARLY_INIT_F /* Call board_pre_init */ #define CONFIG_SYS_MEMTEST_START 0x00200000 /* memtest works on */ #define CONFIG_SYS_MEMTEST_END 0x1fffffff #define CONFIG_PANIC_HANG /* do not reset board on panic */ #define CONFIG_SYS_CCSRBAR 0xffe00000 #define CONFIG_SYS_CCSRBAR_PHYS_LOW CONFIG_SYS_CCSRBAR /* DDR Setup */ #define CONFIG_SYS_FSL_DDR3 #define CONFIG_SYS_SDRAM_SIZE_LAW LAW_SIZE_512M #define CONFIG_CHIP_SELECTS_PER_CTRL 1 #define CONFIG_SYS_SDRAM_SIZE (1u << (CONFIG_SYS_SDRAM_SIZE_LAW - 19)) #define CONFIG_SYS_DDR_SDRAM_BASE 0x00000000 #define CONFIG_SYS_SDRAM_BASE CONFIG_SYS_DDR_SDRAM_BASE #define CONFIG_NUM_DDR_CONTROLLERS 1 #define CONFIG_DIMM_SLOTS_PER_CTLR 1 /* Default settings for DDR3 */ #define CONFIG_SYS_DDR_CS0_BNDS 0x0000001f #define CONFIG_SYS_DDR_CS0_CONFIG 0x80014202 #define CONFIG_SYS_DDR_CS0_CONFIG_2 0x00000000 #define CONFIG_SYS_DDR_CS1_BNDS 0x00000000 #define CONFIG_SYS_DDR_CS1_CONFIG 0x00000000 #define CONFIG_SYS_DDR_CS1_CONFIG_2 0x00000000 #define CONFIG_SYS_DDR_DATA_INIT 0xdeadbeef #define CONFIG_SYS_DDR_INIT_ADDR 0x00000000 #define CONFIG_SYS_DDR_INIT_EXT_ADDR 0x00000000 #define CONFIG_SYS_DDR_MODE_CONTROL 0x00000000 #define CONFIG_SYS_DDR_ZQ_CONTROL 0x89080600 #define CONFIG_SYS_DDR_WRLVL_CONTROL 0x8655a608 #define CONFIG_SYS_DDR_SR_CNTR 0x00000000 #define CONFIG_SYS_DDR_RCW_1 0x00000000 #define CONFIG_SYS_DDR_RCW_2 0x00000000 #define CONFIG_SYS_DDR_CONTROL 0xc70c0000 /* Type = DDR3 */ #define CONFIG_SYS_DDR_CONTROL_2 0x04401050 #define CONFIG_SYS_DDR_TIMING_4 0x00220001 #define CONFIG_SYS_DDR_TIMING_5 0x03402400 #define CONFIG_SYS_DDR_TIMING_3 0x00020000 #define CONFIG_SYS_DDR_TIMING_0 0x00220004 #define CONFIG_SYS_DDR_TIMING_1 0x5c5b6544 #define CONFIG_SYS_DDR_TIMING_2 0x0fa880de #define CONFIG_SYS_DDR_CLK_CTRL 0x03000000 #define CONFIG_SYS_DDR_MODE_1 0x80461320 #define CONFIG_SYS_DDR_MODE_2 0x00008000 #define CONFIG_SYS_DDR_INTERVAL 0x09480000 /* * Memory map * * 0x0000_0000 0x1fff_ffff DDR Up to 512MB cacheable * 0x8000_0000 0xdfff_ffff PCI Express Mem 1.5G non-cacheable(PCIe * 3) * 0xffc0_0000 0xffc3_ffff PCI IO range 256k non-cacheable * * Localbus * 0xe000_0000 0xe002_0000 SSD1289 128K non-cacheable * 0xec00_0000 0xefff_ffff FLASH Up to 64M non-cacheable * * 0xff90_0000 0xff97_ffff L2 SRAM Up to 512K cacheable * 0xffd0_0000 0xffd0_3fff init ram 16K Cacheable * 0xffe0_0000 0xffef_ffff CCSR 1M non-cacheable */ /* * Local Bus Definitions */ #define CONFIG_SYS_MAX_FLASH_SECT 512 /* 64M */ #define CONFIG_SYS_FLASH_BASE 0xec000000 #define CONFIG_SYS_FLASH_BASE_PHYS CONFIG_SYS_FLASH_BASE #define CONFIG_FLASH_BR_PRELIM (BR_PHYS_ADDR((CONFIG_SYS_FLASH_BASE_PHYS)) \ | BR_PS_16 | BR_V) #define CONFIG_FLASH_OR_PRELIM 0xfc0000b1 #define CONFIG_SYS_SSD_BASE 0xe0000000 #define CONFIG_SYS_SSD_BASE_PHYS CONFIG_SYS_SSD_BASE #define CONFIG_SSD_BR_PRELIM (BR_PHYS_ADDR(CONFIG_SYS_SSD_BASE_PHYS) | \ BR_PS_16 | BR_V) #define CONFIG_SSD_OR_PRELIM (OR_AM_64KB | OR_GPCM_CSNT | OR_GPCM_XACS | \ OR_GPCM_ACS_DIV2 | OR_GPCM_SCY | \ OR_GPCM_TRLX | OR_GPCM_EHTR | OR_GPCM_EAD) #define CONFIG_SYS_BR2_PRELIM CONFIG_SSD_BR_PRELIM #define CONFIG_SYS_OR2_PRELIM CONFIG_SSD_OR_PRELIM #define CONFIG_SYS_FLASH_BANKS_LIST {CONFIG_SYS_FLASH_BASE_PHYS} #define CONFIG_SYS_FLASH_QUIET_TEST #define CONFIG_FLASH_SHOW_PROGRESS 45 /* count down from 45/5: 9..1 */ #define CONFIG_SYS_MAX_FLASH_BANKS 1 /* number of banks */ #undef CONFIG_SYS_FLASH_CHECKSUM #define CONFIG_SYS_FLASH_ERASE_TOUT 60000 /* Flash Erase Timeout (ms) */ #define CONFIG_SYS_FLASH_WRITE_TOUT 500 /* Flash Write Timeout (ms) */ #define CONFIG_FLASH_CFI_DRIVER #define CONFIG_SYS_FLASH_CFI #define CONFIG_SYS_FLASH_EMPTY_INFO #define CONFIG_SYS_FLASH_USE_BUFFER_WRITE #define CONFIG_BOARD_EARLY_INIT_R /* call board_early_init_r function */ #define CONFIG_SYS_INIT_RAM_LOCK #define CONFIG_SYS_INIT_RAM_ADDR 0xffd00000 /* Initial L1 address */ #define CONFIG_SYS_INIT_RAM_ADDR_PHYS CONFIG_SYS_INIT_RAM_ADDR #define CONFIG_SYS_INIT_RAM_ADDR_PHYS_HIGH 0 #define CONFIG_SYS_INIT_RAM_ADDR_PHYS_LOW CONFIG_SYS_INIT_RAM_ADDR_PHYS /* Size of used area in RAM */ #define CONFIG_SYS_INIT_RAM_SIZE 0x00004000 #define CONFIG_SYS_GBL_DATA_OFFSET (CONFIG_SYS_INIT_RAM_SIZE - \ GENERATED_GBL_DATA_SIZE) #define CONFIG_SYS_INIT_SP_OFFSET CONFIG_SYS_GBL_DATA_OFFSET #define CONFIG_SYS_MONITOR_LEN (768 * 1024) #define CONFIG_SYS_MALLOC_LEN (1024 * 1024)/* Reserved for malloc */ #define CONFIG_SYS_BR0_PRELIM CONFIG_FLASH_BR_PRELIM /* NOR Base Address */ #define CONFIG_SYS_OR0_PRELIM CONFIG_FLASH_OR_PRELIM /* NOR Options */ /* Serial Port * open - index 2 * shorted - index 1 */ #define CONFIG_CONS_INDEX 1 #undef CONFIG_SERIAL_SOFTWARE_FIFO #define CONFIG_SYS_NS16550 #define CONFIG_SYS_NS16550_SERIAL #define CONFIG_SYS_NS16550_REG_SIZE 1 #define CONFIG_SYS_NS16550_CLK get_bus_freq(0) #define CONFIG_SYS_BAUDRATE_TABLE \ {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200} #define CONFIG_SYS_NS16550_COM1 (CONFIG_SYS_CCSRBAR+0x4500) #define CONFIG_SYS_NS16550_COM2 (CONFIG_SYS_CCSRBAR+0x4600) /* Use the HUSH parser */ #define CONFIG_SYS_HUSH_PARSER #ifdef CONFIG_SYS_HUSH_PARSER #define CONFIG_SYS_PROMPT_HUSH_PS2 "> " #endif /* * Pass open firmware flat tree */ #define CONFIG_OF_LIBFDT #define CONFIG_OF_BOARD_SETUP #define CONFIG_OF_STDOUT_VIA_ALIAS /* new uImage format support */ #define CONFIG_FIT #define CONFIG_FIT_VERBOSE /* enable fit_format_{error,warning}() */ /* I2C */ #define CONFIG_SYS_I2C #define CONFIG_SYS_I2C_FSL /* Use FSL common I2C driver */ #define CONFIG_SYS_FSL_I2C_SPEED 400000 /* I2C spd and slave address */ #define CONFIG_SYS_FSL_I2C_SLAVE 0x7F #define CONFIG_SYS_FSL_I2C_OFFSET 0x3000 #define CONFIG_SYS_I2C_EEPROM_ADDR 0x52 /* * I2C2 EEPROM */ #define CONFIG_SYS_FSL_I2C2_SPEED 400000 /* I2C spd and slave address */ #define CONFIG_SYS_FSL_I2C2_SLAVE 0x7F #define CONFIG_SYS_FSL_I2C2_OFFSET 0x3100 #define CONFIG_SYS_I2C_PCA9555_ADDR 0x23 /* enable read and write access to EEPROM */ #define CONFIG_CMD_EEPROM #define CONFIG_SYS_I2C_MULTI_EEPROMS #define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1 #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5 /* * eSPI - Enhanced SPI */ #define CONFIG_HARD_SPI #define CONFIG_FSL_ESPI #if defined(CONFIG_PCI) /* * General PCI * Memory space is mapped 1-1, but I/O space must start from 0. */ /* controller 2, direct to uli, tgtid 2, Base address 9000 */ #define CONFIG_SYS_PCIE2_NAME "TWR-ELEV PCIe SLOT" #define CONFIG_SYS_PCIE2_MEM_VIRT 0xa0000000 #define CONFIG_SYS_PCIE2_MEM_BUS 0xa0000000 #define CONFIG_SYS_PCIE2_MEM_PHYS 0xa0000000 #define CONFIG_SYS_PCIE2_MEM_SIZE 0x20000000 /* 512M */ #define CONFIG_SYS_PCIE2_IO_VIRT 0xffc10000 #define CONFIG_SYS_PCIE2_IO_BUS 0x00000000 #define CONFIG_SYS_PCIE2_IO_PHYS 0xffc10000 #define CONFIG_SYS_PCIE2_IO_SIZE 0x00010000 /* 64k */ /* controller 1, tgtid 1, Base address a000 */ #define CONFIG_SYS_PCIE1_NAME "mini PCIe SLOT" #define CONFIG_SYS_PCIE1_MEM_VIRT 0x80000000 #define CONFIG_SYS_PCIE1_MEM_BUS 0x80000000 #define CONFIG_SYS_PCIE1_MEM_PHYS 0x80000000 #define CONFIG_SYS_PCIE1_MEM_SIZE 0x20000000 /* 512M */ #define CONFIG_SYS_PCIE1_IO_VIRT 0xffc00000 #define CONFIG_SYS_PCIE1_IO_BUS 0x00000000 #define CONFIG_SYS_PCIE1_IO_PHYS 0xffc00000 #define CONFIG_SYS_PCIE1_IO_SIZE 0x00010000 /* 64k */ #define CONFIG_NET_MULTI #define CONFIG_PCI_PNP /* do pci plug-and-play */ #define CONFIG_E1000 /* Defind e1000 pci Ethernet card*/ #define CONFIG_CMD_PCI #define CONFIG_CMD_NET #define CONFIG_PCI_SCAN_SHOW /* show pci devices on startup */ #define CONFIG_DOS_PARTITION #endif /* CONFIG_PCI */ #if defined(CONFIG_TSEC_ENET) #ifndef CONFIG_NET_MULTI #define CONFIG_NET_MULTI #endif #define CONFIG_MII /* MII PHY management */ #define CONFIG_TSEC1 #define CONFIG_TSEC1_NAME "eTSEC1" #undef CONFIG_TSEC2 #undef CONFIG_TSEC2_NAME #define CONFIG_TSEC3 #define CONFIG_TSEC3_NAME "eTSEC3" #define TSEC1_PHY_ADDR 2 #define TSEC2_PHY_ADDR 0 #define TSEC3_PHY_ADDR 1 #define TSEC1_FLAGS (TSEC_GIGABIT | TSEC_REDUCED) #define TSEC2_FLAGS (TSEC_GIGABIT | TSEC_REDUCED) #define TSEC3_FLAGS (TSEC_GIGABIT | TSEC_REDUCED) #define TSEC1_PHYIDX 0 #define TSEC2_PHYIDX 0 #define TSEC3_PHYIDX 0 #define CONFIG_ETHPRIME "eTSEC1" #define CONFIG_PHY_GIGE 1 /* Include GbE speed/duplex detection */ #define CONFIG_HAS_ETH0 #define CONFIG_HAS_ETH1 #undef CONFIG_HAS_ETH2 #endif /* CONFIG_TSEC_ENET */ #ifdef CONFIG_QE /* QE microcode/firmware address */ #define CONFIG_SYS_QE_FMAN_FW_IN_NOR #define CONFIG_SYS_QE_FW_ADDR 0xefec0000 #define CONFIG_SYS_QE_FMAN_FW_LENGTH 0x10000 #endif /* CONFIG_QE */ #ifdef CONFIG_TWR_P1025 /* * QE UEC ethernet configuration */ #define CONFIG_MIIM_ADDRESS (CONFIG_SYS_CCSRBAR + 0x82120) #undef CONFIG_UEC_ETH #define CONFIG_PHY_MODE_NEED_CHANGE #define CONFIG_UEC_ETH1 /* ETH1 */ #define CONFIG_HAS_ETH0 #ifdef CONFIG_UEC_ETH1 #define CONFIG_SYS_UEC1_UCC_NUM 0 /* UCC1 */ #define CONFIG_SYS_UEC1_RX_CLK QE_CLK12 /* CLK12 for MII */ #define CONFIG_SYS_UEC1_TX_CLK QE_CLK9 /* CLK9 for MII */ #define CONFIG_SYS_UEC1_ETH_TYPE FAST_ETH #define CONFIG_SYS_UEC1_PHY_ADDR 0x18 /* 0x18 for MII */ #define CONFIG_SYS_UEC1_INTERFACE_TYPE PHY_INTERFACE_MODE_MII #define CONFIG_SYS_UEC1_INTERFACE_SPEED 100 #endif /* CONFIG_UEC_ETH1 */ #define CONFIG_UEC_ETH5 /* ETH5 */ #define CONFIG_HAS_ETH1 #ifdef CONFIG_UEC_ETH5 #define CONFIG_SYS_UEC5_UCC_NUM 4 /* UCC5 */ #define CONFIG_SYS_UEC5_RX_CLK QE_CLK_NONE #define CONFIG_SYS_UEC5_TX_CLK QE_CLK13 /* CLK 13 for RMII */ #define CONFIG_SYS_UEC5_ETH_TYPE FAST_ETH #define CONFIG_SYS_UEC5_PHY_ADDR 0x19 /* 0x19 for RMII */ #define CONFIG_SYS_UEC5_INTERFACE_TYPE PHY_INTERFACE_MODE_RMII #define CONFIG_SYS_UEC5_INTERFACE_SPEED 100 #endif /* CONFIG_UEC_ETH5 */ #endif /* CONFIG_TWR-P1025 */ /* * Environment */ #ifdef CONFIG_SYS_RAMBOOT #ifdef CONFIG_RAMBOOT_SDCARD #define CONFIG_ENV_IS_IN_MMC #define CONFIG_ENV_SIZE 0x2000 #define CONFIG_SYS_MMC_ENV_DEV 0 #else #define CONFIG_ENV_IS_NOWHERE /* Store ENV in memory only */ #define CONFIG_ENV_ADDR (CONFIG_SYS_MONITOR_BASE - 0x1000) #define CONFIG_ENV_SIZE 0x2000 #endif #else #define CONFIG_ENV_IS_IN_FLASH #define CONFIG_ENV_ADDR (CONFIG_SYS_MONITOR_BASE - CONFIG_ENV_SECT_SIZE) #define CONFIG_ENV_SIZE 0x2000 #define CONFIG_ENV_SECT_SIZE 0x20000 /* 128K (one sector) */ #endif #define CONFIG_LOADS_ECHO /* echo on for serial download */ #define CONFIG_SYS_LOADS_BAUD_CHANGE /* allow baudrate change */ /* * Command line configuration. */ #include #define CONFIG_CMD_IRQ #define CONFIG_CMD_PING #define CONFIG_CMD_I2C #define CONFIG_CMD_MII #define CONFIG_CMD_ELF #define CONFIG_CMD_SETEXPR #define CONFIG_CMD_REGINFO /* * USB */ #define CONFIG_HAS_FSL_DR_USB #if defined(CONFIG_HAS_FSL_DR_USB) #define CONFIG_USB_EHCI #ifdef CONFIG_USB_EHCI #define CONFIG_CMD_USB #define CONFIG_EHCI_HCD_INIT_AFTER_RESET #define CONFIG_USB_EHCI_FSL #define CONFIG_USB_STORAGE #endif #endif #define CONFIG_MMC #ifdef CONFIG_MMC #define CONFIG_FSL_ESDHC #define CONFIG_SYS_FSL_ESDHC_ADDR CONFIG_SYS_MPC85xx_ESDHC_ADDR #define CONFIG_CMD_MMC #define CONFIG_GENERIC_MMC #endif #if defined(CONFIG_MMC) || defined(CONFIG_USB_EHCI) \ || defined(CONFIG_FSL_SATA) #define CONFIG_CMD_EXT2 #define CONFIG_CMD_FAT #define CONFIG_DOS_PARTITION #endif #undef CONFIG_WATCHDOG /* watchdog disabled */ /* * Miscellaneous configurable options */ #define CONFIG_SYS_LONGHELP /* undef to save memory */ #define CONFIG_CMDLINE_EDITING /* Command-line editing */ #define CONFIG_SYS_LOAD_ADDR 0x2000000 /* default load address */ #if defined(CONFIG_CMD_KGDB) #define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ #else #define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */ #endif #define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16) /* Print Buffer Size */ #define CONFIG_SYS_MAXARGS 16 /* max number of command args */ #define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE/* Boot Argument Buffer Size */ /* * For booting Linux, the board info and command line data * have to be in the first 64 MB of memory, since this is * the maximum mapped by the Linux kernel during initialization. */ #define CONFIG_SYS_BOOTMAPSZ (64 << 20) /* Initial Memory for Linux*/ #define CONFIG_SYS_BOOTM_LEN (64 << 20) /* Increase max gunzip size */ /* * Environment Configuration */ #define CONFIG_HOSTNAME unknown #define CONFIG_ROOTPATH "/opt/nfsroot" #define CONFIG_BOOTFILE "uImage" #define CONFIG_UBOOTPATH u-boot.bin /* U-Boot image on TFTP server */ /* default location for tftp and bootm */ #define CONFIG_LOADADDR 1000000 #define CONFIG_BOOTDELAY 10 /* -1 disables auto-boot */ #define CONFIG_BOOTARGS /* the boot command will set bootargs */ #define CONFIG_BAUDRATE 115200 #define CONFIG_EXTRA_ENV_SETTINGS \ "netdev=eth0\0" \ "uboot=" __stringify(CONFIG_UBOOTPATH) "\0" \ "loadaddr=1000000\0" \ "bootfile=uImage\0" \ "dtbfile=twr-p1025twr.dtb\0" \ "ramdiskfile=rootfs.ext2.gz.uboot\0" \ "qefirmwarefile=fsl_qe_ucode_1021_10_A.bin\0" \ "tftpflash=tftpboot $loadaddr $uboot; " \ "protect off " __stringify(CONFIG_SYS_TEXT_BASE) " +$filesize; " \ "erase " __stringify(CONFIG_SYS_TEXT_BASE) " +$filesize; " \ "cp.b $loadaddr " __stringify(CONFIG_SYS_TEXT_BASE) " $filesize; " \ "protect on " __stringify(CONFIG_SYS_TEXT_BASE) " +$filesize; " \ "cmp.b $loadaddr " __stringify(CONFIG_SYS_TEXT_BASE) " $filesize\0" \ "kernelflash=tftpboot $loadaddr $bootfile; " \ "protect off 0xefa80000 +$filesize; " \ "erase 0xefa80000 +$filesize; " \ "cp.b $loadaddr 0xefa80000 $filesize; " \ "protect on 0xefa80000 +$filesize; " \ "cmp.b $loadaddr 0xefa80000 $filesize\0" \ "dtbflash=tftpboot $loadaddr $dtbfile; " \ "protect off 0xefe80000 +$filesize; " \ "erase 0xefe80000 +$filesize; " \ "cp.b $loadaddr 0xefe80000 $filesize; " \ "protect on 0xefe80000 +$filesize; " \ "cmp.b $loadaddr 0xefe80000 $filesize\0" \ "ramdiskflash=tftpboot $loadaddr $ramdiskfile; " \ "protect off 0xeeb80000 +$filesize; " \ "erase 0xeeb80000 +$filesize; " \ "cp.b $loadaddr 0xeeb80000 $filesize; " \ "protect on 0xeeb80000 +$filesize; " \ "cmp.b $loadaddr 0xeeb80000 $filesize\0" \ "qefirmwareflash=tftpboot $loadaddr $qefirmwarefile; " \ "protect off 0xefec0000 +$filesize; " \ "erase 0xefec0000 +$filesize; " \ "cp.b $loadaddr 0xefec0000 $filesize; " \ "protect on 0xefec0000 +$filesize; " \ "cmp.b $loadaddr 0xefec0000 $filesize\0" \ "consoledev=ttyS0\0" \ "ramdiskaddr=2000000\0" \ "ramdiskfile=rootfs.ext2.gz.uboot\0" \ "fdtaddr=c00000\0" \ "bdev=sda1\0" \ "norbootaddr=ef080000\0" \ "norfdtaddr=ef040000\0" \ "ramdisk_size=120000\0" \ "usbboot=setenv bootargs root=/dev/sda1 rw rootdelay=5 " \ "console=$consoledev,$baudrate $othbootargs ; bootm 0xefa80000 - 0xefe80000" #define CONFIG_NFSBOOTCOMMAND \ "setenv bootargs root=/dev/nfs rw " \ "nfsroot=$serverip:$rootpath " \ "ip=$ipaddr:$serverip:$gatewayip:$netmask:$hostname:$netdev:off " \ "console=$consoledev,$baudrate $othbootargs;" \ "tftp $loadaddr $bootfile&&" \ "tftp $fdtaddr $fdtfile&&" \ "bootm $loadaddr - $fdtaddr" #define CONFIG_HDBOOT \ "setenv bootargs root=/dev/$bdev rw rootdelay=30 " \ "console=$consoledev,$baudrate $othbootargs;" \ "usb start;" \ "ext2load usb 0:1 $loadaddr /boot/$bootfile;" \ "ext2load usb 0:1 $fdtaddr /boot/$fdtfile;" \ "bootm $loadaddr - $fdtaddr" #define CONFIG_USB_FAT_BOOT \ "setenv bootargs root=/dev/ram rw " \ "console=$consoledev,$baudrate $othbootargs " \ "ramdisk_size=$ramdisk_size;" \ "usb start;" \ "fatload usb 0:2 $loadaddr $bootfile;" \ "fatload usb 0:2 $fdtaddr $fdtfile;" \ "fatload usb 0:2 $ramdiskaddr $ramdiskfile;" \ "bootm $loadaddr $ramdiskaddr $fdtaddr" #define CONFIG_USB_EXT2_BOOT \ "setenv bootargs root=/dev/ram rw " \ "console=$consoledev,$baudrate $othbootargs " \ "ramdisk_size=$ramdisk_size;" \ "usb start;" \ "ext2load usb 0:4 $loadaddr $bootfile;" \ "ext2load usb 0:4 $fdtaddr $fdtfile;" \ "ext2load usb 0:4 $ramdiskaddr $ramdiskfile;" \ "bootm $loadaddr $ramdiskaddr $fdtaddr" #define CONFIG_NORBOOT \ "setenv bootargs root=/dev/mtdblock3 rw " \ "console=$consoledev,$baudrate rootfstype=jffs2 $othbootargs;" \ "bootm $norbootaddr - $norfdtaddr" #define CONFIG_RAMBOOTCOMMAND_TFTP \ "setenv bootargs root=/dev/ram rw " \ "console=$consoledev,$baudrate $othbootargs " \ "ramdisk_size=$ramdisk_size;" \ "tftp $ramdiskaddr $ramdiskfile;" \ "tftp $loadaddr $bootfile;" \ "tftp $fdtaddr $fdtfile;" \ "bootm $loadaddr $ramdiskaddr $fdtaddr" #define CONFIG_RAMBOOTCOMMAND \ "setenv bootargs root=/dev/ram rw " \ "console=$consoledev,$baudrate $othbootargs " \ "ramdisk_size=$ramdisk_size;" \ "bootm 0xefa80000 0xeeb80000 0xefe80000" #define CONFIG_BOOTCOMMAND CONFIG_RAMBOOTCOMMAND #endif /* __CONFIG_H */ 4 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
/*
 *  Atheros AR71xx built-in ethernet mac driver
 *
 *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
 *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
 *
 *  Based on Atheros' AG7100 driver
 *
 *  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 "ag71xx.h"

#define AG71XX_DEFAULT_MSG_ENABLE	\
	( NETIF_MSG_DRV 		\
	| NETIF_MSG_PROBE		\
	| NETIF_MSG_LINK		\
	| NETIF_MSG_TIMER		\
	| NETIF_MSG_IFDOWN		\
	| NETIF_MSG_IFUP		\
	| NETIF_MSG_RX_ERR		\
	| NETIF_MSG_TX_ERR )

static int ag71xx_msg_level = -1;

module_param_named(msg_level, ag71xx_msg_level, int, 0);
MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");

static void ag71xx_dump_dma_regs(struct ag71xx *ag)
{
	DBG("%s: dma_tx_ctrl=%08x, dma_tx_desc=%08x, dma_tx_status=%08x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_TX_CTRL),
		ag71xx_rr(ag, AG71XX_REG_TX_DESC),
		ag71xx_rr(ag, AG71XX_REG_TX_STATUS));

	DBG("%s: dma_rx_ctrl=%08x, dma_rx_desc=%08x, dma_rx_status=%08x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_RX_CTRL),
		ag71xx_rr(ag, AG71XX_REG_RX_DESC),
		ag71xx_rr(ag, AG71XX_REG_RX_STATUS));
}

static void ag71xx_dump_regs(struct ag71xx *ag)
{
	DBG("%s: mac_cfg1=%08x, mac_cfg2=%08x, ipg=%08x, hdx=%08x, mfl=%08x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_MAC_CFG1),
		ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
		ag71xx_rr(ag, AG71XX_REG_MAC_IPG),
		ag71xx_rr(ag, AG71XX_REG_MAC_HDX),
		ag71xx_rr(ag, AG71XX_REG_MAC_MFL));
	DBG("%s: mac_ifctl=%08x, mac_addr1=%08x, mac_addr2=%08x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
		ag71xx_rr(ag, AG71XX_REG_MAC_ADDR1),
		ag71xx_rr(ag, AG71XX_REG_MAC_ADDR2));
	DBG("%s: fifo_cfg0=%08x, fifo_cfg1=%08x, fifo_cfg2=%08x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0),
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2));
	DBG("%s: fifo_cfg3=%08x, fifo_cfg4=%08x, fifo_cfg5=%08x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));
}

static inline void ag71xx_dump_intr(struct ag71xx *ag, char *label, u32 intr)
{
	DBG("%s: %s intr=%08x %s%s%s%s%s%s\n",
		ag->dev->name, label, intr,
		(intr & AG71XX_INT_TX_PS) ? "TXPS " : "",
		(intr & AG71XX_INT_TX_UR) ? "TXUR " : "",
		(intr & AG71XX_INT_TX_BE) ? "TXBE " : "",
		(intr & AG71XX_INT_RX_PR) ? "RXPR " : "",
		(intr & AG71XX_INT_RX_OF) ? "RXOF " : "",
		(intr & AG71XX_INT_RX_BE) ? "RXBE " : "");
}

static void ag71xx_ring_free(struct ag71xx_ring *ring)
{
	kfree(ring->buf);

	if (ring->descs_cpu)
		dma_free_coherent(NULL, ring->size * ring->desc_size,
				  ring->descs_cpu, ring->descs_dma);
}

static int ag71xx_ring_alloc(struct ag71xx_ring *ring, unsigned int size)
{
	int err;
	int i;

	ring->desc_size = sizeof(struct ag71xx_desc);
	if (ring->desc_size % cache_line_size()) {
		DBG("ag71xx: ring %p, desc size %u rounded to %u\n",
			ring, ring->desc_size,
			roundup(ring->desc_size, cache_line_size()));
		ring->desc_size = roundup(ring->desc_size, cache_line_size());
	}

	ring->descs_cpu = dma_alloc_coherent(NULL, size * ring->desc_size,
					     &ring->descs_dma, GFP_ATOMIC);
	if (!ring->descs_cpu) {
		err = -ENOMEM;
		goto err;
	}

	ring->size = size;

	ring->buf = kzalloc(size * sizeof(*ring->buf), GFP_KERNEL);
	if (!ring->buf) {
		err = -ENOMEM;
		goto err;
	}

	for (i = 0; i < size; i++) {
		ring->buf[i].desc = (struct ag71xx_desc *)&ring->descs_cpu[i * ring->desc_size];
		DBG("ag71xx: ring %p, desc %d at %p\n",
			ring, i, ring->buf[i].desc);
	}

	return 0;

 err:
	return err;
}

static void ag71xx_ring_tx_clean(struct ag71xx *ag)
{
	struct ag71xx_ring *ring = &ag->tx_ring;
	struct net_device *dev = ag->dev;

	while (ring->curr != ring->dirty) {
		u32 i = ring->dirty % AG71XX_TX_RING_SIZE;

		if (!ag71xx_desc_empty(ring->buf[i].desc)) {
			ring->buf[i].desc->ctrl = 0;
			dev->stats.tx_errors++;
		}

		if (ring->buf[i].skb)
			dev_kfree_skb_any(ring->buf[i].skb);

		ring->buf[i].skb = NULL;

		ring->dirty++;
	}

	/* flush descriptors */
	wmb();

}

static void ag71xx_ring_tx_init(struct ag71xx *ag)
{
	struct ag71xx_ring *ring = &ag->tx_ring;
	int i;

	for (i = 0; i < AG71XX_TX_RING_SIZE; i++) {
		ring->buf[i].desc->next = (u32) (ring->descs_dma +
			ring->desc_size * ((i + 1) % AG71XX_TX_RING_SIZE));

		ring->buf[i].desc->ctrl = DESC_EMPTY;
		ring->buf[i].skb = NULL;
	}

	/* flush descriptors */
	wmb();

	ring->curr = 0;
	ring->dirty = 0;
}

static void ag71xx_ring_rx_clean(struct ag71xx *ag)
{
	struct ag71xx_ring *ring = &ag->rx_ring;
	int i;

	if (!ring->buf)
		return;

	for (i = 0; i < AG71XX_RX_RING_SIZE; i++)
		if (ring->buf[i].skb) {
			dma_unmap_single(&ag->dev->dev, ring->buf[i].dma_addr,
					 AG71XX_RX_PKT_SIZE, DMA_FROM_DEVICE);
			kfree_skb(ring->buf[i].skb);
		}
}

static int ag71xx_ring_rx_init(struct ag71xx *ag)
{
	struct ag71xx_ring *ring = &ag->rx_ring;
	unsigned int i;
	int ret;

	ret = 0;
	for (i = 0; i < AG71XX_RX_RING_SIZE; i++) {
		ring->buf[i].desc->next = (u32) (ring->descs_dma +
			ring->desc_size * ((i + 1) % AG71XX_RX_RING_SIZE));

		DBG("ag71xx: RX desc at %p, next is %08x\n",
			ring->buf[i].desc,
			ring->buf[i].desc->next);
	}

	for (i = 0; i < AG71XX_RX_RING_SIZE; i++) {
		struct sk_buff *skb;
		dma_addr_t dma_addr;

		skb = dev_alloc_skb(AG71XX_RX_PKT_SIZE + AG71XX_RX_PKT_RESERVE);
		if (!skb) {
			ret = -ENOMEM;
			break;
		}

		skb->dev = ag->dev;
		skb_reserve(skb, AG71XX_RX_PKT_RESERVE);

		dma_addr = dma_map_single(&ag->dev->dev, skb->data,
					  AG71XX_RX_PKT_SIZE,
					  DMA_FROM_DEVICE);
		ring->buf[i].skb = skb;
		ring->buf[i].dma_addr = dma_addr;
		ring->buf[i].desc->data = (u32) dma_addr;
		ring->buf[i].desc->ctrl = DESC_EMPTY;
	}

	/* flush descriptors */
	wmb();

	ring->curr = 0;
	ring->dirty = 0;

	return ret;
}

static int ag71xx_ring_rx_refill(struct ag71xx *ag)
{
	struct ag71xx_ring *ring = &ag->rx_ring;
	unsigned int count;

	count = 0;
	for (; ring->curr - ring->dirty > 0; ring->dirty++) {
		unsigned int i;

		i = ring->dirty % AG71XX_RX_RING_SIZE;

		if (ring->buf[i].skb == NULL) {
			dma_addr_t dma_addr;
			struct sk_buff *skb;

			skb = dev_alloc_skb(AG71XX_RX_PKT_SIZE +
					    AG71XX_RX_PKT_RESERVE);
			if (skb == NULL)
				break;

			skb_reserve(skb, AG71XX_RX_PKT_RESERVE);
			skb->dev = ag->dev;

			dma_addr = dma_map_single(&ag->dev->dev, skb->data,
						  AG71XX_RX_PKT_SIZE,
						  DMA_FROM_DEVICE);

			ring->buf[i].skb = skb;
			ring->buf[i].dma_addr = dma_addr;
			ring->buf[i].desc->data = (u32) dma_addr;
		}

		ring->buf[i].desc->ctrl = DESC_EMPTY;
		count++;
	}

	/* flush descriptors */
	wmb();

	DBG("%s: %u rx descriptors refilled\n", ag->dev->name, count);

	return count;
}

static int ag71xx_rings_init(struct ag71xx *ag)
{
	int ret;

	ret = ag71xx_ring_alloc(&ag->tx_ring, AG71XX_TX_RING_SIZE);
	if (ret)
		return ret;

	ag71xx_ring_tx_init(ag);

	ret = ag71xx_ring_alloc(&ag->rx_ring, AG71XX_RX_RING_SIZE);
	if (ret)
		return ret;

	ret = ag71xx_ring_rx_init(ag);
	return ret;
}

static void ag71xx_rings_cleanup(struct ag71xx *ag)
{
	ag71xx_ring_rx_clean(ag);
	ag71xx_ring_free(&ag->rx_ring);

	ag71xx_ring_tx_clean(ag);
	ag71xx_ring_free(&ag->tx_ring);
}

static unsigned char *ag71xx_speed_str(struct ag71xx *ag)
{
	switch (ag->speed) {
	case SPEED_1000:
		return "1000";
	case SPEED_100:
		return "100";
	case SPEED_10:
		return "10";
	}

	return "?";
}

void ag71xx_link_adjust(struct ag71xx *ag)
{
	struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
	u32 cfg2;
	u32 ifctl;
	u32 fifo5;
	u32 mii_speed;

	if (!ag->link) {
		netif_carrier_off(ag->dev);
		if (netif_msg_link(ag))
			printk(KERN_INFO "%s: link down\n", ag->dev->name);
		return;
	}

	cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2);
	cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX);
	cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0;

	ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL);
	ifctl &= ~(MAC_IFCTL_SPEED);

	fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5);
	fifo5 &= ~FIFO_CFG5_BM;

	switch (ag->speed) {
	case SPEED_1000:
		mii_speed =  MII_CTRL_SPEED_1000;
		cfg2 |= MAC_CFG2_IF_1000;
		fifo5 |= FIFO_CFG5_BM;
		break;
	case SPEED_100:
		mii_speed = MII_CTRL_SPEED_100;
		cfg2 |= MAC_CFG2_IF_10_100;
		ifctl |= MAC_IFCTL_SPEED;
		break;
	case SPEED_10:
		mii_speed = MII_CTRL_SPEED_10;
		cfg2 |= MAC_CFG2_IF_10_100;
		break;
	default:
		BUG();
		return;
	}

	if (pdata->is_ar91xx)
		ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x00780fff);
	else if (pdata->is_ar724x)
		ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, pdata->fifo_cfg3);
	else
		ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x008001ff);

	if (pdata->set_pll)
		pdata->set_pll(ag->speed);

	ag71xx_mii_ctrl_set_speed(ag, mii_speed);

	ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2);
	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5);
	ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl);

	netif_carrier_on(ag->dev);
	if (netif_msg_link(ag))
		printk(KERN_INFO "%s: link up (%sMbps/%s duplex)\n",
			ag->dev->name,
			ag71xx_speed_str(ag),
			(DUPLEX_FULL == ag->duplex) ? "Full" : "Half");

	DBG("%s: fifo_cfg0=%#x, fifo_cfg1=%#x, fifo_cfg2=%#x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0),
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2));

	DBG("%s: fifo_cfg3=%#x, fifo_cfg4=%#x, fifo_cfg5=%#x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));

	DBG("%s: mac_cfg2=%#x, mac_ifctl=%#x, mii_ctrl=%#x\n",
		ag->dev->name,
		ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
		ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
		ag71xx_mii_ctrl_rr(ag));
}

static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac)
{
	u32 t;

	t = (((u32) mac[5]) << 24) | (((u32) mac[4]) << 16)
	  | (((u32) mac[3]) << 8) | ((u32) mac[2]);

	ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t);

	t = (((u32) mac[1]) << 24) | (((u32) mac[0]) << 16);
	ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t);
}

static void ag71xx_dma_reset(struct ag71xx *ag)
{
	u32 val;
	int i;

	ag71xx_dump_dma_regs(ag);

	/* stop RX and TX */
	ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0);
	ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0);

	/* clear descriptor addresses */
	ag71xx_wr(ag, AG71XX_REG_TX_DESC, 0);
	ag71xx_wr(ag, AG71XX_REG_RX_DESC, 0);

	/* clear pending RX/TX interrupts */
	for (i = 0; i < 256; i++) {
		ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR);
		ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS);
	}

	/* clear pending errors */
	ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE | RX_STATUS_OF);
	ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE | TX_STATUS_UR);

	val = ag71xx_rr(ag, AG71XX_REG_RX_STATUS);
	if (val)
		printk(KERN_ALERT "%s: unable to clear DMA Rx status: %08x\n",
			ag->dev->name, val);

	val = ag71xx_rr(ag, AG71XX_REG_TX_STATUS);

	/* mask out reserved bits */
	val &= ~0xff000000;

	if (val)
		printk(KERN_ALERT "%s: unable to clear DMA Tx status: %08x\n",
			ag->dev->name, val);

	ag71xx_dump_dma_regs(ag);
}

#define MAC_CFG1_INIT	(MAC_CFG1_RXE | MAC_CFG1_TXE | \
			 MAC_CFG1_SRX | MAC_CFG1_STX)

#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 ag71xx_hw_init(struct ag71xx *ag)
{
	struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);

	ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR);
	udelay(20);

	ar71xx_device_stop(pdata->reset_bit);
	mdelay(100);
	ar71xx_device_start(pdata->reset_bit);
	mdelay(100);

	/* setup MAC configuration registers */
	if (pdata->is_ar724x)
		ag71xx_wr(ag, AG71XX_REG_MAC_CFG1,
			  MAC_CFG1_INIT | MAC_CFG1_TFC | MAC_CFG1_RFC);
	else
		ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_INIT);

	ag71xx_sb(ag, AG71XX_REG_MAC_CFG2,
		  MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK);

	/* setup max frame length */
	ag71xx_wr(ag, AG71XX_REG_MAC_MFL, AG71XX_TX_MTU_LEN);

	/* setup MII interface type */
	ag71xx_mii_ctrl_set_if(ag, pdata->mii_if);

	/* setup FIFO configuration registers */
	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, FIFO_CFG0_INIT);
	if (pdata->is_ar724x) {
		ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, pdata->fifo_cfg1);
		ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, pdata->fifo_cfg2);
	} else {
		ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, 0x0fff0000);
		ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, 0x00001fff);
	}
	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG4, FIFO_CFG4_INIT);
	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, FIFO_CFG5_INIT);

	ag71xx_dma_reset(ag);
}

static void ag71xx_hw_start(struct ag71xx *ag)
{
	/* start RX engine */
	ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE);

	/* enable interrupts */
	ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, AG71XX_INT_INIT);
}

static void ag71xx_hw_stop(struct ag71xx *ag)
{
	/* disable all interrupts */
	ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, 0);

	ag71xx_dma_reset(ag);
}

static int ag71xx_open(struct net_device *dev)
{
	struct ag71xx *ag = netdev_priv(dev);
	int ret;

	ret = ag71xx_rings_init(ag);
	if (ret)
		goto err;

	napi_enable(&ag->napi);

	netif_carrier_off(dev);
	ag71xx_phy_start(ag);

	ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma);
	ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma);

	ag71xx_hw_set_macaddr(ag, dev->dev_addr);

	ag71xx_hw_start(ag);

	netif_start_queue(dev);

	return 0;

 err:
	ag71xx_rings_cleanup(ag);
	return ret;
}

static int ag71xx_stop(struct net_device *dev)
{
	struct ag71xx *ag = netdev_priv(dev);
	unsigned long flags;

	netif_carrier_off(dev);
	ag71xx_phy_stop(ag);

	spin_lock_irqsave(&ag->lock, flags);

	netif_stop_queue(dev);

	ag71xx_hw_stop(ag);

	napi_disable(&ag->napi);
	del_timer_sync(&ag->oom_timer);

	spin_unlock_irqrestore(&ag->lock, flags);

	ag71xx_rings_cleanup(ag);

	return 0;
}

static netdev_tx_t ag71xx_hard_start_xmit(struct sk_buff *skb,
					  struct net_device *dev)
{
	struct ag71xx *ag = netdev_priv(dev);
	struct ag71xx_ring *ring = &ag->tx_ring;
	struct ag71xx_desc *desc;
	dma_addr_t dma_addr;
	int i;

	i = ring->curr % AG71XX_TX_RING_SIZE;
	desc = ring->buf[i].desc;

	if (!ag71xx_desc_empty(desc))
		goto err_drop;

	ag71xx_add_ar8216_header(ag, skb);

	if (skb->len <= 0) {
		DBG("%s: packet len is too small\n", ag->dev->name);
		goto err_drop;
	}

	dma_addr = dma_map_single(&dev->dev, skb->data, skb->len,
				  DMA_TO_DEVICE);

	ring->buf[i].skb = skb;

	/* setup descriptor fields */
	desc->data = (u32) dma_addr;
	desc->ctrl = (skb->len & DESC_PKTLEN_M);

	/* flush descriptor */
	wmb();

	ring->curr++;
	if (ring->curr == (ring->dirty + AG71XX_TX_THRES_STOP)) {
		DBG("%s: tx queue full\n", ag->dev->name);
		netif_stop_queue(dev);
	}

	DBG("%s: packet injected into TX queue\n", ag->dev->name);

	/* enable TX engine */
	ag71xx_wr(ag, AG71XX_REG_TX_CTRL, TX_CTRL_TXE);

	return NETDEV_TX_OK;

 err_drop:
	dev->stats.tx_dropped++;

	dev_kfree_skb(skb);
	return NETDEV_TX_OK;
}

static int ag71xx_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
	struct mii_ioctl_data *data = (struct mii_ioctl_data *) &ifr->ifr_data;
	struct ag71xx *ag = netdev_priv(dev);
	int ret;

	switch (cmd) {
	case SIOCETHTOOL:
		if (ag->phy_dev == NULL)
			break;

		spin_lock_irq(&ag->lock);
		ret = phy_ethtool_ioctl(ag->phy_dev, (void *) ifr->ifr_data);
		spin_unlock_irq(&ag->lock);
		return ret;

	case SIOCSIFHWADDR:
		if (copy_from_user
			(dev->dev_addr, ifr->ifr_data, sizeof(dev->dev_addr)))
			return -EFAULT;
		return 0;

	case SIOCGIFHWADDR:
		if (copy_to_user
			(ifr->ifr_data, dev->dev_addr, sizeof(dev->dev_addr)))
			return -EFAULT;
		return 0;

	case SIOCGMIIPHY:
	case SIOCGMIIREG:
	case SIOCSMIIREG:
		if (ag->phy_dev == NULL)
			break;

		return phy_mii_ioctl(ag->phy_dev, data, cmd);

	default:
		break;
	}

	return -EOPNOTSUPP;
}

static void ag71xx_oom_timer_handler(unsigned long data)
{
	struct net_device *dev = (struct net_device *) data;
	struct ag71xx *ag = netdev_priv(dev);

	napi_schedule(&ag->napi);
}

static void ag71xx_tx_timeout(struct net_device *dev)
{
	struct ag71xx *ag = netdev_priv(dev);

	if (netif_msg_tx_err(ag))
		printk(KERN_DEBUG "%s: tx timeout\n", ag->dev->name);

	schedule_work(&ag->restart_work);
}

static void ag71xx_restart_work_func(struct work_struct *work)
{
	struct ag71xx *ag = container_of(work, struct ag71xx, restart_work);

	ag71xx_stop(ag->dev);
	ag71xx_open(ag->dev);
}

static int ag71xx_tx_packets(struct ag71xx *ag)
{
	struct ag71xx_ring *ring = &ag->tx_ring;
	int sent;

	DBG("%s: processing TX ring\n", ag->dev->name);

	sent = 0;
	while (ring->dirty != ring->curr) {
		unsigned int i = ring->dirty % AG71XX_TX_RING_SIZE;
		struct ag71xx_desc *desc = ring->buf[i].desc;
		struct sk_buff *skb = ring->buf[i].skb;

		if (!ag71xx_desc_empty(desc))
			break;

		ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS);

		ag->dev->stats.tx_bytes += skb->len;
		ag->dev->stats.tx_packets++;

		dev_kfree_skb_any(skb);
		ring->buf[i].skb = NULL;

		ring->dirty++;
		sent++;
	}

	DBG("%s: %d packets sent out\n", ag->dev->name, sent);

	if ((ring->curr - ring->dirty) < AG71XX_TX_THRES_WAKEUP)
		netif_wake_queue(ag->dev);

	return sent;
}

static int ag71xx_rx_packets(struct ag71xx *ag, int limit)
{
	struct net_device *dev = ag->dev;
	struct ag71xx_ring *ring = &ag->rx_ring;
	int done = 0;

	DBG("%s: rx packets, limit=%d, curr=%u, dirty=%u\n",
			dev->name, limit, ring->curr, ring->dirty);

	while (done < limit) {
		unsigned int i = ring->curr % AG71XX_RX_RING_SIZE;
		struct ag71xx_desc *desc = ring->buf[i].desc;
		struct sk_buff *skb;
		int pktlen;
		int err;

		if (ag71xx_desc_empty(desc))
			break;

		if ((ring->dirty + AG71XX_RX_RING_SIZE) == ring->curr) {
			ag71xx_assert(0);
			break;
		}

		ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR);

		skb = ring->buf[i].skb;
		pktlen = ag71xx_desc_pktlen(desc);
		pktlen -= ETH_FCS_LEN;

		dma_unmap_single(&dev->dev, ring->buf[i].dma_addr,
				 AG71XX_RX_PKT_SIZE, DMA_FROM_DEVICE);

		dev->last_rx = jiffies;
		dev->stats.rx_packets++;
		dev->stats.rx_bytes += pktlen;

		err = ag71xx_remove_ar8216_header(ag, skb);
		if (err) {
			dev->stats.rx_dropped++;
			kfree_skb(skb);
		} else {
			skb_put(skb, pktlen);

			skb->dev = dev;
			skb->ip_summed = CHECKSUM_NONE;
			skb->protocol = eth_type_trans(skb, dev);
			netif_receive_skb(skb);
		}

		ring->buf[i].skb = NULL;
		done++;

		ring->curr++;
	}

	ag71xx_ring_rx_refill(ag);

	DBG("%s: rx finish, curr=%u, dirty=%u, done=%d\n",
		dev->name, ring->curr, ring->dirty, done);

	return done;
}

static int ag71xx_poll(struct napi_struct *napi, int limit)
{
	struct ag71xx *ag = container_of(napi, struct ag71xx, napi);
	struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
	struct net_device *dev = ag->dev;
	struct ag71xx_ring *rx_ring;
	unsigned long flags;
	u32 status;
	int tx_done;
	int rx_done;

	pdata->ddr_flush();
	tx_done = ag71xx_tx_packets(ag);

	DBG("%s: processing RX ring\n", dev->name);
	rx_done = ag71xx_rx_packets(ag, limit);

	ag71xx_debugfs_update_napi_stats(ag, rx_done, tx_done);

	rx_ring = &ag->rx_ring;
	if (rx_ring->buf[rx_ring->dirty % AG71XX_RX_RING_SIZE].skb == NULL)
		goto oom;

	status = ag71xx_rr(ag, AG71XX_REG_RX_STATUS);
	if (unlikely(status & RX_STATUS_OF)) {
		ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_OF);
		dev->stats.rx_fifo_errors++;

		/* restart RX */
		ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE);
	}

	if (rx_done < limit) {
		if (status & RX_STATUS_PR)
			goto more;

		status = ag71xx_rr(ag, AG71XX_REG_TX_STATUS);
		if (status & TX_STATUS_PS)
			goto more;

		DBG("%s: disable polling mode, rx=%d, tx=%d,limit=%d\n",
			dev->name, rx_done, tx_done, limit);

		napi_complete(napi);

		/* enable interrupts */
		spin_lock_irqsave(&ag->lock, flags);
		ag71xx_int_enable(ag, AG71XX_INT_POLL);
		spin_unlock_irqrestore(&ag->lock, flags);
		return rx_done;
	}

 more:
	DBG("%s: stay in polling mode, rx=%d, tx=%d, limit=%d\n",
			dev->name, rx_done, tx_done, limit);
	return rx_done;

 oom:
	if (netif_msg_rx_err(ag))
		printk(KERN_DEBUG "%s: out of memory\n", dev->name);

	mod_timer(&ag->oom_timer, jiffies + AG71XX_OOM_REFILL);
	napi_complete(napi);
	return 0;
}

static irqreturn_t ag71xx_interrupt(int irq, void *dev_id)
{
	struct net_device *dev = dev_id;
	struct ag71xx *ag = netdev_priv(dev);
	u32 status;

	status = ag71xx_rr(ag, AG71XX_REG_INT_STATUS);
	ag71xx_dump_intr(ag, "raw", status);

	if (unlikely(!status))
		return IRQ_NONE;

	if (unlikely(status & AG71XX_INT_ERR)) {
		if (status & AG71XX_INT_TX_BE) {
			ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE);
			dev_err(&dev->dev, "TX BUS error\n");
		}
		if (status & AG71XX_INT_RX_BE) {
			ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE);
			dev_err(&dev->dev, "RX BUS error\n");
		}
	}

	if (likely(status & AG71XX_INT_POLL)) {
		ag71xx_int_disable(ag, AG71XX_INT_POLL);
		DBG("%s: enable polling mode\n", dev->name);
		napi_schedule(&ag->napi);
	}

	ag71xx_debugfs_update_int_stats(ag, status);

	return IRQ_HANDLED;
}

static void ag71xx_set_multicast_list(struct net_device *dev)
{
	/* TODO */
}

#ifdef CONFIG_NET_POLL_CONTROLLER
/*
 * Polling 'interrupt' - used by things like netconsole to send skbs
 * without having to re-enable interrupts. It's not called while
 * the interrupt routine is executing.
 */
static void ag71xx_netpoll(struct net_device *dev)
{
	disable_irq(dev->irq);
	ag71xx_interrupt(dev->irq, dev);
	enable_irq(dev->irq);
}
#endif

static const struct net_device_ops ag71xx_netdev_ops = {
	.ndo_open		= ag71xx_open,
	.ndo_stop		= ag71xx_stop,
	.ndo_start_xmit		= ag71xx_hard_start_xmit,
	.ndo_set_multicast_list	= ag71xx_set_multicast_list,
	.ndo_do_ioctl		= ag71xx_do_ioctl,
	.ndo_tx_timeout		= ag71xx_tx_timeout,
	.ndo_change_mtu		= eth_change_mtu,
	.ndo_set_mac_address	= eth_mac_addr,
	.ndo_validate_addr	= eth_validate_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
	.ndo_poll_controller	= ag71xx_netpoll,
#endif
};

static int __init ag71xx_probe(struct platform_device *pdev)
{
	struct net_device *dev;
	struct resource *res;
	struct ag71xx *ag;
	struct ag71xx_platform_data *pdata;
	int err;

	pdata = pdev->dev.platform_data;
	if (!pdata) {
		dev_err(&pdev->dev, "no platform data specified\n");
		err = -ENXIO;
		goto err_out;
	}

	if (pdata->mii_bus_dev == NULL) {
		dev_err(&pdev->dev, "no MII bus device specified\n");
		err = -EINVAL;
		goto err_out;
	}

	dev = alloc_etherdev(sizeof(*ag));
	if (!dev) {
		dev_err(&pdev->dev, "alloc_etherdev failed\n");
		err = -ENOMEM;
		goto err_out;
	}

	SET_NETDEV_DEV(dev, &pdev->dev);

	ag = netdev_priv(dev);
	ag->pdev = pdev;
	ag->dev = dev;
	ag->msg_enable = netif_msg_init(ag71xx_msg_level,
					AG71XX_DEFAULT_MSG_ENABLE);
	spin_lock_init(&ag->lock);

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base");
	if (!res) {
		dev_err(&pdev->dev, "no mac_base resource found\n");
		err = -ENXIO;
		goto err_out;
	}

	ag->mac_base = ioremap_nocache(res->start, res->end - res->start + 1);
	if (!ag->mac_base) {
		dev_err(&pdev->dev, "unable to ioremap mac_base\n");
		err = -ENOMEM;
		goto err_free_dev;
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mii_ctrl");
	if (!res) {
		dev_err(&pdev->dev, "no mii_ctrl resource found\n");
		err = -ENXIO;
		goto err_unmap_base;
	}

	ag->mii_ctrl = ioremap_nocache(res->start, res->end - res->start + 1);
	if (!ag->mii_ctrl) {
		dev_err(&pdev->dev, "unable to ioremap mii_ctrl\n");
		err = -ENOMEM;
		goto err_unmap_base;
	}

	dev->irq = platform_get_irq(pdev, 0);
	err = request_irq(dev->irq, ag71xx_interrupt,
			  IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
			  dev->name, dev);
	if (err) {
		dev_err(&pdev->dev, "unable to request IRQ %d\n", dev->irq);
		goto err_unmap_mii_ctrl;
	}

	dev->base_addr = (unsigned long)ag->mac_base;
	dev->netdev_ops = &ag71xx_netdev_ops;
	dev->ethtool_ops = &ag71xx_ethtool_ops;

	INIT_WORK(&ag->restart_work, ag71xx_restart_work_func);

	init_timer(&ag->oom_timer);
	ag->oom_timer.data = (unsigned long) dev;
	ag->oom_timer.function = ag71xx_oom_timer_handler;

	memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN);

	netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT);

	err = register_netdev(dev);
	if (err) {
		dev_err(&pdev->dev, "unable to register net device\n");
		goto err_free_irq;
	}

	printk(KERN_INFO "%s: Atheros AG71xx at 0x%08lx, irq %d\n",
	       dev->name, dev->base_addr, dev->irq);

	ag71xx_dump_regs(ag);

	ag71xx_hw_init(ag);

	ag71xx_dump_regs(ag);

	err = ag71xx_phy_connect(ag);
	if (err)
		goto err_unregister_netdev;

	err = ag71xx_debugfs_init(ag);
	if (err)
		goto err_phy_disconnect;

	platform_set_drvdata(pdev, dev);

	return 0;

 err_phy_disconnect:
	ag71xx_phy_disconnect(ag);
 err_unregister_netdev:
	unregister_netdev(dev);
 err_free_irq:
	free_irq(dev->irq, dev);
 err_unmap_mii_ctrl:
	iounmap(ag->mii_ctrl);
 err_unmap_base:
	iounmap(ag->mac_base);
 err_free_dev:
	kfree(dev);
 err_out:
	platform_set_drvdata(pdev, NULL);
	return err;
}

static int __exit ag71xx_remove(struct platform_device *pdev)
{
	struct net_device *dev = platform_get_drvdata(pdev);

	if (dev) {
		struct ag71xx *ag = netdev_priv(dev);

		ag71xx_debugfs_exit(ag);
		ag71xx_phy_disconnect(ag);
		unregister_netdev(dev);
		free_irq(dev->irq, dev);
		iounmap(ag->mii_ctrl);
		iounmap(ag->mac_base);
		kfree(dev);
		platform_set_drvdata(pdev, NULL);
	}

	return 0;
}

static struct platform_driver ag71xx_driver = {
	.probe		= ag71xx_probe,
	.remove		= __exit_p(ag71xx_remove),
	.driver = {
		.name	= AG71XX_DRV_NAME,
	}
};

static int __init ag71xx_module_init(void)
{
	int ret;

	ret = ag71xx_debugfs_root_init();
	if (ret)
		goto err_out;

	ret = ag71xx_mdio_driver_init();
	if (ret)
		goto err_debugfs_exit;

	ret = platform_driver_register(&ag71xx_driver);
	if (ret)
		goto err_mdio_exit;

	return 0;

 err_mdio_exit:
	ag71xx_mdio_driver_exit();
 err_debugfs_exit:
	ag71xx_debugfs_root_exit();
 err_out:
	return ret;
}

static void __exit ag71xx_module_exit(void)
{
	platform_driver_unregister(&ag71xx_driver);
	ag71xx_mdio_driver_exit();
	ag71xx_debugfs_root_exit();
}

module_init(ag71xx_module_init);
module_exit(ag71xx_module_exit);

MODULE_VERSION(AG71XX_DRV_VERSION);
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" AG71XX_DRV_NAME);