brintos

brintos / linux-shallow public Read only

0
0
Text · 10.3 KiB · 0a93ac7 Raw
439 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2005-2014, 2020-2021 Intel Corporation4 * Copyright (C) 2016 Intel Deutschland GmbH5 */6#include <linux/slab.h>7#include <linux/string.h>8#include <linux/export.h>9 10#include "iwl-drv.h"11#include "iwl-phy-db.h"12#include "iwl-debug.h"13#include "iwl-op-mode.h"14#include "iwl-trans.h"15 16struct iwl_phy_db_entry {17	u16	size;18	u8	*data;19};20 21/**22 * struct iwl_phy_db - stores phy configuration and calibration data.23 *24 * @cfg: phy configuration.25 * @calib_nch: non channel specific calibration data.26 * @n_group_papd: number of entries in papd channel group.27 * @calib_ch_group_papd: calibration data related to papd channel group.28 * @n_group_txp: number of entries in tx power channel group.29 * @calib_ch_group_txp: calibration data related to tx power chanel group.30 * @trans: transport layer31 */32struct iwl_phy_db {33	struct iwl_phy_db_entry	cfg;34	struct iwl_phy_db_entry	calib_nch;35	int n_group_papd;36	struct iwl_phy_db_entry	*calib_ch_group_papd;37	int n_group_txp;38	struct iwl_phy_db_entry	*calib_ch_group_txp;39 40	struct iwl_trans *trans;41};42 43enum iwl_phy_db_section_type {44	IWL_PHY_DB_CFG = 1,45	IWL_PHY_DB_CALIB_NCH,46	IWL_PHY_DB_UNUSED,47	IWL_PHY_DB_CALIB_CHG_PAPD,48	IWL_PHY_DB_CALIB_CHG_TXP,49	IWL_PHY_DB_MAX50};51 52#define PHY_DB_CMD 0x6c53 54/* for parsing of tx power channel group data that comes from the firmware*/55struct iwl_phy_db_chg_txp {56	__le32 space;57	__le16 max_channel_idx;58} __packed;59 60struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)61{62	struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),63					    GFP_KERNEL);64 65	if (!phy_db)66		return phy_db;67 68	phy_db->trans = trans;69 70	phy_db->n_group_txp = -1;71	phy_db->n_group_papd = -1;72 73	/* TODO: add default values of the phy db. */74	return phy_db;75}76IWL_EXPORT_SYMBOL(iwl_phy_db_init);77 78/*79 * get phy db section: returns a pointer to a phy db section specified by80 * type and channel group id.81 */82static struct iwl_phy_db_entry *83iwl_phy_db_get_section(struct iwl_phy_db *phy_db,84		       enum iwl_phy_db_section_type type,85		       u16 chg_id)86{87	if (!phy_db || type >= IWL_PHY_DB_MAX)88		return NULL;89 90	switch (type) {91	case IWL_PHY_DB_CFG:92		return &phy_db->cfg;93	case IWL_PHY_DB_CALIB_NCH:94		return &phy_db->calib_nch;95	case IWL_PHY_DB_CALIB_CHG_PAPD:96		if (chg_id >= phy_db->n_group_papd)97			return NULL;98		return &phy_db->calib_ch_group_papd[chg_id];99	case IWL_PHY_DB_CALIB_CHG_TXP:100		if (chg_id >= phy_db->n_group_txp)101			return NULL;102		return &phy_db->calib_ch_group_txp[chg_id];103	default:104		return NULL;105	}106	return NULL;107}108 109static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,110				    enum iwl_phy_db_section_type type,111				    u16 chg_id)112{113	struct iwl_phy_db_entry *entry =114				iwl_phy_db_get_section(phy_db, type, chg_id);115	if (!entry)116		return;117 118	kfree(entry->data);119	entry->data = NULL;120	entry->size = 0;121}122 123void iwl_phy_db_free(struct iwl_phy_db *phy_db)124{125	int i;126 127	if (!phy_db)128		return;129 130	iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);131	iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);132 133	for (i = 0; i < phy_db->n_group_papd; i++)134		iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);135	kfree(phy_db->calib_ch_group_papd);136 137	for (i = 0; i < phy_db->n_group_txp; i++)138		iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);139	kfree(phy_db->calib_ch_group_txp);140 141	kfree(phy_db);142}143IWL_EXPORT_SYMBOL(iwl_phy_db_free);144 145int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,146			   struct iwl_rx_packet *pkt)147{148	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);149	struct iwl_calib_res_notif_phy_db *phy_db_notif =150			(struct iwl_calib_res_notif_phy_db *)pkt->data;151	enum iwl_phy_db_section_type type;152	u16 size;153	struct iwl_phy_db_entry *entry;154	u16 chg_id = 0;155 156	if (pkt_len < sizeof(*phy_db_notif))157		return -EINVAL;158 159	type = le16_to_cpu(phy_db_notif->type);160	size = le16_to_cpu(phy_db_notif->length);161 162	if (pkt_len < sizeof(*phy_db_notif) + size)163		return -EINVAL;164 165	if (!phy_db)166		return -EINVAL;167 168	if (type == IWL_PHY_DB_CALIB_CHG_PAPD) {169		chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);170		if (phy_db && !phy_db->calib_ch_group_papd) {171			/*172			 * Firmware sends the largest index first, so we can use173			 * it to know how much we should allocate.174			 */175			phy_db->calib_ch_group_papd = kcalloc(chg_id + 1,176							      sizeof(struct iwl_phy_db_entry),177							      GFP_ATOMIC);178			if (!phy_db->calib_ch_group_papd)179				return -ENOMEM;180			phy_db->n_group_papd = chg_id + 1;181		}182	} else if (type == IWL_PHY_DB_CALIB_CHG_TXP) {183		chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);184		if (phy_db && !phy_db->calib_ch_group_txp) {185			/*186			 * Firmware sends the largest index first, so we can use187			 * it to know how much we should allocate.188			 */189			phy_db->calib_ch_group_txp = kcalloc(chg_id + 1,190							     sizeof(struct iwl_phy_db_entry),191							     GFP_ATOMIC);192			if (!phy_db->calib_ch_group_txp)193				return -ENOMEM;194			phy_db->n_group_txp = chg_id + 1;195		}196	}197 198	entry = iwl_phy_db_get_section(phy_db, type, chg_id);199	if (!entry)200		return -EINVAL;201 202	kfree(entry->data);203	entry->data = kmemdup(phy_db_notif->data, size, GFP_ATOMIC);204	if (!entry->data) {205		entry->size = 0;206		return -ENOMEM;207	}208 209	entry->size = size;210 211	IWL_DEBUG_INFO(phy_db->trans,212		       "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",213		       __func__, __LINE__, type, size);214 215	return 0;216}217IWL_EXPORT_SYMBOL(iwl_phy_db_set_section);218 219static int is_valid_channel(u16 ch_id)220{221	if (ch_id <= 14 ||222	    (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||223	    (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||224	    (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))225		return 1;226	return 0;227}228 229static u8 ch_id_to_ch_index(u16 ch_id)230{231	if (WARN_ON(!is_valid_channel(ch_id)))232		return 0xff;233 234	if (ch_id <= 14)235		return ch_id - 1;236	if (ch_id <= 64)237		return (ch_id + 20) / 4;238	if (ch_id <= 140)239		return (ch_id - 12) / 4;240	return (ch_id - 13) / 4;241}242 243 244static u16 channel_id_to_papd(u16 ch_id)245{246	if (WARN_ON(!is_valid_channel(ch_id)))247		return 0xff;248 249	if (1 <= ch_id && ch_id <= 14)250		return 0;251	if (36 <= ch_id && ch_id <= 64)252		return 1;253	if (100 <= ch_id && ch_id <= 140)254		return 2;255	return 3;256}257 258static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)259{260	struct iwl_phy_db_chg_txp *txp_chg;261	int i;262	u8 ch_index = ch_id_to_ch_index(ch_id);263	if (ch_index == 0xff)264		return 0xff;265 266	for (i = 0; i < phy_db->n_group_txp; i++) {267		txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;268		if (!txp_chg)269			return 0xff;270		/*271		 * Looking for the first channel group that its max channel is272		 * higher then wanted channel.273		 */274		if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)275			return i;276	}277	return 0xff;278}279static280int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,281				u32 type, u8 **data, u16 *size, u16 ch_id)282{283	struct iwl_phy_db_entry *entry;284	u16 ch_group_id = 0;285 286	if (!phy_db)287		return -EINVAL;288 289	/* find wanted channel group */290	if (type == IWL_PHY_DB_CALIB_CHG_PAPD)291		ch_group_id = channel_id_to_papd(ch_id);292	else if (type == IWL_PHY_DB_CALIB_CHG_TXP)293		ch_group_id = channel_id_to_txp(phy_db, ch_id);294 295	entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);296	if (!entry)297		return -EINVAL;298 299	*data = entry->data;300	*size = entry->size;301 302	IWL_DEBUG_INFO(phy_db->trans,303		       "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",304		       __func__, __LINE__, type, *size);305 306	return 0;307}308 309static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,310			       u16 length, void *data)311{312	struct iwl_phy_db_cmd phy_db_cmd;313	struct iwl_host_cmd cmd = {314		.id = PHY_DB_CMD,315	};316 317	IWL_DEBUG_INFO(phy_db->trans,318		       "Sending PHY-DB hcmd of type %d, of length %d\n",319		       type, length);320 321	/* Set phy db cmd variables */322	phy_db_cmd.type = cpu_to_le16(type);323	phy_db_cmd.length = cpu_to_le16(length);324 325	/* Set hcmd variables */326	cmd.data[0] = &phy_db_cmd;327	cmd.len[0] = sizeof(struct iwl_phy_db_cmd);328	cmd.data[1] = data;329	cmd.len[1] = length;330	cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;331 332	return iwl_trans_send_cmd(phy_db->trans, &cmd);333}334 335static int iwl_phy_db_send_all_channel_groups(336					struct iwl_phy_db *phy_db,337					enum iwl_phy_db_section_type type,338					u8 max_ch_groups)339{340	u16 i;341	int err;342	struct iwl_phy_db_entry *entry;343 344	/* Send all the  channel specific groups to operational fw */345	for (i = 0; i < max_ch_groups; i++) {346		entry = iwl_phy_db_get_section(phy_db,347					       type,348					       i);349		if (!entry)350			return -EINVAL;351 352		if (!entry->size)353			continue;354 355		/* Send the requested PHY DB section */356		err = iwl_send_phy_db_cmd(phy_db,357					  type,358					  entry->size,359					  entry->data);360		if (err) {361			IWL_ERR(phy_db->trans,362				"Can't SEND phy_db section %d (%d), err %d\n",363				type, i, err);364			return err;365		}366 367		IWL_DEBUG_INFO(phy_db->trans,368			       "Sent PHY_DB HCMD, type = %d num = %d\n",369			       type, i);370	}371 372	return 0;373}374 375int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)376{377	u8 *data = NULL;378	u16 size = 0;379	int err;380 381	IWL_DEBUG_INFO(phy_db->trans,382		       "Sending phy db data and configuration to runtime image\n");383 384	/* Send PHY DB CFG section */385	err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,386					  &data, &size, 0);387	if (err) {388		IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");389		return err;390	}391 392	err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);393	if (err) {394		IWL_ERR(phy_db->trans,395			"Cannot send HCMD of  Phy DB cfg section\n");396		return err;397	}398 399	err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,400					  &data, &size, 0);401	if (err) {402		IWL_ERR(phy_db->trans,403			"Cannot get Phy DB non specific channel section\n");404		return err;405	}406 407	err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);408	if (err) {409		IWL_ERR(phy_db->trans,410			"Cannot send HCMD of Phy DB non specific channel section\n");411		return err;412	}413 414	/* Send all the TXP channel specific data */415	err = iwl_phy_db_send_all_channel_groups(phy_db,416						 IWL_PHY_DB_CALIB_CHG_PAPD,417						 phy_db->n_group_papd);418	if (err) {419		IWL_ERR(phy_db->trans,420			"Cannot send channel specific PAPD groups\n");421		return err;422	}423 424	/* Send all the TXP channel specific data */425	err = iwl_phy_db_send_all_channel_groups(phy_db,426						 IWL_PHY_DB_CALIB_CHG_TXP,427						 phy_db->n_group_txp);428	if (err) {429		IWL_ERR(phy_db->trans,430			"Cannot send channel specific TX power groups\n");431		return err;432	}433 434	IWL_DEBUG_INFO(phy_db->trans,435		       "Finished sending phy db non channel data\n");436	return 0;437}438IWL_EXPORT_SYMBOL(iwl_send_phy_db_data);439