2013-08-10 10:27:46

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 00/12] brcm80211: new functionality and some cleanup fixes

This series adds new functionality to the brcm80211 drivers listed below:

AMPDU host-reordering:
The brcmfmac driver buffers individual A-MPDU packets
reducing memory requirement in the wireless device.

TDLS:
The 802.11 standards AP bypass is added to brcmfmac.

BCM4313 iPA:
This adds support to brcmsmac for a new variant of the
BCM4313. This was already submitted but reverted due
to regressions for other devices.

Other changes are brcmfmac cleanup work for firmware-signalling
feature and rework in SDIO-specific driver code.

This series is intended for v3.12 and applies to the master branch
of the wireless-next repository.

Arend van Spriel (5):
brcmfmac: use irq safe spinlock in brcmf_sdbrcm_txdata()
brcmfmac: .txdata() bus callback should not call brcmf_txcomplete()
brcmfmac: add AMPDU reordering functionality
brcmfmac: ignore IF event if firmware indicates it
brcmfmac: add support for manual TDLS operations

Franky Lin (4):
brcmfmac: abstract tx packet processing functions
brcmfmac: remove align from brcmf_bus structure
brcmfmac: streamline sdio bus header code
brcmfmac: use configurable sdio bus header length for tx packet

Hante Meuleman (2):
brcmfmac: always use worker thread for tx data.
brcmfmac: no fws locking outside fws module.

Piotr Haber (1):
brcmsmac: support 4313iPA

drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 16 +-
.../net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c | 1 -
drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 31 +-
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 8 +-
.../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 279 ++++++++++--
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 481 +++++++++++++--------
drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 4 +
.../net/wireless/brcm80211/brcmfmac/fwil_types.h | 21 +
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 228 +++++-----
.../net/wireless/brcm80211/brcmfmac/sdio_host.h | 2 +-
drivers/net/wireless/brcm80211/brcmfmac/usb.c | 1 -
.../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 57 ++-
.../net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c | 399 ++++++++++-------
.../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c | 289 ++++++++-----
.../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h | 1 +
include/linux/platform_data/brcmfmac-sdio.h | 6 +
16 files changed, 1228 insertions(+), 596 deletions(-)

--
1.8.1.3




2013-08-12 10:10:15

by Arend van Spriel

[permalink] [raw]
Subject: Re: [PATCH 12/12] brcmsmac: support 4313iPA

On 08/11/2013 02:48 PM, Jonas Gorski wrote:
> Hi,
>
> On Sat, Aug 10, 2013 at 12:27 PM, Arend van Spriel <[email protected]> wrote:
>> From: Piotr Haber <[email protected]>
>>
>> Add support for 4313 iPA variant.
>> It is a variant of already supported 4313 ePA,
>> so this patch adds the required PHY changes to
>> support it properly including an updated switch
>> control table for BT-combo card variants.
>
> Okay, I'll bite. Since this patch was already reverted once, it
> warrants some additional scrutiny.

That's the right attitude ;-) The revert made us cautious as well before
sending out this patch, but thanks for making the effort.

> First of all, the patch is quite large, and I wonder if it couldn't be
> split into smaller patches, especially as it looks like there are
> additional fixes/changes merged in it.

It indeed seems rather large. The original work from Piotr were two
patches that I squashed. I will break up this patch in more individual
patches.

John,

Can you drop this patch from the series?

> Detailed comments below ...
>
>> Tested-by: Maximilian Engelhardt <[email protected]>
>> Tested-by: David Costa <[email protected]>
>> Reviewed-by: Arend Van Spriel <[email protected]>
>> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
>> Signed-off-by: Piotr Haber <[email protected]>
>> Signed-off-by: Arend van Spriel <[email protected]>
>> ---
>
> This is obviously a V2 (or V(n+1) where n was the reverted version),
> so there should be something describing the changes to the reverted
> version. Why should we trust it now to not break things again? (Yes, I
> see those Tested-bys ;-)

This is a gray area. The original patch was taken into the tree so I
considered this to be a new patch.

We tested with a number of 4313 variants having some extra shipped that
we did not have at the time of the original patch. I looked up the
revert patch and noticed it is tagged with Reported-by. So I will ask
David Herrmann to test V(n+2) before sending it (if you don't mind n
will be 0).

>> .../net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c | 399 +++++++++++++--------
>> .../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c | 289 +++++++++------
>> .../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h | 1 +
>> 3 files changed, 436 insertions(+), 253 deletions(-)
>>
>> diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
>> index 3d6b16c..b8ddaad 100644
>> --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
>> +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
>> @@ -1137,8 +1137,9 @@ wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi,
>> gain0_15 = ((biq1 & 0xf) << 12) |
>> ((tia & 0xf) << 8) |
>> ((lna2 & 0x3) << 6) |
>> - ((lna2 &
>> - 0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0);
>> + ((lna2 & 0x3) << 4) |
>> + ((lna1 & 0x3) << 2) |
>> + ((lna1 & 0x3) << 0);
>
> Unrelated style change.

I will put this in a separate patch.

>>
>> mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0);
>> mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0);
>> @@ -1328,6 +1329,43 @@ static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples)
>> return (iq_est.i_pwr + iq_est.q_pwr) / nsamples;
>> }
>>
>> +static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain,
>> + u16 tia_gain, u16 lna2_gain)
>> +{
>> + u32 i_thresh_l, q_thresh_l;
>> + u32 i_thresh_h, q_thresh_h;
>> + struct lcnphy_iq_est iq_est_h, iq_est_l;
>> +
>> + wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 0, biq1_gain, tia_gain,
>> + lna2_gain, 0);
>> +
>> + wlc_lcnphy_rx_gain_override_enable(pi, true);
>> + wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0);
>> + udelay(500);
>> + write_radio_reg(pi, RADIO_2064_REG112, 0);
>> + if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l))
>> + return false;
>> +
>> + wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0);
>> + udelay(500);
>> + write_radio_reg(pi, RADIO_2064_REG112, 0);
>> + if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h))
>> + return false;
>> +
>> + i_thresh_l = (iq_est_l.i_pwr << 1);
>> + i_thresh_h = (iq_est_l.i_pwr << 2) + iq_est_l.i_pwr;
>> +
>> + q_thresh_l = (iq_est_l.q_pwr << 1);
>> + q_thresh_h = (iq_est_l.q_pwr << 2) + iq_est_l.q_pwr;
>
> So X_thresh_l == iq_est_l.X_pwr * 2 and X_thresh_h == iq_est_l.X_pwr *
> 5? Why not trust the compiler to use optimize it? It would be a bit
> more readable.

This is a bit personal. A C programmer is probably as familiar with
bitwise operators as with arithmetic. Anyways, my main reason for having
it this way is maintainability as it closely resembles the internal
driver code base. So I tend to leave it as is.

>> + if ((iq_est_h.i_pwr > i_thresh_l) &&
>> + (iq_est_h.i_pwr < i_thresh_h) &&
>> + (iq_est_h.q_pwr > q_thresh_l) &&
>> + (iq_est_h.q_pwr < q_thresh_h))
>> + return true;
>> +
>> + return false;
>> +}
>> +
>> static bool
>> wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
>> const struct lcnphy_rx_iqcomp *iqcomp,
>> @@ -1342,8 +1380,8 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
>> RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old,
>> rfoverride3_old, rfoverride3val_old, rfoverride4_old,
>> rfoverride4val_old, afectrlovr_old, afectrlovrval_old;
>> - int tia_gain;
>> - u32 received_power, rx_pwr_threshold;
>> + int tia_gain, lna2_gain, biq1_gain;
>> + bool set_gain;
>> u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl;
>> u16 values_to_save[11];
>> s16 *ptr;
>> @@ -1368,126 +1406,134 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
>> goto cal_done;
>> }
>>
>> - if (module == 1) {
>> + WARN_ON(module != 1);
>> + tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
>> + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
>>
>> - tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
>> - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
>
> This indentation change makes it a bit harder to review, but luckily
> there is git show -b ...

I will do the indentation change with a separate patch.

>> + for (i = 0; i < 11; i++)
>> + values_to_save[i] =
>> + read_radio_reg(pi, rxiq_cal_rf_reg[i]);
>> + Core1TxControl_old = read_phy_reg(pi, 0x631);
>> +
>> + or_phy_reg(pi, 0x631, 0x0015);
>> +
>> + RFOverride0_old = read_phy_reg(pi, 0x44c);
>> + RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
>> + rfoverride2_old = read_phy_reg(pi, 0x4b0);
>> + rfoverride2val_old = read_phy_reg(pi, 0x4b1);
>> + rfoverride3_old = read_phy_reg(pi, 0x4f9);
>> + rfoverride3val_old = read_phy_reg(pi, 0x4fa);
>> + rfoverride4_old = read_phy_reg(pi, 0x938);
>> + rfoverride4val_old = read_phy_reg(pi, 0x939);
>> + afectrlovr_old = read_phy_reg(pi, 0x43b);
>> + afectrlovrval_old = read_phy_reg(pi, 0x43c);
>> + old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
>> + old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
>>
>> - for (i = 0; i < 11; i++)
>> - values_to_save[i] =
>> - read_radio_reg(pi, rxiq_cal_rf_reg[i]);
>> - Core1TxControl_old = read_phy_reg(pi, 0x631);
>> -
>> - or_phy_reg(pi, 0x631, 0x0015);
>> -
>> - RFOverride0_old = read_phy_reg(pi, 0x44c);
>> - RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
>> - rfoverride2_old = read_phy_reg(pi, 0x4b0);
>> - rfoverride2val_old = read_phy_reg(pi, 0x4b1);
>> - rfoverride3_old = read_phy_reg(pi, 0x4f9);
>> - rfoverride3val_old = read_phy_reg(pi, 0x4fa);
>> - rfoverride4_old = read_phy_reg(pi, 0x938);
>> - rfoverride4val_old = read_phy_reg(pi, 0x939);
>> - afectrlovr_old = read_phy_reg(pi, 0x43b);
>> - afectrlovrval_old = read_phy_reg(pi, 0x43c);
>> - old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
>> - old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
>> -
>> - tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
>> - if (tx_gain_override_old) {
>> - wlc_lcnphy_get_tx_gain(pi, &old_gains);
>> - tx_gain_index_old = pi_lcn->lcnphy_current_index;
>> - }
>> + tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
>> + if (tx_gain_override_old) {
>> + wlc_lcnphy_get_tx_gain(pi, &old_gains);
>> + tx_gain_index_old = pi_lcn->lcnphy_current_index;
>> + }
>>
>> - wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
>> + wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
>>
>> - mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
>> - mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
>> + mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
>> + mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
>>
>> - mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
>> - mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
>> + mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
>> + mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
>>
>> - write_radio_reg(pi, RADIO_2064_REG116, 0x06);
>> - write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
>> - write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
>> - write_radio_reg(pi, RADIO_2064_REG098, 0x03);
>> - write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
>> - mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
>> - write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
>> - write_radio_reg(pi, RADIO_2064_REG114, 0x01);
>> - write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
>> - write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
>> -
>> - mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
>> - mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
>> - mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
>> - mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
>> - mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
>> - mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
>> - mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
>> - mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
>> - mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
>> - mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
>> -
>> - mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
>> - mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
>> -
>> - wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0);
>> - write_phy_reg(pi, 0x6da, 0xffff);
>> - or_phy_reg(pi, 0x6db, 0x3);
>> - wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
>> - wlc_lcnphy_rx_gain_override_enable(pi, true);
>> -
>> - tia_gain = 8;
>> - rx_pwr_threshold = 950;
>> - while (tia_gain > 0) {
>> - tia_gain -= 1;
>> - wlc_lcnphy_set_rx_gain_by_distribution(pi,
>> - 0, 0, 2, 2,
>> - (u16)
>> - tia_gain, 1, 0);
>> - udelay(500);
>> + write_radio_reg(pi, RADIO_2064_REG116, 0x06);
>> + write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
>> + write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
>> + write_radio_reg(pi, RADIO_2064_REG098, 0x03);
>> + write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
>> + mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
>> + write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
>> + write_radio_reg(pi, RADIO_2064_REG114, 0x01);
>> + write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
>> + write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
>> +
>> + mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
>> + mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
>> + mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
>> + mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
>> + mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
>> + mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
>> + mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
>> + mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
>> + mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
>> + mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
>>
>> - received_power =
>> - wlc_lcnphy_measure_digital_power(pi, 2000);
>> - if (received_power < rx_pwr_threshold)
>> - break;
>> + mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
>> + mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
>> +
>> + write_phy_reg(pi, 0x6da, 0xffff);
>> + or_phy_reg(pi, 0x6db, 0x3);
>> +
>> + wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
>> + set_gain = false;
>> +
>> + lna2_gain = 3;
>> + while ((lna2_gain >= 0) && !set_gain) {
>> + tia_gain = 4;
>> +
>> + while ((tia_gain >= 0) && !set_gain) {
>> + biq1_gain = 6;
>> +
>> + while ((biq1_gain >= 0) && !set_gain) {
>> + set_gain = wlc_lcnphy_rx_iq_cal_gain(pi,
>> + (u16)
>> + biq1_gain,
>> + (u16)
>> + tia_gain,
>> + (u16)
>> + lna2_gain);
>> + biq1_gain -= 1;
>> + }
>> + tia_gain -= 1;
>> }
>> - result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff);
>> + lna2_gain -= 1;
>> + }
>
> This looks like it could be made more readable using for loops and a goto:
>
> for (lna_gain = 3; lna_gain >= 0; lna_gain--) {
> for (tia_gain = 4; tia_gain >= 0; tia_gain--) {
> for (big1_gain = 6; big1_gain >= 0; big1_gain--) {
> set_gain = wlc_lcnphy_rx_iq_cal_gain(...);
> if (set_gain)
> goto found;
> }
> }
> }
> found:
> ...

That is a sensible change. Will do.

>>
>> - wlc_lcnphy_stop_tx_tone(pi);
>> + if (set_gain)
>> + result = wlc_lcnphy_calc_rx_iq_comp(pi, 1024);
>> + else
>> + result = false;
>>
>> - write_phy_reg(pi, 0x631, Core1TxControl_old);
>> + wlc_lcnphy_stop_tx_tone(pi);
>>
>> - write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
>> - write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
>> - write_phy_reg(pi, 0x4b0, rfoverride2_old);
>> - write_phy_reg(pi, 0x4b1, rfoverride2val_old);
>> - write_phy_reg(pi, 0x4f9, rfoverride3_old);
>> - write_phy_reg(pi, 0x4fa, rfoverride3val_old);
>> - write_phy_reg(pi, 0x938, rfoverride4_old);
>> - write_phy_reg(pi, 0x939, rfoverride4val_old);
>> - write_phy_reg(pi, 0x43b, afectrlovr_old);
>> - write_phy_reg(pi, 0x43c, afectrlovrval_old);
>> - write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
>> - write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
>> + write_phy_reg(pi, 0x631, Core1TxControl_old);
>> +
>> + write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
>> + write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
>> + write_phy_reg(pi, 0x4b0, rfoverride2_old);
>> + write_phy_reg(pi, 0x4b1, rfoverride2val_old);
>> + write_phy_reg(pi, 0x4f9, rfoverride3_old);
>> + write_phy_reg(pi, 0x4fa, rfoverride3val_old);
>> + write_phy_reg(pi, 0x938, rfoverride4_old);
>> + write_phy_reg(pi, 0x939, rfoverride4val_old);
>> + write_phy_reg(pi, 0x43b, afectrlovr_old);
>> + write_phy_reg(pi, 0x43c, afectrlovrval_old);
>> + write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
>> + write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
>>
>> - wlc_lcnphy_clear_trsw_override(pi);
>> + wlc_lcnphy_clear_trsw_override(pi);
>>
>> - mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
>> + mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
>>
>> - for (i = 0; i < 11; i++)
>> - write_radio_reg(pi, rxiq_cal_rf_reg[i],
>> - values_to_save[i]);
>> + for (i = 0; i < 11; i++)
>> + write_radio_reg(pi, rxiq_cal_rf_reg[i],
>> + values_to_save[i]);
>>
>> - if (tx_gain_override_old)
>> - wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
>> - else
>> - wlc_lcnphy_disable_tx_gain_override(pi);
>> + if (tx_gain_override_old)
>> + wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
>> + else
>> + wlc_lcnphy_disable_tx_gain_override(pi);
>>
>> - wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
>> - wlc_lcnphy_rx_gain_override_enable(pi, false);
>> - }
>> + wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
>> + wlc_lcnphy_rx_gain_override_enable(pi, false);
>>
>> cal_done:
>> kfree(ptr);
>> @@ -1789,6 +1835,17 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel)
>> write_radio_reg(pi, RADIO_2064_REG038, 3);
>> write_radio_reg(pi, RADIO_2064_REG091, 7);
>> }
>> +
>> + if (!(pi->sh->boardflags & BFL_FEM)) {
>> + u8 reg038[14] = {0xd, 0xe, 0xd, 0xd, 0xd, 0xc,
>> + 0xa, 0xb, 0xb, 0x3, 0x3, 0x2, 0x0, 0x0};
>> +
>> + write_radio_reg(pi, RADIO_2064_REG02A, 0xf);
>> + write_radio_reg(pi, RADIO_2064_REG091, 0x3);
>> + write_radio_reg(pi, RADIO_2064_REG038, 0x3);
>> +
>> + write_radio_reg(pi, RADIO_2064_REG038, reg038[channel - 1]);
>> + }
>> }
>>
>> static int
>> @@ -1983,6 +2040,16 @@ wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos)
>> } else {
>> mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1);
>> mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
>> + mod_radio_reg(pi, RADIO_2064_REG028, 0x1, 0x0);
>> + mod_radio_reg(pi, RADIO_2064_REG11A, 0x4, 1<<2);
>> + mod_radio_reg(pi, RADIO_2064_REG036, 0x10, 0x0);
>> + mod_radio_reg(pi, RADIO_2064_REG11A, 0x10, 1<<4);
>> + mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
>> + mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x77);
>> + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0xe<<1);
>> + mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1<<7);
>> + mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 1<<1);
>> + mod_radio_reg(pi, RADIO_2064_REG029, 0xf0, 0<<4);
>
> What does this do? This seems to be applied regardless of iPA or ePA,
> so I looks like it fixes something? Or is this needed for iPA and
> harmless for ePA?

When reviewing this patch that was one of my comments as well. It was
added because the iPA changes were ported from a more recent branch of
our internal driver.

What changed is the TSSI algorithm for both ePA and iPA. I will try to
create a separate patch for that for ePA. Most of the comments below are
related to this.

>> }
>> } else {
>> mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2);
>> @@ -2069,12 +2136,14 @@ static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi)
>> (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12));
>>
>> mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5));
>> + mod_radio_reg(pi, RADIO_2064_REG07C, (1 << 0), (1 << 0));
>
> Same here.
>
>> }
>>
>> static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
>> {
>> struct phytbl_info tab;
>> u32 rfseq, ind;
>> + u8 tssi_sel;
>>
>> tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
>> tab.tbl_width = 32;
>> @@ -2096,7 +2165,13 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
>>
>> mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4);
>>
>> - wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT);
>> + if (pi->sh->boardflags & BFL_FEM) {
>> + tssi_sel = 0x1;
>> + wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT);
>> + } else {
>> + tssi_sel = 0xe;
>> + wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_POST_PA);
>> + }
>
> Doesn't this change change tssi_sel from 0xe to 0x1 for (already
> supported) ePA cards, and sets it to the old value 0xe for iPA ones? I
> would have expected 0xe for BFL_FEM /ePA and 0x1 for iPA ... .
>
>> mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14);
>>
>> mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15);
>> @@ -2132,9 +2207,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
>> mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0);
>>
>> if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
>> - mod_radio_reg(pi, RADIO_2064_REG028, 0xf, 0xe);
>> + mod_radio_reg(pi, RADIO_2064_REG028, 0xf, tssi_sel);
>> mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4);
>> } else {
>> + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, tssi_sel << 1);
>
> Okay, this one is new also for the ePA case, so I wonder why this
> wasn't needed before?
>
>> mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1);
>> mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3);
>> }
>> @@ -2181,6 +2257,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
>>
>> mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8);
>>
>> + mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x0);
>> + mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
>> + mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
>> +
>
> Same for these three writes.
>
>> wlc_lcnphy_pwrctrl_rssiparams(pi);
>> }
>>
>> @@ -2799,6 +2879,8 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
>> read_radio_reg(pi, RADIO_2064_REG007) & 1;
>> u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10;
>> u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4;
>> + u8 SAVE_bbmult = wlc_lcnphy_get_bbmult(pi);
>> +
>
> These changes also look at a first glance unrelated to iPA, as well as ...
>
>> idleTssi = read_phy_reg(pi, 0x4ab);
>> suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) &
>> MCTL_EN_MAC));
>> @@ -2816,6 +2898,12 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
>> mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4);
>> mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2);
>> wlc_lcnphy_tssi_setup(pi);
>> +
>> + mod_phy_reg(pi, 0x4d7, (0x1 << 0), (1 << 0));
>> + mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1 << 6));
>> +
>> + wlc_lcnphy_set_bbmult(pi, 0x0);
>> +
>
> These ones.
>
>> wlc_phy_do_dummy_tx(pi, true, OFF);
>> idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0))
>> >> 0);
>> @@ -2837,6 +2925,7 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
>>
>> mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12);
>>
>> + wlc_lcnphy_set_bbmult(pi, SAVE_bbmult);
>> wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old);
>> wlc_lcnphy_set_tx_gain(pi, &old_gains);
>> wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl);
>> @@ -3050,6 +3139,11 @@ static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi)
>> wlc_lcnphy_write_table(pi, &tab);
>> tab.tbl_offset++;
>> }
>> + mod_phy_reg(pi, 0x4d0, (0x1 << 0), (0) << 0);
>> + mod_phy_reg(pi, 0x4d3, (0xff << 0), (0) << 0);
>> + mod_phy_reg(pi, 0x4d3, (0xff << 8), (0) << 8);
>> + mod_phy_reg(pi, 0x4d0, (0x1 << 4), (0) << 4);
>> + mod_phy_reg(pi, 0x4d0, (0x1 << 2), (0) << 2);
>>
>> mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7);
>>
>> @@ -3851,7 +3945,6 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
>> target_gains.pad_gain = 21;
>> target_gains.dac_gain = 0;
>> wlc_lcnphy_set_tx_gain(pi, &target_gains);
>> - wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
>>
>> if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) {
>>
>> @@ -3862,6 +3955,7 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
>> lcnphy_recal ? LCNPHY_CAL_RECAL :
>> LCNPHY_CAL_FULL), false);
>> } else {
>> + wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
>> wlc_lcnphy_tx_iqlo_soft_cal_full(pi);
>> }
>>
>> @@ -4286,17 +4380,22 @@ wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi,
>> if (CHSPEC_IS5G(pi->radio_chanspec))
>> pa_gain = 0x70;
>> else
>> - pa_gain = 0x70;
>> + pa_gain = 0x60;
>
> You are reducing the value for !BFL_FEM, I assume this is a fix for something?

Your guess is as good as mine. Internally, we get phy code deliveries
from system engineering team and unfortunately the changelog is pretty
high-level.

>>
>> if (pi->sh->boardflags & BFL_FEM)
>> pa_gain = 0x10;
>> +
>
> Unnecessary whitespace change.

Probably, Whitespace jEdit plugin did that for me. Sorry about that.

>> tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
>> tab.tbl_width = 32;
>> tab.tbl_len = 1;
>> tab.tbl_ptr = &val;
>>
>> for (j = 0; j < 128; j++) {
>> - gm_gain = gain_table[j].gm;
>> + if (pi->sh->boardflags & BFL_FEM)
>> + gm_gain = gain_table[j].gm;
>> + else
>> + gm_gain = 15;
>> +
>> val = (((u32) pa_gain << 24) |
>> (gain_table[j].pad << 16) |
>> (gain_table[j].pga << 8) | gm_gain);
>> @@ -4507,7 +4606,10 @@ static void wlc_radio_2064_init(struct brcms_phy *pi)
>>
>> write_phy_reg(pi, 0x4ea, 0x4688);
>>
>> - mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
>> + if (pi->sh->boardflags & BFL_FEM)
>> + mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
>> + else
>> + mod_phy_reg(pi, 0x4eb, (0x7 << 0), 3 << 0);
>>
>> mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6);
>>
>> @@ -4518,6 +4620,13 @@ static void wlc_radio_2064_init(struct brcms_phy *pi)
>> wlc_lcnphy_rcal(pi);
>>
>> wlc_lcnphy_rc_cal(pi);
>> +
>> + if (!(pi->sh->boardflags & BFL_FEM)) {
>> + write_radio_reg(pi, RADIO_2064_REG032, 0x6f);
>> + write_radio_reg(pi, RADIO_2064_REG033, 0x19);
>> + write_radio_reg(pi, RADIO_2064_REG039, 0xe);
>> + }
>> +
>> }
>>
>> static void wlc_lcnphy_radio_init(struct brcms_phy *pi)
>> @@ -4530,6 +4639,7 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
>> uint idx;
>> u8 phybw40;
>> struct phytbl_info tab;
>> + const struct phytbl_info *tb;
>> u32 val;
>>
>> phybw40 = CHSPEC_IS40(pi->radio_chanspec);
>> @@ -4547,22 +4657,20 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
>> wlc_lcnphy_write_table(pi, &tab);
>> }
>>
>> - tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
>> - tab.tbl_width = 16;
>> - tab.tbl_ptr = &val;
>> - tab.tbl_len = 1;
>> -
>> - val = 114;
>> - tab.tbl_offset = 0;
>> - wlc_lcnphy_write_table(pi, &tab);
>> + if (!(pi->sh->boardflags & BFL_FEM)) {
>> + tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
>> + tab.tbl_width = 16;
>> + tab.tbl_ptr = &val;
>> + tab.tbl_len = 1;
>>
>> - val = 130;
>> - tab.tbl_offset = 1;
>> - wlc_lcnphy_write_table(pi, &tab);
>> + val = 150;
>> + tab.tbl_offset = 0;
>> + wlc_lcnphy_write_table(pi, &tab);
>>
>> - val = 6;
>> - tab.tbl_offset = 8;
>> - wlc_lcnphy_write_table(pi, &tab);
>> + val = 220;
>> + tab.tbl_offset = 1;
>> + wlc_lcnphy_write_table(pi, &tab);
>> + }
>
> So this isn't needed anymore for ePA cards?

Suspicious. I will look into this one.

>>
>> if (CHSPEC_IS2G(pi->radio_chanspec)) {
>> if (pi->sh->boardflags & BFL_FEM)
>> @@ -4576,7 +4684,6 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
>> }
>>
>> if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
>> - const struct phytbl_info *tb;
>> int l;
>>
>> if (CHSPEC_IS2G(pi->radio_chanspec)) {
>> @@ -4597,21 +4704,22 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
>> wlc_lcnphy_write_table(pi, &tb[idx]);
>> }
>>
>> - if ((pi->sh->boardflags & BFL_FEM)
>> - && !(pi->sh->boardflags & BFL_FEM_BT))
>> - wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313_epa);
>> - else if (pi->sh->boardflags & BFL_FEM_BT) {
>> - if (pi->sh->boardrev < 0x1250)
>> - wlc_lcnphy_write_table(
>> - pi,
>> - &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa);
>> + if (pi->sh->boardflags & BFL_FEM) {
>> + if (pi->sh->boardflags & BFL_FEM_BT) {
>> + if (pi->sh->boardrev < 0x1250)
>> + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
>> + else
>> + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250;
>> + } else {
>> + tb = &dot11lcn_sw_ctrl_tbl_info_4313_epa;
>> + }
>> + } else {
>> + if (pi->sh->boardflags & BFL_FEM_BT)
>> + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa;
>> else
>> - wlc_lcnphy_write_table(
>> - pi,
>> - &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250);
>> - } else
>> - wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313);
>> -
>> + tb = &dot11lcn_sw_ctrl_tbl_info_4313;
>> + }
>> + wlc_lcnphy_write_table(pi, tb);
>> wlc_lcnphy_load_rfpower(pi);
>>
>> wlc_lcnphy_clear_papd_comptable(pi);
>> @@ -4955,6 +5063,8 @@ void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec)
>> wlc_lcnphy_load_tx_iir_filter(pi, true, 3);
>>
>> mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3);
>> + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
>> + wlc_lcnphy_tssi_setup(pi);
>> }
>>
>> void wlc_phy_detach_lcnphy(struct brcms_phy *pi)
>> @@ -4993,8 +5103,7 @@ bool wlc_phy_attach_lcnphy(struct brcms_phy *pi)
>> if (!wlc_phy_txpwr_srom_read_lcnphy(pi))
>> return false;
>>
>> - if ((pi->sh->boardflags & BFL_FEM) &&
>> - (LCNREV_IS(pi->pubpi.phy_rev, 1))) {
>> + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
>> if (pi_lcn->lcnphy_tempsense_option == 3) {
>> pi->hwpwrctrl = true;
>> pi->hwpwrctrl_capable = true;
>> diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
>> index 622c01c..9fb0ca2 100644
>> --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
>> +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
>> @@ -2058,6 +2058,73 @@ static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = {
>> 0x0005,
>> };
>>
>> +static const u16 dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[] = {
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> + 0x0005,
>> + 0x0006,
>> + 0x0009,
>> + 0x000a,
>> +};
>> +
>> static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = {
>> 0x0004,
>> 0x0004,
>> @@ -2834,6 +2901,12 @@ const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = {
>> sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16
>> };
>>
>> +const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa = {
>> + &dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo,
>> + sizeof(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo) /
>> + sizeof(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[0]), 15, 0, 16
>
> Not necessarily in this patch but maybe in a follow up cleanup patch:
> ARRAY_SIZE() please?

As I am reworking the patch, I will incorporate this comment as well.

>
>
> I guess that's it from me.

Thanks again,
Arend



2013-08-10 10:28:07

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 12/12] brcmsmac: support 4313iPA

From: Piotr Haber <[email protected]>

Add support for 4313 iPA variant.
It is a variant of already supported 4313 ePA,
so this patch adds the required PHY changes to
support it properly including an updated switch
control table for BT-combo card variants.

Tested-by: Maximilian Engelhardt <[email protected]>
Tested-by: David Costa <[email protected]>
Reviewed-by: Arend Van Spriel <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Piotr Haber <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
.../net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c | 399 +++++++++++++--------
.../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c | 289 +++++++++------
.../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h | 1 +
3 files changed, 436 insertions(+), 253 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
index 3d6b16c..b8ddaad 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
@@ -1137,8 +1137,9 @@ wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi,
gain0_15 = ((biq1 & 0xf) << 12) |
((tia & 0xf) << 8) |
((lna2 & 0x3) << 6) |
- ((lna2 &
- 0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0);
+ ((lna2 & 0x3) << 4) |
+ ((lna1 & 0x3) << 2) |
+ ((lna1 & 0x3) << 0);

mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0);
mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0);
@@ -1328,6 +1329,43 @@ static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples)
return (iq_est.i_pwr + iq_est.q_pwr) / nsamples;
}

+static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain,
+ u16 tia_gain, u16 lna2_gain)
+{
+ u32 i_thresh_l, q_thresh_l;
+ u32 i_thresh_h, q_thresh_h;
+ struct lcnphy_iq_est iq_est_h, iq_est_l;
+
+ wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 0, biq1_gain, tia_gain,
+ lna2_gain, 0);
+
+ wlc_lcnphy_rx_gain_override_enable(pi, true);
+ wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0);
+ udelay(500);
+ write_radio_reg(pi, RADIO_2064_REG112, 0);
+ if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l))
+ return false;
+
+ wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0);
+ udelay(500);
+ write_radio_reg(pi, RADIO_2064_REG112, 0);
+ if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h))
+ return false;
+
+ i_thresh_l = (iq_est_l.i_pwr << 1);
+ i_thresh_h = (iq_est_l.i_pwr << 2) + iq_est_l.i_pwr;
+
+ q_thresh_l = (iq_est_l.q_pwr << 1);
+ q_thresh_h = (iq_est_l.q_pwr << 2) + iq_est_l.q_pwr;
+ if ((iq_est_h.i_pwr > i_thresh_l) &&
+ (iq_est_h.i_pwr < i_thresh_h) &&
+ (iq_est_h.q_pwr > q_thresh_l) &&
+ (iq_est_h.q_pwr < q_thresh_h))
+ return true;
+
+ return false;
+}
+
static bool
wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
const struct lcnphy_rx_iqcomp *iqcomp,
@@ -1342,8 +1380,8 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old,
rfoverride3_old, rfoverride3val_old, rfoverride4_old,
rfoverride4val_old, afectrlovr_old, afectrlovrval_old;
- int tia_gain;
- u32 received_power, rx_pwr_threshold;
+ int tia_gain, lna2_gain, biq1_gain;
+ bool set_gain;
u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl;
u16 values_to_save[11];
s16 *ptr;
@@ -1368,126 +1406,134 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
goto cal_done;
}

- if (module == 1) {
+ WARN_ON(module != 1);
+ tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
+ wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);

- tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
- wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
+ for (i = 0; i < 11; i++)
+ values_to_save[i] =
+ read_radio_reg(pi, rxiq_cal_rf_reg[i]);
+ Core1TxControl_old = read_phy_reg(pi, 0x631);
+
+ or_phy_reg(pi, 0x631, 0x0015);
+
+ RFOverride0_old = read_phy_reg(pi, 0x44c);
+ RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
+ rfoverride2_old = read_phy_reg(pi, 0x4b0);
+ rfoverride2val_old = read_phy_reg(pi, 0x4b1);
+ rfoverride3_old = read_phy_reg(pi, 0x4f9);
+ rfoverride3val_old = read_phy_reg(pi, 0x4fa);
+ rfoverride4_old = read_phy_reg(pi, 0x938);
+ rfoverride4val_old = read_phy_reg(pi, 0x939);
+ afectrlovr_old = read_phy_reg(pi, 0x43b);
+ afectrlovrval_old = read_phy_reg(pi, 0x43c);
+ old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
+ old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);

- for (i = 0; i < 11; i++)
- values_to_save[i] =
- read_radio_reg(pi, rxiq_cal_rf_reg[i]);
- Core1TxControl_old = read_phy_reg(pi, 0x631);
-
- or_phy_reg(pi, 0x631, 0x0015);
-
- RFOverride0_old = read_phy_reg(pi, 0x44c);
- RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
- rfoverride2_old = read_phy_reg(pi, 0x4b0);
- rfoverride2val_old = read_phy_reg(pi, 0x4b1);
- rfoverride3_old = read_phy_reg(pi, 0x4f9);
- rfoverride3val_old = read_phy_reg(pi, 0x4fa);
- rfoverride4_old = read_phy_reg(pi, 0x938);
- rfoverride4val_old = read_phy_reg(pi, 0x939);
- afectrlovr_old = read_phy_reg(pi, 0x43b);
- afectrlovrval_old = read_phy_reg(pi, 0x43c);
- old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
- old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
-
- tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
- if (tx_gain_override_old) {
- wlc_lcnphy_get_tx_gain(pi, &old_gains);
- tx_gain_index_old = pi_lcn->lcnphy_current_index;
- }
+ tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
+ if (tx_gain_override_old) {
+ wlc_lcnphy_get_tx_gain(pi, &old_gains);
+ tx_gain_index_old = pi_lcn->lcnphy_current_index;
+ }

- wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
+ wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);

- mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
+ mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);

- mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
+ mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);

- write_radio_reg(pi, RADIO_2064_REG116, 0x06);
- write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
- write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
- write_radio_reg(pi, RADIO_2064_REG098, 0x03);
- write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
- mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
- write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
- write_radio_reg(pi, RADIO_2064_REG114, 0x01);
- write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
- write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
-
- mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
- mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
- mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
- mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
- mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
- mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
- mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
-
- mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
-
- wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0);
- write_phy_reg(pi, 0x6da, 0xffff);
- or_phy_reg(pi, 0x6db, 0x3);
- wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
- wlc_lcnphy_rx_gain_override_enable(pi, true);
-
- tia_gain = 8;
- rx_pwr_threshold = 950;
- while (tia_gain > 0) {
- tia_gain -= 1;
- wlc_lcnphy_set_rx_gain_by_distribution(pi,
- 0, 0, 2, 2,
- (u16)
- tia_gain, 1, 0);
- udelay(500);
+ write_radio_reg(pi, RADIO_2064_REG116, 0x06);
+ write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
+ write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
+ write_radio_reg(pi, RADIO_2064_REG098, 0x03);
+ write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
+ mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
+ write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
+ write_radio_reg(pi, RADIO_2064_REG114, 0x01);
+ write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
+ write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
+
+ mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
+ mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
+ mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
+ mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
+ mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
+ mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
+ mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);

- received_power =
- wlc_lcnphy_measure_digital_power(pi, 2000);
- if (received_power < rx_pwr_threshold)
- break;
+ mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
+
+ write_phy_reg(pi, 0x6da, 0xffff);
+ or_phy_reg(pi, 0x6db, 0x3);
+
+ wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
+ set_gain = false;
+
+ lna2_gain = 3;
+ while ((lna2_gain >= 0) && !set_gain) {
+ tia_gain = 4;
+
+ while ((tia_gain >= 0) && !set_gain) {
+ biq1_gain = 6;
+
+ while ((biq1_gain >= 0) && !set_gain) {
+ set_gain = wlc_lcnphy_rx_iq_cal_gain(pi,
+ (u16)
+ biq1_gain,
+ (u16)
+ tia_gain,
+ (u16)
+ lna2_gain);
+ biq1_gain -= 1;
+ }
+ tia_gain -= 1;
}
- result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff);
+ lna2_gain -= 1;
+ }

- wlc_lcnphy_stop_tx_tone(pi);
+ if (set_gain)
+ result = wlc_lcnphy_calc_rx_iq_comp(pi, 1024);
+ else
+ result = false;

- write_phy_reg(pi, 0x631, Core1TxControl_old);
+ wlc_lcnphy_stop_tx_tone(pi);

- write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
- write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
- write_phy_reg(pi, 0x4b0, rfoverride2_old);
- write_phy_reg(pi, 0x4b1, rfoverride2val_old);
- write_phy_reg(pi, 0x4f9, rfoverride3_old);
- write_phy_reg(pi, 0x4fa, rfoverride3val_old);
- write_phy_reg(pi, 0x938, rfoverride4_old);
- write_phy_reg(pi, 0x939, rfoverride4val_old);
- write_phy_reg(pi, 0x43b, afectrlovr_old);
- write_phy_reg(pi, 0x43c, afectrlovrval_old);
- write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
- write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
+ write_phy_reg(pi, 0x631, Core1TxControl_old);
+
+ write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
+ write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
+ write_phy_reg(pi, 0x4b0, rfoverride2_old);
+ write_phy_reg(pi, 0x4b1, rfoverride2val_old);
+ write_phy_reg(pi, 0x4f9, rfoverride3_old);
+ write_phy_reg(pi, 0x4fa, rfoverride3val_old);
+ write_phy_reg(pi, 0x938, rfoverride4_old);
+ write_phy_reg(pi, 0x939, rfoverride4val_old);
+ write_phy_reg(pi, 0x43b, afectrlovr_old);
+ write_phy_reg(pi, 0x43c, afectrlovrval_old);
+ write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
+ write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);

- wlc_lcnphy_clear_trsw_override(pi);
+ wlc_lcnphy_clear_trsw_override(pi);

- mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
+ mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);

- for (i = 0; i < 11; i++)
- write_radio_reg(pi, rxiq_cal_rf_reg[i],
- values_to_save[i]);
+ for (i = 0; i < 11; i++)
+ write_radio_reg(pi, rxiq_cal_rf_reg[i],
+ values_to_save[i]);

- if (tx_gain_override_old)
- wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
- else
- wlc_lcnphy_disable_tx_gain_override(pi);
+ if (tx_gain_override_old)
+ wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
+ else
+ wlc_lcnphy_disable_tx_gain_override(pi);

- wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
- wlc_lcnphy_rx_gain_override_enable(pi, false);
- }
+ wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
+ wlc_lcnphy_rx_gain_override_enable(pi, false);

cal_done:
kfree(ptr);
@@ -1789,6 +1835,17 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel)
write_radio_reg(pi, RADIO_2064_REG038, 3);
write_radio_reg(pi, RADIO_2064_REG091, 7);
}
+
+ if (!(pi->sh->boardflags & BFL_FEM)) {
+ u8 reg038[14] = {0xd, 0xe, 0xd, 0xd, 0xd, 0xc,
+ 0xa, 0xb, 0xb, 0x3, 0x3, 0x2, 0x0, 0x0};
+
+ write_radio_reg(pi, RADIO_2064_REG02A, 0xf);
+ write_radio_reg(pi, RADIO_2064_REG091, 0x3);
+ write_radio_reg(pi, RADIO_2064_REG038, 0x3);
+
+ write_radio_reg(pi, RADIO_2064_REG038, reg038[channel - 1]);
+ }
}

static int
@@ -1983,6 +2040,16 @@ wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos)
} else {
mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1);
mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
+ mod_radio_reg(pi, RADIO_2064_REG028, 0x1, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG11A, 0x4, 1<<2);
+ mod_radio_reg(pi, RADIO_2064_REG036, 0x10, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG11A, 0x10, 1<<4);
+ mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x77);
+ mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0xe<<1);
+ mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1<<7);
+ mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 1<<1);
+ mod_radio_reg(pi, RADIO_2064_REG029, 0xf0, 0<<4);
}
} else {
mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2);
@@ -2069,12 +2136,14 @@ static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi)
(auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12));

mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5));
+ mod_radio_reg(pi, RADIO_2064_REG07C, (1 << 0), (1 << 0));
}

static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
{
struct phytbl_info tab;
u32 rfseq, ind;
+ u8 tssi_sel;

tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
tab.tbl_width = 32;
@@ -2096,7 +2165,13 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)

mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4);

- wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT);
+ if (pi->sh->boardflags & BFL_FEM) {
+ tssi_sel = 0x1;
+ wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT);
+ } else {
+ tssi_sel = 0xe;
+ wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_POST_PA);
+ }
mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14);

mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15);
@@ -2132,9 +2207,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0);

if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
- mod_radio_reg(pi, RADIO_2064_REG028, 0xf, 0xe);
+ mod_radio_reg(pi, RADIO_2064_REG028, 0xf, tssi_sel);
mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4);
} else {
+ mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, tssi_sel << 1);
mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1);
mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3);
}
@@ -2181,6 +2257,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)

mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8);

+ mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
+
wlc_lcnphy_pwrctrl_rssiparams(pi);
}

@@ -2799,6 +2879,8 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
read_radio_reg(pi, RADIO_2064_REG007) & 1;
u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10;
u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4;
+ u8 SAVE_bbmult = wlc_lcnphy_get_bbmult(pi);
+
idleTssi = read_phy_reg(pi, 0x4ab);
suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) &
MCTL_EN_MAC));
@@ -2816,6 +2898,12 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4);
mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2);
wlc_lcnphy_tssi_setup(pi);
+
+ mod_phy_reg(pi, 0x4d7, (0x1 << 0), (1 << 0));
+ mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1 << 6));
+
+ wlc_lcnphy_set_bbmult(pi, 0x0);
+
wlc_phy_do_dummy_tx(pi, true, OFF);
idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0))
>> 0);
@@ -2837,6 +2925,7 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)

mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12);

+ wlc_lcnphy_set_bbmult(pi, SAVE_bbmult);
wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old);
wlc_lcnphy_set_tx_gain(pi, &old_gains);
wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl);
@@ -3050,6 +3139,11 @@ static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi)
wlc_lcnphy_write_table(pi, &tab);
tab.tbl_offset++;
}
+ mod_phy_reg(pi, 0x4d0, (0x1 << 0), (0) << 0);
+ mod_phy_reg(pi, 0x4d3, (0xff << 0), (0) << 0);
+ mod_phy_reg(pi, 0x4d3, (0xff << 8), (0) << 8);
+ mod_phy_reg(pi, 0x4d0, (0x1 << 4), (0) << 4);
+ mod_phy_reg(pi, 0x4d0, (0x1 << 2), (0) << 2);

mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7);

@@ -3851,7 +3945,6 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
target_gains.pad_gain = 21;
target_gains.dac_gain = 0;
wlc_lcnphy_set_tx_gain(pi, &target_gains);
- wlc_lcnphy_set_tx_pwr_by_index(pi, 16);

if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) {

@@ -3862,6 +3955,7 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
lcnphy_recal ? LCNPHY_CAL_RECAL :
LCNPHY_CAL_FULL), false);
} else {
+ wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
wlc_lcnphy_tx_iqlo_soft_cal_full(pi);
}

@@ -4286,17 +4380,22 @@ wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi,
if (CHSPEC_IS5G(pi->radio_chanspec))
pa_gain = 0x70;
else
- pa_gain = 0x70;
+ pa_gain = 0x60;

if (pi->sh->boardflags & BFL_FEM)
pa_gain = 0x10;
+
tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
tab.tbl_width = 32;
tab.tbl_len = 1;
tab.tbl_ptr = &val;

for (j = 0; j < 128; j++) {
- gm_gain = gain_table[j].gm;
+ if (pi->sh->boardflags & BFL_FEM)
+ gm_gain = gain_table[j].gm;
+ else
+ gm_gain = 15;
+
val = (((u32) pa_gain << 24) |
(gain_table[j].pad << 16) |
(gain_table[j].pga << 8) | gm_gain);
@@ -4507,7 +4606,10 @@ static void wlc_radio_2064_init(struct brcms_phy *pi)

write_phy_reg(pi, 0x4ea, 0x4688);

- mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
+ if (pi->sh->boardflags & BFL_FEM)
+ mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
+ else
+ mod_phy_reg(pi, 0x4eb, (0x7 << 0), 3 << 0);

mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6);

@@ -4518,6 +4620,13 @@ static void wlc_radio_2064_init(struct brcms_phy *pi)
wlc_lcnphy_rcal(pi);

wlc_lcnphy_rc_cal(pi);
+
+ if (!(pi->sh->boardflags & BFL_FEM)) {
+ write_radio_reg(pi, RADIO_2064_REG032, 0x6f);
+ write_radio_reg(pi, RADIO_2064_REG033, 0x19);
+ write_radio_reg(pi, RADIO_2064_REG039, 0xe);
+ }
+
}

static void wlc_lcnphy_radio_init(struct brcms_phy *pi)
@@ -4530,6 +4639,7 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
uint idx;
u8 phybw40;
struct phytbl_info tab;
+ const struct phytbl_info *tb;
u32 val;

phybw40 = CHSPEC_IS40(pi->radio_chanspec);
@@ -4547,22 +4657,20 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
wlc_lcnphy_write_table(pi, &tab);
}

- tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
- tab.tbl_width = 16;
- tab.tbl_ptr = &val;
- tab.tbl_len = 1;
-
- val = 114;
- tab.tbl_offset = 0;
- wlc_lcnphy_write_table(pi, &tab);
+ if (!(pi->sh->boardflags & BFL_FEM)) {
+ tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
+ tab.tbl_width = 16;
+ tab.tbl_ptr = &val;
+ tab.tbl_len = 1;

- val = 130;
- tab.tbl_offset = 1;
- wlc_lcnphy_write_table(pi, &tab);
+ val = 150;
+ tab.tbl_offset = 0;
+ wlc_lcnphy_write_table(pi, &tab);

- val = 6;
- tab.tbl_offset = 8;
- wlc_lcnphy_write_table(pi, &tab);
+ val = 220;
+ tab.tbl_offset = 1;
+ wlc_lcnphy_write_table(pi, &tab);
+ }

if (CHSPEC_IS2G(pi->radio_chanspec)) {
if (pi->sh->boardflags & BFL_FEM)
@@ -4576,7 +4684,6 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
}

if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
- const struct phytbl_info *tb;
int l;

if (CHSPEC_IS2G(pi->radio_chanspec)) {
@@ -4597,21 +4704,22 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
wlc_lcnphy_write_table(pi, &tb[idx]);
}

- if ((pi->sh->boardflags & BFL_FEM)
- && !(pi->sh->boardflags & BFL_FEM_BT))
- wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313_epa);
- else if (pi->sh->boardflags & BFL_FEM_BT) {
- if (pi->sh->boardrev < 0x1250)
- wlc_lcnphy_write_table(
- pi,
- &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa);
+ if (pi->sh->boardflags & BFL_FEM) {
+ if (pi->sh->boardflags & BFL_FEM_BT) {
+ if (pi->sh->boardrev < 0x1250)
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
+ else
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250;
+ } else {
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313_epa;
+ }
+ } else {
+ if (pi->sh->boardflags & BFL_FEM_BT)
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa;
else
- wlc_lcnphy_write_table(
- pi,
- &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250);
- } else
- wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313);
-
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313;
+ }
+ wlc_lcnphy_write_table(pi, tb);
wlc_lcnphy_load_rfpower(pi);

wlc_lcnphy_clear_papd_comptable(pi);
@@ -4955,6 +5063,8 @@ void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec)
wlc_lcnphy_load_tx_iir_filter(pi, true, 3);

mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3);
+ if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
+ wlc_lcnphy_tssi_setup(pi);
}

void wlc_phy_detach_lcnphy(struct brcms_phy *pi)
@@ -4993,8 +5103,7 @@ bool wlc_phy_attach_lcnphy(struct brcms_phy *pi)
if (!wlc_phy_txpwr_srom_read_lcnphy(pi))
return false;

- if ((pi->sh->boardflags & BFL_FEM) &&
- (LCNREV_IS(pi->pubpi.phy_rev, 1))) {
+ if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
if (pi_lcn->lcnphy_tempsense_option == 3) {
pi->hwpwrctrl = true;
pi->hwpwrctrl_capable = true;
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
index 622c01c..9fb0ca2 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
@@ -2058,6 +2058,73 @@ static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = {
0x0005,
};

+static const u16 dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[] = {
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+};
+
static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = {
0x0004,
0x0004,
@@ -2834,6 +2901,12 @@ const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = {
sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16
};

+const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa = {
+ &dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo,
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo) /
+ sizeof(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[0]), 15, 0, 16
+};
+
const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa = {
&dot11lcn_sw_ctrl_tbl_4313_epa_rev0,
sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0) /
@@ -2988,134 +3061,134 @@ dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = {
};

const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = {
- {7, 0, 31, 0, 72},
- {7, 0, 31, 0, 70},
- {7, 0, 31, 0, 68},
- {7, 0, 30, 0, 67},
- {7, 0, 29, 0, 68},
- {7, 0, 28, 0, 68},
- {7, 0, 27, 0, 69},
- {7, 0, 26, 0, 70},
- {7, 0, 25, 0, 70},
- {7, 0, 24, 0, 71},
- {7, 0, 23, 0, 72},
- {7, 0, 23, 0, 70},
- {7, 0, 22, 0, 71},
- {7, 0, 21, 0, 72},
- {7, 0, 21, 0, 70},
- {7, 0, 21, 0, 68},
- {7, 0, 21, 0, 66},
- {7, 0, 21, 0, 64},
- {7, 0, 21, 0, 63},
- {7, 0, 20, 0, 64},
- {7, 0, 19, 0, 65},
- {7, 0, 19, 0, 64},
- {7, 0, 18, 0, 65},
- {7, 0, 18, 0, 64},
- {7, 0, 17, 0, 65},
- {7, 0, 17, 0, 64},
- {7, 0, 16, 0, 65},
- {7, 0, 16, 0, 64},
- {7, 0, 16, 0, 62},
- {7, 0, 16, 0, 60},
- {7, 0, 16, 0, 58},
- {7, 0, 15, 0, 61},
- {7, 0, 15, 0, 59},
- {7, 0, 14, 0, 61},
- {7, 0, 14, 0, 60},
- {7, 0, 14, 0, 58},
- {7, 0, 13, 0, 60},
- {7, 0, 13, 0, 59},
- {7, 0, 12, 0, 62},
- {7, 0, 12, 0, 60},
- {7, 0, 12, 0, 58},
- {7, 0, 11, 0, 62},
- {7, 0, 11, 0, 60},
- {7, 0, 11, 0, 59},
- {7, 0, 11, 0, 57},
- {7, 0, 10, 0, 61},
- {7, 0, 10, 0, 59},
- {7, 0, 10, 0, 57},
- {7, 0, 9, 0, 62},
- {7, 0, 9, 0, 60},
- {7, 0, 9, 0, 58},
- {7, 0, 9, 0, 57},
- {7, 0, 8, 0, 62},
- {7, 0, 8, 0, 60},
- {7, 0, 8, 0, 58},
- {7, 0, 8, 0, 57},
- {7, 0, 8, 0, 55},
- {7, 0, 7, 0, 61},
+ {15, 0, 31, 0, 72},
+ {15, 0, 31, 0, 70},
+ {15, 0, 31, 0, 68},
+ {15, 0, 30, 0, 68},
+ {15, 0, 29, 0, 69},
+ {15, 0, 28, 0, 69},
+ {15, 0, 27, 0, 70},
+ {15, 0, 26, 0, 70},
+ {15, 0, 25, 0, 71},
+ {15, 0, 24, 0, 72},
+ {15, 0, 23, 0, 73},
+ {15, 0, 23, 0, 71},
+ {15, 0, 22, 0, 72},
+ {15, 0, 21, 0, 73},
+ {15, 0, 21, 0, 71},
+ {15, 0, 21, 0, 69},
+ {15, 0, 21, 0, 67},
+ {15, 0, 21, 0, 65},
+ {15, 0, 21, 0, 63},
+ {15, 0, 20, 0, 65},
+ {15, 0, 19, 0, 66},
+ {15, 0, 19, 0, 64},
+ {15, 0, 18, 0, 66},
+ {15, 0, 18, 0, 64},
+ {15, 0, 17, 0, 66},
+ {15, 0, 17, 0, 64},
+ {15, 0, 16, 0, 66},
+ {15, 0, 16, 0, 64},
+ {15, 0, 16, 0, 62},
+ {15, 0, 16, 0, 61},
+ {15, 0, 16, 0, 59},
+ {15, 0, 15, 0, 61},
+ {15, 0, 15, 0, 59},
+ {15, 0, 14, 0, 62},
+ {15, 0, 14, 0, 60},
+ {15, 0, 14, 0, 58},
+ {15, 0, 13, 0, 61},
+ {15, 0, 13, 0, 59},
+ {15, 0, 12, 0, 62},
+ {15, 0, 12, 0, 61},
+ {15, 0, 12, 0, 59},
+ {15, 0, 11, 0, 62},
+ {15, 0, 11, 0, 61},
+ {15, 0, 11, 0, 59},
+ {15, 0, 11, 0, 57},
+ {15, 0, 10, 0, 61},
+ {15, 0, 10, 0, 59},
+ {15, 0, 10, 0, 58},
+ {15, 0, 9, 0, 62},
+ {15, 0, 9, 0, 61},
+ {15, 0, 9, 0, 59},
+ {15, 0, 9, 0, 57},
+ {15, 0, 8, 0, 62},
+ {15, 0, 8, 0, 61},
+ {15, 0, 8, 0, 59},
+ {15, 0, 8, 0, 57},
+ {15, 0, 8, 0, 56},
+ {15, 0, 8, 0, 54},
+ {15, 0, 8, 0, 53},
+ {15, 0, 8, 0, 51},
+ {15, 0, 8, 0, 50},
+ {7, 0, 7, 0, 69},
+ {7, 0, 7, 0, 67},
+ {7, 0, 7, 0, 65},
+ {7, 0, 7, 0, 64},
+ {7, 0, 7, 0, 62},
{7, 0, 7, 0, 60},
{7, 0, 7, 0, 58},
- {7, 0, 7, 0, 56},
+ {7, 0, 7, 0, 57},
{7, 0, 7, 0, 55},
{7, 0, 6, 0, 62},
- {7, 0, 6, 0, 60},
- {7, 0, 6, 0, 58},
+ {7, 0, 6, 0, 61},
+ {7, 0, 6, 0, 59},
{7, 0, 6, 0, 57},
- {7, 0, 6, 0, 55},
+ {7, 0, 6, 0, 56},
{7, 0, 6, 0, 54},
- {7, 0, 6, 0, 52},
+ {7, 0, 6, 0, 53},
{7, 0, 5, 0, 61},
- {7, 0, 5, 0, 59},
- {7, 0, 5, 0, 57},
+ {7, 0, 5, 0, 60},
+ {7, 0, 5, 0, 58},
{7, 0, 5, 0, 56},
- {7, 0, 5, 0, 54},
+ {7, 0, 5, 0, 55},
{7, 0, 5, 0, 53},
- {7, 0, 5, 0, 51},
- {7, 0, 4, 0, 62},
- {7, 0, 4, 0, 60},
- {7, 0, 4, 0, 58},
+ {7, 0, 5, 0, 52},
+ {7, 0, 5, 0, 50},
+ {7, 0, 5, 0, 49},
+ {7, 0, 5, 0, 47},
{7, 0, 4, 0, 57},
- {7, 0, 4, 0, 55},
+ {7, 0, 4, 0, 56},
{7, 0, 4, 0, 54},
- {7, 0, 4, 0, 52},
+ {7, 0, 4, 0, 53},
{7, 0, 4, 0, 51},
- {7, 0, 4, 0, 49},
+ {7, 0, 4, 0, 50},
{7, 0, 4, 0, 48},
+ {7, 0, 4, 0, 47},
{7, 0, 4, 0, 46},
- {7, 0, 3, 0, 60},
- {7, 0, 3, 0, 58},
- {7, 0, 3, 0, 57},
- {7, 0, 3, 0, 55},
- {7, 0, 3, 0, 54},
- {7, 0, 3, 0, 52},
+ {7, 0, 4, 0, 44},
+ {7, 0, 4, 0, 43},
+ {7, 0, 4, 0, 42},
+ {7, 0, 4, 0, 41},
+ {7, 0, 4, 0, 40},
{7, 0, 3, 0, 51},
- {7, 0, 3, 0, 49},
+ {7, 0, 3, 0, 50},
{7, 0, 3, 0, 48},
+ {7, 0, 3, 0, 47},
{7, 0, 3, 0, 46},
- {7, 0, 3, 0, 45},
{7, 0, 3, 0, 44},
{7, 0, 3, 0, 43},
+ {7, 0, 3, 0, 42},
{7, 0, 3, 0, 41},
- {7, 0, 2, 0, 61},
- {7, 0, 2, 0, 59},
- {7, 0, 2, 0, 57},
- {7, 0, 2, 0, 56},
- {7, 0, 2, 0, 54},
- {7, 0, 2, 0, 53},
- {7, 0, 2, 0, 51},
- {7, 0, 2, 0, 50},
- {7, 0, 2, 0, 48},
- {7, 0, 2, 0, 47},
- {7, 0, 2, 0, 46},
- {7, 0, 2, 0, 44},
- {7, 0, 2, 0, 43},
- {7, 0, 2, 0, 42},
- {7, 0, 2, 0, 41},
- {7, 0, 2, 0, 39},
- {7, 0, 2, 0, 38},
- {7, 0, 2, 0, 37},
- {7, 0, 2, 0, 36},
- {7, 0, 2, 0, 35},
- {7, 0, 2, 0, 34},
- {7, 0, 2, 0, 33},
- {7, 0, 2, 0, 32},
- {7, 0, 1, 0, 63},
- {7, 0, 1, 0, 61},
- {7, 0, 1, 0, 59},
- {7, 0, 1, 0, 57},
+ {3, 0, 3, 0, 56},
+ {3, 0, 3, 0, 54},
+ {3, 0, 3, 0, 53},
+ {3, 0, 3, 0, 51},
+ {3, 0, 3, 0, 50},
+ {3, 0, 3, 0, 48},
+ {3, 0, 3, 0, 47},
+ {3, 0, 3, 0, 46},
+ {3, 0, 3, 0, 44},
+ {3, 0, 3, 0, 43},
+ {3, 0, 3, 0, 42},
+ {3, 0, 3, 0, 41},
+ {3, 0, 3, 0, 39},
+ {3, 0, 3, 0, 38},
+ {3, 0, 3, 0, 37},
+ {3, 0, 3, 0, 36},
+ {3, 0, 3, 0, 35},
+ {3, 0, 3, 0, 34},
};

const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = {
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
index 5f75e16..489422a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
@@ -20,6 +20,7 @@
extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[];
extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev0;
extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313;
+extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa;
extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa;
extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa_combo;
extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
--
1.8.1.3



2013-08-10 10:28:07

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 07/12] brcmfmac: add support for manual TDLS operations

Implement the .tdls_oper() callback and indicate TDLS support
in the wiphy flags.

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
.../net/wireless/brcm80211/brcmfmac/fwil_types.h | 21 ++++++++
.../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 57 +++++++++++++++++++++-
2 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
index 665ef69..ecabb04 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
@@ -69,4 +69,25 @@ struct brcmf_fil_bss_enable_le {
__le32 enable;
};

+/**
+ * struct tdls_iovar - common structure for tdls iovars.
+ *
+ * @ea: ether address of peer station.
+ * @mode: mode value depending on specific tdls iovar.
+ * @chanspec: channel specification.
+ * @pad: unused (for future use).
+ */
+struct brcmf_tdls_iovar_le {
+ u8 ea[ETH_ALEN]; /* Station address */
+ u8 mode; /* mode: depends on iovar */
+ __le16 chanspec;
+ __le32 pad; /* future */
+};
+
+enum brcmf_tdls_manual_ep_ops {
+ BRCMF_TDLS_MANUAL_EP_CREATE = 1,
+ BRCMF_TDLS_MANUAL_EP_DELETE = 3,
+ BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6
+};
+
#endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index c3dfea3..0370e44 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -4126,6 +4126,53 @@ static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
}

+static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
+{
+ int ret;
+
+ switch (oper) {
+ case NL80211_TDLS_DISCOVERY_REQ:
+ ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
+ break;
+ case NL80211_TDLS_SETUP:
+ ret = BRCMF_TDLS_MANUAL_EP_CREATE;
+ break;
+ case NL80211_TDLS_TEARDOWN:
+ ret = BRCMF_TDLS_MANUAL_EP_DELETE;
+ break;
+ default:
+ brcmf_err("unsupported operation: %d\n", oper);
+ ret = -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
+ struct net_device *ndev, u8 *peer,
+ enum nl80211_tdls_operation oper)
+{
+ struct brcmf_if *ifp;
+ struct brcmf_tdls_iovar_le info;
+ int ret = 0;
+
+ ret = brcmf_convert_nl80211_tdls_oper(oper);
+ if (ret < 0)
+ return ret;
+
+ ifp = netdev_priv(ndev);
+ memset(&info, 0, sizeof(info));
+ info.mode = (u8)ret;
+ if (peer)
+ memcpy(info.ea, peer, ETH_ALEN);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
+ &info, sizeof(info));
+ if (ret < 0)
+ brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
+
+ return ret;
+}
+
static struct cfg80211_ops wl_cfg80211_ops = {
.add_virtual_intf = brcmf_cfg80211_add_iface,
.del_virtual_intf = brcmf_cfg80211_del_iface,
@@ -4164,6 +4211,7 @@ static struct cfg80211_ops wl_cfg80211_ops = {
.stop_p2p_device = brcmf_p2p_stop_device,
.crit_proto_start = brcmf_cfg80211_crit_proto_start,
.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
+ .tdls_oper = brcmf_cfg80211_tdls_oper,
CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode)
};

@@ -4285,7 +4333,8 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
WIPHY_FLAG_OFFCHAN_TX |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+ WIPHY_FLAG_SUPPORTS_TDLS;
wiphy->mgmt_stypes = brcmf_txrx_stypes;
wiphy->max_remain_on_channel_duration = 5000;
brcmf_wiphy_pno_params(wiphy);
@@ -4906,6 +4955,12 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
goto cfg80211_p2p_attach_out;
}

+ err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
+ if (err) {
+ brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
+ wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+ }
+
err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION,
&io_type);
if (err) {
--
1.8.1.3



2013-08-10 10:27:56

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 10/12] brcmfmac: streamline sdio bus header code

From: Franky Lin <[email protected]>

Streamlining sdio bus specific header related code as preparation for host
tx glomming

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Franky Lin <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 243 ++++++++++-----------
1 file changed, 120 insertions(+), 123 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 2563875..02ab5cd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -201,13 +201,6 @@ struct rte_console {
#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */
#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */

-/* HW frame tag */
-#define SDPCM_FRAMETAG_LEN 4 /* 2 bytes len, 2 bytes check val */
-
-/* Total length of frame header for dongle protocol */
-#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
-#define SDPCM_RESERVE (SDPCM_HDRLEN + BRCMF_SDALIGN)
-
/*
* Software allocation of To SB Mailbox resources
*/
@@ -250,38 +243,6 @@ struct rte_console {
/* Current protocol version */
#define SDPCM_PROT_VERSION 4

-/* SW frame header */
-#define SDPCM_PACKET_SEQUENCE(p) (((u8 *)p)[0] & 0xff)
-
-#define SDPCM_CHANNEL_MASK 0x00000f00
-#define SDPCM_CHANNEL_SHIFT 8
-#define SDPCM_PACKET_CHANNEL(p) (((u8 *)p)[1] & 0x0f)
-
-#define SDPCM_NEXTLEN_OFFSET 2
-
-/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
-#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */
-#define SDPCM_DOFFSET_VALUE(p) (((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
-#define SDPCM_DOFFSET_MASK 0xff000000
-#define SDPCM_DOFFSET_SHIFT 24
-#define SDPCM_FCMASK_OFFSET 4 /* Flow control */
-#define SDPCM_FCMASK_VALUE(p) (((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff)
-#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */
-#define SDPCM_WINDOW_VALUE(p) (((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
-
-#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */
-
-/* logical channel numbers */
-#define SDPCM_CONTROL_CHANNEL 0 /* Control channel Id */
-#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */
-#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */
-#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets */
-#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */
-
-#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for 8bit frame seq */
-
-#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
-
/*
* Shared structure between dongle and the host.
* The structure contains pointers to trap or assert information.
@@ -396,8 +357,8 @@ struct sdpcm_shared_le {
__le32 brpt_addr;
};

-/* SDIO read frame info */
-struct brcmf_sdio_read {
+/* dongle SDIO bus specific header info */
+struct brcmf_sdio_hdrinfo {
u8 seq_num;
u8 channel;
u16 len;
@@ -431,7 +392,7 @@ struct brcmf_sdio {
u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN];
u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
u8 rx_seq; /* Receive sequence number (expected) */
- struct brcmf_sdio_read cur_read;
+ struct brcmf_sdio_hdrinfo cur_read;
/* info of current read frame */
bool rxskip; /* Skip receive (awaiting NAK ACK) */
bool rxpending; /* Data frame pending in dongle */
@@ -1042,18 +1003,64 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus)
}
}

-static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
- struct brcmf_sdio_read *rd,
- enum brcmf_sdio_frmtype type)
+/**
+ * brcmfmac sdio bus specific header
+ * This is the lowest layer header wrapped on the packets transmitted between
+ * host and WiFi dongle which contains information needed for SDIO core and
+ * firmware
+ *
+ * It consists of 2 parts: hw header and software header
+ * hardware header (frame tag) - 4 bytes
+ * Byte 0~1: Frame length
+ * Byte 2~3: Checksum, bit-wise inverse of frame length
+ * software header - 8 bytes
+ * Byte 0: Rx/Tx sequence number
+ * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
+ * Byte 2: Length of next data frame, reserved for Tx
+ * Byte 3: Data offset
+ * Byte 4: Flow control bits, reserved for Tx
+ * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet
+ * Byte 6~7: Reserved
+ */
+#define SDPCM_HWHDR_LEN 4
+#define SDPCM_SWHDR_LEN 8
+#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
+#define SDPCM_RESERVE (SDPCM_HDRLEN + BRCMF_SDALIGN)
+/* software header */
+#define SDPCM_SEQ_MASK 0x000000ff
+#define SDPCM_SEQ_WRAP 256
+#define SDPCM_CHANNEL_MASK 0x00000f00
+#define SDPCM_CHANNEL_SHIFT 8
+#define SDPCM_CONTROL_CHANNEL 0 /* Control */
+#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */
+#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */
+#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */
+#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */
+#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
+#define SDPCM_NEXTLEN_MASK 0x00ff0000
+#define SDPCM_NEXTLEN_SHIFT 16
+#define SDPCM_DOFFSET_MASK 0xff000000
+#define SDPCM_DOFFSET_SHIFT 24
+#define SDPCM_FCMASK_MASK 0x000000ff
+#define SDPCM_WINDOW_MASK 0x0000ff00
+#define SDPCM_WINDOW_SHIFT 8
+
+static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
+{
+ u32 hdrvalue;
+ hdrvalue = *(u32 *)swheader;
+ return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
+}
+
+static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *rd,
+ enum brcmf_sdio_frmtype type)
{
u16 len, checksum;
u8 rx_seq, fc, tx_seq_max;
+ u32 swheader;

- /*
- * 4 bytes hardware header (frame tag)
- * Byte 0~1: Frame length
- * Byte 2~3: Checksum, bit-wise inverse of frame length
- */
+ /* hw header */
len = get_unaligned_le16(header);
checksum = get_unaligned_le16(header + sizeof(u16));
/* All zero means no more to read */
@@ -1082,24 +1089,16 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
}
rd->len = len;

- /*
- * 8 bytes hardware header
- * Byte 0: Rx sequence number
- * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
- * Byte 2: Length of next data frame
- * Byte 3: Data offset
- * Byte 4: Flow control bits
- * Byte 5: Maximum Sequence number allow for Tx
- * Byte 6~7: Reserved
- */
- if (type == BRCMF_SDIO_FT_SUPER &&
- SDPCM_GLOMDESC(&header[SDPCM_FRAMETAG_LEN])) {
+ /* software header */
+ header += SDPCM_HWHDR_LEN;
+ swheader = le32_to_cpu(*(__le32 *)header);
+ if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) {
brcmf_err("Glom descriptor found in superframe head\n");
rd->len = 0;
return -EINVAL;
}
- rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]);
- rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]);
+ rx_seq = (u8)(swheader & SDPCM_SEQ_MASK);
+ rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT;
if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
type != BRCMF_SDIO_FT_SUPER) {
brcmf_err("HW header length too long\n");
@@ -1119,7 +1118,7 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
rd->len = 0;
return -EINVAL;
}
- rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+ rd->dat_offset = brcmf_sdio_getdatoffset(header);
if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
brcmf_err("seq %d: bad data offset\n", rx_seq);
bus->sdcnt.rx_badhdr++;
@@ -1136,14 +1135,15 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
/* no need to check the reset for subframe */
if (type == BRCMF_SDIO_FT_SUB)
return 0;
- rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+ rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT;
if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
/* only warm for NON glom packet */
if (rd->channel != SDPCM_GLOM_CHANNEL)
brcmf_err("seq %d: next length error\n", rx_seq);
rd->len_nxtfrm = 0;
}
- fc = SDPCM_FCMASK_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+ swheader = le32_to_cpu(*(__le32 *)(header + 4));
+ fc = swheader & SDPCM_FCMASK_MASK;
if (bus->flowcontrol != fc) {
if (~bus->flowcontrol & fc)
bus->sdcnt.fc_xoff++;
@@ -1152,7 +1152,7 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
bus->sdcnt.fc_rcvd++;
bus->flowcontrol = fc;
}
- tx_seq_max = SDPCM_WINDOW_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+ tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT;
if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
brcmf_err("seq %d: max tx seq number error\n", rx_seq);
tx_seq_max = bus->tx_seq + 2;
@@ -1162,6 +1162,28 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
return 0;
}

+static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)
+{
+ *(__le16 *)header = cpu_to_le16(frm_length);
+ *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length);
+}
+
+static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *hd_info)
+{
+ u32 sw_header;
+
+ brcmf_sdio_update_hwhdr(header, hd_info->len);
+
+ sw_header = bus->tx_seq;
+ sw_header |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) &
+ SDPCM_CHANNEL_MASK;
+ sw_header |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) &
+ SDPCM_DOFFSET_MASK;
+ *(((__le32 *)header) + 1) = cpu_to_le32(sw_header);
+ *(((__le32 *)header) + 2) = 0;
+}
+
static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
{
u16 dlen, totlen;
@@ -1173,7 +1195,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
int errcode;
u8 doff, sfdoff;

- struct brcmf_sdio_read rd_new;
+ struct brcmf_sdio_hdrinfo rd_new;

/* If packets, issue read(s) and send up packet chain */
/* Return sequence numbers consumed? */
@@ -1309,8 +1331,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
rd_new.seq_num = rxseq;
rd_new.len = dlen;
sdio_claim_host(bus->sdiodev->func[1]);
- errcode = brcmf_sdio_hdparser(bus, pfirst->data, &rd_new,
- BRCMF_SDIO_FT_SUPER);
+ errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new,
+ BRCMF_SDIO_FT_SUPER);
sdio_release_host(bus->sdiodev->func[1]);
bus->cur_read.len = rd_new.len_nxtfrm << 4;

@@ -1328,8 +1350,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
rd_new.len = pnext->len;
rd_new.seq_num = rxseq++;
sdio_claim_host(bus->sdiodev->func[1]);
- errcode = brcmf_sdio_hdparser(bus, pnext->data, &rd_new,
- BRCMF_SDIO_FT_SUB);
+ errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new,
+ BRCMF_SDIO_FT_SUB);
sdio_release_host(bus->sdiodev->func[1]);
brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
pnext->data, 32, "subframe:\n");
@@ -1361,7 +1383,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
dptr = (u8 *) (pfirst->data);
sublen = get_unaligned_le16(dptr);
- doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+ doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]);

brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
dptr, pfirst->len,
@@ -1539,7 +1561,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
uint rxleft = 0; /* Remaining number of frames allowed */
int ret; /* Return code from calls */
uint rxcount = 0; /* Total frames read */
- struct brcmf_sdio_read *rd = &bus->cur_read, rd_new;
+ struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
u8 head_read = 0;

brcmf_dbg(TRACE, "Enter\n");
@@ -1587,8 +1609,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
bus->rxhdr, SDPCM_HDRLEN,
"RxHdr:\n");

- if (brcmf_sdio_hdparser(bus, bus->rxhdr, rd,
- BRCMF_SDIO_FT_NORMAL)) {
+ if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd,
+ BRCMF_SDIO_FT_NORMAL)) {
sdio_release_host(bus->sdiodev->func[1]);
if (!bus->rxpending)
break;
@@ -1652,8 +1674,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
rd_new.seq_num = rd->seq_num;
sdio_claim_host(bus->sdiodev->func[1]);
- if (brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new,
- BRCMF_SDIO_FT_NORMAL)) {
+ if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new,
+ BRCMF_SDIO_FT_NORMAL)) {
rd->len = 0;
brcmu_pkt_buf_free_skb(pkt);
}
@@ -1697,7 +1719,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)

/* Save superframe descriptor and allocate packet frame */
if (rd->channel == SDPCM_GLOM_CHANNEL) {
- if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
+ if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) {
brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
rd->len);
brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
@@ -1783,13 +1805,12 @@ static int
brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
uint chan)
{
- u16 head_pad, tail_pad, tail_chop, pkt_len;
- u16 head_align, sg_align;
- u32 sw_header;
+ u16 head_pad, tail_pad, tail_chop, head_align, sg_align;
int ntail;
struct sk_buff *pkt_next, *pkt_new;
u8 *dat_buf;
unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+ struct brcmf_sdio_hdrinfo hd_info = {0};

/* SDIO ADMA requires at least 32 bit alignment */
head_align = 4;
@@ -1848,34 +1869,18 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
}

/* Now prep the header */
- /* 4 bytes hardware header (frame tag)
- * Byte 0~1: Frame length
- * Byte 2~3: Checksum, bit-wise inverse of frame length
- */
if (pkt_new)
- pkt_len = pkt_next->len + tail_chop;
+ hd_info.len = pkt_next->len + tail_chop;
else
- pkt_len = pkt_next->len - tail_pad;
- *(__le16 *)dat_buf = cpu_to_le16(pkt_len);
- *(((__le16 *)dat_buf) + 1) = cpu_to_le16(~pkt_len);
- /* 8 bytes software header
- * Byte 0: Tx sequence number
- * Byte 1: 4 MSB Channel number
- * Byte 2: Reserved
- * Byte 3: Data offset
- * Byte 4~7: Reserved
- */
- sw_header = bus->tx_seq;
- sw_header |= ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK);
- sw_header |= ((head_pad + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) &
- SDPCM_DOFFSET_MASK;
- *(((__le32 *)dat_buf) + 1) = cpu_to_le32(sw_header);
- *(((__le32 *)dat_buf) + 2) = 0;
+ hd_info.len = pkt_next->len - tail_pad;
+ hd_info.channel = chan;
+ hd_info.dat_offset = head_pad + SDPCM_HDRLEN;
+ brcmf_sdio_hdpack(bus, dat_buf, &hd_info);

if (BRCMF_BYTES_ON() &&
((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
(BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
- brcmf_dbg_hex_dump(true, pkt_next, pkt_len, "Tx Frame:\n");
+ brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, "Tx Frame:\n");
else if (BRCMF_HDRS_ON())
brcmf_dbg_hex_dump(true, pkt_next, head_pad + SDPCM_HDRLEN,
"Tx Header:\n");
@@ -1913,7 +1918,7 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
__skb_unlink(pkt_next, pktq);
brcmu_pkt_buf_free_skb(pkt_next);
} else {
- hdr = pkt_next->data + SDPCM_FRAMETAG_LEN;
+ hdr = pkt_next->data + SDPCM_HWHDR_LEN;
dat_offset = le32_to_cpu(*(__le32 *)hdr);
dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>
SDPCM_DOFFSET_SHIFT;
@@ -1969,7 +1974,7 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
}
sdio_release_host(bus->sdiodev->func[1]);
if (ret == 0)
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;

done:
brcmf_sdio_txpkt_postp(bus, &localq);
@@ -2325,7 +2330,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
}

} else {
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
}
sdio_release_host(bus->sdiodev->func[1]);
bus->ctrl_frame_stat = false;
@@ -2540,7 +2545,7 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
return ret;
}

- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;

return ret;
}
@@ -2550,13 +2555,13 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
{
u8 *frame;
u16 len;
- u32 swheader;
uint retries = 0;
u8 doff = 0;
int ret = -1;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_sdio *bus = sdiodev->bus;
+ struct brcmf_sdio_hdrinfo hd_info = {0};

brcmf_dbg(TRACE, "Enter\n");

@@ -2595,18 +2600,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
brcmf_sdbrcm_bus_sleep(bus, false, false);
sdio_release_host(bus->sdiodev->func[1]);

- /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
- *(__le16 *) frame = cpu_to_le16((u16) msglen);
- *(((__le16 *) frame) + 1) = cpu_to_le16(~msglen);
-
- /* Software tag: channel, sequence number, data offset */
- swheader =
- ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
- SDPCM_CHANNEL_MASK)
- | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
- SDPCM_DOFFSET_MASK);
- put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
- put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+ hd_info.len = (u16)msglen;
+ hd_info.channel = SDPCM_CONTROL_CHANNEL;
+ hd_info.dat_offset = doff;
+ brcmf_sdio_hdpack(bus, frame, &hd_info);

if (!data_ok(bus)) {
brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
@@ -3856,7 +3853,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
bus->txbound = BRCMF_TXBOUND;
bus->rxbound = BRCMF_RXBOUND;
bus->txminmax = BRCMF_TXMINMAX;
- bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
+ bus->tx_seq = SDPCM_SEQ_WRAP - 1;

INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq");
--
1.8.1.3



2013-08-10 10:27:56

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 09/12] brcmfmac: remove align from brcmf_bus structure

From: Franky Lin <[email protected]>

remove align from brcmf_bus since it is only used by sdio bus layer internally

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Reviewed-by: Arend van Spriel <[email protected]>
Signed-off-by: Franky Lin <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
.../net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c | 1 -
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 2 --
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 25 +++++++++++++++-------
3 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 289e386..64f4a2b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -350,7 +350,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,

sdiodev->bus_if = bus_if;
bus_if->bus_priv.sdio = sdiodev;
- bus_if->align = BRCMF_SDALIGN;
dev_set_drvdata(&func->dev, bus_if);
dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
sdiodev->dev = &sdiodev->func[1]->dev;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
index 9249b6d..f7c1985 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
@@ -69,7 +69,6 @@ struct brcmf_bus_ops {
* @maxctl: maximum size for rxctl request message.
* @tx_realloc: number of tx packets realloced for headroom.
* @dstats: dongle-based statistical data.
- * @align: alignment requirement for the bus.
* @dcmd_list: bus/device specific dongle initialization commands.
* @chip: device identifier of the dongle chip.
* @chiprev: revision of the dongle chip.
@@ -84,7 +83,6 @@ struct brcmf_bus {
enum brcmf_bus_state state;
uint maxctl;
unsigned long tx_realloc;
- u8 align;
u32 chip;
u32 chiprev;
struct list_head dcmd_list;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index aa4caca..2563875 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -1166,7 +1166,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
{
u16 dlen, totlen;
u8 *dptr, num = 0;
-
+ u32 align = 0;
u16 sublen;
struct sk_buff *pfirst, *pnext;

@@ -1181,6 +1181,11 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
brcmf_dbg(SDIO, "start: glomd %p glom %p\n",
bus->glomd, skb_peek(&bus->glom));

+ if (bus->sdiodev->pdata)
+ align = bus->sdiodev->pdata->sd_sgentry_align;
+ if (align < 4)
+ align = 4;
+
/* If there's a descriptor, generate the packet chain */
if (bus->glomd) {
pfirst = pnext = NULL;
@@ -1204,9 +1209,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
pnext = NULL;
break;
}
- if (sublen % BRCMF_SDALIGN) {
+ if (sublen % align) {
brcmf_err("sublen %d not multiple of %d\n",
- sublen, BRCMF_SDALIGN);
+ sublen, align);
}
totlen += sublen;

@@ -1219,7 +1224,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
}

/* Allocate/chain packet for next subframe */
- pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN);
+ pnext = brcmu_pkt_buf_get_skb(sublen + align);
if (pnext == NULL) {
brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",
num, sublen);
@@ -1228,7 +1233,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
skb_queue_tail(&bus->glom, pnext);

/* Adhere to start alignment requirements */
- pkt_align(pnext, sublen, BRCMF_SDALIGN);
+ pkt_align(pnext, sublen, align);
}

/* If all allocations succeeded, save packet chain
@@ -3832,7 +3837,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
struct brcmf_sdio *bus;
struct brcmf_bus_dcmd *dlst;
u32 dngl_txglom;
- u32 dngl_txglomalign;
+ u32 txglomalign = 0;
u8 idx;

brcmf_dbg(TRACE, "Enter\n");
@@ -3926,9 +3931,13 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
dlst->param_len = sizeof(u32);
} else {
/* otherwise, set txglomalign */
- dngl_txglomalign = bus->sdiodev->bus_if->align;
+ if (sdiodev->pdata)
+ txglomalign = sdiodev->pdata->sd_sgentry_align;
+ /* SDIO ADMA requires at least 32 bit alignment */
+ if (txglomalign < 4)
+ txglomalign = 4;
dlst->name = "bus:txglomalign";
- dlst->param = (char *)&dngl_txglomalign;
+ dlst->param = (char *)&txglomalign;
dlst->param_len = sizeof(u32);
}
list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list);
--
1.8.1.3



2013-08-10 10:27:56

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 06/12] brcmfmac: ignore IF event if firmware indicates it

Not every IF event from the firmware needs to result in a
related interface, netdev or wdev, on the host. This is
indicated in the event message. Handle that flag and effectively
ignore the firmware event.

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 2 ++
drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 4 ++++
2 files changed, 6 insertions(+)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index 1273dfd..2eb9e64 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -194,6 +194,8 @@
#define BRCMF_E_IF_DEL 2
#define BRCMF_E_IF_CHANGE 3

+#define BRCMF_E_IF_FLAG_NOIF 1
+
#define BRCMF_E_IF_ROLE_STA 0
#define BRCMF_E_IF_ROLE_AP 1
#define BRCMF_E_IF_ROLE_WDS 2
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
index 83ee53a..fad77dd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
@@ -185,6 +185,10 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
ifevent->action, ifevent->ifidx, ifevent->bssidx,
ifevent->flags, ifevent->role);

+ if (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) {
+ brcmf_dbg(EVENT, "event can be ignored\n");
+ return;
+ }
if (ifevent->ifidx >= BRCMF_MAX_IFS) {
brcmf_err("invalid interface index: %u\n",
ifevent->ifidx);
--
1.8.1.3



2013-08-12 10:23:00

by David Herrmann

[permalink] [raw]
Subject: Re: [PATCH 12/12] brcmsmac: support 4313iPA

Hi

On Mon, Aug 12, 2013 at 12:10 PM, Arend van Spriel <[email protected]> wrote:
> On 08/11/2013 02:48 PM, Jonas Gorski wrote:
>>
>> Hi,
>>
>> On Sat, Aug 10, 2013 at 12:27 PM, Arend van Spriel <[email protected]>
>> wrote:
>>>
>>> From: Piotr Haber <[email protected]>
>>>
>>> Add support for 4313 iPA variant.
>>> It is a variant of already supported 4313 ePA,
>>> so this patch adds the required PHY changes to
>>> support it properly including an updated switch
>>> control table for BT-combo card variants.
>>
>>
>> Okay, I'll bite. Since this patch was already reverted once, it
>> warrants some additional scrutiny.
>
>
> That's the right attitude ;-) The revert made us cautious as well before
> sending out this patch, but thanks for making the effort.
>
>
>> First of all, the patch is quite large, and I wonder if it couldn't be
>> split into smaller patches, especially as it looks like there are
>> additional fixes/changes merged in it.
>
>
> It indeed seems rather large. The original work from Piotr were two patches
> that I squashed. I will break up this patch in more individual patches.
>
> John,
>
> Can you drop this patch from the series?
>
>
>> Detailed comments below ...
>>
>>> Tested-by: Maximilian Engelhardt <[email protected]>
>>> Tested-by: David Costa <[email protected]>
>>> Reviewed-by: Arend Van Spriel <[email protected]>
>>> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
>>> Signed-off-by: Piotr Haber <[email protected]>
>>> Signed-off-by: Arend van Spriel <[email protected]>
>>> ---
>>
>>
>> This is obviously a V2 (or V(n+1) where n was the reverted version),
>> so there should be something describing the changes to the reverted
>> version. Why should we trust it now to not break things again? (Yes, I
>> see those Tested-bys ;-)
>
>
> This is a gray area. The original patch was taken into the tree so I
> considered this to be a new patch.
>
> We tested with a number of 4313 variants having some extra shipped that we
> did not have at the time of the original patch. I looked up the revert patch
> and noticed it is tagged with Reported-by. So I will ask David Herrmann to
> test V(n+2) before sending it (if you don't mind n will be 0).

If you add me to CC for the patch I will happily test it. But for now
I have no idea which patches I should try so I will wait for the next
revision, I guess?

Thanks for letting me know!
David

2013-08-10 10:27:46

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 03/12] brcmfmac: add AMPDU reordering functionality

This feature moves the responsibility of collecting all MPDUs in an
AMPDU session in the correct order from the firmware to the host
driver. This reduces buffer requirement on the firmware side.

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 27 +++
.../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 270 +++++++++++++++++++--
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 13 +-
3 files changed, 283 insertions(+), 27 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index 86cbfe2..3943fb8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -209,6 +209,8 @@
#define BRCMF_DCMD_MEDLEN 1536
#define BRCMF_DCMD_MAXLEN 8192

+#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256
+
/* Pattern matching filter. Specifies an offset within received packets to
* start matching, the pattern to match, the size of the pattern, and a bitmask
* that indicates which bits within the pattern should be matched.
@@ -505,6 +507,25 @@ struct brcmf_dcmd {
uint needed; /* bytes needed (optional) */
};

+/**
+ * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
+ *
+ * @pktslots: dynamic allocated array for ordering AMPDU packets.
+ * @flow_id: AMPDU flow identifier.
+ * @cur_idx: last AMPDU index from firmware.
+ * @exp_idx: expected next AMPDU index.
+ * @max_idx: maximum amount of packets per AMPDU.
+ * @pend_pkts: number of packets currently in @pktslots.
+ */
+struct brcmf_ampdu_rx_reorder {
+ struct sk_buff **pktslots;
+ u8 flow_id;
+ u8 cur_idx;
+ u8 exp_idx;
+ u8 max_idx;
+ u8 pend_pkts;
+};
+
/* Forward decls for struct brcmf_pub (see below) */
struct brcmf_proto; /* device communication protocol info */
struct brcmf_cfg80211_dev; /* cfg80211 device info */
@@ -539,6 +560,9 @@ struct brcmf_pub {
bool fw_signals;
struct brcmf_fws_info *fws;
spinlock_t fws_spinlock;
+
+ struct brcmf_ampdu_rx_reorder
+ *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
#ifdef DEBUG
struct dentry *dbgfs_dir;
#endif
@@ -604,6 +628,9 @@ struct brcmf_if {
wait_queue_head_t pend_8021x_wait;
};

+struct brcmf_skb_reorder_data {
+ u8 *reorder;
+};

extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev);

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 8009901..9bc2785 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -38,6 +38,19 @@ MODULE_LICENSE("Dual BSD/GPL");

#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */

+/* AMPDU rx reordering definitions */
+#define BRCMF_RXREORDER_FLOWID_OFFSET 0
+#define BRCMF_RXREORDER_MAXIDX_OFFSET 2
+#define BRCMF_RXREORDER_FLAGS_OFFSET 4
+#define BRCMF_RXREORDER_CURIDX_OFFSET 6
+#define BRCMF_RXREORDER_EXPIDX_OFFSET 8
+
+#define BRCMF_RXREORDER_DEL_FLOW 0x01
+#define BRCMF_RXREORDER_FLUSH_ALL 0x02
+#define BRCMF_RXREORDER_CURIDX_VALID 0x04
+#define BRCMF_RXREORDER_EXPIDX_VALID 0x08
+#define BRCMF_RXREORDER_NEW_HOLE 0x10
+
/* Error bits */
int brcmf_msg_level;
module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
@@ -279,16 +292,243 @@ void brcmf_txflowblock(struct device *dev, bool state)
}
}

+static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+ skb->dev = ifp->ndev;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ if (skb->pkt_type == PACKET_MULTICAST)
+ ifp->stats.multicast++;
+
+ /* Process special event packets */
+ brcmf_fweh_process_skb(ifp->drvr, skb);
+
+ if (!(ifp->ndev->flags & IFF_UP)) {
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+
+ ifp->stats.rx_bytes += skb->len;
+ ifp->stats.rx_packets++;
+
+ brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol));
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ /* If the receive is not processed inside an ISR,
+ * the softirqd must be woken explicitly to service
+ * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
+ */
+ netif_rx_ni(skb);
+}
+
+static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
+ u8 start, u8 end,
+ struct sk_buff_head *skb_list)
+{
+ /* initialize return list */
+ __skb_queue_head_init(skb_list);
+
+ if (rfi->pend_pkts == 0) {
+ brcmf_dbg(INFO, "no packets in reorder queue\n");
+ return;
+ }
+
+ do {
+ if (rfi->pktslots[start]) {
+ __skb_queue_tail(skb_list, rfi->pktslots[start]);
+ rfi->pktslots[start] = NULL;
+ }
+ start++;
+ if (start > rfi->max_idx)
+ start = 0;
+ } while (start != end);
+ rfi->pend_pkts -= skb_queue_len(skb_list);
+}
+
+static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
+ struct sk_buff *pkt)
+{
+ u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
+ struct brcmf_ampdu_rx_reorder *rfi;
+ struct sk_buff_head reorder_list;
+ struct sk_buff *pnext;
+ u8 flags;
+ u32 buf_size;
+
+ flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
+ flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
+
+ /* validate flags and flow id */
+ if (flags == 0xFF) {
+ brcmf_err("invalid flags...so ignore this packet\n");
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ rfi = ifp->drvr->reorder_flows[flow_id];
+ if (flags & BRCMF_RXREORDER_DEL_FLOW) {
+ brcmf_dbg(INFO, "flow-%d: delete\n",
+ flow_id);
+
+ if (rfi == NULL) {
+ brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+ flow_id);
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
+ &reorder_list);
+ /* add the last packet */
+ __skb_queue_tail(&reorder_list, pkt);
+ kfree(rfi);
+ ifp->drvr->reorder_flows[flow_id] = NULL;
+ goto netif_rx;
+ }
+ /* from here on we need a flow reorder instance */
+ if (rfi == NULL) {
+ buf_size = sizeof(*rfi);
+ max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+
+ buf_size += (max_idx + 1) * sizeof(pkt);
+
+ /* allocate space for flow reorder info */
+ brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
+ flow_id, max_idx);
+ rfi = kzalloc(buf_size, GFP_ATOMIC);
+ if (rfi == NULL) {
+ brcmf_err("failed to alloc buffer\n");
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ ifp->drvr->reorder_flows[flow_id] = rfi;
+ rfi->pktslots = (struct sk_buff **)(rfi+1);
+ rfi->max_idx = max_idx;
+ }
+ if (flags & BRCMF_RXREORDER_NEW_HOLE) {
+ if (rfi->pend_pkts) {
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
+ rfi->exp_idx,
+ &reorder_list);
+ WARN_ON(rfi->pend_pkts);
+ } else {
+ __skb_queue_head_init(&reorder_list);
+ }
+ rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+ rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+ rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+ rfi->pktslots[rfi->cur_idx] = pkt;
+ rfi->pend_pkts++;
+ brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
+ flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
+ } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
+ cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+ if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
+ /* still in the current hole */
+ /* enqueue the current on the buffer chain */
+ if (rfi->pktslots[cur_idx] != NULL) {
+ brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
+ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+ rfi->pktslots[cur_idx] = NULL;
+ }
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+ rfi->cur_idx = cur_idx;
+ brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
+ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+ /* can return now as there is no reorder
+ * list to process.
+ */
+ return;
+ }
+ if (rfi->exp_idx == cur_idx) {
+ if (rfi->pktslots[cur_idx] != NULL) {
+ brcmf_dbg(INFO, "error buffer pending..free it\n");
+ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+ rfi->pktslots[cur_idx] = NULL;
+ }
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+
+ /* got the expected one. flush from current to expected
+ * and update expected
+ */
+ brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
+ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+ rfi->cur_idx = cur_idx;
+ rfi->exp_idx = exp_idx;
+
+ brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
+ &reorder_list);
+ brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
+ flow_id, skb_queue_len(&reorder_list),
+ rfi->pend_pkts);
+ } else {
+ u8 end_idx;
+
+ brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
+ flow_id, flags, rfi->cur_idx, rfi->exp_idx,
+ cur_idx, exp_idx);
+ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+ end_idx = rfi->exp_idx;
+ else
+ end_idx = exp_idx;
+
+ /* flush pkts first */
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+ &reorder_list);
+
+ if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
+ __skb_queue_tail(&reorder_list, pkt);
+ } else {
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+ }
+ rfi->exp_idx = exp_idx;
+ rfi->cur_idx = cur_idx;
+ }
+ } else {
+ /* explicity window move updating the expected index */
+ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+ brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
+ flow_id, flags, rfi->exp_idx, exp_idx);
+ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+ end_idx = rfi->exp_idx;
+ else
+ end_idx = exp_idx;
+
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+ &reorder_list);
+ __skb_queue_tail(&reorder_list, pkt);
+ /* set the new expected idx */
+ rfi->exp_idx = exp_idx;
+ }
+netif_rx:
+ skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+ __skb_unlink(pkt, &reorder_list);
+ brcmf_netif_rx(ifp, pkt);
+ }
+}
+
void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
{
struct sk_buff *skb, *pnext;
struct brcmf_if *ifp;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_skb_reorder_data *rd;
u8 ifidx;
int ret;

- brcmf_dbg(DATA, "Enter\n");
+ brcmf_dbg(DATA, "Enter: %s: count=%u\n", dev_name(dev),
+ skb_queue_len(skb_list));

skb_queue_walk_safe(skb_list, skb, pnext) {
skb_unlink(skb, skb_list);
@@ -304,31 +544,11 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
continue;
}

- skb->dev = ifp->ndev;
- skb->protocol = eth_type_trans(skb, skb->dev);
-
- if (skb->pkt_type == PACKET_MULTICAST)
- ifp->stats.multicast++;
-
- /* Process special event packets */
- brcmf_fweh_process_skb(drvr, skb);
-
- if (!(ifp->ndev->flags & IFF_UP)) {
- brcmu_pkt_buf_free_skb(skb);
- continue;
- }
-
- ifp->stats.rx_bytes += skb->len;
- ifp->stats.rx_packets++;
-
- if (in_interrupt())
- netif_rx(skb);
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+ if (rd->reorder)
+ brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
else
- /* If the receive is not processed inside an ISR,
- * the softirqd must be woken explicitly to service the
- * NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
- */
- netif_rx_ni(skb);
+ brcmf_netif_rx(ifp, skb);
}
}

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index 601b0d0..438c7b9 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -1483,6 +1483,7 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
struct sk_buff *skb)
{
+ struct brcmf_skb_reorder_data *rd;
struct brcmf_fws_info *fws = drvr->fws;
u8 *signal_data;
s16 data_len;
@@ -1536,9 +1537,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,

err = BRCMF_FWS_RET_OK_NOSCHEDULE;
switch (type) {
- case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
case BRCMF_FWS_TYPE_COMP_TXSTATUS:
break;
+ case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+ rd->reorder = data;
+ break;
case BRCMF_FWS_TYPE_MACDESC_ADD:
case BRCMF_FWS_TYPE_MACDESC_DEL:
brcmf_fws_macdesc_indicate(fws, type, data);
@@ -1747,6 +1751,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
bool pae = eh->h_proto == htons(ETH_P_PAE);
int ret;

+ brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
/* determine the priority */
if (!skb->priority)
skb->priority = cfg80211_classify8021d(skb);
@@ -1915,7 +1920,8 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE)
tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
- BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
+ BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
+ BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;

rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
brcmf_fws_notify_credit_map);
@@ -1940,6 +1946,9 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
goto fail_event;
}

+ if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
+ brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
+
brcmf_fws_hanger_init(&drvr->fws->hanger);
brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0);
brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other);
--
1.8.1.3



2013-08-10 10:27:56

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 08/12] brcmfmac: abstract tx packet processing functions

From: Franky Lin <[email protected]>

Abstract brcmf_sdio_txpkt_prep and brcmf_sdio_txpkt_postp as a preparation
of chained tx packets for host side tx glomming.

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Reviewed-by: Arend van Spriel <[email protected]>
Signed-off-by: Franky Lin <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 16 +-
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 237 +++++++++++++++------
.../net/wireless/brcm80211/brcmfmac/sdio_host.h | 2 +-
include/linux/platform_data/brcmfmac-sdio.h | 6 +
4 files changed, 183 insertions(+), 78 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index e3f3c48..e13b1a6 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -592,6 +592,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
uint flags, u8 *buf, uint nbytes)
{
struct sk_buff *mypkt;
+ struct sk_buff_head pktq;
int err;

mypkt = brcmu_pkt_buf_get_skb(nbytes);
@@ -602,7 +603,10 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
}

memcpy(mypkt->data, buf, nbytes);
- err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt);
+ __skb_queue_head_init(&pktq);
+ __skb_queue_tail(&pktq, mypkt);
+ err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq);
+ __skb_dequeue_tail(&pktq);

brcmu_pkt_buf_free_skb(mypkt);
return err;
@@ -611,22 +615,18 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,

int
brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
- uint flags, struct sk_buff *pkt)
+ uint flags, struct sk_buff_head *pktq)
{
uint width;
int err = 0;
- struct sk_buff_head pkt_list;

brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
- fn, addr, pkt->len);
+ fn, addr, pktq->qlen);

width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
brcmf_sdio_addrprep(sdiodev, width, &addr);

- skb_queue_head_init(&pkt_list);
- skb_queue_tail(&pkt_list, pkt);
- err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list);
- skb_dequeue_tail(&pkt_list);
+ err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq);

return err;
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index db31312..aa4caca 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -510,7 +510,6 @@ struct brcmf_sdio {

#ifdef DEBUG
static int qcount[NUMPRIO];
-static int tx_packets[NUMPRIO];
#endif /* DEBUG */

#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */
@@ -1759,85 +1758,185 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus)
return;
}

+/* flag marking a dummy skb added for DMA alignment requirement */
+#define DUMMY_SKB_FLAG 0x10000
+/* bit mask of data length chopped from the previous packet */
+#define DUMMY_SKB_CHOP_LEN_MASK 0xffff
+/**
+ * brcmf_sdio_txpkt_prep - packet preparation for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ * @chan: virtual channel to transmit the packet
+ *
+ * Processes to be applied to the packet
+ * - Align data buffer pointer
+ * - Align data buffer length
+ * - Prepare header
+ * Return: negative value if there is error
+ */
+static int
+brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
+ uint chan)
+{
+ u16 head_pad, tail_pad, tail_chop, pkt_len;
+ u16 head_align, sg_align;
+ u32 sw_header;
+ int ntail;
+ struct sk_buff *pkt_next, *pkt_new;
+ u8 *dat_buf;
+ unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+
+ /* SDIO ADMA requires at least 32 bit alignment */
+ head_align = 4;
+ sg_align = 4;
+ if (bus->sdiodev->pdata) {
+ head_align = bus->sdiodev->pdata->sd_head_align > 4 ?
+ bus->sdiodev->pdata->sd_head_align : 4;
+ sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ?
+ bus->sdiodev->pdata->sd_sgentry_align : 4;
+ }
+ /* sg entry alignment should be a divisor of block size */
+ WARN_ON(blksize % sg_align);
+
+ pkt_next = pktq->next;
+ dat_buf = (u8 *)(pkt_next->data);
+
+ /* Check head padding */
+ head_pad = ((unsigned long)dat_buf % head_align);
+ if (head_pad) {
+ if (skb_headroom(pkt_next) < head_pad) {
+ bus->sdiodev->bus_if->tx_realloc++;
+ head_pad = 0;
+ if (skb_cow(pkt_next, head_pad))
+ return -ENOMEM;
+ }
+ skb_push(pkt_next, head_pad);
+ dat_buf = (u8 *)(pkt_next->data);
+ memset(dat_buf, 0, head_pad + SDPCM_HDRLEN);
+ }
+
+ /* Check tail padding */
+ pkt_new = NULL;
+ tail_chop = pkt_next->len % sg_align;
+ tail_pad = sg_align - tail_chop;
+ tail_pad += blksize - (pkt_next->len + tail_pad) % blksize;
+ if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) {
+ pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop);
+ if (pkt_new == NULL)
+ return -ENOMEM;
+ memcpy(pkt_new->data,
+ pkt_next->data + pkt_next->len - tail_chop,
+ tail_chop);
+ *(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop;
+ skb_trim(pkt_next, pkt_next->len - tail_chop);
+ __skb_queue_after(pktq, pkt_next, pkt_new);
+ } else {
+ ntail = pkt_next->data_len + tail_pad -
+ (pkt_next->end - pkt_next->tail);
+ if (skb_cloned(pkt_next) || ntail > 0)
+ if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC))
+ return -ENOMEM;
+ if (skb_linearize(pkt_next))
+ return -ENOMEM;
+ dat_buf = (u8 *)(pkt_next->data);
+ __skb_put(pkt_next, tail_pad);
+ }
+
+ /* Now prep the header */
+ /* 4 bytes hardware header (frame tag)
+ * Byte 0~1: Frame length
+ * Byte 2~3: Checksum, bit-wise inverse of frame length
+ */
+ if (pkt_new)
+ pkt_len = pkt_next->len + tail_chop;
+ else
+ pkt_len = pkt_next->len - tail_pad;
+ *(__le16 *)dat_buf = cpu_to_le16(pkt_len);
+ *(((__le16 *)dat_buf) + 1) = cpu_to_le16(~pkt_len);
+ /* 8 bytes software header
+ * Byte 0: Tx sequence number
+ * Byte 1: 4 MSB Channel number
+ * Byte 2: Reserved
+ * Byte 3: Data offset
+ * Byte 4~7: Reserved
+ */
+ sw_header = bus->tx_seq;
+ sw_header |= ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK);
+ sw_header |= ((head_pad + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) &
+ SDPCM_DOFFSET_MASK;
+ *(((__le32 *)dat_buf) + 1) = cpu_to_le32(sw_header);
+ *(((__le32 *)dat_buf) + 2) = 0;
+
+ if (BRCMF_BYTES_ON() &&
+ ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
+ (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
+ brcmf_dbg_hex_dump(true, pkt_next, pkt_len, "Tx Frame:\n");
+ else if (BRCMF_HDRS_ON())
+ brcmf_dbg_hex_dump(true, pkt_next, head_pad + SDPCM_HDRLEN,
+ "Tx Header:\n");
+
+ return 0;
+}
+
+/**
+ * brcmf_sdio_txpkt_postp - packet post processing for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ *
+ * Processes to be applied to the packet
+ * - Remove head padding
+ * - Remove tail padding
+ */
+static void
+brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
+{
+ u8 *hdr;
+ u32 dat_offset;
+ u32 dummy_flags, chop_len;
+ struct sk_buff *pkt_next, *tmp, *pkt_prev;
+
+ skb_queue_walk_safe(pktq, pkt_next, tmp) {
+ dummy_flags = *(u32 *)(pkt_next->cb);
+ if (dummy_flags & DUMMY_SKB_FLAG) {
+ chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK;
+ if (chop_len) {
+ pkt_prev = pkt_next->prev;
+ memcpy(pkt_prev->data + pkt_prev->len,
+ pkt_next->data, chop_len);
+ skb_put(pkt_prev, chop_len);
+ }
+ __skb_unlink(pkt_next, pktq);
+ brcmu_pkt_buf_free_skb(pkt_next);
+ } else {
+ hdr = pkt_next->data + SDPCM_FRAMETAG_LEN;
+ dat_offset = le32_to_cpu(*(__le32 *)hdr);
+ dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>
+ SDPCM_DOFFSET_SHIFT;
+ skb_pull(pkt_next, dat_offset);
+ }
+ }
+}
+
/* Writes a HW/SW header into the packet and sends it. */
/* Assumes: (a) header space already there, (b) caller holds lock */
static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
uint chan)
{
int ret;
- u8 *frame;
- u16 len, pad = 0;
- u32 swheader;
int i;
+ struct sk_buff_head localq;

brcmf_dbg(TRACE, "Enter\n");

- frame = (u8 *) (pkt->data);
-
- /* Add alignment padding, allocate new packet if needed */
- pad = ((unsigned long)frame % BRCMF_SDALIGN);
- if (pad) {
- if (skb_headroom(pkt) < pad) {
- brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n",
- skb_headroom(pkt), pad);
- bus->sdiodev->bus_if->tx_realloc++;
- ret = skb_cow(pkt, BRCMF_SDALIGN);
- if (ret)
- goto done;
- pad = ((unsigned long)frame % BRCMF_SDALIGN);
- }
- skb_push(pkt, pad);
- frame = (u8 *) (pkt->data);
- memset(frame, 0, pad + SDPCM_HDRLEN);
- }
- /* precondition: pad < BRCMF_SDALIGN */
-
- /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
- len = (u16) (pkt->len);
- *(__le16 *) frame = cpu_to_le16(len);
- *(((__le16 *) frame) + 1) = cpu_to_le16(~len);
-
- /* Software tag: channel, sequence number, data offset */
- swheader =
- ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
- (((pad +
- SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
-
- *(((__le32 *) frame) + 1) = cpu_to_le32(swheader);
- *(((__le32 *) frame) + 2) = 0;
-
-#ifdef DEBUG
- tx_packets[pkt->priority]++;
-#endif
-
- brcmf_dbg_hex_dump(BRCMF_BYTES_ON() &&
- ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
- (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)),
- frame, len, "Tx Frame:\n");
- brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
- ((BRCMF_CTL_ON() &&
- chan == SDPCM_CONTROL_CHANNEL) ||
- (BRCMF_DATA_ON() &&
- chan != SDPCM_CONTROL_CHANNEL))) &&
- BRCMF_HDRS_ON(),
- frame, min_t(u16, len, 16), "TxHdr:\n");
-
- /* Raise len to next SDIO block to eliminate tail command */
- if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
- u16 pad = bus->blocksize - (len % bus->blocksize);
- if ((pad <= bus->roundup) && (pad < bus->blocksize))
- len += pad;
- } else if (len % BRCMF_SDALIGN) {
- len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
- }
-
- /* Some controllers have trouble with odd bytes -- round to even */
- if (len & (ALIGNMENT - 1))
- len = roundup(len, ALIGNMENT);
+ __skb_queue_head_init(&localq);
+ __skb_queue_tail(&localq, pkt);
+ ret = brcmf_sdio_txpkt_prep(bus, &localq, chan);
+ if (ret)
+ goto done;

sdio_claim_host(bus->sdiodev->func[1]);
ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad,
- SDIO_FUNC_2, F2SYNC, pkt);
+ SDIO_FUNC_2, F2SYNC, &localq);
bus->sdcnt.f2txdata++;

if (ret < 0) {
@@ -1868,8 +1967,8 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;

done:
- /* restore pkt buffer pointer before calling tx complete routine */
- skb_pull(pkt, SDPCM_HDRLEN + pad);
+ brcmf_sdio_txpkt_postp(bus, &localq);
+ __skb_dequeue_tail(&localq);
brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0);
return ret;
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
index 09786a5..2b5407f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
@@ -208,7 +208,7 @@ extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
*/
extern int
brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
- uint flags, struct sk_buff *pkt);
+ uint flags, struct sk_buff_head *pktq);
extern int
brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
uint flags, u8 *buf, uint nbytes);
diff --git a/include/linux/platform_data/brcmfmac-sdio.h b/include/linux/platform_data/brcmfmac-sdio.h
index b717499..e75dcbf 100644
--- a/include/linux/platform_data/brcmfmac-sdio.h
+++ b/include/linux/platform_data/brcmfmac-sdio.h
@@ -94,6 +94,10 @@ void __init brcmfmac_init_pdata(void)
* Set this to true if the SDIO host controller has higher align requirement
* than 32 bytes for each scatterlist item.
*
+ * sd_head_align: alignment requirement for start of data buffer
+ *
+ * sd_sgentry_align: length alignment requirement for each sg entry
+ *
* power_on: This function is called by the brcmfmac when the module gets
* loaded. This can be particularly useful for low power devices. The platform
* spcific routine may for example decide to power up the complete device.
@@ -121,6 +125,8 @@ struct brcmfmac_sdio_platform_data {
unsigned int oob_irq_nr;
unsigned long oob_irq_flags;
bool broken_sg_support;
+ unsigned short sd_head_align;
+ unsigned short sd_sgentry_align;
void (*power_on)(void);
void (*power_off)(void);
void (*reset)(void);
--
1.8.1.3



2013-08-10 10:27:46

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 04/12] brcmfmac: always use worker thread for tx data.

From: Hante Meuleman <[email protected]>

When fw signalling is disabled tx is sent immediately. Using
queues and worker thread allows usb to do synchronous autopm. This
patch makes fws use queues and worker thread even if signalling is
not supported by FW or not enabled.

Reviewed-by: Arend Van Spriel <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Hante Meuleman <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 1 -
.../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 13 +--
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 96 ++++++++++++----------
3 files changed, 54 insertions(+), 56 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index 3943fb8..df94d0e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -557,7 +557,6 @@ struct brcmf_pub {

struct brcmf_fweh_info fweh;

- bool fw_signals;
struct brcmf_fws_info *fws;
spinlock_t fws_spinlock;

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 9bc2785..e067aec 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -278,18 +278,10 @@ void brcmf_txflowblock(struct device *dev, bool state)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
- int i;

brcmf_dbg(TRACE, "Enter\n");

- if (brcmf_fws_fc_active(drvr->fws)) {
- brcmf_fws_bus_blocked(drvr, state);
- } else {
- for (i = 0; i < BRCMF_MAX_IFS; i++)
- brcmf_txflowblock_if(drvr->iflist[i],
- BRCMF_NETIF_STOP_REASON_BLOCK_BUS,
- state);
- }
+ brcmf_fws_bus_blocked(drvr, state);
}

static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
@@ -534,7 +526,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
skb_unlink(skb, skb_list);

/* process and remove protocol-specific header */
- ret = brcmf_proto_hdrpull(drvr, drvr->fw_signals, &ifidx, skb);
+ ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
ifp = drvr->iflist[ifidx];

if (ret || !ifp || !ifp->ndev) {
@@ -1109,7 +1101,6 @@ int brcmf_bus_start(struct device *dev)
if (ret < 0)
goto fail;

- drvr->fw_signals = true;
ret = brcmf_fws_init(drvr);
if (ret < 0)
goto fail;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index 438c7b9..15fc807 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -425,6 +425,7 @@ struct brcmf_fws_info {
struct brcmf_fws_stats stats;
struct brcmf_fws_hanger hanger;
enum brcmf_fws_fcmode fcmode;
+ bool fw_signals;
bool bcmc_credit_check;
struct brcmf_fws_macdesc_table desc;
struct workqueue_struct *fws_wq;
@@ -1160,7 +1161,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
{
/* only schedule dequeue when there are credits for delayed traffic */
- if (fws->fifo_credit_map & fws->fifo_delay_map)
+ if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
+ (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
queue_work(fws->fws_wq, &fws->fws_dequeue_work);
}

@@ -1498,8 +1500,10 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,

WARN_ON(signal_len > skb->len);

+ if (!signal_len)
+ return 0;
/* if flow control disabled, skip to packet data and leave */
- if (!signal_len || !drvr->fw_signals) {
+ if (!fws->fw_signals) {
skb_pull(skb, signal_len);
return 0;
}
@@ -1749,7 +1753,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
int fifo = BRCMF_FWS_FIFO_BCMC;
bool multicast = is_multicast_ether_addr(eh->h_dest);
bool pae = eh->h_proto == htons(ETH_P_PAE);
- int ret;

brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
/* determine the priority */
@@ -1760,17 +1763,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
if (pae)
atomic_inc(&ifp->pend_8021x_cnt);

- if (!brcmf_fws_fc_active(fws)) {
- /* If the protocol uses a data header, apply it */
- brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb);
-
- /* Use bus module to send data frame */
- ret = brcmf_bus_txdata(drvr->bus_if, skb);
- if (ret < 0)
- brcmf_txfinalize(drvr, skb, false);
- return ret;
- }
-
/* set control buffer information */
skcb->if_flags = 0;
skcb->state = BRCMF_FWS_SKBSTATE_NEW;
@@ -1818,7 +1810,7 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
struct brcmf_fws_info *fws = ifp->drvr->fws;
struct brcmf_fws_mac_descriptor *entry;

- if (!ifp->ndev || !ifp->drvr->fw_signals)
+ if (!ifp->ndev)
return;

entry = &fws->desc.iface[ifp->ifidx];
@@ -1849,15 +1841,38 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp)
static void brcmf_fws_dequeue_worker(struct work_struct *worker)
{
struct brcmf_fws_info *fws;
+ struct brcmf_pub *drvr;
struct sk_buff *skb;
ulong flags;
int fifo;
+ u32 hslot;
+ u32 ifidx;
+ int ret;

fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
+ drvr = fws->drvr;

- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(drvr, flags);
for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
fifo--) {
+ if (!brcmf_fws_fc_active(fws)) {
+ while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
+ hslot = brcmf_skb_htod_tag_get_field(skb,
+ HSLOT);
+ brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
+ &skb, true);
+ ifidx = brcmf_skb_if_flags_get_field(skb,
+ INDEX);
+ brcmf_proto_hdrpush(drvr, ifidx, 0, skb);
+ /* Use bus module to send data frame */
+ ret = brcmf_bus_txdata(drvr->bus_if, skb);
+ if (ret < 0)
+ brcmf_txfinalize(drvr, skb, false);
+ if (fws->bus_flow_blocked)
+ break;
+ }
+ continue;
+ }
while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
(fifo == BRCMF_FWS_FIFO_BCMC))) {
skb = brcmf_fws_deq(fws, fifo);
@@ -1885,17 +1900,15 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
}
}
}
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(drvr, flags);
}

int brcmf_fws_init(struct brcmf_pub *drvr)
{
+ struct brcmf_fws_info *fws;
u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
int rc;

- if (!drvr->fw_signals)
- return 0;
-
spin_lock_init(&drvr->fws_spinlock);

drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
@@ -1904,20 +1917,21 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
goto fail;
}

+ fws = drvr->fws;
/* set linkage back */
- drvr->fws->drvr = drvr;
- drvr->fws->fcmode = fcmode;
+ fws->drvr = drvr;
+ fws->fcmode = fcmode;

- drvr->fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
- if (drvr->fws->fws_wq == NULL) {
+ fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
+ if (fws->fws_wq == NULL) {
brcmf_err("workqueue creation failed\n");
rc = -EBADF;
goto fail;
}
- INIT_WORK(&drvr->fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
+ INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);

/* enable firmware signalling if fcmode active */
- if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE)
+ if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
@@ -1937,34 +1951,33 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
goto fail;
}

- /* setting the iovar may fail if feature is unsupported
+ /* Setting the iovar may fail if feature is unsupported
* so leave the rc as is so driver initialization can
- * continue.
+ * continue. Set mode back to none indicating not enabled.
*/
+ fws->fw_signals = true;
if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
brcmf_err("failed to set bdcv2 tlv signaling\n");
- goto fail_event;
+ fws->fcmode = BRCMF_FWS_FCMODE_NONE;
+ fws->fw_signals = false;
}

if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");

- brcmf_fws_hanger_init(&drvr->fws->hanger);
- brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0);
- brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other);
- brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
+ brcmf_fws_hanger_init(&fws->hanger);
+ brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
+ brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
+ brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
BRCMF_FWS_PSQ_LEN);

/* create debugfs file for statistics */
- brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
+ brcmf_debugfs_create_fws_stats(drvr, &fws->stats);

brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
- drvr->fw_signals ? "enabled" : "disabled", tlv);
+ fws->fw_signals ? "enabled" : "disabled", tlv);
return 0;

-fail_event:
- brcmf_fweh_unregister(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT);
- brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
fail:
brcmf_fws_deinit(drvr);
return rc;
@@ -1978,11 +1991,6 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr)
if (!fws)
return;

- /* disable firmware signalling entirely
- * to avoid using the workqueue.
- */
- drvr->fw_signals = false;
-
if (drvr->fws->fws_wq)
destroy_workqueue(drvr->fws->fws_wq);

@@ -1998,7 +2006,7 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr)

bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
{
- if (!fws)
+ if (!fws->creditmap_received)
return false;

return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
--
1.8.1.3



2013-08-10 10:27:56

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 11/12] brcmfmac: use configurable sdio bus header length for tx packet

From: Franky Lin <[email protected]>

Host tx glomming require an extended hardware sdio bus header to store
information for dongle. Introduce a variable in struct brcmf_sdio to replace
macro SDPCM_HDRLEN

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Reviewed-by: Arend van Spriel <[email protected]>
Signed-off-by: Franky Lin <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 30 ++++++++++++----------
1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 02ab5cd..1aa75d5 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -461,6 +461,8 @@ struct brcmf_sdio {
struct brcmf_sdio_count sdcnt;
bool sr_enabled; /* SaveRestore enabled */
bool sleeping; /* SDIO bus sleeping */
+
+ u8 tx_hdrlen; /* sdio bus header length for tx packet */
};

/* clkstate */
@@ -1025,7 +1027,6 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus)
#define SDPCM_HWHDR_LEN 4
#define SDPCM_SWHDR_LEN 8
#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
-#define SDPCM_RESERVE (SDPCM_HDRLEN + BRCMF_SDALIGN)
/* software header */
#define SDPCM_SEQ_MASK 0x000000ff
#define SDPCM_SEQ_WRAP 256
@@ -1838,7 +1839,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
}
skb_push(pkt_next, head_pad);
dat_buf = (u8 *)(pkt_next->data);
- memset(dat_buf, 0, head_pad + SDPCM_HDRLEN);
+ memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
}

/* Check tail padding */
@@ -1874,7 +1875,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
else
hd_info.len = pkt_next->len - tail_pad;
hd_info.channel = chan;
- hd_info.dat_offset = head_pad + SDPCM_HDRLEN;
+ hd_info.dat_offset = head_pad + bus->tx_hdrlen;
brcmf_sdio_hdpack(bus, dat_buf, &hd_info);

if (BRCMF_BYTES_ON() &&
@@ -1882,7 +1883,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
(BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, "Tx Frame:\n");
else if (BRCMF_HDRS_ON())
- brcmf_dbg_hex_dump(true, pkt_next, head_pad + SDPCM_HDRLEN,
+ brcmf_dbg_hex_dump(true, pkt_next, head_pad + bus->tx_hdrlen,
"Tx Header:\n");

return 0;
@@ -1989,7 +1990,6 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
u32 intstatus = 0;
int ret = 0, prec_out;
uint cnt = 0;
- uint datalen;
u8 tx_prec_map;

brcmf_dbg(TRACE, "Enter\n");
@@ -2005,7 +2005,6 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
break;
}
spin_unlock_bh(&bus->txqlock);
- datalen = pkt->len - SDPCM_HDRLEN;

ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL);

@@ -2392,7 +2391,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
datalen = pkt->len;

/* Add space for the header */
- skb_push(pkt, SDPCM_HDRLEN);
+ skb_push(pkt, bus->tx_hdrlen);
/* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */

prec = prio2prec((pkt->priority & PRIOMASK));
@@ -2405,7 +2404,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
/* Priority based enq */
spin_lock_irqsave(&bus->txqlock, flags);
if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
- skb_pull(pkt, SDPCM_HDRLEN);
+ skb_pull(pkt, bus->tx_hdrlen);
brcmf_err("out of bus->txq !!!\n");
ret = -ENOSR;
} else {
@@ -2566,8 +2565,8 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
brcmf_dbg(TRACE, "Enter\n");

/* Back the pointer to make a room for bus header */
- frame = msg - SDPCM_HDRLEN;
- len = (msglen += SDPCM_HDRLEN);
+ frame = msg - bus->tx_hdrlen;
+ len = (msglen += bus->tx_hdrlen);

/* Add alignment padding (optional for ctl frames) */
doff = ((unsigned long)frame % BRCMF_SDALIGN);
@@ -2575,10 +2574,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
frame -= doff;
len += doff;
msglen += doff;
- memset(frame, 0, doff + SDPCM_HDRLEN);
+ memset(frame, 0, doff + bus->tx_hdrlen);
}
/* precondition: doff < BRCMF_SDALIGN */
- doff += SDPCM_HDRLEN;
+ doff += bus->tx_hdrlen;

/* Round send length to next SDIO block */
if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
@@ -3895,8 +3894,11 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
bus->sdiodev->bus_if->chip = bus->ci->chip;
bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;

- /* Attach to the brcmf/OS/network interface */
- ret = brcmf_attach(SDPCM_RESERVE, bus->sdiodev->dev);
+ /* default sdio bus header length for tx packet */
+ bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
+
+ /* Attach to the common layer, reserve hdr space */
+ ret = brcmf_attach(bus->tx_hdrlen, bus->sdiodev->dev);
if (ret != 0) {
brcmf_err("brcmf_attach failed\n");
goto fail;
--
1.8.1.3



2013-08-10 10:27:56

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 05/12] brcmfmac: no fws locking outside fws module.

From: Hante Meuleman <[email protected]>

FWS uses locking to protect its data while being called from
various entries. On bus_txdata the lock was kept resulting in
unnecessary long locking, but also creating possibility for
deadlock. This update changes the locking to release lock when
bus_txdata is called.

Reviewed-by: Arend Van Spriel <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Hante Meuleman <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 1 -
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 127 ++++++++++-----------
2 files changed, 62 insertions(+), 66 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index df94d0e..1273dfd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -558,7 +558,6 @@ struct brcmf_pub {
struct brcmf_fweh_info fweh;

struct brcmf_fws_info *fws;
- spinlock_t fws_spinlock;

struct brcmf_ampdu_rx_reorder
*reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index 15fc807..82f9140 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -422,6 +422,8 @@ struct brcmf_fws_macdesc_table {

struct brcmf_fws_info {
struct brcmf_pub *drvr;
+ spinlock_t spinlock;
+ ulong flags;
struct brcmf_fws_stats stats;
struct brcmf_fws_hanger hanger;
enum brcmf_fws_fcmode fcmode;
@@ -484,6 +486,18 @@ static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
}
#undef BRCMF_FWS_TLV_DEF

+static void brcmf_fws_lock(struct brcmf_fws_info *fws)
+ __acquires(&fws->spinlock)
+{
+ spin_lock_irqsave(&fws->spinlock, fws->flags);
+}
+
+static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
+ __releases(&fws->spinlock)
+{
+ spin_unlock_irqrestore(&fws->spinlock, fws->flags);
+}
+
static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
{
u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
@@ -870,8 +884,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
skcb->state = BRCMF_FWS_SKBSTATE_TIM;
bus = fws->drvr->bus_if;
err = brcmf_fws_hdrpush(fws, skb);
- if (err == 0)
+ if (err == 0) {
+ brcmf_fws_unlock(fws);
err = brcmf_bus_txdata(bus, skb);
+ brcmf_fws_lock(fws);
+ }
if (err)
brcmu_pkt_buf_free_skb(skb);
return true;
@@ -906,26 +923,10 @@ static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
return 0;
}

-/* using macro so sparse checking does not complain
- * about locking imbalance.
- */
-#define brcmf_fws_lock(drvr, flags) \
-do { \
- flags = 0; \
- spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \
-} while (0)
-
-/* using macro so sparse checking does not complain
- * about locking imbalance.
- */
-#define brcmf_fws_unlock(drvr, flags) \
- spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags))
-
static
int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
{
struct brcmf_fws_mac_descriptor *entry, *existing;
- ulong flags;
u8 mac_handle;
u8 ifidx;
u8 *addr;
@@ -939,10 +940,10 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
if (entry->occupied) {
brcmf_dbg(TRACE, "deleting %s mac %pM\n",
entry->name, addr);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
brcmf_fws_macdesc_cleanup(fws, entry, -1);
brcmf_fws_macdesc_deinit(entry);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
} else
fws->stats.mac_update_failed++;
return 0;
@@ -951,13 +952,13 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
existing = brcmf_fws_macdesc_lookup(fws, addr);
if (IS_ERR(existing)) {
if (!entry->occupied) {
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
entry->mac_handle = mac_handle;
brcmf_fws_macdesc_init(entry, addr, ifidx);
brcmf_fws_macdesc_set_name(fws, entry);
brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
BRCMF_FWS_PSQ_LEN);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
} else {
fws->stats.mac_update_failed++;
@@ -965,13 +966,13 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
} else {
if (entry != existing) {
brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
memcpy(entry, existing,
offsetof(struct brcmf_fws_mac_descriptor, psq));
entry->mac_handle = mac_handle;
brcmf_fws_macdesc_deinit(existing);
brcmf_fws_macdesc_set_name(fws, entry);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
addr);
} else {
@@ -987,7 +988,6 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
u8 type, u8 *data)
{
struct brcmf_fws_mac_descriptor *entry;
- ulong flags;
u8 mac_handle;
int ret;

@@ -997,7 +997,7 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
fws->stats.mac_ps_update_failed++;
return -ESRCH;
}
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
/* a state update should wipe old credits */
entry->requested_credit = 0;
entry->requested_packet = 0;
@@ -1012,7 +1012,7 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
}
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
return ret;
}

@@ -1020,7 +1020,6 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
u8 type, u8 *data)
{
struct brcmf_fws_mac_descriptor *entry;
- ulong flags;
u8 ifidx;
int ret;

@@ -1039,7 +1038,7 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,

brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
entry->name);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
switch (type) {
case BRCMF_FWS_TYPE_INTERFACE_OPEN:
entry->state = BRCMF_FWS_STATE_OPEN;
@@ -1051,10 +1050,10 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
break;
default:
ret = -EINVAL;
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
goto fail;
}
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
return ret;

fail:
@@ -1066,7 +1065,6 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
u8 *data)
{
struct brcmf_fws_mac_descriptor *entry;
- ulong flags;

entry = &fws->desc.nodes[data[1] & 0x1F];
if (!entry->occupied) {
@@ -1080,14 +1078,14 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
brcmf_fws_get_tlv_name(type), type, entry->name,
data[0], data[2]);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
entry->requested_credit = data[0];
else
entry->requested_packet = data[0];

entry->ac_bitmap = data[2];
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
return BRCMF_FWS_RET_OK_SCHEDULE;
}

@@ -1385,7 +1383,6 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
u8 *data)
{
- ulong flags;
int i;

if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
@@ -1394,19 +1391,18 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
}

brcmf_dbg(DATA, "enter: data %pM\n", data);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
brcmf_fws_return_credits(fws, i, data[i]);

brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
fws->fifo_delay_map);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
return BRCMF_FWS_RET_OK_SCHEDULE;
}

static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
{
- ulong lflags;
__le32 status_le;
u32 status;
u32 hslot;
@@ -1420,9 +1416,9 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
hslot = brcmf_txstatus_get_field(status, HSLOT);
genbit = brcmf_txstatus_get_field(status, GENERATION);

- brcmf_fws_lock(fws->drvr, lflags);
+ brcmf_fws_lock(fws);
brcmf_fws_txs_process(fws, flags, hslot, genbit);
- brcmf_fws_unlock(fws->drvr, lflags);
+ brcmf_fws_unlock(fws);
return BRCMF_FWS_RET_OK_NOSCHEDULE;
}

@@ -1442,7 +1438,6 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
{
struct brcmf_fws_info *fws = ifp->drvr->fws;
int i;
- ulong flags;
u8 *credits = data;

if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
@@ -1455,7 +1450,7 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
fws->creditmap_received = true;

brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
- brcmf_fws_lock(ifp->drvr, flags);
+ brcmf_fws_lock(fws);
for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
if (*credits)
fws->fifo_credit_map |= 1 << i;
@@ -1464,7 +1459,7 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
fws->fifo_credit[i] = *credits++;
}
brcmf_fws_schedule_deq(fws);
- brcmf_fws_unlock(ifp->drvr, flags);
+ brcmf_fws_unlock(fws);
return 0;
}

@@ -1473,12 +1468,11 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
void *data)
{
struct brcmf_fws_info *fws = ifp->drvr->fws;
- ulong flags;

- brcmf_fws_lock(ifp->drvr, flags);
+ brcmf_fws_lock(fws);
if (fws)
fws->bcmc_credit_check = true;
- brcmf_fws_unlock(ifp->drvr, flags);
+ brcmf_fws_unlock(fws);
return 0;
}

@@ -1702,17 +1696,22 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
return PTR_ERR(entry);

brcmf_fws_precommit_skb(fws, fifo, skb);
+ entry->transit_count++;
+ if (entry->suppressed)
+ entry->suppr_transit_count++;
+ brcmf_fws_unlock(fws);
rc = brcmf_bus_txdata(bus, skb);
+ brcmf_fws_lock(fws);
brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
skcb->if_flags, skcb->htod, rc);
if (rc < 0) {
+ entry->transit_count--;
+ if (entry->suppressed)
+ entry->suppr_transit_count--;
brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
goto rollback;
}

- entry->transit_count++;
- if (entry->suppressed)
- entry->suppr_transit_count++;
fws->stats.pkt2bus++;
fws->stats.send_pkts[fifo]++;
if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
@@ -1749,7 +1748,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
struct brcmf_fws_info *fws = drvr->fws;
struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
struct ethhdr *eh = (struct ethhdr *)(skb->data);
- ulong flags;
int fifo = BRCMF_FWS_FIFO_BCMC;
bool multicast = is_multicast_ether_addr(eh->h_dest);
bool pae = eh->h_proto == htons(ETH_P_PAE);
@@ -1770,7 +1768,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
if (!multicast)
fifo = brcmf_fws_prio2fifo[skb->priority];

- brcmf_fws_lock(drvr, flags);
+ brcmf_fws_lock(fws);
if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
fws->borrow_defer_timestamp = jiffies +
BRCMF_FWS_BORROW_DEFER_PERIOD;
@@ -1790,7 +1788,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
}
brcmu_pkt_buf_free_skb(skb);
}
- brcmf_fws_unlock(drvr, flags);
+ brcmf_fws_unlock(fws);
return 0;
}

@@ -1825,17 +1823,16 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
void brcmf_fws_del_interface(struct brcmf_if *ifp)
{
struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
- ulong flags;

if (!entry)
return;

- brcmf_fws_lock(ifp->drvr, flags);
+ brcmf_fws_lock(ifp->drvr->fws);
ifp->fws_desc = NULL;
brcmf_dbg(TRACE, "deleting %s\n", entry->name);
brcmf_fws_macdesc_deinit(entry);
brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
- brcmf_fws_unlock(ifp->drvr, flags);
+ brcmf_fws_unlock(ifp->drvr->fws);
}

static void brcmf_fws_dequeue_worker(struct work_struct *worker)
@@ -1843,7 +1840,6 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
struct brcmf_fws_info *fws;
struct brcmf_pub *drvr;
struct sk_buff *skb;
- ulong flags;
int fifo;
u32 hslot;
u32 ifidx;
@@ -1852,7 +1848,7 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
drvr = fws->drvr;

- brcmf_fws_lock(drvr, flags);
+ brcmf_fws_lock(fws);
for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
fifo--) {
if (!brcmf_fws_fc_active(fws)) {
@@ -1865,7 +1861,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
INDEX);
brcmf_proto_hdrpush(drvr, ifidx, 0, skb);
/* Use bus module to send data frame */
+ brcmf_fws_unlock(fws);
ret = brcmf_bus_txdata(drvr->bus_if, skb);
+ brcmf_fws_lock(fws);
if (ret < 0)
brcmf_txfinalize(drvr, skb, false);
if (fws->bus_flow_blocked)
@@ -1900,7 +1898,7 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
}
}
}
- brcmf_fws_unlock(drvr, flags);
+ brcmf_fws_unlock(fws);
}

int brcmf_fws_init(struct brcmf_pub *drvr)
@@ -1909,8 +1907,6 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
int rc;

- spin_lock_init(&drvr->fws_spinlock);
-
drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
if (!drvr->fws) {
rc = -ENOMEM;
@@ -1918,6 +1914,9 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
}

fws = drvr->fws;
+
+ spin_lock_init(&fws->spinlock);
+
/* set linkage back */
fws->drvr = drvr;
fws->fcmode = fcmode;
@@ -1986,7 +1985,6 @@ fail:
void brcmf_fws_deinit(struct brcmf_pub *drvr)
{
struct brcmf_fws_info *fws = drvr->fws;
- ulong flags;

if (!fws)
return;
@@ -1995,10 +1993,10 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr)
destroy_workqueue(drvr->fws->fws_wq);

/* cleanup */
- brcmf_fws_lock(drvr, flags);
+ brcmf_fws_lock(fws);
brcmf_fws_cleanup(fws, -1);
drvr->fws = NULL;
- brcmf_fws_unlock(drvr, flags);
+ brcmf_fws_unlock(fws);

/* free top structure */
kfree(fws);
@@ -2014,17 +2012,16 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)

void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
{
- ulong flags;
u32 hslot;

if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
brcmu_pkt_buf_free_skb(skb);
return;
}
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
}

void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
--
1.8.1.3



2013-08-10 10:27:46

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 01/12] brcmfmac: use irq safe spinlock in brcmf_sdbrcm_txdata()

Firmware-signalling needs transmit to firmware to be atomic and
uses a spinlock with irq disabled. Therefor, brcmf_sdbrcm_txdata()
should not use spin_unlock_bh() as it would enable the interrupts.

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 6 +++++-
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 5 +++--
2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
index 080395f..9249b6d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
@@ -36,7 +36,11 @@ struct brcmf_bus_dcmd {
*
* @init: prepare for communication with dongle.
* @stop: clear pending frames, disable data flow.
- * @txdata: send a data frame to the dongle (callee disposes skb).
+ * @txdata: send a data frame to the dongle. When the data
+ * has been transferred, the common driver must be
+ * notified using brcmf_txcomplete(). The common
+ * driver calls this function with interrupts
+ * disabled.
* @txctl: transmit a control request message to dongle.
* @rxctl: receive a control response message from dongle.
* @gettxq: obtain a reference of bus transmit queue (optional).
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 2641119..5cbce1d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -2276,6 +2276,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_sdio *bus = sdiodev->bus;
+ ulong flags;

brcmf_dbg(TRACE, "Enter\n");

@@ -2293,7 +2294,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
bus->sdcnt.fcqueued++;

/* Priority based enq */
- spin_lock_bh(&bus->txqlock);
+ spin_lock_irqsave(&bus->txqlock, flags);
if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
skb_pull(pkt, SDPCM_HDRLEN);
brcmf_txcomplete(bus->sdiodev->dev, pkt, false);
@@ -2307,7 +2308,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
bus->txoff = true;
brcmf_txflowblock(bus->sdiodev->dev, true);
}
- spin_unlock_bh(&bus->txqlock);
+ spin_unlock_irqrestore(&bus->txqlock, flags);

#ifdef DEBUG
if (pktq_plen(&bus->txq, prec) > qcount[prec])
--
1.8.1.3



2013-08-10 10:27:46

by Arend van Spriel

[permalink] [raw]
Subject: [PATCH 02/12] brcmfmac: .txdata() bus callback should not call brcmf_txcomplete()

With firmware-signalling the packet handed to the bus specific driver
layer should not be discarded with brcmf_txcomplete() in the failure
path. Instead only an error is returned and the caller decides what
to do with the packet.

Reviewed-by: Hante Meuleman <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 1 -
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 6 +++++-
drivers/net/wireless/brcm80211/brcmfmac/usb.c | 1 -
3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 5cbce1d..db31312 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -2297,7 +2297,6 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
spin_lock_irqsave(&bus->txqlock, flags);
if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
skb_pull(pkt, SDPCM_HDRLEN);
- brcmf_txcomplete(bus->sdiodev->dev, pkt, false);
brcmf_err("out of bus->txq !!!\n");
ret = -ENOSR;
} else {
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index 29b1f24..601b0d0 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -1745,6 +1745,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
int fifo = BRCMF_FWS_FIFO_BCMC;
bool multicast = is_multicast_ether_addr(eh->h_dest);
bool pae = eh->h_proto == htons(ETH_P_PAE);
+ int ret;

/* determine the priority */
if (!skb->priority)
@@ -1759,7 +1760,10 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb);

/* Use bus module to send data frame */
- return brcmf_bus_txdata(drvr->bus_if, skb);
+ ret = brcmf_bus_txdata(drvr->bus_if, skb);
+ if (ret < 0)
+ brcmf_txfinalize(drvr, skb, false);
+ return ret;
}

/* set control buffer information */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index 322cadc..39e01a7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -614,7 +614,6 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
return 0;

fail:
- brcmf_txcomplete(dev, skb, false);
return ret;
}

--
1.8.1.3



2013-08-11 12:49:01

by Jonas Gorski

[permalink] [raw]
Subject: Re: [PATCH 12/12] brcmsmac: support 4313iPA

Hi,

On Sat, Aug 10, 2013 at 12:27 PM, Arend van Spriel <[email protected]> wrote:
> From: Piotr Haber <[email protected]>
>
> Add support for 4313 iPA variant.
> It is a variant of already supported 4313 ePA,
> so this patch adds the required PHY changes to
> support it properly including an updated switch
> control table for BT-combo card variants.

Okay, I'll bite. Since this patch was already reverted once, it
warrants some additional scrutiny.

First of all, the patch is quite large, and I wonder if it couldn't be
split into smaller patches, especially as it looks like there are
additional fixes/changes merged in it.

Detailed comments below ...

> Tested-by: Maximilian Engelhardt <[email protected]>
> Tested-by: David Costa <[email protected]>
> Reviewed-by: Arend Van Spriel <[email protected]>
> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
> Signed-off-by: Piotr Haber <[email protected]>
> Signed-off-by: Arend van Spriel <[email protected]>
> ---

This is obviously a V2 (or V(n+1) where n was the reverted version),
so there should be something describing the changes to the reverted
version. Why should we trust it now to not break things again? (Yes, I
see those Tested-bys ;-)

> .../net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c | 399 +++++++++++++--------
> .../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c | 289 +++++++++------
> .../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h | 1 +
> 3 files changed, 436 insertions(+), 253 deletions(-)
>
> diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
> index 3d6b16c..b8ddaad 100644
> --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
> +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
> @@ -1137,8 +1137,9 @@ wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi,
> gain0_15 = ((biq1 & 0xf) << 12) |
> ((tia & 0xf) << 8) |
> ((lna2 & 0x3) << 6) |
> - ((lna2 &
> - 0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0);
> + ((lna2 & 0x3) << 4) |
> + ((lna1 & 0x3) << 2) |
> + ((lna1 & 0x3) << 0);

Unrelated style change.

>
> mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0);
> mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0);
> @@ -1328,6 +1329,43 @@ static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples)
> return (iq_est.i_pwr + iq_est.q_pwr) / nsamples;
> }
>
> +static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain,
> + u16 tia_gain, u16 lna2_gain)
> +{
> + u32 i_thresh_l, q_thresh_l;
> + u32 i_thresh_h, q_thresh_h;
> + struct lcnphy_iq_est iq_est_h, iq_est_l;
> +
> + wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 0, biq1_gain, tia_gain,
> + lna2_gain, 0);
> +
> + wlc_lcnphy_rx_gain_override_enable(pi, true);
> + wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0);
> + udelay(500);
> + write_radio_reg(pi, RADIO_2064_REG112, 0);
> + if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l))
> + return false;
> +
> + wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0);
> + udelay(500);
> + write_radio_reg(pi, RADIO_2064_REG112, 0);
> + if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h))
> + return false;
> +
> + i_thresh_l = (iq_est_l.i_pwr << 1);
> + i_thresh_h = (iq_est_l.i_pwr << 2) + iq_est_l.i_pwr;
> +
> + q_thresh_l = (iq_est_l.q_pwr << 1);
> + q_thresh_h = (iq_est_l.q_pwr << 2) + iq_est_l.q_pwr;

So X_thresh_l == iq_est_l.X_pwr * 2 and X_thresh_h == iq_est_l.X_pwr *
5? Why not trust the compiler to use optimize it? It would be a bit
more readable.

> + if ((iq_est_h.i_pwr > i_thresh_l) &&
> + (iq_est_h.i_pwr < i_thresh_h) &&
> + (iq_est_h.q_pwr > q_thresh_l) &&
> + (iq_est_h.q_pwr < q_thresh_h))
> + return true;
> +
> + return false;
> +}
> +
> static bool
> wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
> const struct lcnphy_rx_iqcomp *iqcomp,
> @@ -1342,8 +1380,8 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
> RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old,
> rfoverride3_old, rfoverride3val_old, rfoverride4_old,
> rfoverride4val_old, afectrlovr_old, afectrlovrval_old;
> - int tia_gain;
> - u32 received_power, rx_pwr_threshold;
> + int tia_gain, lna2_gain, biq1_gain;
> + bool set_gain;
> u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl;
> u16 values_to_save[11];
> s16 *ptr;
> @@ -1368,126 +1406,134 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
> goto cal_done;
> }
>
> - if (module == 1) {
> + WARN_ON(module != 1);
> + tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
> + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
>
> - tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
> - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);

This indentation change makes it a bit harder to review, but luckily
there is git show -b ...

> + for (i = 0; i < 11; i++)
> + values_to_save[i] =
> + read_radio_reg(pi, rxiq_cal_rf_reg[i]);
> + Core1TxControl_old = read_phy_reg(pi, 0x631);
> +
> + or_phy_reg(pi, 0x631, 0x0015);
> +
> + RFOverride0_old = read_phy_reg(pi, 0x44c);
> + RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
> + rfoverride2_old = read_phy_reg(pi, 0x4b0);
> + rfoverride2val_old = read_phy_reg(pi, 0x4b1);
> + rfoverride3_old = read_phy_reg(pi, 0x4f9);
> + rfoverride3val_old = read_phy_reg(pi, 0x4fa);
> + rfoverride4_old = read_phy_reg(pi, 0x938);
> + rfoverride4val_old = read_phy_reg(pi, 0x939);
> + afectrlovr_old = read_phy_reg(pi, 0x43b);
> + afectrlovrval_old = read_phy_reg(pi, 0x43c);
> + old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
> + old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
>
> - for (i = 0; i < 11; i++)
> - values_to_save[i] =
> - read_radio_reg(pi, rxiq_cal_rf_reg[i]);
> - Core1TxControl_old = read_phy_reg(pi, 0x631);
> -
> - or_phy_reg(pi, 0x631, 0x0015);
> -
> - RFOverride0_old = read_phy_reg(pi, 0x44c);
> - RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
> - rfoverride2_old = read_phy_reg(pi, 0x4b0);
> - rfoverride2val_old = read_phy_reg(pi, 0x4b1);
> - rfoverride3_old = read_phy_reg(pi, 0x4f9);
> - rfoverride3val_old = read_phy_reg(pi, 0x4fa);
> - rfoverride4_old = read_phy_reg(pi, 0x938);
> - rfoverride4val_old = read_phy_reg(pi, 0x939);
> - afectrlovr_old = read_phy_reg(pi, 0x43b);
> - afectrlovrval_old = read_phy_reg(pi, 0x43c);
> - old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
> - old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
> -
> - tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
> - if (tx_gain_override_old) {
> - wlc_lcnphy_get_tx_gain(pi, &old_gains);
> - tx_gain_index_old = pi_lcn->lcnphy_current_index;
> - }
> + tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
> + if (tx_gain_override_old) {
> + wlc_lcnphy_get_tx_gain(pi, &old_gains);
> + tx_gain_index_old = pi_lcn->lcnphy_current_index;
> + }
>
> - wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
> + wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
>
> - mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
> - mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
> + mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
> + mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
>
> - mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
> - mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
> + mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
> + mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
>
> - write_radio_reg(pi, RADIO_2064_REG116, 0x06);
> - write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
> - write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
> - write_radio_reg(pi, RADIO_2064_REG098, 0x03);
> - write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
> - mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
> - write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
> - write_radio_reg(pi, RADIO_2064_REG114, 0x01);
> - write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
> - write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
> -
> - mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
> - mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
> - mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
> - mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
> - mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
> - mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
> - mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
> - mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
> - mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
> - mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
> -
> - mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
> - mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
> -
> - wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0);
> - write_phy_reg(pi, 0x6da, 0xffff);
> - or_phy_reg(pi, 0x6db, 0x3);
> - wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
> - wlc_lcnphy_rx_gain_override_enable(pi, true);
> -
> - tia_gain = 8;
> - rx_pwr_threshold = 950;
> - while (tia_gain > 0) {
> - tia_gain -= 1;
> - wlc_lcnphy_set_rx_gain_by_distribution(pi,
> - 0, 0, 2, 2,
> - (u16)
> - tia_gain, 1, 0);
> - udelay(500);
> + write_radio_reg(pi, RADIO_2064_REG116, 0x06);
> + write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
> + write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
> + write_radio_reg(pi, RADIO_2064_REG098, 0x03);
> + write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
> + mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
> + write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
> + write_radio_reg(pi, RADIO_2064_REG114, 0x01);
> + write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
> + write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
> +
> + mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
> + mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
> + mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
> + mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
> + mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
> + mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
> + mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
> + mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
> + mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
> + mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
>
> - received_power =
> - wlc_lcnphy_measure_digital_power(pi, 2000);
> - if (received_power < rx_pwr_threshold)
> - break;
> + mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
> + mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
> +
> + write_phy_reg(pi, 0x6da, 0xffff);
> + or_phy_reg(pi, 0x6db, 0x3);
> +
> + wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
> + set_gain = false;
> +
> + lna2_gain = 3;
> + while ((lna2_gain >= 0) && !set_gain) {
> + tia_gain = 4;
> +
> + while ((tia_gain >= 0) && !set_gain) {
> + biq1_gain = 6;
> +
> + while ((biq1_gain >= 0) && !set_gain) {
> + set_gain = wlc_lcnphy_rx_iq_cal_gain(pi,
> + (u16)
> + biq1_gain,
> + (u16)
> + tia_gain,
> + (u16)
> + lna2_gain);
> + biq1_gain -= 1;
> + }
> + tia_gain -= 1;
> }
> - result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff);
> + lna2_gain -= 1;
> + }

This looks like it could be made more readable using for loops and a goto:

for (lna_gain = 3; lna_gain >= 0; lna_gain--) {
for (tia_gain = 4; tia_gain >= 0; tia_gain--) {
for (big1_gain = 6; big1_gain >= 0; big1_gain--) {
set_gain = wlc_lcnphy_rx_iq_cal_gain(...);
if (set_gain)
goto found;
}
}
}
found:
...
>
> - wlc_lcnphy_stop_tx_tone(pi);
> + if (set_gain)
> + result = wlc_lcnphy_calc_rx_iq_comp(pi, 1024);
> + else
> + result = false;
>
> - write_phy_reg(pi, 0x631, Core1TxControl_old);
> + wlc_lcnphy_stop_tx_tone(pi);
>
> - write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
> - write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
> - write_phy_reg(pi, 0x4b0, rfoverride2_old);
> - write_phy_reg(pi, 0x4b1, rfoverride2val_old);
> - write_phy_reg(pi, 0x4f9, rfoverride3_old);
> - write_phy_reg(pi, 0x4fa, rfoverride3val_old);
> - write_phy_reg(pi, 0x938, rfoverride4_old);
> - write_phy_reg(pi, 0x939, rfoverride4val_old);
> - write_phy_reg(pi, 0x43b, afectrlovr_old);
> - write_phy_reg(pi, 0x43c, afectrlovrval_old);
> - write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
> - write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
> + write_phy_reg(pi, 0x631, Core1TxControl_old);
> +
> + write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
> + write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
> + write_phy_reg(pi, 0x4b0, rfoverride2_old);
> + write_phy_reg(pi, 0x4b1, rfoverride2val_old);
> + write_phy_reg(pi, 0x4f9, rfoverride3_old);
> + write_phy_reg(pi, 0x4fa, rfoverride3val_old);
> + write_phy_reg(pi, 0x938, rfoverride4_old);
> + write_phy_reg(pi, 0x939, rfoverride4val_old);
> + write_phy_reg(pi, 0x43b, afectrlovr_old);
> + write_phy_reg(pi, 0x43c, afectrlovrval_old);
> + write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
> + write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
>
> - wlc_lcnphy_clear_trsw_override(pi);
> + wlc_lcnphy_clear_trsw_override(pi);
>
> - mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
> + mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
>
> - for (i = 0; i < 11; i++)
> - write_radio_reg(pi, rxiq_cal_rf_reg[i],
> - values_to_save[i]);
> + for (i = 0; i < 11; i++)
> + write_radio_reg(pi, rxiq_cal_rf_reg[i],
> + values_to_save[i]);
>
> - if (tx_gain_override_old)
> - wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
> - else
> - wlc_lcnphy_disable_tx_gain_override(pi);
> + if (tx_gain_override_old)
> + wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
> + else
> + wlc_lcnphy_disable_tx_gain_override(pi);
>
> - wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
> - wlc_lcnphy_rx_gain_override_enable(pi, false);
> - }
> + wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
> + wlc_lcnphy_rx_gain_override_enable(pi, false);
>
> cal_done:
> kfree(ptr);
> @@ -1789,6 +1835,17 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel)
> write_radio_reg(pi, RADIO_2064_REG038, 3);
> write_radio_reg(pi, RADIO_2064_REG091, 7);
> }
> +
> + if (!(pi->sh->boardflags & BFL_FEM)) {
> + u8 reg038[14] = {0xd, 0xe, 0xd, 0xd, 0xd, 0xc,
> + 0xa, 0xb, 0xb, 0x3, 0x3, 0x2, 0x0, 0x0};
> +
> + write_radio_reg(pi, RADIO_2064_REG02A, 0xf);
> + write_radio_reg(pi, RADIO_2064_REG091, 0x3);
> + write_radio_reg(pi, RADIO_2064_REG038, 0x3);
> +
> + write_radio_reg(pi, RADIO_2064_REG038, reg038[channel - 1]);
> + }
> }
>
> static int
> @@ -1983,6 +2040,16 @@ wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos)
> } else {
> mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1);
> mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
> + mod_radio_reg(pi, RADIO_2064_REG028, 0x1, 0x0);
> + mod_radio_reg(pi, RADIO_2064_REG11A, 0x4, 1<<2);
> + mod_radio_reg(pi, RADIO_2064_REG036, 0x10, 0x0);
> + mod_radio_reg(pi, RADIO_2064_REG11A, 0x10, 1<<4);
> + mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
> + mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x77);
> + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0xe<<1);
> + mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1<<7);
> + mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 1<<1);
> + mod_radio_reg(pi, RADIO_2064_REG029, 0xf0, 0<<4);

What does this do? This seems to be applied regardless of iPA or ePA,
so I looks like it fixes something? Or is this needed for iPA and
harmless for ePA?

> }
> } else {
> mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2);
> @@ -2069,12 +2136,14 @@ static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi)
> (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12));
>
> mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5));
> + mod_radio_reg(pi, RADIO_2064_REG07C, (1 << 0), (1 << 0));

Same here.

> }
>
> static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
> {
> struct phytbl_info tab;
> u32 rfseq, ind;
> + u8 tssi_sel;
>
> tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
> tab.tbl_width = 32;
> @@ -2096,7 +2165,13 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
>
> mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4);
>
> - wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT);
> + if (pi->sh->boardflags & BFL_FEM) {
> + tssi_sel = 0x1;
> + wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT);
> + } else {
> + tssi_sel = 0xe;
> + wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_POST_PA);
> + }

Doesn't this change change tssi_sel from 0xe to 0x1 for (already
supported) ePA cards, and sets it to the old value 0xe for iPA ones? I
would have expected 0xe for BFL_FEM /ePA and 0x1 for iPA ... .

> mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14);
>
> mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15);
> @@ -2132,9 +2207,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
> mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0);
>
> if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
> - mod_radio_reg(pi, RADIO_2064_REG028, 0xf, 0xe);
> + mod_radio_reg(pi, RADIO_2064_REG028, 0xf, tssi_sel);
> mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4);
> } else {
> + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, tssi_sel << 1);

Okay, this one is new also for the ePA case, so I wonder why this
wasn't needed before?

> mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1);
> mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3);
> }
> @@ -2181,6 +2257,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
>
> mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8);
>
> + mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x0);
> + mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
> + mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
> +

Same for these three writes.

> wlc_lcnphy_pwrctrl_rssiparams(pi);
> }
>
> @@ -2799,6 +2879,8 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
> read_radio_reg(pi, RADIO_2064_REG007) & 1;
> u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10;
> u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4;
> + u8 SAVE_bbmult = wlc_lcnphy_get_bbmult(pi);
> +

These changes also look at a first glance unrelated to iPA, as well as ...

> idleTssi = read_phy_reg(pi, 0x4ab);
> suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) &
> MCTL_EN_MAC));
> @@ -2816,6 +2898,12 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
> mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4);
> mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2);
> wlc_lcnphy_tssi_setup(pi);
> +
> + mod_phy_reg(pi, 0x4d7, (0x1 << 0), (1 << 0));
> + mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1 << 6));
> +
> + wlc_lcnphy_set_bbmult(pi, 0x0);
> +

These ones.

> wlc_phy_do_dummy_tx(pi, true, OFF);
> idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0))
> >> 0);
> @@ -2837,6 +2925,7 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
>
> mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12);
>
> + wlc_lcnphy_set_bbmult(pi, SAVE_bbmult);
> wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old);
> wlc_lcnphy_set_tx_gain(pi, &old_gains);
> wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl);
> @@ -3050,6 +3139,11 @@ static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi)
> wlc_lcnphy_write_table(pi, &tab);
> tab.tbl_offset++;
> }
> + mod_phy_reg(pi, 0x4d0, (0x1 << 0), (0) << 0);
> + mod_phy_reg(pi, 0x4d3, (0xff << 0), (0) << 0);
> + mod_phy_reg(pi, 0x4d3, (0xff << 8), (0) << 8);
> + mod_phy_reg(pi, 0x4d0, (0x1 << 4), (0) << 4);
> + mod_phy_reg(pi, 0x4d0, (0x1 << 2), (0) << 2);
>
> mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7);
>
> @@ -3851,7 +3945,6 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
> target_gains.pad_gain = 21;
> target_gains.dac_gain = 0;
> wlc_lcnphy_set_tx_gain(pi, &target_gains);
> - wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
>
> if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) {
>
> @@ -3862,6 +3955,7 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
> lcnphy_recal ? LCNPHY_CAL_RECAL :
> LCNPHY_CAL_FULL), false);
> } else {
> + wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
> wlc_lcnphy_tx_iqlo_soft_cal_full(pi);
> }
>
> @@ -4286,17 +4380,22 @@ wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi,
> if (CHSPEC_IS5G(pi->radio_chanspec))
> pa_gain = 0x70;
> else
> - pa_gain = 0x70;
> + pa_gain = 0x60;

You are reducing the value for !BFL_FEM, I assume this is a fix for something?

>
> if (pi->sh->boardflags & BFL_FEM)
> pa_gain = 0x10;
> +

Unnecessary whitespace change.

> tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
> tab.tbl_width = 32;
> tab.tbl_len = 1;
> tab.tbl_ptr = &val;
>
> for (j = 0; j < 128; j++) {
> - gm_gain = gain_table[j].gm;
> + if (pi->sh->boardflags & BFL_FEM)
> + gm_gain = gain_table[j].gm;
> + else
> + gm_gain = 15;
> +
> val = (((u32) pa_gain << 24) |
> (gain_table[j].pad << 16) |
> (gain_table[j].pga << 8) | gm_gain);
> @@ -4507,7 +4606,10 @@ static void wlc_radio_2064_init(struct brcms_phy *pi)
>
> write_phy_reg(pi, 0x4ea, 0x4688);
>
> - mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
> + if (pi->sh->boardflags & BFL_FEM)
> + mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
> + else
> + mod_phy_reg(pi, 0x4eb, (0x7 << 0), 3 << 0);
>
> mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6);
>
> @@ -4518,6 +4620,13 @@ static void wlc_radio_2064_init(struct brcms_phy *pi)
> wlc_lcnphy_rcal(pi);
>
> wlc_lcnphy_rc_cal(pi);
> +
> + if (!(pi->sh->boardflags & BFL_FEM)) {
> + write_radio_reg(pi, RADIO_2064_REG032, 0x6f);
> + write_radio_reg(pi, RADIO_2064_REG033, 0x19);
> + write_radio_reg(pi, RADIO_2064_REG039, 0xe);
> + }
> +
> }
>
> static void wlc_lcnphy_radio_init(struct brcms_phy *pi)
> @@ -4530,6 +4639,7 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
> uint idx;
> u8 phybw40;
> struct phytbl_info tab;
> + const struct phytbl_info *tb;
> u32 val;
>
> phybw40 = CHSPEC_IS40(pi->radio_chanspec);
> @@ -4547,22 +4657,20 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
> wlc_lcnphy_write_table(pi, &tab);
> }
>
> - tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
> - tab.tbl_width = 16;
> - tab.tbl_ptr = &val;
> - tab.tbl_len = 1;
> -
> - val = 114;
> - tab.tbl_offset = 0;
> - wlc_lcnphy_write_table(pi, &tab);
> + if (!(pi->sh->boardflags & BFL_FEM)) {
> + tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
> + tab.tbl_width = 16;
> + tab.tbl_ptr = &val;
> + tab.tbl_len = 1;
>
> - val = 130;
> - tab.tbl_offset = 1;
> - wlc_lcnphy_write_table(pi, &tab);
> + val = 150;
> + tab.tbl_offset = 0;
> + wlc_lcnphy_write_table(pi, &tab);
>
> - val = 6;
> - tab.tbl_offset = 8;
> - wlc_lcnphy_write_table(pi, &tab);
> + val = 220;
> + tab.tbl_offset = 1;
> + wlc_lcnphy_write_table(pi, &tab);
> + }

So this isn't needed anymore for ePA cards?

>
> if (CHSPEC_IS2G(pi->radio_chanspec)) {
> if (pi->sh->boardflags & BFL_FEM)
> @@ -4576,7 +4684,6 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
> }
>
> if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
> - const struct phytbl_info *tb;
> int l;
>
> if (CHSPEC_IS2G(pi->radio_chanspec)) {
> @@ -4597,21 +4704,22 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
> wlc_lcnphy_write_table(pi, &tb[idx]);
> }
>
> - if ((pi->sh->boardflags & BFL_FEM)
> - && !(pi->sh->boardflags & BFL_FEM_BT))
> - wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313_epa);
> - else if (pi->sh->boardflags & BFL_FEM_BT) {
> - if (pi->sh->boardrev < 0x1250)
> - wlc_lcnphy_write_table(
> - pi,
> - &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa);
> + if (pi->sh->boardflags & BFL_FEM) {
> + if (pi->sh->boardflags & BFL_FEM_BT) {
> + if (pi->sh->boardrev < 0x1250)
> + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
> + else
> + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250;
> + } else {
> + tb = &dot11lcn_sw_ctrl_tbl_info_4313_epa;
> + }
> + } else {
> + if (pi->sh->boardflags & BFL_FEM_BT)
> + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa;
> else
> - wlc_lcnphy_write_table(
> - pi,
> - &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250);
> - } else
> - wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313);
> -
> + tb = &dot11lcn_sw_ctrl_tbl_info_4313;
> + }
> + wlc_lcnphy_write_table(pi, tb);
> wlc_lcnphy_load_rfpower(pi);
>
> wlc_lcnphy_clear_papd_comptable(pi);
> @@ -4955,6 +5063,8 @@ void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec)
> wlc_lcnphy_load_tx_iir_filter(pi, true, 3);
>
> mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3);
> + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
> + wlc_lcnphy_tssi_setup(pi);
> }
>
> void wlc_phy_detach_lcnphy(struct brcms_phy *pi)
> @@ -4993,8 +5103,7 @@ bool wlc_phy_attach_lcnphy(struct brcms_phy *pi)
> if (!wlc_phy_txpwr_srom_read_lcnphy(pi))
> return false;
>
> - if ((pi->sh->boardflags & BFL_FEM) &&
> - (LCNREV_IS(pi->pubpi.phy_rev, 1))) {
> + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
> if (pi_lcn->lcnphy_tempsense_option == 3) {
> pi->hwpwrctrl = true;
> pi->hwpwrctrl_capable = true;
> diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
> index 622c01c..9fb0ca2 100644
> --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
> +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
> @@ -2058,6 +2058,73 @@ static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = {
> 0x0005,
> };
>
> +static const u16 dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[] = {
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> + 0x0005,
> + 0x0006,
> + 0x0009,
> + 0x000a,
> +};
> +
> static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = {
> 0x0004,
> 0x0004,
> @@ -2834,6 +2901,12 @@ const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = {
> sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16
> };
>
> +const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa = {
> + &dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo,
> + sizeof(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo) /
> + sizeof(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[0]), 15, 0, 16

Not necessarily in this patch but maybe in a follow up cleanup patch:
ARRAY_SIZE() please?



I guess that's it from me.

Regards
Jonas