18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Atheros CARL9170 driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Basic HW register/memory/command access functions
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
98c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
108c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
118c2ecf20Sopenharmony_ci * (at your option) any later version.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful,
148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
168c2ecf20Sopenharmony_ci * GNU General Public License for more details.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
198c2ecf20Sopenharmony_ci * along with this program; see the file COPYING.  If not, see
208c2ecf20Sopenharmony_ci * http://www.gnu.org/licenses/.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * This file incorporates work covered by the following copyright and
238c2ecf20Sopenharmony_ci * permission notice:
248c2ecf20Sopenharmony_ci *    Copyright (c) 2007-2008 Atheros Communications, Inc.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci *    Permission to use, copy, modify, and/or distribute this software for any
278c2ecf20Sopenharmony_ci *    purpose with or without fee is hereby granted, provided that the above
288c2ecf20Sopenharmony_ci *    copyright notice and this permission notice appear in all copies.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci *    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
318c2ecf20Sopenharmony_ci *    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
328c2ecf20Sopenharmony_ci *    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
338c2ecf20Sopenharmony_ci *    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
348c2ecf20Sopenharmony_ci *    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
358c2ecf20Sopenharmony_ci *    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
368c2ecf20Sopenharmony_ci *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include <asm/div64.h>
408c2ecf20Sopenharmony_ci#include "carl9170.h"
418c2ecf20Sopenharmony_ci#include "cmd.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ciint carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	const __le32 buf[2] = {
468c2ecf20Sopenharmony_ci		cpu_to_le32(reg),
478c2ecf20Sopenharmony_ci		cpu_to_le32(val),
488c2ecf20Sopenharmony_ci	};
498c2ecf20Sopenharmony_ci	int err;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	err = carl9170_exec_cmd(ar, CARL9170_CMD_WREG, sizeof(buf),
528c2ecf20Sopenharmony_ci				(u8 *) buf, 0, NULL);
538c2ecf20Sopenharmony_ci	if (err) {
548c2ecf20Sopenharmony_ci		if (net_ratelimit()) {
558c2ecf20Sopenharmony_ci			wiphy_err(ar->hw->wiphy, "writing reg %#x "
568c2ecf20Sopenharmony_ci				"(val %#x) failed (%d)\n", reg, val, err);
578c2ecf20Sopenharmony_ci		}
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci	return err;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciint carl9170_read_mreg(struct ar9170 *ar, const int nregs,
638c2ecf20Sopenharmony_ci		       const u32 *regs, u32 *out)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	int i, err;
668c2ecf20Sopenharmony_ci	__le32 *offs, *res;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* abuse "out" for the register offsets, must be same length */
698c2ecf20Sopenharmony_ci	offs = (__le32 *)out;
708c2ecf20Sopenharmony_ci	for (i = 0; i < nregs; i++)
718c2ecf20Sopenharmony_ci		offs[i] = cpu_to_le32(regs[i]);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* also use the same buffer for the input */
748c2ecf20Sopenharmony_ci	res = (__le32 *)out;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG,
778c2ecf20Sopenharmony_ci				4 * nregs, (u8 *)offs,
788c2ecf20Sopenharmony_ci				4 * nregs, (u8 *)res);
798c2ecf20Sopenharmony_ci	if (err) {
808c2ecf20Sopenharmony_ci		if (net_ratelimit()) {
818c2ecf20Sopenharmony_ci			wiphy_err(ar->hw->wiphy, "reading regs failed (%d)\n",
828c2ecf20Sopenharmony_ci				  err);
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci		return err;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* convert result to cpu endian */
888c2ecf20Sopenharmony_ci	for (i = 0; i < nregs; i++)
898c2ecf20Sopenharmony_ci		out[i] = le32_to_cpu(res[i]);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ciint carl9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	return carl9170_read_mreg(ar, 1, &reg, val);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciint carl9170_echo_test(struct ar9170 *ar, const u32 v)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	u32 echores;
1028c2ecf20Sopenharmony_ci	int err;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	err = carl9170_exec_cmd(ar, CARL9170_CMD_ECHO,
1058c2ecf20Sopenharmony_ci				4, (u8 *)&v,
1068c2ecf20Sopenharmony_ci				4, (u8 *)&echores);
1078c2ecf20Sopenharmony_ci	if (err)
1088c2ecf20Sopenharmony_ci		return err;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (v != echores) {
1118c2ecf20Sopenharmony_ci		wiphy_info(ar->hw->wiphy, "wrong echo %x != %x", v, echores);
1128c2ecf20Sopenharmony_ci		return -EINVAL;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar,
1198c2ecf20Sopenharmony_ci	const enum carl9170_cmd_oids cmd, const unsigned int len)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct carl9170_cmd *tmp;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	tmp = kzalloc(sizeof(struct carl9170_cmd_head) + len, GFP_ATOMIC);
1248c2ecf20Sopenharmony_ci	if (tmp) {
1258c2ecf20Sopenharmony_ci		tmp->hdr.cmd = cmd;
1268c2ecf20Sopenharmony_ci		tmp->hdr.len = len;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return tmp;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ciint carl9170_reboot(struct ar9170 *ar)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct carl9170_cmd *cmd;
1358c2ecf20Sopenharmony_ci	int err;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	cmd = carl9170_cmd_buf(ar, CARL9170_CMD_REBOOT_ASYNC, 0);
1388c2ecf20Sopenharmony_ci	if (!cmd)
1398c2ecf20Sopenharmony_ci		return -ENOMEM;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	err = __carl9170_exec_cmd(ar, cmd, true);
1428c2ecf20Sopenharmony_ci	return err;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciint carl9170_mac_reset(struct ar9170 *ar)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	return carl9170_exec_cmd(ar, CARL9170_CMD_SWRST,
1488c2ecf20Sopenharmony_ci				 0, NULL, 0, NULL);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ciint carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
1528c2ecf20Sopenharmony_ci		       const u32 mode, const u32 addr, const u32 len)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct carl9170_cmd *cmd;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	cmd = carl9170_cmd_buf(ar, CARL9170_CMD_BCN_CTRL_ASYNC,
1578c2ecf20Sopenharmony_ci			       sizeof(struct carl9170_bcn_ctrl_cmd));
1588c2ecf20Sopenharmony_ci	if (!cmd)
1598c2ecf20Sopenharmony_ci		return -ENOMEM;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	cmd->bcn_ctrl.vif_id = cpu_to_le32(vif_id);
1628c2ecf20Sopenharmony_ci	cmd->bcn_ctrl.mode = cpu_to_le32(mode);
1638c2ecf20Sopenharmony_ci	cmd->bcn_ctrl.bcn_addr = cpu_to_le32(addr);
1648c2ecf20Sopenharmony_ci	cmd->bcn_ctrl.bcn_len = cpu_to_le32(len);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return __carl9170_exec_cmd(ar, cmd, true);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ciint carl9170_collect_tally(struct ar9170 *ar)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct carl9170_tally_rsp tally;
1728c2ecf20Sopenharmony_ci	struct survey_info *info;
1738c2ecf20Sopenharmony_ci	unsigned int tick;
1748c2ecf20Sopenharmony_ci	int err;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	err = carl9170_exec_cmd(ar, CARL9170_CMD_TALLY, 0, NULL,
1778c2ecf20Sopenharmony_ci				sizeof(tally), (u8 *)&tally);
1788c2ecf20Sopenharmony_ci	if (err)
1798c2ecf20Sopenharmony_ci		return err;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	tick = le32_to_cpu(tally.tick);
1828c2ecf20Sopenharmony_ci	if (tick) {
1838c2ecf20Sopenharmony_ci		ar->tally.active += le32_to_cpu(tally.active) / tick;
1848c2ecf20Sopenharmony_ci		ar->tally.cca += le32_to_cpu(tally.cca) / tick;
1858c2ecf20Sopenharmony_ci		ar->tally.tx_time += le32_to_cpu(tally.tx_time) / tick;
1868c2ecf20Sopenharmony_ci		ar->tally.rx_total += le32_to_cpu(tally.rx_total);
1878c2ecf20Sopenharmony_ci		ar->tally.rx_overrun += le32_to_cpu(tally.rx_overrun);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		if (ar->channel) {
1908c2ecf20Sopenharmony_ci			info = &ar->survey[ar->channel->hw_value];
1918c2ecf20Sopenharmony_ci			info->time = ar->tally.active;
1928c2ecf20Sopenharmony_ci			info->time_busy = ar->tally.cca;
1938c2ecf20Sopenharmony_ci			info->time_tx = ar->tally.tx_time;
1948c2ecf20Sopenharmony_ci			do_div(info->time, 1000);
1958c2ecf20Sopenharmony_ci			do_div(info->time_busy, 1000);
1968c2ecf20Sopenharmony_ci			do_div(info->time_tx, 1000);
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ciint carl9170_powersave(struct ar9170 *ar, const bool ps)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct carl9170_cmd *cmd;
2058c2ecf20Sopenharmony_ci	u32 state;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	cmd = carl9170_cmd_buf(ar, CARL9170_CMD_PSM_ASYNC,
2088c2ecf20Sopenharmony_ci			       sizeof(struct carl9170_psm));
2098c2ecf20Sopenharmony_ci	if (!cmd)
2108c2ecf20Sopenharmony_ci		return -ENOMEM;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (ps) {
2138c2ecf20Sopenharmony_ci		/* Sleep until next TBTT */
2148c2ecf20Sopenharmony_ci		state = CARL9170_PSM_SLEEP | 1;
2158c2ecf20Sopenharmony_ci	} else {
2168c2ecf20Sopenharmony_ci		/* wake up immediately */
2178c2ecf20Sopenharmony_ci		state = 1;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	cmd->psm.state = cpu_to_le32(state);
2218c2ecf20Sopenharmony_ci	return __carl9170_exec_cmd(ar, cmd, true);
2228c2ecf20Sopenharmony_ci}
223