/*
 * Copyright (c) 2013-2014, 2016-2017 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 "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(void __iomem *mem, uint32_t reg,
				       uint32_t bitmask, uint32_t val)
{
	uint32_t value = hif_read32_mb(mem + (reg));
	uint32_t shift = 0;

	value &= ~(bitmask);
	while (!((bitmask >> shift) & 0x01))
		shift++;

	value |= (((val) << shift) & (bitmask));
	hif_write32_mb(mem + (reg), value);
}

static inline uint32_t get_target_reg_bits(void __iomem *mem,
					   uint32_t reg, uint32_t bitmask)
{
	uint32_t value = hif_read32_mb(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->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->mem + BB_gains_min_offsets);
	set_target_reg_bits(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->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->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->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->mem + BB_chn_tables_intf_addr,
			      0x80003200);
	}
	if (chain1) {
		hif_write32_mb(scn->mem + BB_chn1_tables_intf_addr,
			      0x80003200);
	}

	set_target_reg_bits(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->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->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->mem +
					      BB_chn_tables_intf_addr,
					      0x80003300);
				len = 61;
				for (i = 0; i < len; i++) {
					tmp =
						hif_read32_mb(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->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->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->mem +
					      BB_chn1_tables_intf_addr,
					      0x80003300);
				len = 61;
				for (i = 0; i < len; i++) {
					tmp =
						hif_read32_mb(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->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->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->mem +
				PHY_BB_CHN_TABLES_INTF_ADDR,
				BB_chaninfo_tab_b0 + i * 4);
			val = hif_read32_mb(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->mem +
				PHY_BB_CHN1_TABLES_INTF_ADDR,
				BB_chaninfo_tab_b0 + i * 4);
			val = hif_read32_mb(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->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->mem + BB_watchdog_status);
	qdf_debug("0x%x\t", val);
	val = hif_read32_mb(scn->mem + BB_watchdog_ctrl_1);
	qdf_debug("0x%x\t", val);
	val = hif_read32_mb(scn->mem + BB_watchdog_ctrl_2);
	qdf_debug("0x%x\t", val);
	val = hif_read32_mb(scn->mem + BB_watchdog_status_B);
	qdf_debug("0x%x", val);
	HIF_TRACE("%s: BB watchdog dump X", __func__);
}
