text: /*
   * Copyright (c) 2013 The Linux Foundation. All rights reserved.
   *
   * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
   *
   *
   * Permission to use, copy, modify, and/or distribute this software for
   * any purpose with or without fee is hereby granted, provided that the
   * above copyright notice and this permission notice appear in all
   * copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
   * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
   * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
   * PERFORMANCE OF THIS SOFTWARE.
   */
  
  /*
   * This file was originally distributed by Qualcomm Atheros, Inc.
   * under proprietary terms before Copyright ownership was assigned
   * to the Linux Foundation.
   */
  
  #include "if_pci.h"
  
  /*chaninfo*/
  #define  CHANINFOMEM_S2_READ_MASK               0x00000008
  #define  CHANINFO_CTRL_CAPTURE_CHAN_INFO_MASK   0x00000001
  #define  CHANINFO_CTRL_CHANINFOMEM_BW_MASK      0x00000030
  #define  MULTICHAIN_ENABLE_RX_CHAIN_MASK_MASK   0x00000007
  
  /*agc*/
  #define GAINS_MIN_OFFSETS_CF_AGC_HIST_ENABLE_MASK              0x00040000
  #define GAINS_MIN_OFFSETS_CF_AGC_HIST_GC_MASK                  0x00080000
  #define GAINS_MIN_OFFSETS_CF_AGC_HIST_VOTING_MASK              0x00100000
  #define GAINS_MIN_OFFSETS_CF_AGC_HIST_PHY_ERR_MASK             0x00200000
  #define  AGC_HISTORY_DUMP_MASK    GAINS_MIN_OFFSETS_CF_AGC_HIST_ENABLE_MASK| \
                                    GAINS_MIN_OFFSETS_CF_AGC_HIST_GC_MASK| \
                                    GAINS_MIN_OFFSETS_CF_AGC_HIST_VOTING_MASK| \
                                    GAINS_MIN_OFFSETS_CF_AGC_HIST_PHY_ERR_MASK
  
  #define BB_chaninfo_ctrl         0x1a370
  #define BB_multichain_enable     0x1a2a0
  #define BB_chn_tables_intf_addr  0x19894
  #define BB_chn1_tables_intf_addr 0x1a894
  #define BB_chn_tables_intf_data  0x19898
  #define BB_chn1_tables_intf_data 0x1a898
  #define BB_gains_min_offsets     0x19e08
  #define BB_chaninfo_tab_b0       0x03200
  #define BB_chaninfo_tab_b1       0x03300
  #define BB_watchdog_status       0x1a7c0
  #define BB_watchdog_ctrl_1       0x1a7c4
  #define BB_watchdog_ctrl_2       0x1a7c8
  #define BB_watchdog_status_B     0x1a7e0
  
  #define PHY_BB_CHN_TABLES_INTF_ADDR 0x19894
  #define PHY_BB_CHN_TABLES_INTF_DATA 0x19898
  
  #define PHY_BB_CHN1_TABLES_INTF_ADDR 0x1a894
  #define PHY_BB_CHN1_TABLES_INTF_DATA 0x1a898
  
  
  struct priv_ctrl_ctx {
      u_int32_t chaninfo_ctrl_orig;
      u_int32_t gain_min_offsets_orig;
      u_int32_t anyreg_start;
      u_int32_t anyreg_len;
  };
  
  static struct priv_ctrl_ctx g_Priv_Dump_Ctx;
  
  static INLINE void set_target_reg_bits(struct hif_pci_softc* hif, u_int32_t reg, u_int32_t bitmask, u_int32_t val)
  {
      u_int32_t value = A_PCI_READ32(hif->mem + (reg));
      u_int32_t shift = 0;
      value &= ~(bitmask);
      while (!((bitmask >> shift) & 0x01)) {
          shift++;
      }
      value |= (((val)<<shift)&(bitmask));
      A_PCI_WRITE32(hif->mem + (reg), value);
  }
  
  static INLINE u_int32_t get_target_reg_bits(struct hif_pci_softc* hif, u_int32_t reg, u_int32_t bitmask)
  {
      u_int32_t value = A_PCI_READ32(hif->mem + (reg));
      u_int32_t shift = 0;
      while (!((bitmask >> shift) & 0x01)) {
          shift++;
      }
      return (value>>shift)&bitmask;
  }
  
  void priv_start_cap_chaninfo(struct hif_pci_softc* hif_sc)
  {
      set_target_reg_bits(hif_sc, BB_chaninfo_ctrl, CHANINFO_CTRL_CAPTURE_CHAN_INFO_MASK, 1);
  }
  
  void priv_start_agc(struct hif_pci_softc* hif_sc)
  {
      g_Priv_Dump_Ctx.gain_min_offsets_orig = A_PCI_READ32(hif_sc->mem + BB_gains_min_offsets);
      set_target_reg_bits(hif_sc, BB_gains_min_offsets, AGC_HISTORY_DUMP_MASK, 0x0f);
  }
  
  void priv_stop_agc(struct hif_pci_softc* hif_sc)
  {
      set_target_reg_bits(hif_sc, BB_gains_min_offsets, AGC_HISTORY_DUMP_MASK, 0);
  }
  
  void priv_dump_chaninfo(struct hif_pci_softc* hif_sc)
  {
      u_int32_t bw, val;
      u_int32_t len, i, tmp;
      u_int32_t chain_mask;
      u_int32_t chain0, chain1;
  
      chain_mask = get_target_reg_bits(hif_sc, BB_multichain_enable, MULTICHAIN_ENABLE_RX_CHAIN_MASK_MASK);
      chain0 = chain_mask & 1;
      chain1 = chain_mask & 2;
  
      printk("\nChannel info dump:\n");
      bw = get_target_reg_bits(hif_sc, BB_chaninfo_ctrl, CHANINFO_CTRL_CHANINFOMEM_BW_MASK);
  
      if (bw == 0) {
          len = 53;
      } else if (bw == 1) {
          len = 57;
      } else if (bw == 2) {
          len = 59*2-1;
      } else {
          len = 60*2+61*2;
      }
  
      /*
       * each tone is 16 bit valid, write to 32bit buffer each.
       * bw==0(legacy20): 53 tones.
       * bw==1(ht/vht20): 57 tones.
       * bw==2(ht/vht40): 59+58 tones.
       * bw==3(vht80): 60*2+61*2 tones.
       */
  
      if (chain0) {
          A_PCI_WRITE32(hif_sc->mem+BB_chn_tables_intf_addr, 0x80003200);
      }
      if (chain1) {
          A_PCI_WRITE32(hif_sc->mem+BB_chn1_tables_intf_addr, 0x80003200);
      }
  
      set_target_reg_bits(hif_sc, BB_chaninfo_ctrl, CHANINFOMEM_S2_READ_MASK, 0);
  
      if (chain0) {
          if (bw < 2) {
              len = (bw == 0) ? 53 : 57;
              for (i=0; i<len; i++) {
                  val = A_PCI_READ32(hif_sc->mem+BB_chn_tables_intf_data) & 0x0000ffff;
                  printk("0x%x\t",val);
                  if (i%4 == 0)
                      printk("\n");
              }
          } else {
              len = (bw == 2) ? 59 : 60;
              for (i=0; i<len; i++) {
                  tmp = A_PCI_READ32(hif_sc->mem+BB_chn_tables_intf_data);
                  printk("0x%x\t", ((tmp>>16) & 0x0000ffff));
                  printk("0x%x\t", (tmp & 0x0000ffff));
                  if (i%2 == 0)
                      printk("\n");
              }
              if (bw > 2) {
                  //bw == 3 for vht80
                  A_PCI_WRITE32(hif_sc->mem+BB_chn_tables_intf_addr, 0x80003300);
                  len = 61;
                  for (i=0; i<len; i++) {
                      tmp = A_PCI_READ32(hif_sc->mem+BB_chn_tables_intf_data);
                      printk("0x%x\t", ((tmp>>16) & 0x0000ffff));
                      printk("0x%x\t", (tmp & 0x0000ffff));
                      if (i%2 == 0)
                          printk("\n");
                  }
              }
          }
      }
      if (chain1) {
          if (bw < 2) {
              len = (bw == 0) ? 53 : 57;
              for (i=0; i<len; i++) {
                  val = A_PCI_READ32(hif_sc->mem+BB_chn1_tables_intf_data) & 0x0000ffff;
                  printk("0x%x\t",val);
                  if (i%4 == 0)
                      printk("\n");
              }
          } else {
              len = (bw == 2) ? 59 : 60;
              for (i=0; i<len; i++) {
                  tmp = A_PCI_READ32(hif_sc->mem+BB_chn1_tables_intf_data);
                  printk("0x%x\n", (tmp>>16) & 0x0000ffff);
                  printk("0x%x\n", tmp & 0x0000ffff);
                  if (i%2 == 0)
                      printk("\n");
              }
              if (bw > 2) {
                  //bw == 3 for vht80
                  A_PCI_WRITE32(hif_sc->mem+BB_chn1_tables_intf_addr, 0x80003300);
                  len = 61;
                  for (i=0; i<len; i++) {
                      tmp = A_PCI_READ32(hif_sc->mem+BB_chn1_tables_intf_data);
                      printk("0x%x\t", ((tmp>>16) & 0x0000ffff));
                      printk("0x%x\t", (tmp & 0x0000ffff));
                      if (i%2 == 0)
                          printk("\n");
                  }
              }
          }
      }
      printk("\nChannel info dump complete\n");
  }
  
  void priv_dump_agc(struct hif_pci_softc* hif_sc)
  {
      int i, len = 30;  //check this value for Rome and Peregrine
      u_int32_t chain0, chain1, chain_mask, val;
      A_target_id_t targid = TARGID(hif_sc);
  
      A_TARGET_ACCESS_BEGIN(targid);
  
      chain_mask = get_target_reg_bits(hif_sc, BB_multichain_enable, MULTICHAIN_ENABLE_RX_CHAIN_MASK_MASK);
      chain0 = chain_mask & 1;
      chain1 = chain_mask & 2;
  
      len = len<<1;  //each agc item is 64bit, total*2
      priv_stop_agc(hif_sc);
  
      set_target_reg_bits(hif_sc, BB_chaninfo_ctrl, CHANINFOMEM_S2_READ_MASK, 0);
  
      printk("AGC history buffer dump:\n");
      if (chain0) {
          for (i = 0; i < len; i++) {
              A_PCI_WRITE32(hif_sc->mem + PHY_BB_CHN_TABLES_INTF_ADDR,
                    BB_chaninfo_tab_b0 + i*4);
              val = A_PCI_READ32(hif_sc->mem + PHY_BB_CHN_TABLES_INTF_DATA);
              printk("0x%x\t", val);
              if (i%4 == 0)
                  printk("\n");
          }
      }
      if (chain1) {
          for (i = 0; i < len; i++) {
              A_PCI_WRITE32(hif_sc->mem + PHY_BB_CHN1_TABLES_INTF_ADDR,
                    BB_chaninfo_tab_b0 + i*4);
              val = A_PCI_READ32(hif_sc->mem + PHY_BB_CHN1_TABLES_INTF_DATA);
              printk("0x%x\t", val);
              if (i%4 == 0)
                  printk("\n");
          }
      }
      printk("\nAGC history buffer dump complete\n");
      //restore original value
      A_PCI_WRITE32(hif_sc->mem + BB_gains_min_offsets, g_Priv_Dump_Ctx.gain_min_offsets_orig);
  
      A_TARGET_ACCESS_END(targid);
  
      return;
  }
  
  void priv_dump_bbwatchdog(struct hif_pci_softc* hif_sc)
  {
      u_int32_t val;
  
      printk("BB watchdog dump\n");
      val = A_PCI_READ32(hif_sc->mem + BB_watchdog_status);
      printk("0x%x\t",val);
      val = A_PCI_READ32(hif_sc->mem + BB_watchdog_ctrl_1);
      printk("0x%x\t",val);
      val = A_PCI_READ32(hif_sc->mem + BB_watchdog_ctrl_2);
      printk("0x%x\t",val);
      val = A_PCI_READ32(hif_sc->mem + BB_watchdog_status_B);
      printk("0x%x\n",val);
  }
  