diff options
Diffstat (limited to 'drivers/net/wireless/digiPiper/piper.c')
-rw-r--r-- | drivers/net/wireless/digiPiper/piper.c | 1032 |
1 files changed, 1032 insertions, 0 deletions
diff --git a/drivers/net/wireless/digiPiper/piper.c b/drivers/net/wireless/digiPiper/piper.c new file mode 100644 index 000000000000..a3c24720dd11 --- /dev/null +++ b/drivers/net/wireless/digiPiper/piper.c @@ -0,0 +1,1032 @@ +/* + * piper.c + * + * Copyright (C) 2008 by Digi International Inc. + * All rights reserved. + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <net/mac80211.h> +#include <linux/usb.h> +#include <linux/kthread.h> +#include <linux/platform_device.h> +#include <asm/gpio.h> +#include <linux/timer.h> + +#include "pipermain.h" +#include "mac.h" +#include "phy.h" +#include "airoha.h" +#include "airohaCalibration.h" +#include "piperDsp.h" +#include "piperMacAssist.h" +#include "digiPs.h" + +#define WANT_AIROHA_CALIBRATION (1) +#define WANT_DEBUG_COMMANDS (1) + + +static void piper_clear_irq_mask(struct piper_priv *piperp, unsigned int bits) +{ + piperp->ac->wr_reg(piperp, BB_IRQ_MASK, ~bits, op_and); +} + +static void piper_set_irq_mask(struct piper_priv *piperp, unsigned int bits) +{ + piperp->ac->wr_reg(piperp, BB_IRQ_MASK, bits, op_or); +} + +/* Generate a random number */ +static int local_rand(void) +{ + static unsigned long next = 1; + + /* RAND_MAX assumed to be 32767 */ + next = next * 1103515245 + 12345; + return((unsigned)(next/65536) % 32768); +} + +/* + * Load the MAC Assist firmware into the chip. This is done by setting a bit + * in the control register to enable MAC Assist firmware download, and then + * writing the firmware into the data FIFO. + */ +void piper_load_mac_firmware(struct piper_priv *piperp) +{ + unsigned int i; + + printk(KERN_DEBUG PIPER_DRIVER_NAME ": loading MAC Assist firmware\n"); + + /* Zero out MAC assist SRAM (put into known state before enabling MAC assist) */ + for (i = 0; i < 0x100; i += 4) + piperp->ac->wr_reg(piperp, i, 0, op_write); + + /* Enable download the MAC Assist program RAM */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_FW_LOAD_ENABLE, op_or); + + /* load MAC Assist data */ + for (i = 0; i < piper_macassist_data_len; i++) + piperp->ac->wr_reg(piperp, BB_DATA_FIFO, piper_wifi_macassist_ucode[i], + op_write); + + /* disable MAC Assist download */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_FW_LOAD_ENABLE, op_and); +} + +/* + * Load the DSP firmware into the chip. This is done by setting a bit + * in the control register to enable DSP firmware download, and then + * writing the firmware into the data FIFO. + */ +void piper_load_dsp_firmware(struct piper_priv *piperp) +{ + unsigned int i; + + printk(KERN_DEBUG PIPER_DRIVER_NAME ": loading DSP firmware\n"); + + /* Enable load of DSP firmware */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_DSP_LOAD_ENABLE, op_or); + + /* load DSP data */ + for (i = 0; i < piper_dsp_data_len; i++) + piperp->ac->wr_reg(piperp, BB_DATA_FIFO, piper_wifi_dsp_ucode[i], + op_write); + + /* Disable load of DSP firmware */ + udelay(10); + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_DSP_LOAD_ENABLE, op_and); + + /* Let her rip */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or); +} + + +/* + * This routine corrects a bug in the Piper chip where internal clocks would + * be out of sync with each other and cause the chip to generate noise spikes. + * This problem should be fixed in the next chip (Chopper). + * + * I'm not sure exactly what this code is doing. It comes straight from the + * guy who designed the chip. + */ +int piper_spike_suppression(struct piper_priv *piperp, bool retry) +{ + int timeout1 = 300, timeout2 = 300; + int ret = 0; + + /* + * Initial timing measurement to avoid spike + * The new "magic" value is 0x63 at address 0xA62. Bit-0 indicates the + * timing measurement is complete. Bit-1 indicates that a second timing + * measurment was performed. The upper nibble is the timing measurement + * value. This code should eliminate the possibility of spikes at the + * beginning of all PSK/CCK frames and eliminate the spikes at the end of + * all PSK (1M, 2M) frames. + */ + + /* reset the timing value */ + piperp->ac->wr_reg(piperp, MAC_STATUS, 0xffff00ff, op_and); + + while ((piperp->ac->rd_reg(piperp, MAC_STATUS) & 0x0000ff00) != 0x00006300) { + + /* reset the timing value */ + piperp->ac->wr_reg(piperp, MAC_STATUS, 0xffff00ff, op_and); + + /* issue WiFi soft reset */ + piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x40000000, op_write); + + /* Set TX_ON Low */ + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xffffff3f, op_and); + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x00000080, op_or); + + /* Set PA_2G Low */ + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xfffff0ff, op_and); + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x00000a00, op_or); + + /* Set RX_ON low */ + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xcfffffff, op_and); + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x20000000, op_or); + + /* start the WiFi mac & dsp */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720820, op_write); + timeout1 = 500; + + /* Wait for timing measurement to finish */ + while ((piperp->ac->rd_reg(piperp, MAC_STATUS) & 0x0000ff00) != 0x00000100) { + udelay(2); + timeout1--; + if (!timeout1) + break; + } + + timeout2--; + if (!timeout2) { + ret = -EIO; + break; + } + + if (!retry) { + ret = -EIO; + break; + } + } + + /* Set TX_ON/RXHP_ON and RX to normal wifi, restore the reset value to HW_OUT_CTRL */ + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x1, op_write); + + return ret; +} +EXPORT_SYMBOL_GPL(piper_spike_suppression); + +void piper_reset_mac(struct piper_priv *piperp) +{ + int i; + + /* set the TX-hold bit */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write); + + /* clear the TX-FIFO memory */ + for (i = 0; i < 448; i++) + piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write); + + /* reset the TX-FIFO */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200C0, op_write); + + /* release the TX-hold and reset */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write); + +/* iowrite32(ioread32(piperp->vbase + MAC_STATUS) & ~0x40000000, + piperp->vbase + BB_GENERAL_STAT);*/ + mdelay(1); +} + +/* + * Load the MAC address into the chip. Use the value stored in the + * environment, if there is one, otherwise use the default value. + */ +void piper_set_macaddr(struct piper_priv *piperp) +{ + /* Default MAC Addr used if the nvram parameters are corrupted */ + u8 mac[6] = {0x00, 0x04, 0xf3, 0x11, 0x43, 0x35}; + u8 *pmac = piperp->pdata->macaddr; + int i; + static bool firstTime = true; + + for (i = 0; i < 6; i++) { + if (*(pmac + i) != 0xff) + break; + if (i == 5) { + /* There is a problem with the parameters, use default */ + if (firstTime) { + printk(KERN_INFO PIPER_DRIVER_NAME + ": invalid mac address, using default\n"); + } + memcpy(piperp->pdata->macaddr, mac, sizeof(piperp->pdata->macaddr)); + } + } + + firstTime = false; + memcpy(piperp->hw->wiphy->perm_addr, piperp->pdata->macaddr, + sizeof(piperp->hw->wiphy->perm_addr)); + + /* configure ethernet address */ + piperp->ac->wr_reg(piperp, MAC_STA_ID0, *(pmac + 3) | *(pmac + 2) << 8 | + *(pmac + 1) << 16 | *(pmac + 0) << 24, op_write); + piperp->ac->wr_reg(piperp, MAC_STA_ID1, *(pmac + 5) << 16 | *(pmac + 4) << 24, + op_write); +} +EXPORT_SYMBOL_GPL(piper_set_macaddr); + + +/* Configure the H/W with the antenna settings */ +static int piper_set_antenna(struct piper_priv *piperp, enum antenna_select sel) +{ + if (sel == ANTENNA_BOTH) { + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, + BB_GENERAL_CTL_ANT_DIV, op_or); + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, + ~BB_GENERAL_CTL_ANT_SEL, op_and); + } else { + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, + ~BB_GENERAL_CTL_ANT_DIV, op_and); + /* select the antenna if !diversity */ + if (sel == ANTENNA_1) + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, + ~BB_GENERAL_CTL_ANT_SEL, op_and); + else + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, + BB_GENERAL_CTL_ANT_SEL, op_or); + } + + /* select which antenna to transmit on */ + piperp->ac->wr_reg(piperp, BB_RSSI, ~BB_RSSI_ANT_MASK, op_and); + if (sel == ANTENNA_BOTH) + piperp->ac->wr_reg(piperp, BB_RSSI, BB_RSSI_ANT_DIV_MAP, op_or); + else + piperp->ac->wr_reg(piperp, BB_RSSI, BB_RSSI_ANT_NO_DIV_MAP, op_or); + + return 0; +} + +/* + * Compute a beacon backoff time as described in section 11.1.2.2 of 802.11 spec. + * + */ +static u16 get_next_beacon_backoff(void) +{ +#define MAX_BEACON_BACKOFF (2 * ASLOT_TIME * DEFAULT_CW_MIN) + + /* + * We shift the result of local_rand() by 4 bits because the notes + * for the algorithm say that we shouldn't rely on the last few + * bits being random. Other than that, we just take the random + * value and make sure it is less than MAX_BEACON_BACKOFF. + */ + return (local_rand() >> 4) % MAX_BEACON_BACKOFF; +} + +static int load_beacon(struct piper_priv *digi, unsigned char *buffer, + unsigned int length) +{ + return digi->ac->wr_fifo(digi, BEACON_FIFO, buffer, length); +} + +static int piper_init_rx_tx(struct piper_priv *piperp) +{ + tasklet_init(&piperp->rx_tasklet, piper_rx_tasklet, (unsigned long)piperp); + tasklet_disable(&piperp->rx_tasklet); + piperp->expectingAck = false; + + spin_lock_init(&piperp->tx_tasklet_lock); + spin_lock_init(&piperp->tx_queue_lock); + piperp->tx_tasklet_running = false; + memset(&piperp->tx_queue, 0, sizeof(piperp->tx_queue)); + piperp->tx_queue_head = 0; + piperp->tx_queue_tail = 0; + piperp->tx_queue_count = 0; + tasklet_init(&piperp->tx_tasklet, piper_tx_tasklet, (unsigned long)piperp); + tasklet_disable(&piperp->tx_tasklet); + + return 0; +} + +static void piper_free_rx_tx(struct piper_priv *piperp) +{ + tasklet_disable(&piperp->rx_tasklet); + tasklet_kill(&piperp->rx_tasklet); + tasklet_disable(&piperp->tx_tasklet); + tasklet_kill(&piperp->tx_tasklet); + piper_empty_tx_queue(piperp); +} + +/* + * This function sets the tracking control according to a channel's + * frequency. + */ +static int piper_set_tracking_constant(struct piper_priv *piperp, unsigned megahertz) +{ + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, ~TRACK_CONSTANT_MASK, op_and); + if (megahertz < 4920) + { + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or); + } + else if (megahertz <= 4980) + { + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_4920_4980_A_BAND, op_or); + } + else if (megahertz <= 5350) + { + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5150_5350_A_BAND, op_or); + } + else if (megahertz <= 5725) + { + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5470_5725_A_BAND, op_or); + } + else + { + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5725_5825_A_BAND, op_or); + } + + return 0; +} + +/* + * This function is called to set the value of the B_TX_GAIN field of the + * HW_CONF1 mac register. This register must be set to different values depending + * on the H/W revision of the board due to changes in the board design. + */ +static unsigned int get_b_tx_gain(struct piper_priv *piperp) +{ + u16 platform = piperp->pdata->wcd.header.hw_platform & WCD_PLATFORM_MASK; + u16 hw_revision = piperp->pdata->wcd.header.hw_platform & WCD_HW_REV_MASK; + unsigned int tx_gain = 0; + + switch (platform) { + case WCD_CCW9P_PLATFORM: + tx_gain = TRACK_TX_B_GAIN_NORMAL; + break; + case WCD_CCW9M_PLATFORM: + switch (hw_revision) { + case WCD_HW_REV_PROTOTYPE: + case WCD_HW_REV_PILOT: + tx_gain = 0xc0000000; + break; + case WCD_HW_REV_A: + default: + tx_gain = 0x90000000; + break; + } + break; + } + return tx_gain; +} + + +static int piper_init_hw(struct piper_priv *piperp, enum ieee80211_band band) +{ + int ret = 0; + + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_INIT, op_write); + + /* Initialize baseband general control register for the specific transceiver */ + if (piperp->pdata->rf_transceiver == RF_AIROHA_7230) { + if (band == IEEE80211_BAND_2GHZ) { + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_24GHZ, op_write); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or); + digi_dbg("piper_init_hw Initialized for band B / BG\n"); + } else { + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_50GHZ, op_write); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5150_5350_A_BAND, op_or); + digi_dbg("piper_init_hw Initialized for band A\n"); + } + piperp->ac->wr_reg(piperp, BB_CONF_2, 0x09325ad4, op_write); + /* Initialize the SPI word length */ + piperp->ac->wr_reg(piperp, BB_SPI_CTRL, SPI_INIT_AIROHA, op_write); + } else if (piperp->pdata->rf_transceiver == RF_AIROHA_2236) { + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_24GHZ, op_write); + piperp->ac->wr_reg(piperp, BB_CONF_2, 0x09325ad4, op_write); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or); + /* Initialize the SPI word length */ + piperp->ac->wr_reg(piperp, BB_SPI_CTRL, SPI_INIT_AIROHA2236, op_write); + } else { + printk(KERN_WARNING PIPER_DRIVER_NAME ": undefined rf transceiver!\n"); + return -EINVAL; + } + + /* + *Clear the Intretupt Mask Register before enabling external intretupts. + * Also clear out any status bits in the Intretupt Status Register. + */ + piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write); + piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write); + + /* + * If this firmware supports additional MAC addresses. + */ + if (((piperp->ac->rd_reg(piperp, MAC_STATUS) >> 16) & 0xff) >= 8) { + /* Disable additional addresses to start with */ + piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_MAC_FLTR, op_and); + piperp->ac->wr_reg(piperp, MAC_STA2_ID0, 0, op_write); + piperp->ac->wr_reg(piperp, MAC_STA2_ID1, 0, op_write); + piperp->ac->wr_reg(piperp, MAC_STA3_ID0, 0, op_write); + piperp->ac->wr_reg(piperp, MAC_STA3_ID1, 0, op_write); + } + /* TODO: Set this register programatically */ + piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, 0x0, op_write); + + /* + * Note that antenna diversity will be set by hw_start, which is the + * caller of this function. + */ + + /* reset RX and TX FIFOs */ + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_RXFIFORST + | BB_GENERAL_CTL_TXFIFORST, op_or); + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~(BB_GENERAL_CTL_RXFIFORST + | BB_GENERAL_CTL_TXFIFORST), op_and); + + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xC043002C, op_write); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, ~TRACK_TX_B_GAIN_MASK, op_and); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, get_b_tx_gain(piperp), op_or); + + /* Initialize RF transceiver */ + piperp->rf->init(piperp->hw, band); + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x04000001, op_or); + piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, 0x0, op_write); + piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, ~(BB_GENERAL_STAT_DC_DIS + | BB_GENERAL_STAT_SPRD_DIS), op_and); + piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, ~(BB_GENERAL_STAT_SRC_DIS + | BB_GENERAL_STAT_DLL_DIS), op_and); + + piperp->ac->wr_reg(piperp, MAC_SSID_LEN, (MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK), + op_write); + + /* + * Set BSSID to the broadcast address so that we receive all packets. The stack + * will set a real BSSID when it's ready. + */ + piperp->ac->wr_reg(piperp, MAC_BSS_ID0, 0xffffffff, op_write); + piperp->ac->wr_reg(piperp, MAC_BSS_ID1, 0xffffffff, op_write); + + piperp->ps.poweredDown = false; + +#if WANT_AIROHA_CALIBRATION + digi_dbg("Calling digiWifiInitCalibration()\n"); + digiWifiInitCalibration(piperp); +#endif + + return ret; +} + +static int piper_deinit_hw(struct piper_priv *piperp) +{ + int ret = 0; +#if WANT_AIROHA_CALIBRATION + digi_dbg("Calling digiWifiDeInitCalibration()\n"); + digiWifiDeInitCalibration(piperp); +#endif + + return ret; +} + + +static void adjust_max_agc(struct piper_priv *piperp, unsigned int rssi, _80211HeaderType *header) +{ +#define LOWEST_MAXAGC_AL2236 0x76 +#define HIGHEST_MAXAGC_AL2236 0x7B +#define HIGHEST_MAXAGC_AL7230_24GHZ 0x7c +#define LOWEST_MAXAGC_AL7230_24GHZ 0x76 +#define HIGHEST_MAXAGC_AL7230_50GHZ 0x79 +#define LOWEST_MAXAGC_AL7230_50GHZ 0x73 +#define RSSI_AVG_COUNT 8 + + unsigned char maxgain = 0; + static unsigned char lowest = 0, highest = 0; + static int k=0, j=0, i =0, tempRssi=0; + static unsigned int savedRSSI[RSSI_AVG_COUNT]; /****/ + + savedRSSI[k % RSSI_AVG_COUNT] = rssi; + if ( (piperp->pdata->rf_transceiver == RF_AIROHA_2236) + || (piperp->pdata->rf_transceiver == RF_AIROHA_7230)) { + + if (piperp->pdata->rf_transceiver == RF_AIROHA_2236) + { + lowest = LOWEST_MAXAGC_AL2236; + highest = HIGHEST_MAXAGC_AL2236; + } + else + { + + if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_5GHZ) { + highest = HIGHEST_MAXAGC_AL7230_50GHZ; + lowest = LOWEST_MAXAGC_AL7230_50GHZ; + } + else { + highest = HIGHEST_MAXAGC_AL7230_24GHZ; + lowest = LOWEST_MAXAGC_AL7230_24GHZ; + } + } + + if (piperp->areWeAssociated) + { + + if ( (piperp->if_type == NL80211_IFTYPE_ADHOC) + || (piperp->if_type == NL80211_IFTYPE_MESH_POINT)) + { + //Monitor the receive signal strength from Ad-hoc network + if (memcmp (piperp->bssid, header->addr3, sizeof(piperp->bssid)) == 0) + { + /* we don't do avareging on all the signals here because it may come from different + * unit in that Ad-hoc network. Instead, we do avareging on the signals with higher rssi + */ + + if ((rssi + 4) > lowest) + { + k++; + tempRssi += rssi; + + if (k >= RSSI_AVG_COUNT) + { + maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ; + k = 0; + tempRssi = 0; + i =0; + } + } + else + { + i++; + if (i >= (RSSI_AVG_COUNT*4)) + { + maxgain = lowest; + i = 0; + } + + } + } + } + else + { + //Monitor the receive signal strength from the frames we received from the associated AP + if (memcmp (piperp->bssid, header->addr2, sizeof(piperp->bssid)) == 0) + { + //averaging all the signals because they come from the same AP + k++; + tempRssi += rssi; + + if (k >= RSSI_AVG_COUNT*2) + { + if (((tempRssi/k) + 4) > lowest) + maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ; + else + maxgain = lowest; + + k = 0; + tempRssi = 0; + } + } + } + j = 0; + } + else + { + j++; + if (j >= (RSSI_AVG_COUNT*4)) + { + maxgain = highest; + j = 0; + } + k = 0; + tempRssi = 0; + } + + if( (maxgain != 0) + && (maxgain != ((piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_MAX_GAIN_MASK) >> 16))) + { + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and); + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, (maxgain << 16) & BB_GENERAL_CTL_MAX_GAIN_MASK, op_or); + } + } +} + + +/* Make sure all keys are disabled when we start */ +static void piper_init_keys(struct piper_priv *piperp) +{ + unsigned int i; + + for (i = 0; i < PIPER_MAX_KEYS; i++) + piperp->key[i].valid = false; + + piperp->aes_key_count = 0; +} + +static void tx_timer_timeout(unsigned long arg) +{ + struct piper_priv *piperp = (struct piper_priv *) arg; + + tasklet_hi_schedule(&piperp->tx_tasklet); +} + +/* sysfs entries to get/set antenna mode */ +static ssize_t show_antenna_sel(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct piper_priv *piperp = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", piperp->antenna == ANTENNA_BOTH ? "diversity" : + piperp->antenna == ANTENNA_1 ? "primary" : "secondary"); +} + +static ssize_t store_antenna_sel(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct piper_priv *piperp = dev_get_drvdata(dev); + enum antenna_select ant; + size_t len = count; + int ret; + + ant = piperp->antenna; + + if (buf[count - 1] == '\n') + len--; + + /* TODO check also string length */ + if (!strncmp("diversity", buf, len)) + ant = ANTENNA_BOTH; + else if (!strncmp("primary", buf, len)) + ant = ANTENNA_1; + else if (!strncmp("secondary", buf, len)) + ant = ANTENNA_2; + + if (ant != piperp->antenna) { + if ((ret = piperp->set_antenna(piperp, ant)) != 0) { + printk(KERN_WARNING PIPER_DRIVER_NAME + ": error setting antenna to %d (err: %d)\n", ant, ret); + } else + piperp->antenna = ant; + } + + return count; +} +static DEVICE_ATTR(antenna_sel, S_IWUSR | S_IRUGO, show_antenna_sel, store_antenna_sel); + +static ssize_t show_power_duty(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct piper_priv *piperp = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", piperp->power_duty); +} + +static ssize_t store_power_duty(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#define MINIMUM_DUTY_CYCLE (33) +#define LIMIT_LINEAL_DUTY_CYCLE (75) + struct piper_priv *piperp = dev_get_drvdata(dev); + int pw_duty; + ssize_t ret = -EINVAL; + + ret = sscanf(buf, "%d\n", &pw_duty); + if (ret > 0) { + if (pw_duty < MINIMUM_DUTY_CYCLE) { + piperp->power_duty = MINIMUM_DUTY_CYCLE; + } else if (pw_duty > LIMIT_LINEAL_DUTY_CYCLE && pw_duty < 100) { + piperp->power_duty = LIMIT_LINEAL_DUTY_CYCLE; + } else if (pw_duty == 100 || + (pw_duty >= MINIMUM_DUTY_CYCLE && pw_duty <= LIMIT_LINEAL_DUTY_CYCLE)) { + piperp->power_duty = pw_duty; + } + } + + return ret < 0 ? ret : count; +} +static DEVICE_ATTR(power_duty, S_IWUSR | S_IRUGO, show_power_duty, store_power_duty); + +#if WANT_DEBUG_COMMANDS + +static ssize_t show_debug_cmd(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct piper_priv *piperp = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", piperp->debug_cmd); +} + +static ssize_t store_debug_cmd(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct piper_priv *piperp = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + + if (strlen(buf) < sizeof(piperp->debug_cmd)) + { + if (strstr(buf, "dump") != NULL) { + digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS); + ret = 1; + } else if (strstr(buf, "ps_state") != NULL) { + printk(KERN_ERR "rxTaskletRunning = %d, allowTransmits = %d, stopped_tx_queues = %d\n", + piperp->ps.rxTaskletRunning, piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues); + ret = 1; + } else if (strstr(buf, "rssi_dump") != NULL) { + spinlock_t lock; + unsigned long flags; + unsigned int rssi; + + spin_lock_init(&lock); + spin_lock_irqsave(&piperp->ps.lock, flags); + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and); + udelay(15); + rssi = piperp->ac->rd_reg(piperp, BB_RSSI); + printk(KERN_ERR "\n**rssi = 0x%8.8X\n", rssi); + digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS); + ret = 1; + spin_unlock_irqrestore(&lock, flags); + } else { + strcpy(piperp->debug_cmd, buf); + piperp->debug_cmd[strlen(buf)-1] = 0; /* truncate the \n */ + } + ret = count; + } + + return ret < 0 ? ret : count; +} +static DEVICE_ATTR(debug_cmd, S_IWUSR | S_IRUGO, show_debug_cmd, store_debug_cmd); + +#endif + +#ifdef CONFIG_PM + +static int piper_suspend(struct platform_device *dev, pm_message_t state) +{ + struct piper_priv *piperp = platform_get_drvdata(dev); + unsigned long flags; + + /* TODO, use in future the ps.lock instead of fully disabling interrupts here */ + piperp->power_save_was_on_when_suspended = (piperp->ps.mode == PS_MODE_LOW_POWER); + if (piperp->power_save_was_on_when_suspended) + piper_ps_set(piperp, false); + mdelay(10); + piper_sendNullDataFrame(piperp, true); + ssleep(1); + + local_irq_save(flags); + /* + * Save power save state and then make sure power save is turned off. + */ + piper_MacEnterSleepMode(piperp, true); + local_irq_restore(flags); + + return 0; +} + +static int piper_resume(struct platform_device *dev) +{ + struct piper_priv *piperp = platform_get_drvdata(dev); + unsigned long flags; + + if (piperp->pdata->early_resume) + piperp->pdata->early_resume(piperp); + + /* TODO, use in future the ps.lock instead of fully disabling interrupts here */ + local_irq_save(flags); + piper_MacEnterActiveMode(piperp, true); + if (piperp->tx_tasklet_running) { + tasklet_hi_schedule(&piperp->tx_tasklet); + } else { + ieee80211_wake_queues(piperp->hw); + } + local_irq_restore(flags); + + /* + * Restore power save if it was on before + */ + if (piperp->power_save_was_on_when_suspended) { + piper_ps_set(piperp, true); + } else { + piper_sendNullDataFrame(piperp, false); + } + + return 0; +} +#else +#define piper_suspend NULL +#define piper_resume NULL +#endif + +static int __init piper_probe(struct platform_device* pdev) +{ + struct piper_pdata *pdata = pdev->dev.platform_data; + struct piper_priv *piperp; + int ret = 0; + + if (!pdata) + return -EINVAL; + + ret = piper_alloc_hw(&piperp, sizeof(*piperp)); + if (ret) { + printk(KERN_ERR PIPER_DRIVER_NAME ": failed to alloc piper_priv\n"); + return ret; + } + + piperp->ac = kzalloc(sizeof(struct access_ops), GFP_KERNEL); + if (!piperp->ac){ + printk(KERN_ERR PIPER_DRIVER_NAME ": failed to alloc memory for ac struct\n"); + ret = -ENOMEM; + goto error_alloc; + } + + piperp->drv_name = PIPER_DRIVER_NAME; + dev_set_drvdata(&pdev->dev, piperp); + piperp->pdata = pdata; + pdata->piperp = piperp; + spin_lock_init(&piperp->ac->reg_lock); + spin_lock_init(&piperp->aesLock); + + piperp->vbase = ioremap(pdev->resource[0].start, + pdev[0].resource->end - pdev->resource[0].start); + + if (!piperp->vbase) { + printk(KERN_ERR PIPER_DRIVER_NAME ": ioremap base %x, len %x error\n", + pdev->resource[0].start, pdev[0].resource->end - pdev->resource[0].start); + ret = -ENOMEM; + goto error_remap; + } + + piperp->pstats.tx_start_count = 0; + piperp->pstats.tx_complete_count = 0; + + /* + * Platform initialization. This will initialize the hardware, including the load + * of the mac and dsp firmware into the piper chip + */ + if (pdata->init) { + if ((ret = pdata->init(piperp)) != 0) { + printk(KERN_ERR PIPER_DRIVER_NAME + ": platform init() returned error (%d)\n", ret); + goto error_init; + } + } + + piper_ps_init(piperp); + init_timer(&piperp->tx_timer); + piperp->tx_timer.function = tx_timer_timeout; + piperp->tx_timer.data = (unsigned long) piperp; + piper_init_rx_tx(piperp); + piper_init_keys(piperp); + + piperp->init_hw = piper_init_hw; + piperp->deinit_hw = piper_deinit_hw; + piperp->set_irq_mask_bit = piper_set_irq_mask; + piperp->clear_irq_mask_bit = piper_clear_irq_mask; + piperp->load_beacon = load_beacon; + piperp->rand = local_rand; + piperp->get_next_beacon_backoff = get_next_beacon_backoff; + piperp->set_antenna = piper_set_antenna; + piperp->set_tracking_constant = piper_set_tracking_constant; + piperp->antenna = ANTENNA_1; + piperp->adjust_max_agc = adjust_max_agc; + + /* + * Set the default duty cycle value. Note that duty cycling + * is disabled reguardless of what this variable is set to until + * the user types "iwconfig wlan0 power on". I just love the + * "power on" syntax to turn *down* the power. + */ + piperp->power_duty = 100; + + /* TODO this should be read earlier and actions should be taken + * based on different revisions at driver initialization or runtime */ + piperp->version = piperp->ac->rd_reg(piperp, BB_VERSION); + + piperp->irq = pdev->resource[1].start; + piperp->tx_cts = false; + piperp->beacon.loaded = false; + piperp->beacon.enabled = false; + piperp->beacon.weSentLastOne = false; + + ret = request_irq(piperp->irq, piper_irq_handler, + IRQF_TRIGGER_HIGH, PIPER_DRIVER_NAME, piperp); + if (ret) { + printk(KERN_ERR PIPER_DRIVER_NAME ": unable to request irq %d (%d)", + piperp->irq, ret); + goto retor_irq; + } + + disable_irq(piperp->irq); + + ret = piper_register_hw(piperp, &pdev->dev, &al7230_rf_ops); + if (ret) { + printk(KERN_ERR PIPER_DRIVER_NAME ": failed to register priv\n"); + goto error_reg_hw; + } + + if (pdata->late_init) + pdata->late_init(piperp); + + ret = device_create_file(&pdev->dev, &dev_attr_antenna_sel); + if (ret) { + printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n"); + goto error_sysfs; + } + + ret = device_create_file(&pdev->dev, &dev_attr_power_duty); + if (ret) { + printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n"); + goto error_sysfs; + } + + strcpy(piperp->debug_cmd, "off"); +#if WANT_DEBUG_COMMANDS + ret = device_create_file(&pdev->dev, &dev_attr_debug_cmd); + if (ret) { + printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n"); + goto error_sysfs; + } +#endif + + printk(KERN_INFO PIPER_DRIVER_NAME ": driver loaded (fw ver = 0x%08x)\n", + piperp->version); + + return 0; + +error_sysfs: + piper_unregister_hw(piperp); +error_reg_hw: + piper_ps_deinit(piperp); + piper_free_rx_tx(piperp); +retor_irq: + free_irq(piperp->irq, piperp); +error_init: + iounmap(piperp->vbase); + piperp->vbase = NULL; +error_remap: + release_resource(pdev->resource); +error_alloc: + piper_free_hw(piperp); + return ret; +} + +static int piper_remove(struct platform_device *pdev) +{ + struct piper_priv *piperp = dev_get_drvdata(&pdev->dev); + + printk(KERN_DEBUG PIPER_DRIVER_NAME " %s\n", __func__); + + device_remove_file(&pdev->dev, &dev_attr_antenna_sel); + device_remove_file(&pdev->dev, &dev_attr_power_duty); +#if WANT_DEBUG_COMMANDS + device_remove_file(&pdev->dev, &dev_attr_debug_cmd); +#endif + + piper_ps_deinit(piperp); + piper_unregister_hw(piperp); + disable_irq(piperp->irq); + piper_clear_irq_mask(piperp, 0xffffffff); + free_irq(piperp->irq, piperp); + piper_free_rx_tx(piperp); + release_resource(pdev->resource); + piper_free_hw(piperp); + + return 0; +} + +/* describes the driver */ +static struct platform_driver piper_driver = { + .probe = piper_probe, + .remove = piper_remove, + .suspend = piper_suspend, + .resume = piper_resume, + .driver = { + .name = PIPER_DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init piper_init_module(void) +{ + return platform_driver_register(&piper_driver); +} + +static void __exit piper_exit_module(void) +{ + platform_driver_unregister(&piper_driver); +} + +module_init(piper_init_module); +module_exit(piper_exit_module); + +MODULE_DESCRIPTION("Digi Piper WLAN Driver"); +MODULE_AUTHOR("Contact support@digi.com for questions on this code"); +MODULE_VERSION(DRV_VERS); +MODULE_LICENSE("GPL"); |