18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * EEPROM parser code for mac80211 Prism54 drivers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
68c2ecf20Sopenharmony_ci * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
78c2ecf20Sopenharmony_ci * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based on:
108c2ecf20Sopenharmony_ci * - the islsm (softmac prism54) driver, which is:
118c2ecf20Sopenharmony_ci *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
128c2ecf20Sopenharmony_ci * - stlc45xx driver
138c2ecf20Sopenharmony_ci *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/firmware.h>
178c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
188c2ecf20Sopenharmony_ci#include <linux/sort.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <net/mac80211.h>
228c2ecf20Sopenharmony_ci#include <linux/crc-ccitt.h>
238c2ecf20Sopenharmony_ci#include <linux/export.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "p54.h"
268c2ecf20Sopenharmony_ci#include "eeprom.h"
278c2ecf20Sopenharmony_ci#include "lmac.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic struct ieee80211_rate p54_bgrates[] = {
308c2ecf20Sopenharmony_ci	{ .bitrate = 10, .hw_value = 0, },
318c2ecf20Sopenharmony_ci	{ .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
328c2ecf20Sopenharmony_ci	{ .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
338c2ecf20Sopenharmony_ci	{ .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
348c2ecf20Sopenharmony_ci	{ .bitrate = 60, .hw_value = 4, },
358c2ecf20Sopenharmony_ci	{ .bitrate = 90, .hw_value = 5, },
368c2ecf20Sopenharmony_ci	{ .bitrate = 120, .hw_value = 6, },
378c2ecf20Sopenharmony_ci	{ .bitrate = 180, .hw_value = 7, },
388c2ecf20Sopenharmony_ci	{ .bitrate = 240, .hw_value = 8, },
398c2ecf20Sopenharmony_ci	{ .bitrate = 360, .hw_value = 9, },
408c2ecf20Sopenharmony_ci	{ .bitrate = 480, .hw_value = 10, },
418c2ecf20Sopenharmony_ci	{ .bitrate = 540, .hw_value = 11, },
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic struct ieee80211_rate p54_arates[] = {
458c2ecf20Sopenharmony_ci	{ .bitrate = 60, .hw_value = 4, },
468c2ecf20Sopenharmony_ci	{ .bitrate = 90, .hw_value = 5, },
478c2ecf20Sopenharmony_ci	{ .bitrate = 120, .hw_value = 6, },
488c2ecf20Sopenharmony_ci	{ .bitrate = 180, .hw_value = 7, },
498c2ecf20Sopenharmony_ci	{ .bitrate = 240, .hw_value = 8, },
508c2ecf20Sopenharmony_ci	{ .bitrate = 360, .hw_value = 9, },
518c2ecf20Sopenharmony_ci	{ .bitrate = 480, .hw_value = 10, },
528c2ecf20Sopenharmony_ci	{ .bitrate = 540, .hw_value = 11, },
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic struct p54_rssi_db_entry p54_rssi_default = {
568c2ecf20Sopenharmony_ci	/*
578c2ecf20Sopenharmony_ci	 * The defaults are taken from usb-logs of the
588c2ecf20Sopenharmony_ci	 * vendor driver. So, they should be safe to
598c2ecf20Sopenharmony_ci	 * use in case we can't get a match from the
608c2ecf20Sopenharmony_ci	 * rssi <-> dBm conversion database.
618c2ecf20Sopenharmony_ci	 */
628c2ecf20Sopenharmony_ci	.mul = 130,
638c2ecf20Sopenharmony_ci	.add = -398,
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define CHAN_HAS_CAL		BIT(0)
678c2ecf20Sopenharmony_ci#define CHAN_HAS_LIMIT		BIT(1)
688c2ecf20Sopenharmony_ci#define CHAN_HAS_CURVE		BIT(2)
698c2ecf20Sopenharmony_ci#define CHAN_HAS_ALL		(CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE)
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct p54_channel_entry {
728c2ecf20Sopenharmony_ci	u16 freq;
738c2ecf20Sopenharmony_ci	u16 data;
748c2ecf20Sopenharmony_ci	int index;
758c2ecf20Sopenharmony_ci	int max_power;
768c2ecf20Sopenharmony_ci	enum nl80211_band band;
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistruct p54_channel_list {
808c2ecf20Sopenharmony_ci	struct p54_channel_entry *channels;
818c2ecf20Sopenharmony_ci	size_t entries;
828c2ecf20Sopenharmony_ci	size_t max_entries;
838c2ecf20Sopenharmony_ci	size_t band_channel_num[NUM_NL80211_BANDS];
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int p54_get_band_from_freq(u16 freq)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	/* FIXME: sync these values with the 802.11 spec */
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if ((freq >= 2412) && (freq <= 2484))
918c2ecf20Sopenharmony_ci		return NL80211_BAND_2GHZ;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if ((freq >= 4920) && (freq <= 5825))
948c2ecf20Sopenharmony_ci		return NL80211_BAND_5GHZ;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return -1;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int same_band(u16 freq, u16 freq2)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int p54_compare_channels(const void *_a,
1058c2ecf20Sopenharmony_ci				const void *_b)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	const struct p54_channel_entry *a = _a;
1088c2ecf20Sopenharmony_ci	const struct p54_channel_entry *b = _b;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return a->freq - b->freq;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int p54_compare_rssichan(const void *_a,
1148c2ecf20Sopenharmony_ci				const void *_b)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	const struct p54_rssi_db_entry *a = _a;
1178c2ecf20Sopenharmony_ci	const struct p54_rssi_db_entry *b = _b;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return a->freq - b->freq;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int p54_fill_band_bitrates(struct ieee80211_hw *dev,
1238c2ecf20Sopenharmony_ci				  struct ieee80211_supported_band *band_entry,
1248c2ecf20Sopenharmony_ci				  enum nl80211_band band)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	/* TODO: generate rate array dynamically */
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	switch (band) {
1298c2ecf20Sopenharmony_ci	case NL80211_BAND_2GHZ:
1308c2ecf20Sopenharmony_ci		band_entry->bitrates = p54_bgrates;
1318c2ecf20Sopenharmony_ci		band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates);
1328c2ecf20Sopenharmony_ci		break;
1338c2ecf20Sopenharmony_ci	case NL80211_BAND_5GHZ:
1348c2ecf20Sopenharmony_ci		band_entry->bitrates = p54_arates;
1358c2ecf20Sopenharmony_ci		band_entry->n_bitrates = ARRAY_SIZE(p54_arates);
1368c2ecf20Sopenharmony_ci		break;
1378c2ecf20Sopenharmony_ci	default:
1388c2ecf20Sopenharmony_ci		return -EINVAL;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int p54_generate_band(struct ieee80211_hw *dev,
1458c2ecf20Sopenharmony_ci			     struct p54_channel_list *list,
1468c2ecf20Sopenharmony_ci			     unsigned int *chan_num,
1478c2ecf20Sopenharmony_ci			     enum nl80211_band band)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
1508c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *tmp, *old;
1518c2ecf20Sopenharmony_ci	unsigned int i, j;
1528c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if ((!list->entries) || (!list->band_channel_num[band]))
1558c2ecf20Sopenharmony_ci		return -EINVAL;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
1588c2ecf20Sopenharmony_ci	if (!tmp)
1598c2ecf20Sopenharmony_ci		goto err_out;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	tmp->channels = kcalloc(list->band_channel_num[band],
1628c2ecf20Sopenharmony_ci				sizeof(struct ieee80211_channel),
1638c2ecf20Sopenharmony_ci				GFP_KERNEL);
1648c2ecf20Sopenharmony_ci	if (!tmp->channels)
1658c2ecf20Sopenharmony_ci		goto err_out;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ret = p54_fill_band_bitrates(dev, tmp, band);
1688c2ecf20Sopenharmony_ci	if (ret)
1698c2ecf20Sopenharmony_ci		goto err_out;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	for (i = 0, j = 0; (j < list->band_channel_num[band]) &&
1728c2ecf20Sopenharmony_ci			   (i < list->entries); i++) {
1738c2ecf20Sopenharmony_ci		struct p54_channel_entry *chan = &list->channels[i];
1748c2ecf20Sopenharmony_ci		struct ieee80211_channel *dest = &tmp->channels[j];
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		if (chan->band != band)
1778c2ecf20Sopenharmony_ci			continue;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		if (chan->data != CHAN_HAS_ALL) {
1808c2ecf20Sopenharmony_ci			wiphy_err(dev->wiphy, "%s%s%s is/are missing for "
1818c2ecf20Sopenharmony_ci				  "channel:%d [%d MHz].\n",
1828c2ecf20Sopenharmony_ci				  (chan->data & CHAN_HAS_CAL ? "" :
1838c2ecf20Sopenharmony_ci				   " [iqauto calibration data]"),
1848c2ecf20Sopenharmony_ci				  (chan->data & CHAN_HAS_LIMIT ? "" :
1858c2ecf20Sopenharmony_ci				   " [output power limits]"),
1868c2ecf20Sopenharmony_ci				  (chan->data & CHAN_HAS_CURVE ? "" :
1878c2ecf20Sopenharmony_ci				   " [curve data]"),
1888c2ecf20Sopenharmony_ci				  chan->index, chan->freq);
1898c2ecf20Sopenharmony_ci			continue;
1908c2ecf20Sopenharmony_ci		}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		dest->band = chan->band;
1938c2ecf20Sopenharmony_ci		dest->center_freq = chan->freq;
1948c2ecf20Sopenharmony_ci		dest->max_power = chan->max_power;
1958c2ecf20Sopenharmony_ci		priv->survey[*chan_num].channel = &tmp->channels[j];
1968c2ecf20Sopenharmony_ci		priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM |
1978c2ecf20Sopenharmony_ci			SURVEY_INFO_TIME |
1988c2ecf20Sopenharmony_ci			SURVEY_INFO_TIME_BUSY |
1998c2ecf20Sopenharmony_ci			SURVEY_INFO_TIME_TX;
2008c2ecf20Sopenharmony_ci		dest->hw_value = (*chan_num);
2018c2ecf20Sopenharmony_ci		j++;
2028c2ecf20Sopenharmony_ci		(*chan_num)++;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (j == 0) {
2068c2ecf20Sopenharmony_ci		wiphy_err(dev->wiphy, "Disabling totally damaged %d GHz band\n",
2078c2ecf20Sopenharmony_ci			  (band == NL80211_BAND_2GHZ) ? 2 : 5);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		ret = -ENODATA;
2108c2ecf20Sopenharmony_ci		goto err_out;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	tmp->n_channels = j;
2148c2ecf20Sopenharmony_ci	old = priv->band_table[band];
2158c2ecf20Sopenharmony_ci	priv->band_table[band] = tmp;
2168c2ecf20Sopenharmony_ci	if (old) {
2178c2ecf20Sopenharmony_ci		kfree(old->channels);
2188c2ecf20Sopenharmony_ci		kfree(old);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cierr_out:
2248c2ecf20Sopenharmony_ci	if (tmp) {
2258c2ecf20Sopenharmony_ci		kfree(tmp->channels);
2268c2ecf20Sopenharmony_ci		kfree(tmp);
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return ret;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic struct p54_channel_entry *p54_update_channel_param(struct p54_channel_list *list,
2338c2ecf20Sopenharmony_ci							  u16 freq, u16 data)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	int i;
2368c2ecf20Sopenharmony_ci	struct p54_channel_entry *entry = NULL;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/*
2398c2ecf20Sopenharmony_ci	 * usually all lists in the eeprom are mostly sorted.
2408c2ecf20Sopenharmony_ci	 * so it's very likely that the entry we are looking for
2418c2ecf20Sopenharmony_ci	 * is right at the end of the list
2428c2ecf20Sopenharmony_ci	 */
2438c2ecf20Sopenharmony_ci	for (i = list->entries; i >= 0; i--) {
2448c2ecf20Sopenharmony_ci		if (freq == list->channels[i].freq) {
2458c2ecf20Sopenharmony_ci			entry = &list->channels[i];
2468c2ecf20Sopenharmony_ci			break;
2478c2ecf20Sopenharmony_ci		}
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if ((i < 0) && (list->entries < list->max_entries)) {
2518c2ecf20Sopenharmony_ci		/* entry does not exist yet. Initialize a new one. */
2528c2ecf20Sopenharmony_ci		int band = p54_get_band_from_freq(freq);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		/*
2558c2ecf20Sopenharmony_ci		 * filter out frequencies which don't belong into
2568c2ecf20Sopenharmony_ci		 * any supported band.
2578c2ecf20Sopenharmony_ci		 */
2588c2ecf20Sopenharmony_ci		if (band >= 0) {
2598c2ecf20Sopenharmony_ci			i = list->entries++;
2608c2ecf20Sopenharmony_ci			list->band_channel_num[band]++;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci			entry = &list->channels[i];
2638c2ecf20Sopenharmony_ci			entry->freq = freq;
2648c2ecf20Sopenharmony_ci			entry->band = band;
2658c2ecf20Sopenharmony_ci			entry->index = ieee80211_frequency_to_channel(freq);
2668c2ecf20Sopenharmony_ci			entry->max_power = 0;
2678c2ecf20Sopenharmony_ci			entry->data = 0;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (entry)
2728c2ecf20Sopenharmony_ci		entry->data |= data;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return entry;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic int p54_get_maxpower(struct p54_common *priv, void *data)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	switch (priv->rxhw & PDR_SYNTH_FRONTEND_MASK) {
2808c2ecf20Sopenharmony_ci	case PDR_SYNTH_FRONTEND_LONGBOW: {
2818c2ecf20Sopenharmony_ci		struct pda_channel_output_limit_longbow *pda = data;
2828c2ecf20Sopenharmony_ci		int j;
2838c2ecf20Sopenharmony_ci		u16 rawpower = 0;
2848c2ecf20Sopenharmony_ci		pda = data;
2858c2ecf20Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(pda->point); j++) {
2868c2ecf20Sopenharmony_ci			struct pda_channel_output_limit_point_longbow *point =
2878c2ecf20Sopenharmony_ci				&pda->point[j];
2888c2ecf20Sopenharmony_ci			rawpower = max_t(u16,
2898c2ecf20Sopenharmony_ci				rawpower, le16_to_cpu(point->val_qpsk));
2908c2ecf20Sopenharmony_ci			rawpower = max_t(u16,
2918c2ecf20Sopenharmony_ci				rawpower, le16_to_cpu(point->val_bpsk));
2928c2ecf20Sopenharmony_ci			rawpower = max_t(u16,
2938c2ecf20Sopenharmony_ci				rawpower, le16_to_cpu(point->val_16qam));
2948c2ecf20Sopenharmony_ci			rawpower = max_t(u16,
2958c2ecf20Sopenharmony_ci				rawpower, le16_to_cpu(point->val_64qam));
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci		/* longbow seems to use 1/16 dBm units */
2988c2ecf20Sopenharmony_ci		return rawpower / 16;
2998c2ecf20Sopenharmony_ci		}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	case PDR_SYNTH_FRONTEND_DUETTE3:
3028c2ecf20Sopenharmony_ci	case PDR_SYNTH_FRONTEND_DUETTE2:
3038c2ecf20Sopenharmony_ci	case PDR_SYNTH_FRONTEND_FRISBEE:
3048c2ecf20Sopenharmony_ci	case PDR_SYNTH_FRONTEND_XBOW: {
3058c2ecf20Sopenharmony_ci		struct pda_channel_output_limit *pda = data;
3068c2ecf20Sopenharmony_ci		u8 rawpower = 0;
3078c2ecf20Sopenharmony_ci		rawpower = max(rawpower, pda->val_qpsk);
3088c2ecf20Sopenharmony_ci		rawpower = max(rawpower, pda->val_bpsk);
3098c2ecf20Sopenharmony_ci		rawpower = max(rawpower, pda->val_16qam);
3108c2ecf20Sopenharmony_ci		rawpower = max(rawpower, pda->val_64qam);
3118c2ecf20Sopenharmony_ci		/* raw values are in 1/4 dBm units */
3128c2ecf20Sopenharmony_ci		return rawpower / 4;
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	default:
3168c2ecf20Sopenharmony_ci		return 20;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int p54_generate_channel_lists(struct ieee80211_hw *dev)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
3238c2ecf20Sopenharmony_ci	struct p54_channel_list *list;
3248c2ecf20Sopenharmony_ci	unsigned int i, j, k, max_channel_num;
3258c2ecf20Sopenharmony_ci	int ret = 0;
3268c2ecf20Sopenharmony_ci	u16 freq;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if ((priv->iq_autocal_len != priv->curve_data->entries) ||
3298c2ecf20Sopenharmony_ci	    (priv->iq_autocal_len != priv->output_limit->entries))
3308c2ecf20Sopenharmony_ci		wiphy_err(dev->wiphy,
3318c2ecf20Sopenharmony_ci			  "Unsupported or damaged EEPROM detected. "
3328c2ecf20Sopenharmony_ci			  "You may not be able to use all channels.\n");
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	max_channel_num = max_t(unsigned int, priv->output_limit->entries,
3358c2ecf20Sopenharmony_ci				priv->iq_autocal_len);
3368c2ecf20Sopenharmony_ci	max_channel_num = max_t(unsigned int, max_channel_num,
3378c2ecf20Sopenharmony_ci				priv->curve_data->entries);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	list = kzalloc(sizeof(*list), GFP_KERNEL);
3408c2ecf20Sopenharmony_ci	if (!list) {
3418c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3428c2ecf20Sopenharmony_ci		goto free;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci	priv->chan_num = max_channel_num;
3458c2ecf20Sopenharmony_ci	priv->survey = kcalloc(max_channel_num, sizeof(struct survey_info),
3468c2ecf20Sopenharmony_ci			       GFP_KERNEL);
3478c2ecf20Sopenharmony_ci	if (!priv->survey) {
3488c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3498c2ecf20Sopenharmony_ci		goto free;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	list->max_entries = max_channel_num;
3538c2ecf20Sopenharmony_ci	list->channels = kcalloc(max_channel_num,
3548c2ecf20Sopenharmony_ci				 sizeof(struct p54_channel_entry),
3558c2ecf20Sopenharmony_ci				 GFP_KERNEL);
3568c2ecf20Sopenharmony_ci	if (!list->channels) {
3578c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3588c2ecf20Sopenharmony_ci		goto free;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	for (i = 0; i < max_channel_num; i++) {
3628c2ecf20Sopenharmony_ci		if (i < priv->iq_autocal_len) {
3638c2ecf20Sopenharmony_ci			freq = le16_to_cpu(priv->iq_autocal[i].freq);
3648c2ecf20Sopenharmony_ci			p54_update_channel_param(list, freq, CHAN_HAS_CAL);
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		if (i < priv->output_limit->entries) {
3688c2ecf20Sopenharmony_ci			struct p54_channel_entry *tmp;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci			void *data = (void *) ((unsigned long) i *
3718c2ecf20Sopenharmony_ci				priv->output_limit->entry_size +
3728c2ecf20Sopenharmony_ci				priv->output_limit->offset +
3738c2ecf20Sopenharmony_ci				priv->output_limit->data);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci			freq = le16_to_cpup((__le16 *) data);
3768c2ecf20Sopenharmony_ci			tmp = p54_update_channel_param(list, freq,
3778c2ecf20Sopenharmony_ci						       CHAN_HAS_LIMIT);
3788c2ecf20Sopenharmony_ci			if (tmp) {
3798c2ecf20Sopenharmony_ci				tmp->max_power = p54_get_maxpower(priv, data);
3808c2ecf20Sopenharmony_ci			}
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		if (i < priv->curve_data->entries) {
3848c2ecf20Sopenharmony_ci			freq = le16_to_cpup((__le16 *) (i *
3858c2ecf20Sopenharmony_ci					    priv->curve_data->entry_size +
3868c2ecf20Sopenharmony_ci					    priv->curve_data->offset +
3878c2ecf20Sopenharmony_ci					    priv->curve_data->data));
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci			p54_update_channel_param(list, freq, CHAN_HAS_CURVE);
3908c2ecf20Sopenharmony_ci		}
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/* sort the channel list by frequency */
3948c2ecf20Sopenharmony_ci	sort(list->channels, list->entries, sizeof(struct p54_channel_entry),
3958c2ecf20Sopenharmony_ci	     p54_compare_channels, NULL);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	k = 0;
3988c2ecf20Sopenharmony_ci	for (i = 0, j = 0; i < NUM_NL80211_BANDS; i++) {
3998c2ecf20Sopenharmony_ci		if (p54_generate_band(dev, list, &k, i) == 0)
4008c2ecf20Sopenharmony_ci			j++;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci	if (j == 0) {
4038c2ecf20Sopenharmony_ci		/* no useable band available. */
4048c2ecf20Sopenharmony_ci		ret = -EINVAL;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cifree:
4088c2ecf20Sopenharmony_ci	if (list) {
4098c2ecf20Sopenharmony_ci		kfree(list->channels);
4108c2ecf20Sopenharmony_ci		kfree(list);
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci	if (ret) {
4138c2ecf20Sopenharmony_ci		kfree(priv->survey);
4148c2ecf20Sopenharmony_ci		priv->survey = NULL;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return ret;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int p54_convert_rev0(struct ieee80211_hw *dev,
4218c2ecf20Sopenharmony_ci			    struct pda_pa_curve_data *curve_data)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
4248c2ecf20Sopenharmony_ci	struct p54_pa_curve_data_sample *dst;
4258c2ecf20Sopenharmony_ci	struct pda_pa_curve_data_sample_rev0 *src;
4268c2ecf20Sopenharmony_ci	size_t cd_len = sizeof(*curve_data) +
4278c2ecf20Sopenharmony_ci		(curve_data->points_per_channel*sizeof(*dst) + 2) *
4288c2ecf20Sopenharmony_ci		 curve_data->channels;
4298c2ecf20Sopenharmony_ci	unsigned int i, j;
4308c2ecf20Sopenharmony_ci	void *source, *target;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len,
4338c2ecf20Sopenharmony_ci				   GFP_KERNEL);
4348c2ecf20Sopenharmony_ci	if (!priv->curve_data)
4358c2ecf20Sopenharmony_ci		return -ENOMEM;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	priv->curve_data->entries = curve_data->channels;
4388c2ecf20Sopenharmony_ci	priv->curve_data->entry_size = sizeof(__le16) +
4398c2ecf20Sopenharmony_ci		sizeof(*dst) * curve_data->points_per_channel;
4408c2ecf20Sopenharmony_ci	priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data);
4418c2ecf20Sopenharmony_ci	priv->curve_data->len = cd_len;
4428c2ecf20Sopenharmony_ci	memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data));
4438c2ecf20Sopenharmony_ci	source = curve_data->data;
4448c2ecf20Sopenharmony_ci	target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data;
4458c2ecf20Sopenharmony_ci	for (i = 0; i < curve_data->channels; i++) {
4468c2ecf20Sopenharmony_ci		__le16 *freq = source;
4478c2ecf20Sopenharmony_ci		source += sizeof(__le16);
4488c2ecf20Sopenharmony_ci		*((__le16 *)target) = *freq;
4498c2ecf20Sopenharmony_ci		target += sizeof(__le16);
4508c2ecf20Sopenharmony_ci		for (j = 0; j < curve_data->points_per_channel; j++) {
4518c2ecf20Sopenharmony_ci			dst = target;
4528c2ecf20Sopenharmony_ci			src = source;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci			dst->rf_power = src->rf_power;
4558c2ecf20Sopenharmony_ci			dst->pa_detector = src->pa_detector;
4568c2ecf20Sopenharmony_ci			dst->data_64qam = src->pcv;
4578c2ecf20Sopenharmony_ci			/* "invent" the points for the other modulations */
4588c2ecf20Sopenharmony_ci#define SUB(x, y) (u8)(((x) - (y)) > (x) ? 0 : (x) - (y))
4598c2ecf20Sopenharmony_ci			dst->data_16qam = SUB(src->pcv, 12);
4608c2ecf20Sopenharmony_ci			dst->data_qpsk = SUB(dst->data_16qam, 12);
4618c2ecf20Sopenharmony_ci			dst->data_bpsk = SUB(dst->data_qpsk, 12);
4628c2ecf20Sopenharmony_ci			dst->data_barker = SUB(dst->data_bpsk, 14);
4638c2ecf20Sopenharmony_ci#undef SUB
4648c2ecf20Sopenharmony_ci			target += sizeof(*dst);
4658c2ecf20Sopenharmony_ci			source += sizeof(*src);
4668c2ecf20Sopenharmony_ci		}
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	return 0;
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic int p54_convert_rev1(struct ieee80211_hw *dev,
4738c2ecf20Sopenharmony_ci			    struct pda_pa_curve_data *curve_data)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
4768c2ecf20Sopenharmony_ci	struct p54_pa_curve_data_sample *dst;
4778c2ecf20Sopenharmony_ci	struct pda_pa_curve_data_sample_rev1 *src;
4788c2ecf20Sopenharmony_ci	size_t cd_len = sizeof(*curve_data) +
4798c2ecf20Sopenharmony_ci		(curve_data->points_per_channel*sizeof(*dst) + 2) *
4808c2ecf20Sopenharmony_ci		 curve_data->channels;
4818c2ecf20Sopenharmony_ci	unsigned int i, j;
4828c2ecf20Sopenharmony_ci	void *source, *target;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data),
4858c2ecf20Sopenharmony_ci				   GFP_KERNEL);
4868c2ecf20Sopenharmony_ci	if (!priv->curve_data)
4878c2ecf20Sopenharmony_ci		return -ENOMEM;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	priv->curve_data->entries = curve_data->channels;
4908c2ecf20Sopenharmony_ci	priv->curve_data->entry_size = sizeof(__le16) +
4918c2ecf20Sopenharmony_ci		sizeof(*dst) * curve_data->points_per_channel;
4928c2ecf20Sopenharmony_ci	priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data);
4938c2ecf20Sopenharmony_ci	priv->curve_data->len = cd_len;
4948c2ecf20Sopenharmony_ci	memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data));
4958c2ecf20Sopenharmony_ci	source = curve_data->data;
4968c2ecf20Sopenharmony_ci	target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data;
4978c2ecf20Sopenharmony_ci	for (i = 0; i < curve_data->channels; i++) {
4988c2ecf20Sopenharmony_ci		__le16 *freq = source;
4998c2ecf20Sopenharmony_ci		source += sizeof(__le16);
5008c2ecf20Sopenharmony_ci		*((__le16 *)target) = *freq;
5018c2ecf20Sopenharmony_ci		target += sizeof(__le16);
5028c2ecf20Sopenharmony_ci		for (j = 0; j < curve_data->points_per_channel; j++) {
5038c2ecf20Sopenharmony_ci			memcpy(target, source, sizeof(*src));
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci			target += sizeof(*dst);
5068c2ecf20Sopenharmony_ci			source += sizeof(*src);
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci		source++;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	return 0;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2",
5158c2ecf20Sopenharmony_ci	"Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" };
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int p54_parse_rssical(struct ieee80211_hw *dev,
5188c2ecf20Sopenharmony_ci			     u8 *data, int len, u16 type)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
5218c2ecf20Sopenharmony_ci	struct p54_rssi_db_entry *entry;
5228c2ecf20Sopenharmony_ci	size_t db_len, entries;
5238c2ecf20Sopenharmony_ci	int offset = 0, i;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) {
5268c2ecf20Sopenharmony_ci		entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2;
5278c2ecf20Sopenharmony_ci		if (len != sizeof(struct pda_rssi_cal_entry) * entries) {
5288c2ecf20Sopenharmony_ci			wiphy_err(dev->wiphy, "rssical size mismatch.\n");
5298c2ecf20Sopenharmony_ci			goto err_data;
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci	} else {
5328c2ecf20Sopenharmony_ci		/*
5338c2ecf20Sopenharmony_ci		 * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...)
5348c2ecf20Sopenharmony_ci		 * have an empty two byte header.
5358c2ecf20Sopenharmony_ci		 */
5368c2ecf20Sopenharmony_ci		if (*((__le16 *)&data[offset]) == cpu_to_le16(0))
5378c2ecf20Sopenharmony_ci			offset += 2;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		entries = (len - offset) /
5408c2ecf20Sopenharmony_ci			sizeof(struct pda_rssi_cal_ext_entry);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		if (len < offset ||
5438c2ecf20Sopenharmony_ci		    (len - offset) % sizeof(struct pda_rssi_cal_ext_entry) ||
5448c2ecf20Sopenharmony_ci		    entries == 0) {
5458c2ecf20Sopenharmony_ci			wiphy_err(dev->wiphy, "invalid rssi database.\n");
5468c2ecf20Sopenharmony_ci			goto err_data;
5478c2ecf20Sopenharmony_ci		}
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	db_len = sizeof(*entry) * entries;
5518c2ecf20Sopenharmony_ci	priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL);
5528c2ecf20Sopenharmony_ci	if (!priv->rssi_db)
5538c2ecf20Sopenharmony_ci		return -ENOMEM;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	priv->rssi_db->offset = 0;
5568c2ecf20Sopenharmony_ci	priv->rssi_db->entries = entries;
5578c2ecf20Sopenharmony_ci	priv->rssi_db->entry_size = sizeof(*entry);
5588c2ecf20Sopenharmony_ci	priv->rssi_db->len = db_len;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset);
5618c2ecf20Sopenharmony_ci	if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) {
5628c2ecf20Sopenharmony_ci		struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset];
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci		for (i = 0; i < entries; i++) {
5658c2ecf20Sopenharmony_ci			entry[i].freq = le16_to_cpu(cal[i].freq);
5668c2ecf20Sopenharmony_ci			entry[i].mul = (s16) le16_to_cpu(cal[i].mul);
5678c2ecf20Sopenharmony_ci			entry[i].add = (s16) le16_to_cpu(cal[i].add);
5688c2ecf20Sopenharmony_ci		}
5698c2ecf20Sopenharmony_ci	} else {
5708c2ecf20Sopenharmony_ci		struct pda_rssi_cal_entry *cal = (void *) &data[offset];
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		for (i = 0; i < entries; i++) {
5738c2ecf20Sopenharmony_ci			u16 freq = 0;
5748c2ecf20Sopenharmony_ci			switch (i) {
5758c2ecf20Sopenharmony_ci			case NL80211_BAND_2GHZ:
5768c2ecf20Sopenharmony_ci				freq = 2437;
5778c2ecf20Sopenharmony_ci				break;
5788c2ecf20Sopenharmony_ci			case NL80211_BAND_5GHZ:
5798c2ecf20Sopenharmony_ci				freq = 5240;
5808c2ecf20Sopenharmony_ci				break;
5818c2ecf20Sopenharmony_ci			}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci			entry[i].freq = freq;
5848c2ecf20Sopenharmony_ci			entry[i].mul = (s16) le16_to_cpu(cal[i].mul);
5858c2ecf20Sopenharmony_ci			entry[i].add = (s16) le16_to_cpu(cal[i].add);
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* sort the list by channel frequency */
5908c2ecf20Sopenharmony_ci	sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL);
5918c2ecf20Sopenharmony_ci	return 0;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cierr_data:
5948c2ecf20Sopenharmony_ci	wiphy_err(dev->wiphy,
5958c2ecf20Sopenharmony_ci		  "rssi calibration data packing type:(%x) len:%d.\n",
5968c2ecf20Sopenharmony_ci		  type, len);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	wiphy_err(dev->wiphy, "please report this issue.\n");
6018c2ecf20Sopenharmony_ci	return -EINVAL;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistruct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct p54_rssi_db_entry *entry;
6078c2ecf20Sopenharmony_ci	int i, found = -1;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	if (!priv->rssi_db)
6108c2ecf20Sopenharmony_ci		return &p54_rssi_default;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset);
6138c2ecf20Sopenharmony_ci	for (i = 0; i < priv->rssi_db->entries; i++) {
6148c2ecf20Sopenharmony_ci		if (!same_band(freq, entry[i].freq))
6158c2ecf20Sopenharmony_ci			continue;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		if (found == -1) {
6188c2ecf20Sopenharmony_ci			found = i;
6198c2ecf20Sopenharmony_ci			continue;
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		/* nearest match */
6238c2ecf20Sopenharmony_ci		if (abs(freq - entry[i].freq) <
6248c2ecf20Sopenharmony_ci		    abs(freq - entry[found].freq)) {
6258c2ecf20Sopenharmony_ci			found = i;
6268c2ecf20Sopenharmony_ci			continue;
6278c2ecf20Sopenharmony_ci		} else {
6288c2ecf20Sopenharmony_ci			break;
6298c2ecf20Sopenharmony_ci		}
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	return found < 0 ? &p54_rssi_default : &entry[found];
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic void p54_parse_default_country(struct ieee80211_hw *dev,
6368c2ecf20Sopenharmony_ci				      void *data, int len)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct pda_country *country;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (len != sizeof(*country)) {
6418c2ecf20Sopenharmony_ci		wiphy_err(dev->wiphy,
6428c2ecf20Sopenharmony_ci			  "found possible invalid default country eeprom entry. (entry size: %d)\n",
6438c2ecf20Sopenharmony_ci			  len);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci		print_hex_dump_bytes("country:", DUMP_PREFIX_NONE,
6468c2ecf20Sopenharmony_ci				     data, len);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci		wiphy_err(dev->wiphy, "please report this issue.\n");
6498c2ecf20Sopenharmony_ci		return;
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	country = (struct pda_country *) data;
6538c2ecf20Sopenharmony_ci	if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO)
6548c2ecf20Sopenharmony_ci		regulatory_hint(dev->wiphy, country->alpha2);
6558c2ecf20Sopenharmony_ci	else {
6568c2ecf20Sopenharmony_ci		/* TODO:
6578c2ecf20Sopenharmony_ci		 * write a shared/common function that converts
6588c2ecf20Sopenharmony_ci		 * "Regulatory domain codes" (802.11-2007 14.8.2.2)
6598c2ecf20Sopenharmony_ci		 * into ISO/IEC 3166-1 alpha2 for regulatory_hint.
6608c2ecf20Sopenharmony_ci		 */
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic int p54_convert_output_limits(struct ieee80211_hw *dev,
6658c2ecf20Sopenharmony_ci				     u8 *data, size_t len)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (len < 2)
6708c2ecf20Sopenharmony_ci		return -EINVAL;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (data[0] != 0) {
6738c2ecf20Sopenharmony_ci		wiphy_err(dev->wiphy, "unknown output power db revision:%x\n",
6748c2ecf20Sopenharmony_ci			  data[0]);
6758c2ecf20Sopenharmony_ci		return -EINVAL;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len)
6798c2ecf20Sopenharmony_ci		return -EINVAL;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	priv->output_limit = kmalloc(data[1] *
6828c2ecf20Sopenharmony_ci		sizeof(struct pda_channel_output_limit) +
6838c2ecf20Sopenharmony_ci		sizeof(*priv->output_limit), GFP_KERNEL);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	if (!priv->output_limit)
6868c2ecf20Sopenharmony_ci		return -ENOMEM;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	priv->output_limit->offset = 0;
6898c2ecf20Sopenharmony_ci	priv->output_limit->entries = data[1];
6908c2ecf20Sopenharmony_ci	priv->output_limit->entry_size =
6918c2ecf20Sopenharmony_ci		sizeof(struct pda_channel_output_limit);
6928c2ecf20Sopenharmony_ci	priv->output_limit->len = priv->output_limit->entry_size *
6938c2ecf20Sopenharmony_ci				  priv->output_limit->entries +
6948c2ecf20Sopenharmony_ci				  priv->output_limit->offset;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	memcpy(priv->output_limit->data, &data[2],
6978c2ecf20Sopenharmony_ci	       data[1] * sizeof(struct pda_channel_output_limit));
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	return 0;
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src,
7038c2ecf20Sopenharmony_ci					       size_t total_len)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct p54_cal_database *dst;
7068c2ecf20Sopenharmony_ci	size_t payload_len, entries, entry_size, offset;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	payload_len = le16_to_cpu(src->len);
7098c2ecf20Sopenharmony_ci	entries = le16_to_cpu(src->entries);
7108c2ecf20Sopenharmony_ci	entry_size = le16_to_cpu(src->entry_size);
7118c2ecf20Sopenharmony_ci	offset = le16_to_cpu(src->offset);
7128c2ecf20Sopenharmony_ci	if (((entries * entry_size + offset) != payload_len) ||
7138c2ecf20Sopenharmony_ci	     (payload_len + sizeof(*src) != total_len))
7148c2ecf20Sopenharmony_ci		return NULL;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL);
7178c2ecf20Sopenharmony_ci	if (!dst)
7188c2ecf20Sopenharmony_ci		return NULL;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	dst->entries = entries;
7218c2ecf20Sopenharmony_ci	dst->entry_size = entry_size;
7228c2ecf20Sopenharmony_ci	dst->offset = offset;
7238c2ecf20Sopenharmony_ci	dst->len = payload_len;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	memcpy(dst->data, src->data, payload_len);
7268c2ecf20Sopenharmony_ci	return dst;
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ciint p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
7308c2ecf20Sopenharmony_ci{
7318c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
7328c2ecf20Sopenharmony_ci	struct eeprom_pda_wrap *wrap;
7338c2ecf20Sopenharmony_ci	struct pda_entry *entry;
7348c2ecf20Sopenharmony_ci	unsigned int data_len, entry_len;
7358c2ecf20Sopenharmony_ci	void *tmp;
7368c2ecf20Sopenharmony_ci	int err;
7378c2ecf20Sopenharmony_ci	u8 *end = (u8 *)eeprom + len;
7388c2ecf20Sopenharmony_ci	u16 synth = 0;
7398c2ecf20Sopenharmony_ci	u16 crc16 = ~0;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	wrap = (struct eeprom_pda_wrap *) eeprom;
7428c2ecf20Sopenharmony_ci	entry = (void *)wrap->data + le16_to_cpu(wrap->len);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* verify that at least the entry length/code fits */
7458c2ecf20Sopenharmony_ci	while ((u8 *)entry <= end - sizeof(*entry)) {
7468c2ecf20Sopenharmony_ci		entry_len = le16_to_cpu(entry->len);
7478c2ecf20Sopenharmony_ci		data_len = ((entry_len - 1) << 1);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci		/* abort if entry exceeds whole structure */
7508c2ecf20Sopenharmony_ci		if ((u8 *)entry + sizeof(*entry) + data_len > end)
7518c2ecf20Sopenharmony_ci			break;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci		switch (le16_to_cpu(entry->code)) {
7548c2ecf20Sopenharmony_ci		case PDR_MAC_ADDRESS:
7558c2ecf20Sopenharmony_ci			if (data_len != ETH_ALEN)
7568c2ecf20Sopenharmony_ci				break;
7578c2ecf20Sopenharmony_ci			SET_IEEE80211_PERM_ADDR(dev, entry->data);
7588c2ecf20Sopenharmony_ci			break;
7598c2ecf20Sopenharmony_ci		case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS:
7608c2ecf20Sopenharmony_ci			if (priv->output_limit)
7618c2ecf20Sopenharmony_ci				break;
7628c2ecf20Sopenharmony_ci			err = p54_convert_output_limits(dev, entry->data,
7638c2ecf20Sopenharmony_ci							data_len);
7648c2ecf20Sopenharmony_ci			if (err)
7658c2ecf20Sopenharmony_ci				goto err;
7668c2ecf20Sopenharmony_ci			break;
7678c2ecf20Sopenharmony_ci		case PDR_PRISM_PA_CAL_CURVE_DATA: {
7688c2ecf20Sopenharmony_ci			struct pda_pa_curve_data *curve_data =
7698c2ecf20Sopenharmony_ci				(struct pda_pa_curve_data *)entry->data;
7708c2ecf20Sopenharmony_ci			if (data_len < sizeof(*curve_data)) {
7718c2ecf20Sopenharmony_ci				err = -EINVAL;
7728c2ecf20Sopenharmony_ci				goto err;
7738c2ecf20Sopenharmony_ci			}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci			switch (curve_data->cal_method_rev) {
7768c2ecf20Sopenharmony_ci			case 0:
7778c2ecf20Sopenharmony_ci				err = p54_convert_rev0(dev, curve_data);
7788c2ecf20Sopenharmony_ci				break;
7798c2ecf20Sopenharmony_ci			case 1:
7808c2ecf20Sopenharmony_ci				err = p54_convert_rev1(dev, curve_data);
7818c2ecf20Sopenharmony_ci				break;
7828c2ecf20Sopenharmony_ci			default:
7838c2ecf20Sopenharmony_ci				wiphy_err(dev->wiphy,
7848c2ecf20Sopenharmony_ci					  "unknown curve data revision %d\n",
7858c2ecf20Sopenharmony_ci					  curve_data->cal_method_rev);
7868c2ecf20Sopenharmony_ci				err = -ENODEV;
7878c2ecf20Sopenharmony_ci				break;
7888c2ecf20Sopenharmony_ci			}
7898c2ecf20Sopenharmony_ci			if (err)
7908c2ecf20Sopenharmony_ci				goto err;
7918c2ecf20Sopenharmony_ci			}
7928c2ecf20Sopenharmony_ci			break;
7938c2ecf20Sopenharmony_ci		case PDR_PRISM_ZIF_TX_IQ_CALIBRATION:
7948c2ecf20Sopenharmony_ci			priv->iq_autocal = kmemdup(entry->data, data_len,
7958c2ecf20Sopenharmony_ci						   GFP_KERNEL);
7968c2ecf20Sopenharmony_ci			if (!priv->iq_autocal) {
7978c2ecf20Sopenharmony_ci				err = -ENOMEM;
7988c2ecf20Sopenharmony_ci				goto err;
7998c2ecf20Sopenharmony_ci			}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci			priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry);
8028c2ecf20Sopenharmony_ci			break;
8038c2ecf20Sopenharmony_ci		case PDR_DEFAULT_COUNTRY:
8048c2ecf20Sopenharmony_ci			p54_parse_default_country(dev, entry->data, data_len);
8058c2ecf20Sopenharmony_ci			break;
8068c2ecf20Sopenharmony_ci		case PDR_INTERFACE_LIST:
8078c2ecf20Sopenharmony_ci			tmp = entry->data;
8088c2ecf20Sopenharmony_ci			while ((u8 *)tmp < entry->data + data_len) {
8098c2ecf20Sopenharmony_ci				struct exp_if *exp_if = tmp;
8108c2ecf20Sopenharmony_ci				if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000))
8118c2ecf20Sopenharmony_ci					synth = le16_to_cpu(exp_if->variant);
8128c2ecf20Sopenharmony_ci				tmp += sizeof(*exp_if);
8138c2ecf20Sopenharmony_ci			}
8148c2ecf20Sopenharmony_ci			break;
8158c2ecf20Sopenharmony_ci		case PDR_HARDWARE_PLATFORM_COMPONENT_ID:
8168c2ecf20Sopenharmony_ci			if (data_len < 2)
8178c2ecf20Sopenharmony_ci				break;
8188c2ecf20Sopenharmony_ci			priv->version = *(u8 *)(entry->data + 1);
8198c2ecf20Sopenharmony_ci			break;
8208c2ecf20Sopenharmony_ci		case PDR_RSSI_LINEAR_APPROXIMATION:
8218c2ecf20Sopenharmony_ci		case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND:
8228c2ecf20Sopenharmony_ci		case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED:
8238c2ecf20Sopenharmony_ci			err = p54_parse_rssical(dev, entry->data, data_len,
8248c2ecf20Sopenharmony_ci						le16_to_cpu(entry->code));
8258c2ecf20Sopenharmony_ci			if (err)
8268c2ecf20Sopenharmony_ci				goto err;
8278c2ecf20Sopenharmony_ci			break;
8288c2ecf20Sopenharmony_ci		case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: {
8298c2ecf20Sopenharmony_ci			struct pda_custom_wrapper *pda = (void *) entry->data;
8308c2ecf20Sopenharmony_ci			__le16 *src;
8318c2ecf20Sopenharmony_ci			u16 *dst;
8328c2ecf20Sopenharmony_ci			int i;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci			if (priv->rssi_db || data_len < sizeof(*pda))
8358c2ecf20Sopenharmony_ci				break;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci			priv->rssi_db = p54_convert_db(pda, data_len);
8388c2ecf20Sopenharmony_ci			if (!priv->rssi_db)
8398c2ecf20Sopenharmony_ci				break;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci			src = (void *) priv->rssi_db->data;
8428c2ecf20Sopenharmony_ci			dst = (void *) priv->rssi_db->data;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci			for (i = 0; i < priv->rssi_db->entries; i++)
8458c2ecf20Sopenharmony_ci				*(dst++) = (s16) le16_to_cpu(*(src++));
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci			}
8488c2ecf20Sopenharmony_ci			break;
8498c2ecf20Sopenharmony_ci		case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: {
8508c2ecf20Sopenharmony_ci			struct pda_custom_wrapper *pda = (void *) entry->data;
8518c2ecf20Sopenharmony_ci			if (priv->output_limit || data_len < sizeof(*pda))
8528c2ecf20Sopenharmony_ci				break;
8538c2ecf20Sopenharmony_ci			priv->output_limit = p54_convert_db(pda, data_len);
8548c2ecf20Sopenharmony_ci			}
8558c2ecf20Sopenharmony_ci			break;
8568c2ecf20Sopenharmony_ci		case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: {
8578c2ecf20Sopenharmony_ci			struct pda_custom_wrapper *pda = (void *) entry->data;
8588c2ecf20Sopenharmony_ci			if (priv->curve_data || data_len < sizeof(*pda))
8598c2ecf20Sopenharmony_ci				break;
8608c2ecf20Sopenharmony_ci			priv->curve_data = p54_convert_db(pda, data_len);
8618c2ecf20Sopenharmony_ci			}
8628c2ecf20Sopenharmony_ci			break;
8638c2ecf20Sopenharmony_ci		case PDR_END:
8648c2ecf20Sopenharmony_ci			crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry));
8658c2ecf20Sopenharmony_ci			if (crc16 != le16_to_cpup((__le16 *)entry->data)) {
8668c2ecf20Sopenharmony_ci				wiphy_err(dev->wiphy, "eeprom failed checksum "
8678c2ecf20Sopenharmony_ci					 "test!\n");
8688c2ecf20Sopenharmony_ci				err = -ENOMSG;
8698c2ecf20Sopenharmony_ci				goto err;
8708c2ecf20Sopenharmony_ci			} else {
8718c2ecf20Sopenharmony_ci				goto good_eeprom;
8728c2ecf20Sopenharmony_ci			}
8738c2ecf20Sopenharmony_ci			break;
8748c2ecf20Sopenharmony_ci		default:
8758c2ecf20Sopenharmony_ci			break;
8768c2ecf20Sopenharmony_ci		}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci		crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2);
8798c2ecf20Sopenharmony_ci		entry = (void *)entry + (entry_len + 1) * 2;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n");
8838c2ecf20Sopenharmony_ci	err = -ENODATA;
8848c2ecf20Sopenharmony_ci	goto err;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_cigood_eeprom:
8878c2ecf20Sopenharmony_ci	if (!synth || !priv->iq_autocal || !priv->output_limit ||
8888c2ecf20Sopenharmony_ci	    !priv->curve_data) {
8898c2ecf20Sopenharmony_ci		wiphy_err(dev->wiphy,
8908c2ecf20Sopenharmony_ci			  "not all required entries found in eeprom!\n");
8918c2ecf20Sopenharmony_ci		err = -EINVAL;
8928c2ecf20Sopenharmony_ci		goto err;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	err = p54_generate_channel_lists(dev);
8988c2ecf20Sopenharmony_ci	if (err)
8998c2ecf20Sopenharmony_ci		goto err;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW)
9028c2ecf20Sopenharmony_ci		p54_init_xbow_synth(priv);
9038c2ecf20Sopenharmony_ci	if (!(synth & PDR_SYNTH_24_GHZ_DISABLED))
9048c2ecf20Sopenharmony_ci		dev->wiphy->bands[NL80211_BAND_2GHZ] =
9058c2ecf20Sopenharmony_ci			priv->band_table[NL80211_BAND_2GHZ];
9068c2ecf20Sopenharmony_ci	if (!(synth & PDR_SYNTH_5_GHZ_DISABLED))
9078c2ecf20Sopenharmony_ci		dev->wiphy->bands[NL80211_BAND_5GHZ] =
9088c2ecf20Sopenharmony_ci			priv->band_table[NL80211_BAND_5GHZ];
9098c2ecf20Sopenharmony_ci	if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED)
9108c2ecf20Sopenharmony_ci		priv->rx_diversity_mask = 3;
9118c2ecf20Sopenharmony_ci	if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED)
9128c2ecf20Sopenharmony_ci		priv->tx_diversity_mask = 3;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
9158c2ecf20Sopenharmony_ci		u8 perm_addr[ETH_ALEN];
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci		wiphy_warn(dev->wiphy,
9188c2ecf20Sopenharmony_ci			   "Invalid hwaddr! Using randomly generated MAC addr\n");
9198c2ecf20Sopenharmony_ci		eth_random_addr(perm_addr);
9208c2ecf20Sopenharmony_ci		SET_IEEE80211_PERM_ADDR(dev, perm_addr);
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	priv->cur_rssi = &p54_rssi_default;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n",
9268c2ecf20Sopenharmony_ci		   dev->wiphy->perm_addr, priv->version,
9278c2ecf20Sopenharmony_ci		   p54_rf_chips[priv->rxhw]);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	return 0;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_cierr:
9328c2ecf20Sopenharmony_ci	kfree(priv->iq_autocal);
9338c2ecf20Sopenharmony_ci	kfree(priv->output_limit);
9348c2ecf20Sopenharmony_ci	kfree(priv->curve_data);
9358c2ecf20Sopenharmony_ci	kfree(priv->rssi_db);
9368c2ecf20Sopenharmony_ci	kfree(priv->survey);
9378c2ecf20Sopenharmony_ci	priv->iq_autocal = NULL;
9388c2ecf20Sopenharmony_ci	priv->output_limit = NULL;
9398c2ecf20Sopenharmony_ci	priv->curve_data = NULL;
9408c2ecf20Sopenharmony_ci	priv->rssi_db = NULL;
9418c2ecf20Sopenharmony_ci	priv->survey = NULL;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	wiphy_err(dev->wiphy, "eeprom parse failed!\n");
9448c2ecf20Sopenharmony_ci	return err;
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(p54_parse_eeprom);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ciint p54_read_eeprom(struct ieee80211_hw *dev)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	struct p54_common *priv = dev->priv;
9518c2ecf20Sopenharmony_ci	size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize;
9528c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
9538c2ecf20Sopenharmony_ci	void *eeprom;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	maxblocksize = EEPROM_READBACK_LEN;
9568c2ecf20Sopenharmony_ci	if (priv->fw_var >= 0x509)
9578c2ecf20Sopenharmony_ci		maxblocksize -= 0xc;
9588c2ecf20Sopenharmony_ci	else
9598c2ecf20Sopenharmony_ci		maxblocksize -= 0x4;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	eeprom = kzalloc(eeprom_size, GFP_KERNEL);
9628c2ecf20Sopenharmony_ci	if (unlikely(!eeprom))
9638c2ecf20Sopenharmony_ci		goto free;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	while (eeprom_size) {
9668c2ecf20Sopenharmony_ci		blocksize = min(eeprom_size, maxblocksize);
9678c2ecf20Sopenharmony_ci		ret = p54_download_eeprom(priv, eeprom + offset,
9688c2ecf20Sopenharmony_ci					  offset, blocksize);
9698c2ecf20Sopenharmony_ci		if (unlikely(ret))
9708c2ecf20Sopenharmony_ci			goto free;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		offset += blocksize;
9738c2ecf20Sopenharmony_ci		eeprom_size -= blocksize;
9748c2ecf20Sopenharmony_ci	}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	ret = p54_parse_eeprom(dev, eeprom, offset);
9778c2ecf20Sopenharmony_cifree:
9788c2ecf20Sopenharmony_ci	kfree(eeprom);
9798c2ecf20Sopenharmony_ci	return ret;
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(p54_read_eeprom);
982