/* * Copyright (c) 2013-2014, 2016-2018 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "hif_io32.h" #include "hif_debug.h" #include "mp_dev.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 { uint32_t chaninfo_ctrl_orig; uint32_t gain_min_offsets_orig; uint32_t anyreg_start; uint32_t anyreg_len; }; static struct priv_ctrl_ctx g_priv_dump_ctx; static inline void set_target_reg_bits(struct hif_softc *scn, void __iomem *mem, uint32_t reg, uint32_t bitmask, uint32_t val) { uint32_t value = hif_read32_mb(scn, mem + (reg)); uint32_t shift = 0; value &= ~(bitmask); while (!((bitmask >> shift) & 0x01)) shift++; value |= (((val) << shift) & (bitmask)); hif_write32_mb(scn, mem + (reg), value); } static inline uint32_t get_target_reg_bits(struct hif_softc *scn, void __iomem *mem, uint32_t reg, uint32_t bitmask) { uint32_t value = hif_read32_mb(scn, mem + (reg)); uint32_t shift = 0; while (!((bitmask >> shift) & 0x01)) shift++; return (value >> shift) & bitmask; } void priv_start_cap_chaninfo(struct hif_softc *scn) { set_target_reg_bits(scn, scn->mem, BB_chaninfo_ctrl, CHANINFO_CTRL_CAPTURE_CHAN_INFO_MASK, 1); } void priv_start_agc(struct hif_softc *scn) { g_priv_dump_ctx.gain_min_offsets_orig = hif_read32_mb(scn, scn->mem + BB_gains_min_offsets); set_target_reg_bits(scn, scn->mem, BB_gains_min_offsets, AGC_HISTORY_DUMP_MASK, 0x0f); } static void priv_stop_agc(struct hif_softc *scn) { set_target_reg_bits(scn, scn->mem, BB_gains_min_offsets, AGC_HISTORY_DUMP_MASK, 0); } void priv_dump_chaninfo(struct hif_softc *scn) { uint32_t bw, val; uint32_t len, i, tmp; uint32_t chain_mask; uint32_t chain0, chain1; chain_mask = get_target_reg_bits(scn, scn->mem, BB_multichain_enable, MULTICHAIN_ENABLE_RX_CHAIN_MASK_MASK); chain0 = chain_mask & 1; chain1 = chain_mask & 2; HIF_TRACE("%s: E", __func__); bw = get_target_reg_bits(scn, scn->mem, 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) { hif_write32_mb(scn, scn->mem + BB_chn_tables_intf_addr, 0x80003200); } if (chain1) { hif_write32_mb(scn, scn->mem + BB_chn1_tables_intf_addr, 0x80003200); } set_target_reg_bits(scn, scn->mem, 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 = hif_read32_mb(scn, scn->mem + BB_chn_tables_intf_data) & 0x0000ffff; qdf_debug("0x%x\t", val); if (i % 4 == 0) qdf_debug("\n"); } } else { len = (bw == 2) ? 59 : 60; for (i = 0; i < len; i++) { tmp = hif_read32_mb(scn, scn->mem + BB_chn_tables_intf_data); qdf_debug("0x%x\t", ((tmp >> 16) & 0x0000ffff)); qdf_debug("0x%x\t", (tmp & 0x0000ffff)); if (i % 2 == 0) qdf_debug("\n"); } if (bw > 2) { /* bw == 3 for vht80 */ hif_write32_mb(scn, scn->mem + BB_chn_tables_intf_addr, 0x80003300); len = 61; for (i = 0; i < len; i++) { tmp = hif_read32_mb(scn, scn->mem + BB_chn_tables_intf_data); qdf_debug("0x%x\t", ((tmp >> 16) & 0x0000ffff)); qdf_debug("0x%x\t", (tmp & 0x0000ffff)); if (i % 2 == 0) qdf_debug("\n"); } } } } if (chain1) { if (bw < 2) { len = (bw == 0) ? 53 : 57; for (i = 0; i < len; i++) { val = hif_read32_mb(scn, scn->mem + BB_chn1_tables_intf_data) & 0x0000ffff; qdf_debug("0x%x\t", val); if (i % 4 == 0) qdf_debug("\n"); } } else { len = (bw == 2) ? 59 : 60; for (i = 0; i < len; i++) { tmp = hif_read32_mb(scn, scn->mem + BB_chn1_tables_intf_data); qdf_debug("0x%x\n", (tmp >> 16) & 0x0000ffff); qdf_debug("0x%x\n", tmp & 0x0000ffff); if (i % 2 == 0) qdf_debug("\n"); } if (bw > 2) { /* bw == 3 for vht80 */ hif_write32_mb(scn, scn->mem + BB_chn1_tables_intf_addr, 0x80003300); len = 61; for (i = 0; i < len; i++) { tmp = hif_read32_mb(scn, scn->mem + BB_chn1_tables_intf_data); qdf_debug("0x%x\t", ((tmp >> 16) & 0x0000ffff)); qdf_debug("0x%x\t", (tmp & 0x0000ffff)); if (i % 2 == 0) qdf_debug("\n"); } } } } HIF_TRACE("%s: X", __func__); } void priv_dump_agc(struct hif_softc *scn) { int i, len = 30; /* check this value for Rome and Peregrine */ uint32_t chain0, chain1, chain_mask, val; if (Q_TARGET_ACCESS_BEGIN(scn) < 0) return; chain_mask = get_target_reg_bits(scn, scn->mem, 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(scn); set_target_reg_bits(scn, scn->mem, BB_chaninfo_ctrl, CHANINFOMEM_S2_READ_MASK, 0); HIF_TRACE("%s: AGC history buffer dump: E", __func__); if (chain0) { for (i = 0; i < len; i++) { hif_write32_mb(scn, scn->mem + PHY_BB_CHN_TABLES_INTF_ADDR, BB_chaninfo_tab_b0 + i * 4); val = hif_read32_mb(scn, scn->mem + PHY_BB_CHN_TABLES_INTF_DATA); qdf_debug("0x%x\t", val); if (i % 4 == 0) qdf_debug("\n"); } } if (chain1) { for (i = 0; i < len; i++) { hif_write32_mb(scn, scn->mem + PHY_BB_CHN1_TABLES_INTF_ADDR, BB_chaninfo_tab_b0 + i * 4); val = hif_read32_mb(scn, scn->mem + PHY_BB_CHN1_TABLES_INTF_DATA); qdf_debug("0x%x\t", val); if (i % 4 == 0) qdf_debug("\n"); } } HIF_TRACE("%s: AGC history buffer dump X", __func__); /* restore original value */ hif_write32_mb(scn, scn->mem + BB_gains_min_offsets, g_priv_dump_ctx.gain_min_offsets_orig); Q_TARGET_ACCESS_END(scn); } void priv_dump_bbwatchdog(struct hif_softc *scn) { uint32_t val; HIF_TRACE("%s: BB watchdog dump E", __func__); val = hif_read32_mb(scn, scn->mem + BB_watchdog_status); qdf_debug("0x%x\t", val); val = hif_read32_mb(scn, scn->mem + BB_watchdog_ctrl_1); qdf_debug("0x%x\t", val); val = hif_read32_mb(scn, scn->mem + BB_watchdog_ctrl_2); qdf_debug("0x%x\t", val); val = hif_read32_mb(scn, scn->mem + BB_watchdog_status_B); qdf_debug("0x%x", val); HIF_TRACE("%s: BB watchdog dump X", __func__); }