mirror of https://github.com/proski/madwifi
1114 lines
30 KiB
C
1114 lines
30 KiB
C
/* -*- linux-c -*- */
|
|
/*-
|
|
* Copyright (c) 2007 Nick Kossifidis <mickflemm@gmail.com>
|
|
* Copyright (c) 2007 Joerg Albert <jal2 *at* gmx.de>
|
|
*
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* So here is how it works:
|
|
*
|
|
* First compile...
|
|
*
|
|
* gcc ath_info.c -o ath_info
|
|
*
|
|
* then find card's physical address
|
|
*
|
|
* lspci -v
|
|
*
|
|
* 02:02.0 Ethernet controller: Atheros Communications, Inc. AR5212 802.11abg NIC (rev 01)
|
|
* Subsystem: Fujitsu Limited. Unknown device 1234
|
|
* Flags: bus master, medium devsel, latency 168, IRQ 23
|
|
* Memory at c2000000 (32-bit, non-prefetchable) [size=64K]
|
|
* Capabilities: [44] Power Management version 2
|
|
*
|
|
* address here is 0xc2000000
|
|
*
|
|
* load madwifi-ng or madwifi-old if not already loaded (be sure the
|
|
* interface is down!)
|
|
*
|
|
* modprobe ath_pci
|
|
*
|
|
* OR
|
|
*
|
|
* call:
|
|
* setpci -s 02:02.0 command=0x41f cache_line_size=0x10
|
|
*
|
|
* to enable access to the PCI device.
|
|
*
|
|
* and we run the thing...
|
|
*
|
|
* ./ath_info 0xc2000000
|
|
*
|
|
* In order to change the regdomain to 0, call:
|
|
*
|
|
* ./ath_info -w 0xc2000000 regdomain 0
|
|
*
|
|
* to change any PCI ID value, say:
|
|
*
|
|
* ./ath_info -w 0xc2000000 <name> X
|
|
*
|
|
* with <name> ::= pci_dev_id | pci_vendor_id | pci_class |
|
|
* pci_subsys_dev_id | pci_subsys_vendor_id
|
|
*
|
|
* With newer chipsets (>= AR5004x, i.e. MAC >= AR5213), Atheros introduced
|
|
* write protection on the EEPROM. On a GIGABYTE GN-WI01HT you can set GPIO 4
|
|
* to low to be able to write the EEPROM. This depends highly on the PCB layout,
|
|
* so there may be different GPIO used.
|
|
* This program currently sets GPIO 4 to low for a MAC >= AR5213, but you can
|
|
* override this with the -g option:
|
|
*
|
|
* ./ath_info -g 5:0 -w 0xc2000000 regdomain X
|
|
*
|
|
* would set GPIO 5 to low (and wouldn't touch GPIO 4). -g can be given several times.
|
|
*
|
|
* The write function is currently not tested with 5210 devices.
|
|
*
|
|
* Use at your own risk, entering a false device address will have really
|
|
* nasty results!
|
|
*
|
|
* Writing wrong values to the PCI id fields may prevent the driver from
|
|
* detecting the card!
|
|
*
|
|
* Transmitting on illegal frequencies may violate state laws. Stick to the local
|
|
* regulations!
|
|
*
|
|
* DISCLAIMER:
|
|
* The authors are in no case responsible for damaged hardware or violation of
|
|
* local laws by operating modified hardware.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <endian.h>
|
|
#include <byteswap.h>
|
|
|
|
#undef ARRAY_SIZE
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
|
|
|
#define dbg(fmt, __args__...) \
|
|
do { \
|
|
if (verbose) \
|
|
printf("#DBG %s: " fmt "\n", __FUNCTION__, ##__args__); \
|
|
} while (0)
|
|
|
|
#define err(fmt, __args__...) \
|
|
fprintf(stderr, "#ERR %s: " fmt "\n", __FUNCTION__, ##__args__)
|
|
|
|
#define AR5K_PCI_MEM_SIZE 0x10000
|
|
|
|
#define AR5K_NUM_GPIO 6
|
|
|
|
#define AR5K_GPIOCR 0x4014 /* Register Address */
|
|
#define AR5K_GPIOCR_OUT(n) (3 << ((n) * 2)) /* Mode 3 for pin n */
|
|
#define AR5K_GPIOCR_INT_SEL(n) ((n) << 12) /* Interrupt for GPIO pin n */
|
|
|
|
/*
|
|
* GPIO (General Purpose Input/Output) data output register
|
|
*/
|
|
#define AR5K_GPIODO 0x4018
|
|
|
|
/*
|
|
* GPIO (General Purpose Input/Output) data input register
|
|
*/
|
|
#define AR5K_GPIODI 0x401c
|
|
|
|
/*
|
|
* Common silicon revision/version values
|
|
*/
|
|
enum ath5k_srev_type {
|
|
AR5K_VERSION_VER,
|
|
AR5K_VERSION_REV,
|
|
AR5K_VERSION_RAD,
|
|
};
|
|
|
|
struct ath5k_srev_name {
|
|
const char *sr_name;
|
|
enum ath5k_srev_type sr_type;
|
|
u_int sr_val;
|
|
};
|
|
|
|
#define AR5K_SREV_UNKNOWN 0xffff
|
|
|
|
/* Known MAC revision numbers */
|
|
#define AR5K_SREV_VER_AR5210 0x00
|
|
#define AR5K_SREV_VER_AR5311 0x10
|
|
#define AR5K_SREV_VER_AR5311A 0x20
|
|
#define AR5K_SREV_VER_AR5311B 0x30
|
|
#define AR5K_SREV_VER_AR5211 0x40
|
|
#define AR5K_SREV_VER_AR5212 0x50
|
|
#define AR5K_SREV_VER_AR5213 0x55
|
|
#define AR5K_SREV_VER_AR5213A 0x59
|
|
#define AR5K_SREV_VER_AR2424 0xa0
|
|
#define AR5K_SREV_VER_AR5424 0xa3
|
|
#define AR5K_SREV_VER_AR5413 0xa4
|
|
#define AR5K_SREV_VER_AR5414 0xa5
|
|
#define AR5K_SREV_VER_AR5416 0xc0
|
|
#define AR5K_SREV_VER_AR5418 0xca
|
|
#define AR5K_SREV_VER_AR2425 0xe0
|
|
|
|
/* Known PHY revision nymbers */
|
|
#define AR5K_SREV_RAD_5110 0x00
|
|
#define AR5K_SREV_RAD_5111 0x10
|
|
#define AR5K_SREV_RAD_5111A 0x15
|
|
#define AR5K_SREV_RAD_2111 0x20
|
|
#define AR5K_SREV_RAD_5112 0x30
|
|
#define AR5K_SREV_RAD_5112A 0x35
|
|
#define AR5K_SREV_RAD_2112 0x40
|
|
#define AR5K_SREV_RAD_2112A 0x45
|
|
#define AR5K_SREV_RAD_SC1 0x63 /* Found on 5413/5414 */
|
|
#define AR5K_SREV_RAD_SC2 0xa2 /* Found on 2424/5424 */
|
|
#define AR5K_SREV_RAD_5133 0xc0 /* MIMO found on 5418 */
|
|
|
|
static const struct ath5k_srev_name ath5k_srev_names[] = {
|
|
{"5210", AR5K_VERSION_VER, AR5K_SREV_VER_AR5210},
|
|
{"5311", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311},
|
|
{"5311A", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311A},
|
|
{"5311B", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311B},
|
|
{"5211", AR5K_VERSION_VER, AR5K_SREV_VER_AR5211},
|
|
{"5212", AR5K_VERSION_VER, AR5K_SREV_VER_AR5212},
|
|
{"5213", AR5K_VERSION_VER, AR5K_SREV_VER_AR5213},
|
|
{"5213A", AR5K_VERSION_VER, AR5K_SREV_VER_AR5213A},
|
|
{"2424", AR5K_VERSION_VER, AR5K_SREV_VER_AR2424},
|
|
{"5424", AR5K_VERSION_VER, AR5K_SREV_VER_AR5424},
|
|
{"5413", AR5K_VERSION_VER, AR5K_SREV_VER_AR5413},
|
|
{"5414", AR5K_VERSION_VER, AR5K_SREV_VER_AR5414},
|
|
{"5416", AR5K_VERSION_VER, AR5K_SREV_VER_AR5416},
|
|
{"5418", AR5K_VERSION_VER, AR5K_SREV_VER_AR5418},
|
|
{"2425", AR5K_VERSION_VER, AR5K_SREV_VER_AR2425},
|
|
{"xxxxx", AR5K_VERSION_VER, AR5K_SREV_UNKNOWN},
|
|
{"5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110},
|
|
{"5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111},
|
|
{"2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111},
|
|
{"5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112},
|
|
{"5112a", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A},
|
|
{"2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112},
|
|
{"2112a", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A},
|
|
{"SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC1},
|
|
{"SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC2},
|
|
{"5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133},
|
|
{"xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN},
|
|
};
|
|
|
|
/*
|
|
* Silicon revision register
|
|
*/
|
|
#define AR5K_SREV 0x4020 /* Register Address */
|
|
#define AR5K_SREV_REV 0x0000000f /* Mask for revision */
|
|
#define AR5K_SREV_REV_S 0
|
|
#define AR5K_SREV_VER 0x000000ff /* Mask for version */
|
|
#define AR5K_SREV_VER_S 4
|
|
|
|
/*
|
|
* PHY chip revision register
|
|
*/
|
|
#define AR5K_PHY_CHIP_ID 0x9818
|
|
|
|
/*
|
|
* PHY register
|
|
*/
|
|
#define AR5K_PHY_BASE 0x9800
|
|
#define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2))
|
|
#define AR5K_PHY_SHIFT_2GHZ 0x00004007
|
|
#define AR5K_PHY_SHIFT_5GHZ 0x00000007
|
|
|
|
#define AR5K_RESET_CTL 0x4000 /* Register Address */
|
|
#define AR5K_RESET_CTL_PCU 0x00000001 /* Protocol Control Unit reset */
|
|
#define AR5K_RESET_CTL_DMA 0x00000002 /* DMA (Rx/Tx) reset -5210 only */
|
|
#define AR5K_RESET_CTL_BASEBAND 0x00000002 /* Baseband reset (5211/5212) */
|
|
#define AR5K_RESET_CTL_MAC 0x00000004 /* MAC reset (PCU+Baseband?) -5210 only */
|
|
#define AR5K_RESET_CTL_PHY 0x00000008 /* PHY reset -5210 only */
|
|
#define AR5K_RESET_CTL_PCI 0x00000010 /* PCI Core reset (interrupts etc) */
|
|
#define AR5K_RESET_CTL_CHIP (AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA | \
|
|
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY)
|
|
|
|
/*
|
|
* Sleep control register
|
|
*/
|
|
#define AR5K_SLEEP_CTL 0x4004 /* Register Address */
|
|
#define AR5K_SLEEP_CTL_SLDUR 0x0000ffff /* Sleep duration mask */
|
|
#define AR5K_SLEEP_CTL_SLDUR_S 0
|
|
#define AR5K_SLEEP_CTL_SLE 0x00030000 /* Sleep enable mask */
|
|
#define AR5K_SLEEP_CTL_SLE_S 16
|
|
#define AR5K_SLEEP_CTL_SLE_WAKE 0x00000000 /* Force chip awake */
|
|
#define AR5K_SLEEP_CTL_SLE_SLP 0x00010000 /* Force chip sleep */
|
|
#define AR5K_SLEEP_CTL_SLE_ALLOW 0x00020000
|
|
#define AR5K_SLEEP_CTL_SLE_UNITS 0x00000008 /* not on 5210 */
|
|
|
|
#define AR5K_PCICFG 0x4010 /* Register Address */
|
|
#define AR5K_PCICFG_EEAE 0x00000001 /* Eeprom access enable [5210] */
|
|
#define AR5K_PCICFG_CLKRUNEN 0x00000004 /* CLKRUN enable [5211+] */
|
|
#define AR5K_PCICFG_EESIZE 0x00000018 /* Mask for EEPROM size [5211+] */
|
|
#define AR5K_PCICFG_EESIZE_S 3
|
|
#define AR5K_PCICFG_EESIZE_4K 0 /* 4K */
|
|
#define AR5K_PCICFG_EESIZE_8K 1 /* 8K */
|
|
#define AR5K_PCICFG_EESIZE_16K 2 /* 16K */
|
|
#define AR5K_PCICFG_EESIZE_FAIL 3 /* Failed to get size (?) [5211+] */
|
|
|
|
#define AR5K_PCICFG_SPWR_DN 0x00010000 /* Mask for power status (5210) */
|
|
|
|
#define AR5K_EEPROM_BASE 0x6000
|
|
|
|
#define AR5K_EEPROM_MAGIC 0x003d /* Offset for EEPROM Magic number */
|
|
#define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */
|
|
#define AR5K_EEPROM_MAGIC_5212 0x0000145c /* 5212 */
|
|
#define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */
|
|
#define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */
|
|
|
|
/*
|
|
* EEPROM data register
|
|
*/
|
|
#define AR5K_EEPROM_DATA_5211 0x6004
|
|
#define AR5K_EEPROM_DATA_5210 0x6800
|
|
#define AR5K_EEPROM_DATA (mac_version == AR5K_SREV_VER_AR5210 ? \
|
|
AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211)
|
|
|
|
/*
|
|
* EEPROM command register
|
|
*/
|
|
#define AR5K_EEPROM_CMD 0x6008 /* Register Addres */
|
|
#define AR5K_EEPROM_CMD_READ 0x00000001 /* EEPROM read */
|
|
#define AR5K_EEPROM_CMD_WRITE 0x00000002 /* EEPROM write */
|
|
#define AR5K_EEPROM_CMD_RESET 0x00000004 /* EEPROM reset */
|
|
|
|
/*
|
|
* EEPROM status register
|
|
*/
|
|
#define AR5K_EEPROM_STAT_5210 0x6c00 /* Register Address [5210] */
|
|
#define AR5K_EEPROM_STAT_5211 0x600c /* Register Address [5211+] */
|
|
#define AR5K_EEPROM_STATUS (mac_version == AR5K_SREV_VER_AR5210 ? \
|
|
AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211)
|
|
#define AR5K_EEPROM_STAT_RDERR 0x00000001 /* EEPROM read failed */
|
|
#define AR5K_EEPROM_STAT_RDDONE 0x00000002 /* EEPROM read successful */
|
|
#define AR5K_EEPROM_STAT_WRERR 0x00000004 /* EEPROM write failed */
|
|
#define AR5K_EEPROM_STAT_WRDONE 0x00000008 /* EEPROM write successful */
|
|
|
|
#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* Offset for EEPROM regulatory domain */
|
|
#define AR5K_EEPROM_INFO_BASE 0x00c0 /* Offset for EEPROM header */
|
|
#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE)
|
|
#define AR5K_EEPROM_INFO_CKSUM 0xffff
|
|
#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n))
|
|
#define AR5K_EEPROM_MODE_11A 0
|
|
#define AR5K_EEPROM_MODE_11B 1
|
|
#define AR5K_EEPROM_MODE_11G 2
|
|
|
|
#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1)
|
|
|
|
#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */
|
|
#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1) /* Device has a support */
|
|
#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1) /* Device has b support */
|
|
#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1) /* Device has g support */
|
|
#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz (?) */
|
|
#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */
|
|
#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7)
|
|
#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz (?) */
|
|
#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
|
|
|
|
/* Misc values available since EEPROM 4.0 */
|
|
#define AR5K_EEPROM_MISC0 0x00c4
|
|
#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
|
|
#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
|
|
#define AR5K_EEPROM_MISC1 0x00c5
|
|
#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
|
|
#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1)
|
|
|
|
/*
|
|
* Read data by masking
|
|
*/
|
|
#define AR5K_REG_MS(_val, _flags) \
|
|
(((_val) & (_flags)) >> _flags##_S)
|
|
|
|
/*
|
|
* Access device registers
|
|
*/
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
#define AR5K_REG_READ(_reg) \
|
|
__bswap_32(*((volatile u_int32_t *)(mem + (_reg))))
|
|
#define AR5K_REG_WRITE(_reg, _val) \
|
|
(*((volatile u_int32_t *)(mem + (_reg))) = __bswap_32(_val))
|
|
#else
|
|
#define AR5K_REG_READ(_reg) \
|
|
(*((volatile u_int32_t *)(mem + (_reg))))
|
|
#define AR5K_REG_WRITE(_reg, _val) \
|
|
(*((volatile u_int32_t *)(mem + (_reg))) = (_val))
|
|
#endif
|
|
|
|
|
|
#define AR5K_REG_ENABLE_BITS(_reg, _flags) \
|
|
AR5K_REG_WRITE(_reg, AR5K_REG_READ(_reg) | (_flags))
|
|
|
|
#define AR5K_REG_DISABLE_BITS(_reg, _flags) \
|
|
AR5K_REG_WRITE(_reg, AR5K_REG_READ(_reg) & ~(_flags))
|
|
|
|
#define AR5K_TUNE_REGISTER_TIMEOUT 20000
|
|
|
|
/* names for eeprom fields */
|
|
struct eeprom_entry {
|
|
const char *name;
|
|
int addr;
|
|
};
|
|
|
|
static const struct eeprom_entry eeprom_addr[] = {
|
|
{"pci_dev_id", 0},
|
|
{"pci_vendor_id", 1},
|
|
{"pci_class", 2},
|
|
{"pci_rev_id", 3},
|
|
{"pci_subsys_dev_id", 7},
|
|
{"pci_subsys_vendor_id", 8},
|
|
{"regdomain", AR5K_EEPROM_REG_DOMAIN},
|
|
};
|
|
|
|
static const int eeprom_addr_len = sizeof(eeprom_addr) / sizeof(eeprom_addr[0]);
|
|
|
|
static int force_write = 0;
|
|
static int verbose = 0;
|
|
|
|
/* forward decl. */
|
|
static void usage(const char *n);
|
|
|
|
static u_int32_t ath5k_hw_bitswap(u_int32_t val, u_int bits)
|
|
{
|
|
u_int32_t retval = 0, bit, i;
|
|
|
|
for (i = 0; i < bits; i++) {
|
|
bit = (val >> i) & 1;
|
|
retval = (retval << 1) | bit;
|
|
}
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* Get the PHY Chip revision
|
|
*/
|
|
static u_int16_t
|
|
ath5k_hw_radio_revision(u_int16_t mac_version, void *mem, u_int8_t chip)
|
|
{
|
|
int i;
|
|
u_int32_t srev;
|
|
u_int16_t ret;
|
|
|
|
/*
|
|
* Set the radio chip access register
|
|
*/
|
|
switch (chip) {
|
|
case 0:
|
|
AR5K_REG_WRITE(AR5K_PHY(0), AR5K_PHY_SHIFT_2GHZ);
|
|
break;
|
|
case 1:
|
|
AR5K_REG_WRITE(AR5K_PHY(0), AR5K_PHY_SHIFT_5GHZ);
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
|
|
usleep(2000);
|
|
|
|
/* ...wait until PHY is ready and read the selected radio revision */
|
|
AR5K_REG_WRITE(AR5K_PHY(0x34), 0x00001c16);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
AR5K_REG_WRITE(AR5K_PHY(0x20), 0x00010000);
|
|
|
|
if (mac_version == AR5K_SREV_VER_AR5210) {
|
|
srev = AR5K_REG_READ(AR5K_PHY(256) >> 28) & 0xf;
|
|
|
|
ret = (u_int16_t)ath5k_hw_bitswap(srev, 4) + 1;
|
|
} else {
|
|
srev = (AR5K_REG_READ(AR5K_PHY(0x100)) >> 24) & 0xff;
|
|
|
|
ret = (u_int16_t)ath5k_hw_bitswap(((srev & 0xf0) >> 4) |
|
|
((srev & 0x0f) << 4), 8);
|
|
}
|
|
|
|
/* Reset to the 5GHz mode */
|
|
AR5K_REG_WRITE(AR5K_PHY(0), AR5K_PHY_SHIFT_5GHZ);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Write to EEPROM
|
|
*/
|
|
static int
|
|
ath5k_hw_eeprom_write(void *mem, u_int32_t offset, u_int16_t data,
|
|
u_int8_t mac_version)
|
|
{
|
|
u_int32_t status, timeout;
|
|
|
|
/*
|
|
* Initialize EEPROM access
|
|
*/
|
|
|
|
if (mac_version == AR5K_SREV_VER_AR5210) {
|
|
|
|
AR5K_REG_ENABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_EEAE);
|
|
|
|
/* data to write */
|
|
(void)AR5K_REG_WRITE(AR5K_EEPROM_BASE + (4 * offset), data);
|
|
|
|
} else {
|
|
/* not 5210 */
|
|
/* reset eeprom access */
|
|
AR5K_REG_WRITE(AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_RESET);
|
|
usleep(5);
|
|
|
|
AR5K_REG_WRITE(AR5K_EEPROM_DATA, data);
|
|
|
|
/* set offset in EEPROM to write to */
|
|
AR5K_REG_WRITE(AR5K_EEPROM_BASE, offset);
|
|
usleep(5);
|
|
|
|
/* issue write command */
|
|
AR5K_REG_WRITE(AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_WRITE);
|
|
}
|
|
|
|
for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
|
|
status = AR5K_REG_READ(AR5K_EEPROM_STATUS);
|
|
if (status & AR5K_EEPROM_STAT_WRDONE) {
|
|
if (status & AR5K_EEPROM_STAT_WRERR) {
|
|
err("eeprom write access to 0x%04x failed",
|
|
offset);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
usleep(15);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Read from EEPROM
|
|
*/
|
|
static int
|
|
ath5k_hw_eeprom_read(void *mem, u_int32_t offset, u_int16_t *data,
|
|
u_int8_t mac_version)
|
|
{
|
|
u_int32_t status, timeout;
|
|
|
|
/*
|
|
* Initialize EEPROM access
|
|
*/
|
|
if (mac_version == AR5K_SREV_VER_AR5210) {
|
|
AR5K_REG_ENABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_EEAE);
|
|
(void)AR5K_REG_READ(AR5K_EEPROM_BASE + (4 * offset));
|
|
} else {
|
|
AR5K_REG_WRITE(AR5K_EEPROM_BASE, offset);
|
|
AR5K_REG_ENABLE_BITS(AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_READ);
|
|
}
|
|
|
|
for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
|
|
status = AR5K_REG_READ(AR5K_EEPROM_STATUS);
|
|
if (status & AR5K_EEPROM_STAT_RDDONE) {
|
|
if (status & AR5K_EEPROM_STAT_RDERR)
|
|
return 1;
|
|
*data = (u_int16_t)
|
|
(AR5K_REG_READ(AR5K_EEPROM_DATA) & 0xffff);
|
|
return (0);
|
|
}
|
|
usleep(15);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static const char *ath5k_hw_get_part_name(enum ath5k_srev_type type,
|
|
u_int32_t val)
|
|
{
|
|
const char *name = "xxxxx";
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ath5k_srev_names); i++) {
|
|
if (ath5k_srev_names[i].sr_type != type ||
|
|
ath5k_srev_names[i].sr_val == AR5K_SREV_UNKNOWN)
|
|
continue;
|
|
if ((val & 0xff) < ath5k_srev_names[i + 1].sr_val) {
|
|
name = ath5k_srev_names[i].sr_name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (name);
|
|
}
|
|
|
|
/* returns -1 on unknown name */
|
|
static int eeprom_name2addr(const char *name)
|
|
{
|
|
int i;
|
|
if (!name || !name[0])
|
|
return -1;
|
|
for (i = 0; i < eeprom_addr_len; i++)
|
|
if (!strcmp(name, eeprom_addr[i].name))
|
|
return eeprom_addr[i].addr;
|
|
return -1;
|
|
} /* eeprom_name2addr */
|
|
|
|
/* returns "<unknown>" on unknown address */
|
|
static const char *eeprom_addr2name(int addr)
|
|
{
|
|
int i;
|
|
for (i = 0; i < eeprom_addr_len; i++)
|
|
if (eeprom_addr[i].addr == addr)
|
|
return eeprom_addr[i].name;
|
|
return "<unknown>";
|
|
} /* eeprom_addr2name */
|
|
|
|
static int
|
|
do_write_pairs(int anr, int argc, char **argv, unsigned char *mem,
|
|
int mac_version)
|
|
{
|
|
#define MAX_NR_WRITES 16
|
|
struct {
|
|
int addr;
|
|
unsigned int val;
|
|
} wr_ops[MAX_NR_WRITES];
|
|
int wr_ops_len = 0;
|
|
int i;
|
|
char *end;
|
|
int errors = 0; /* count errors during write/verify */
|
|
|
|
if (anr >= argc) {
|
|
err("missing values to write.");
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if ((argc - anr) % 2) {
|
|
err("write spec. needs an even number of arguments.");
|
|
usage(argv[0]);
|
|
return 2;
|
|
}
|
|
|
|
if ((argc - anr) / 2 > MAX_NR_WRITES) {
|
|
err("too many values to write (max. %d)", MAX_NR_WRITES);
|
|
return 3;
|
|
}
|
|
|
|
/* get the (addr,val) pairs we have to write */
|
|
i = 0;
|
|
while (anr < (argc - 1)) {
|
|
wr_ops[i].addr = strtoul(argv[anr], &end, 16);
|
|
if (end == argv[anr]) {
|
|
/* maybe a symbolic name for the address? */
|
|
if ((wr_ops[i].addr =
|
|
eeprom_name2addr(argv[anr])) == -1) {
|
|
err("pair %d: bad address %s", i, argv[anr]);
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
if (wr_ops[i].addr >= AR5K_EEPROM_INFO_BASE) {
|
|
err("offset 0x%04x in CRC protected area is "
|
|
"not supported", wr_ops[i].addr);
|
|
return 5;
|
|
}
|
|
|
|
anr++;
|
|
wr_ops[i].val = strtoul(argv[anr], &end, 16);
|
|
if (end == argv[anr]) {
|
|
err("pair %d: bad val %s", i, argv[anr]);
|
|
return 5;
|
|
}
|
|
|
|
if (wr_ops[i].val > 0xffff) {
|
|
err("pair %d: value %u too large", i, wr_ops[i].val);
|
|
return 6;
|
|
}
|
|
anr++;
|
|
i++;
|
|
} /* while (anr < (argc-1)) */
|
|
|
|
if (!(wr_ops_len = i)) {
|
|
err("no (addr,val) pairs given");
|
|
return 7;
|
|
}
|
|
|
|
if (verbose || !force_write) {
|
|
for (i = 0; i < wr_ops_len; i++)
|
|
printf("%20s (0x%04x) := 0x%04x\n",
|
|
eeprom_addr2name(wr_ops[i].addr), wr_ops[i].addr,
|
|
wr_ops[i].val);
|
|
}
|
|
|
|
if (!force_write) {
|
|
int c;
|
|
printf
|
|
("WARNING: The write function may easy brick your device or\n"
|
|
"violate state regulation on frequency usage.\n"
|
|
"Proceed on your own risk!\n"
|
|
"Shall I write the above value(s)? (y/n)\n");
|
|
c = getchar();
|
|
if (c != 'y' && c != 'Y') {
|
|
printf("user abort\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < wr_ops_len; i++) {
|
|
u_int16_t oldval, u;
|
|
|
|
if (ath5k_hw_eeprom_read
|
|
(mem, wr_ops[i].addr, &oldval, mac_version)) {
|
|
err("failed to read old value from offset 0x%04x ",
|
|
wr_ops[i].addr);
|
|
errors++;
|
|
}
|
|
|
|
if (oldval == wr_ops[i].val) {
|
|
dbg("pair %d: skipped, value already there", i);
|
|
continue;
|
|
}
|
|
|
|
dbg("writing *0x%04x := 0x%04x", wr_ops[i].addr, wr_ops[i].val);
|
|
if (ath5k_hw_eeprom_write
|
|
(mem, wr_ops[i].addr, wr_ops[i].val, mac_version)) {
|
|
err("failed to write 0x%04x to offset 0x%04x",
|
|
wr_ops[i].val, wr_ops[i].addr);
|
|
errors++;
|
|
} else {
|
|
if (ath5k_hw_eeprom_read
|
|
(mem, wr_ops[i].addr, &u, mac_version)) {
|
|
err("failed to read offset 0x%04x for "
|
|
"verification", wr_ops[i].addr);
|
|
errors++;
|
|
} else {
|
|
if (u != wr_ops[i].val) {
|
|
err("offset 0x%04x: wrote 0x%04x but "
|
|
"read 0x%04x", wr_ops[i].addr,
|
|
wr_ops[i].val, u);
|
|
errors++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return errors ? 11 : 0;
|
|
} /* do_write_pairs */
|
|
|
|
static void usage(const char *n)
|
|
{
|
|
int i;
|
|
|
|
fprintf(stderr, "%s [-w [-g N:M]] [-v] [-f] [-d] <base_address> "
|
|
"[<name1> <val1> [<name2> <val2> ...]]\n\n", n);
|
|
fprintf(stderr,
|
|
"-w write values into EEPROM\n"
|
|
"-g N:M set GPIO N to level M (only used with -w)\n"
|
|
"-v verbose output\n"
|
|
"-f force; suppress question before writing\n"
|
|
"-d dump eeprom (file 'ath-eeprom-dump.bin' and screen)\n"
|
|
"<base_address> device base address (see lspci output)\n\n");
|
|
|
|
fprintf(stderr,
|
|
"- read info:\n"
|
|
" %s <base_address>\n\n"
|
|
"- set regdomain to N:\n"
|
|
" %s -w <base_address> regdomain N\n\n"
|
|
"- set a PCI id field to value N:\n"
|
|
" %s -w <base_address> <field> N\n"
|
|
" where <field> is on of:\n ", n, n, n);
|
|
for (i = 0; i < eeprom_addr_len; i++)
|
|
fprintf(stderr, " %s", eeprom_addr[i].name);
|
|
fprintf(stderr, "\n\n");
|
|
fprintf(stderr,
|
|
"You may need to set a GPIO to a certain value in order to enable\n"
|
|
"writing to the EEPROM with newer chipsets, e.g. set GPIO 4 to low:\n"
|
|
" %s -g 4:0 -w <base_address> regdomain N\n", n);
|
|
fprintf(stderr,
|
|
"\nDISCLAIMER: The authors are not responsible for any damages caused by\n"
|
|
"this program. Writing improper values may damage the card or cause\n"
|
|
"unlawful radio transmissions!\n\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
u_int32_t dev_addr;
|
|
u_int16_t eeprom_header, srev, phy_rev_5ghz, phy_rev_2ghz;
|
|
u_int16_t eeprom_version, mac_version, regdomain, has_crystal, ee_magic;
|
|
u_int8_t error, has_a, has_b, has_g, has_rfkill, eeprom_size;
|
|
int byte_size = 0;
|
|
void *mem;
|
|
int fd;
|
|
int i, anr = 1;
|
|
int do_write = 0; /* default: read only */
|
|
int do_dump = 0;
|
|
|
|
struct {
|
|
int valid;
|
|
int value;
|
|
} gpio_set[AR5K_NUM_GPIO];
|
|
int nr_gpio_set = 0;
|
|
|
|
for (i = 0; i < sizeof(gpio_set) / sizeof(gpio_set[0]); i++)
|
|
gpio_set[i].valid = 0;
|
|
|
|
if (argc < 2) {
|
|
usage(argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
while (anr < argc && argv[anr][0] == '-') {
|
|
switch (argv[anr][1]) {
|
|
case 'w':
|
|
do_write = 1;
|
|
break;
|
|
case 'g':
|
|
anr++;
|
|
if (strlen(argv[anr]) != 3 || argv[anr][1] != ':' ||
|
|
argv[anr][0] < '0' || argv[anr][0] > '5' ||
|
|
(argv[anr][2] != '0' && argv[anr][2] != '1')) {
|
|
err("invalid gpio spec. %s", argv[anr]);
|
|
return 2;
|
|
}
|
|
gpio_set[argv[anr][0] - '0'].valid = 1;
|
|
gpio_set[argv[anr][0] - '0'].value = argv[anr][2] - '0';
|
|
nr_gpio_set++;
|
|
break;
|
|
|
|
case 'f':
|
|
force_write = 1;
|
|
break;
|
|
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
|
|
case 'd':
|
|
do_dump = 1;
|
|
break;
|
|
|
|
case 'h':
|
|
usage(argv[0]);
|
|
return 0;
|
|
break;
|
|
|
|
default:
|
|
err("unknown option %s", argv[anr]);
|
|
return 2;
|
|
} /* switch (argv[anr][1]) */
|
|
|
|
anr++;
|
|
} /* while (anr < argc && ...) */
|
|
|
|
if (anr >= argc) {
|
|
err("missing device address");
|
|
usage(argv[0]);
|
|
return 3;
|
|
}
|
|
|
|
dev_addr = strtoul(argv[anr], NULL, 16);
|
|
|
|
fd = open("/dev/mem", O_RDWR);
|
|
if (fd < 0) {
|
|
printf("Opening /dev/mem failed!\n");
|
|
return -2;
|
|
}
|
|
|
|
mem = mmap(NULL, AR5K_PCI_MEM_SIZE, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED | MAP_FILE, fd, dev_addr);
|
|
|
|
if (mem == MAP_FAILED) {
|
|
printf("Mmap of device at 0x%08X for 0x%X bytes failed - "
|
|
"%s\n", dev_addr, AR5K_PCI_MEM_SIZE, strerror(errno));
|
|
return -3;
|
|
}
|
|
|
|
/* wake from power-down and remove reset (in case the driver isn't running) */
|
|
{
|
|
u_int32_t
|
|
sleep_ctl = AR5K_REG_READ(AR5K_SLEEP_CTL),
|
|
reset_ctl = AR5K_REG_READ(AR5K_RESET_CTL);
|
|
|
|
dbg("sleep_ctl reg %08x reset_ctl reg %08x",
|
|
sleep_ctl, reset_ctl);
|
|
if (sleep_ctl & AR5K_SLEEP_CTL_SLE_SLP) {
|
|
dbg("waking up the chip");
|
|
AR5K_REG_WRITE(AR5K_SLEEP_CTL,
|
|
(sleep_ctl & ~AR5K_SLEEP_CTL_SLE_SLP));
|
|
}
|
|
|
|
if (reset_ctl) {
|
|
dbg("removing resets");
|
|
AR5K_REG_WRITE(AR5K_RESET_CTL, 0);
|
|
}
|
|
}
|
|
|
|
AR5K_REG_DISABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_SPWR_DN);
|
|
usleep(500);
|
|
|
|
srev = AR5K_REG_READ(AR5K_SREV);
|
|
mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER) << 4;
|
|
|
|
/* Verify eeprom magic value first */
|
|
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_MAGIC, &ee_magic,
|
|
mac_version);
|
|
|
|
if (error) {
|
|
printf("Unable to read EEPROM Magic value!\n");
|
|
return -1;
|
|
}
|
|
|
|
if (ee_magic != AR5K_EEPROM_MAGIC_VALUE) {
|
|
printf("Warning: Invalid EEPROM Magic number!\n");
|
|
}
|
|
|
|
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_HDR, &eeprom_header,
|
|
mac_version);
|
|
|
|
if (error) {
|
|
printf("Unable to read EEPROM Header!\n");
|
|
return -1;
|
|
}
|
|
|
|
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_VERSION, &eeprom_version,
|
|
mac_version);
|
|
|
|
if (error) {
|
|
printf("Unable to read EEPROM version!\n");
|
|
return -1;
|
|
}
|
|
|
|
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_REG_DOMAIN, ®domain,
|
|
mac_version);
|
|
|
|
if (error) {
|
|
printf("Unable to read Regdomain!\n");
|
|
return -1;
|
|
}
|
|
|
|
if (eeprom_version >= 0x4000) {
|
|
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_MISC0,
|
|
&has_crystal, mac_version);
|
|
|
|
if (error) {
|
|
printf("Unable to read EEPROM Misc data!\n");
|
|
return -1;
|
|
}
|
|
|
|
has_crystal = AR5K_EEPROM_HAS32KHZCRYSTAL(has_crystal);
|
|
} else {
|
|
has_crystal = 2;
|
|
}
|
|
|
|
eeprom_size = AR5K_REG_MS(AR5K_REG_READ(AR5K_PCICFG),
|
|
AR5K_PCICFG_EESIZE);
|
|
|
|
has_a = AR5K_EEPROM_HDR_11A(eeprom_header);
|
|
has_b = AR5K_EEPROM_HDR_11B(eeprom_header);
|
|
has_g = AR5K_EEPROM_HDR_11G(eeprom_header);
|
|
has_rfkill = AR5K_EEPROM_HDR_RFKILL(eeprom_header);
|
|
|
|
if (has_a)
|
|
phy_rev_5ghz = ath5k_hw_radio_revision(mac_version, mem, 1);
|
|
else
|
|
phy_rev_5ghz = 0;
|
|
|
|
if (has_b)
|
|
phy_rev_2ghz = ath5k_hw_radio_revision(mac_version, mem, 0);
|
|
else
|
|
phy_rev_2ghz = 0;
|
|
|
|
printf(" -==Device Information==-\n");
|
|
|
|
printf("MAC Version: %-5s (0x%02x)\n",
|
|
ath5k_hw_get_part_name(AR5K_VERSION_VER, mac_version),
|
|
mac_version);
|
|
|
|
printf("MAC Revision: %-5s (0x%02x)\n",
|
|
ath5k_hw_get_part_name(AR5K_VERSION_VER, srev), srev);
|
|
|
|
/* Single-chip PHY with a/b/g support */
|
|
if (has_b && !phy_rev_2ghz) {
|
|
printf("PHY Revision: %-5s (0x%02x)\n",
|
|
ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_5ghz),
|
|
phy_rev_5ghz);
|
|
phy_rev_5ghz = 0;
|
|
}
|
|
|
|
/* Single-chip PHY with b/g support */
|
|
if (!has_a) {
|
|
printf("PHY Revision: %-5s (0x%02x)\n",
|
|
ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_2ghz),
|
|
phy_rev_2ghz);
|
|
phy_rev_2ghz = 0;
|
|
}
|
|
|
|
/* Different chip for 5Ghz and 2Ghz */
|
|
if (phy_rev_5ghz) {
|
|
printf("5Ghz PHY Revision: %-5s (0x%2x)\n",
|
|
ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_5ghz),
|
|
phy_rev_5ghz);
|
|
}
|
|
if (phy_rev_2ghz) {
|
|
printf("2Ghz PHY Revision: %-5s (0x%2x)\n",
|
|
ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_2ghz),
|
|
phy_rev_2ghz);
|
|
}
|
|
|
|
printf(" -==EEPROM Information==-\n");
|
|
|
|
printf("EEPROM Version: %x.%x\n",
|
|
(eeprom_version & 0xF000) >> 12, eeprom_version & 0xFFF);
|
|
|
|
printf("EEPROM Size: ");
|
|
|
|
if (eeprom_size == 0) {
|
|
printf(" 4K\n");
|
|
byte_size = 4096;
|
|
} else if (eeprom_size == 1) {
|
|
printf(" 8K\n");
|
|
byte_size = 8192;
|
|
} else if (eeprom_size == 2) {
|
|
printf(" 16K\n");
|
|
byte_size = 16384;
|
|
} else
|
|
printf(" ??\n");
|
|
|
|
printf("Regulatory Domain: 0x%X\n", regdomain);
|
|
|
|
printf(" -==== Capabilities ====-\n");
|
|
|
|
printf("| 802.11a Support: ");
|
|
if (has_a)
|
|
printf("yes |\n");
|
|
else
|
|
printf("no |\n");
|
|
|
|
printf("| 802.11b Support: ");
|
|
if (has_b)
|
|
printf("yes |\n");
|
|
else
|
|
printf("no |\n");
|
|
|
|
printf("| 802.11g Support: ");
|
|
if (has_g)
|
|
printf("yes |\n");
|
|
else
|
|
printf("no |\n");
|
|
|
|
printf("| RFKill Support: ");
|
|
if (has_rfkill)
|
|
printf("yes |\n");
|
|
else
|
|
printf("no |\n");
|
|
|
|
if (has_crystal != 2) {
|
|
printf("| 32KHz Crystal: ");
|
|
if (has_crystal)
|
|
printf("yes |\n");
|
|
else
|
|
printf("no |\n");
|
|
}
|
|
printf(" ========================\n");
|
|
|
|
/* print current GPIO settings */
|
|
printf("GPIO registers: CR %08x DO %08x DI %08x\n",
|
|
AR5K_REG_READ(AR5K_GPIOCR), AR5K_REG_READ(AR5K_GPIODO),
|
|
AR5K_REG_READ(AR5K_GPIODI));
|
|
|
|
if (do_dump) {
|
|
u_int16_t data;
|
|
FILE *dumpfile = fopen("ath-eeprom-dump.bin", "w");
|
|
|
|
printf("\nEEPROM dump (%d byte)\n", byte_size);
|
|
printf("==============================================");
|
|
for (i = 1; i <= (byte_size / 2); i++) {
|
|
error =
|
|
ath5k_hw_eeprom_read(mem, i, &data, mac_version);
|
|
if (error) {
|
|
printf("\nUnable to read at %04x\n", i);
|
|
continue;
|
|
}
|
|
if (!((i - 1) % 8))
|
|
printf("\n%04x: ", i);
|
|
printf("%04x ", data);
|
|
fwrite(&data, 2, 1, dumpfile);
|
|
}
|
|
printf("\n==============================================\n");
|
|
fclose(dumpfile);
|
|
}
|
|
|
|
if (do_write) {
|
|
u_int32_t rcr = AR5K_REG_READ(AR5K_GPIOCR),
|
|
rdo = AR5K_REG_READ(AR5K_GPIODO);
|
|
u_int32_t old_cr = rcr, old_do = rdo;
|
|
int rc;
|
|
|
|
if (mac_version >= AR5K_SREV_VER_AR5213 && !nr_gpio_set) {
|
|
dbg("new MAC %x (>= AR5213) set gpio4 to low",
|
|
mac_version);
|
|
gpio_set[4].valid = 1;
|
|
gpio_set[4].value = 0;
|
|
}
|
|
|
|
/* set gpios */
|
|
dbg("old GPIO CR %08x DO %08x DI %08x",
|
|
rcr, rdo, AR5K_REG_READ(AR5K_GPIODI));
|
|
|
|
for (i = 0; i < sizeof(gpio_set) / sizeof(gpio_set[0]); i++) {
|
|
if (gpio_set[i].valid) {
|
|
rcr |= AR5K_GPIOCR_OUT(i); /* we use mode 3 */
|
|
rcr &= ~AR5K_GPIOCR_INT_SEL(i);
|
|
rdo &= ~(1 << i);
|
|
rdo |= (gpio_set[i].value << i);
|
|
}
|
|
}
|
|
|
|
if (rcr != old_cr) {
|
|
dbg("GPIO CR %x -> %x", old_cr, rcr);
|
|
AR5K_REG_WRITE(AR5K_GPIOCR, rcr);
|
|
}
|
|
usleep(5);
|
|
|
|
if (rdo != old_do) {
|
|
dbg("GPIO CR %x -> %x", old_do, rdo);
|
|
AR5K_REG_WRITE(AR5K_GPIODO, rdo);
|
|
}
|
|
|
|
/* dump current values again if we have written anything */
|
|
if (rcr != old_cr || rdo != old_do)
|
|
dbg("new GPIO CR %08x DO %08x DI %08x",
|
|
AR5K_REG_READ(AR5K_GPIOCR),
|
|
AR5K_REG_READ(AR5K_GPIODO),
|
|
AR5K_REG_READ(AR5K_GPIODI));
|
|
|
|
/* let argv[anr] be the first write parameter */
|
|
anr++;
|
|
|
|
rc = do_write_pairs(anr, argc, argv, mem, mac_version);
|
|
|
|
/* restore old GPIO settings */
|
|
if (rcr != old_cr) {
|
|
dbg("restoring GPIO CR %x -> %x", rcr, old_cr);
|
|
AR5K_REG_WRITE(AR5K_GPIOCR, old_cr);
|
|
}
|
|
usleep(5);
|
|
|
|
if (rdo != old_do) {
|
|
dbg("restoring GPIO CR %x -> %x", rdo, old_do);
|
|
AR5K_REG_WRITE(AR5K_GPIODO, old_do);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|