net: wcnss: Add snapshot of wcnss driver

This is a snapshot of the wcnss driver and associated files as of
msm-3.18 commit:

e70ad0cd5efdd9dc91a77dcdac31d6132e1315c1 (Promotion of kernel.lnx.
3.18-151201.)

Signed-off-by: Yue Ma <yuem@codeaurora.org>
This commit is contained in:
Yue Ma 2016-01-26 17:11:10 -08:00 committed by David Keitel
parent 81f2b08a8f
commit ffe5134f23
10 changed files with 4795 additions and 0 deletions

View file

@ -0,0 +1,98 @@
* Qualcomm WCNSS Platform Driver
WCNSS driver is the platform driver. It is used for performing the cold
boot-up of the wireless device. It is responsible for adjusting
the necessary I/O rails and enabling appropriate gpios for wireless
connectivity subsystem.
Required properties:
- compatible: "wcnss_wlan"
- reg: physical address and length of the register set for the device.
- reg-names: "wcnss_mmio", "wcnss_fiq", "pronto_phy_base", "riva_phy_base",
"riva_ccu_base", "pronto_a2xb_base", "pronto_ccpu_base",
"pronto_saw2_base", "wlan_tx_phy_aborts","wlan_brdg_err_source",
"wlan_tx_status", "alarms_txctl", "alarms_tactl",
"pronto_mcu_base".
- interupts: Pronto to Apps interrupts for tx done and rx pending.
- qcom,pronto-vddmx-supply: regulator to supply pronto pll.
- qcom,pronto-vddcx-supply: voltage corner regulator to supply WLAN/BT/FM
digital module.
- qcom,pronto-vddpx-supply: regulator to supply WLAN DAC.
- qcom,iris-vddxo-supply : regulator to supply RF XO.
- qcom,iris-vddrfa-supply : regulator to supply RFA digital.
- qcom,iris-vddpa-supply : regulator to supply RF PA.
- qcom,iris-vdddig-supply : regulator to supply RF digital(BT/FM).
- gpios: gpio numbers to configure 5-wire interface of WLAN connectivity
- qcom,has-48mhz-xo: boolean flag to determine the usage of 24MHz XO from RF
- qcom,has-pronto-hw: boolean flag to determine the revId of the WLAN subsystem
- qcom,wcnss-adc_tm: ADC handle for vbatt notification APIs.
- qcom,wcnss-vadc: VADC handle for battery voltage notification APIs.
- pinctrl-<n> : Pinctrl states as described in bindings/pinctrl/pinctrl-bindings.txt
- pinctrl-names : Names corresponding to the numbered pinctrl states
- clocks: from common clock binding: handle to xo and rf_clk clocks.
- clock-names: Names of all the clocks that are accessed by the subsystem
- qcom,vdd-voltage-level: This property represents (nominal, min, max) voltage
for iris and pronto regulators in milli-volts.
- qcom,vdd-current: This property represents current value for
iris and pronto regulators in micro-amps.
Optional properties:
- qcom,has-autodetect-xo: boolean flag to determine whether Iris XO auto detect
should be performed during boot up.
- qcom,wlan-rx-buff-count: WLAN RX buffer count is a configurable value,
using a smaller count for this buffer will reduce the memory usage.
- qcom,is-pronto-v3: boolean flag to determine the pronto hardware version
in use. subsequently correct workqueue will be used by DXE engine to push frames
in TX data path.
- qcom,is-pronto-vadc: boolean flag to determine Battery voltage feature
support for pronto hardware.
- qcom,wcnss-pm : <Core rail LDO#, PA rail LDO#, XO settling time,
RPM power collapse enabled, standalone power collapse enabled>
Power manager related parameter for LDO configuration.
11 - WCN CORE rail LDO number
21 - WCN PA rail LDO number
1200 - WCN XO settling time (usec)
1 - WCN RPM power collapse enabled
1 - WCN standalone power collapse enabled
6 - GPIO strength value
- qcom,has-vsys-adc-channel: boolean flag to determine which ADC HW channel need
to use for VBATT feature.
Example:
qcom,wcnss-wlan@fb000000 {
compatible = "qcom,wcnss_wlan";
reg = <0xfb000000 0x280000>,
<0xf9011008 0x04>;
reg-names = "wcnss_mmio", "wcnss_fiq";
interrupts = <0 145 0 0 146 0>;
interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq";
qcom,pronto-vddmx-supply = <&pm8841_s1>;
qcom,pronto-vddcx-supply = <&pm8841_s2_corner>;
qcom,pronto-vddpx-supply = <&pm8941_s3>;
qcom,iris-vddxo-supply = <&pm8941_l6>;
qcom,iris-vddrfa-supply = <&pm8941_l11>;
qcom,iris-vddpa-supply = <&pm8941_l19>;
qcom,iris-vdddig-supply = <&pm8941_l3>;
gpios = <&msmgpio 36 0>, <&msmgpio 37 0>, <&msmgpio 38 0>,
<&msmgpio 39 0>, <&msmgpio 40 0>;
qcom,has-48mhz-xo;
qcom,is-pronto-vt;
qcom,wlan-rx-buff-count = <512>;
qcom,has-pronto-hw;
qcom,wcnss-adc_tm = <&pm8226_adc_tm>;
pinctrl-names = "wcnss_default", "wcnss_sleep";
pinctrl-0 = <&wcnss_default>;
pinctrl-1 = <&wcnss_sleep>;
pinctrl-2 = <&wcnss_gpio_default>;
clocks = <&clock_rpm clk_xo_wlan_clk>,
<&clock_rpm clk_rf_clk2>,
<&clock_debug clk_gcc_debug_mux>,
<&clock_gcc clk_wcnss_m_clk>;
clock-names = "xo", "rf_clk", "measure", "wcnss_debug";
qcom,wcnss-pm = <11 21 1200 1 1 6>;
};

View file

@ -265,6 +265,36 @@ config MWL8K
To compile this driver as a module, choose M here: the module
will be called mwl8k. If unsure, say N.
config WIFI_CONTROL_FUNC
bool "Enable WiFi control function abstraction"
help
Enables Power/Reset/Carddetect function abstraction
config WCNSS_CORE
tristate "Qualcomm WCNSS CORE driver"
select WIRELESS_EXT
select WEXT_PRIV
select WEXT_CORE
select WEXT_SPY
---help---
Core driver for the Qualcomm WCNSS triple play connectivity subsystem
config WCNSS_CORE_PRONTO
tristate "Qualcomm WCNSS Pronto Support"
depends on WCNSS_CORE
---help---
Pronto Support for the Qualcomm WCNSS triple play connectivity subsystem
config WCNSS_REGISTER_DUMP_ON_BITE
bool "Enable/disable WCNSS register dump when there is a WCNSS bite"
depends on WCNSS_CORE_PRONTO
---help---
When Apps recieves a WDOG bite from WCNSS, collecting a register dump
of WCNSS is helpful to root cause the failure. WCNSS may not be
properly clocked in some WCNSS bite cases, and that may cause unclocked
register access failures. So this feature is to enable/disable the
register dump on WCNSS WDOG bite.
source "drivers/net/wireless/ath/Kconfig"
source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/b43legacy/Kconfig"

View file

@ -60,3 +60,5 @@ obj-$(CONFIG_BRCMSMAC) += brcm80211/
obj-$(CONFIG_CW1200) += cw1200/
obj-$(CONFIG_RSI_91X) += rsi/
obj-$(CONFIG_WCNSS_CORE) += wcnss/

View file

@ -0,0 +1,6 @@
# Makefile for WCNSS triple-play driver
wcnsscore-objs += wcnss_wlan.o wcnss_vreg.o
obj-$(CONFIG_WCNSS_CORE) += wcnsscore.o

View file

@ -0,0 +1,179 @@
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/export.h>
#include <linux/qcomwlan_secif.h>
#include <crypto/aes.h>
/* APIs for calling crypto routines from kernel
*/
struct crypto_ahash *wcnss_wlan_crypto_alloc_ahash(const char *alg_name,
u32 type, u32 mask)
{
return crypto_alloc_ahash(alg_name, type, mask);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_ahash);
int wcnss_wlan_crypto_ahash_digest(struct ahash_request *req)
{
return crypto_ahash_digest(req);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_ahash_digest);
void wcnss_wlan_crypto_free_ahash(struct crypto_ahash *tfm)
{
crypto_free_ahash(tfm);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_free_ahash);
int wcnss_wlan_crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int keylen)
{
return crypto_ahash_setkey(tfm, key, keylen);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_ahash_setkey);
struct crypto_ablkcipher *
wcnss_wlan_crypto_alloc_ablkcipher(const char *alg_name, u32 type, u32 mask)
{
return crypto_alloc_ablkcipher(alg_name, type, mask);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_ablkcipher);
void wcnss_wlan_ablkcipher_request_free(struct ablkcipher_request *req)
{
ablkcipher_request_free(req);
}
EXPORT_SYMBOL(wcnss_wlan_ablkcipher_request_free);
void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm)
{
crypto_free_ablkcipher(tfm);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_free_ablkcipher);
void wcnss_wlan_crypto_free_cipher(struct crypto_cipher *tfm)
{
crypto_free_cipher(tfm);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_free_cipher);
struct crypto_cipher *
wcnss_wlan_crypto_alloc_cipher(const char *alg_name, u32 type, u32 mask)
{
return crypto_alloc_cipher(alg_name, type, mask);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_cipher);
static inline void xor_128(const u8 *a, const u8 *b, u8 *out)
{
u8 i;
for (i = 0; i < AES_BLOCK_SIZE; i++)
out[i] = a[i] ^ b[i];
}
static inline void leftshift_onebit(const u8 *input, u8 *output)
{
int i, overflow = 0;
for (i = (AES_BLOCK_SIZE - 1); i >= 0; i--) {
output[i] = input[i] << 1;
output[i] |= overflow;
overflow = (input[i] & 0x80) ? 1 : 0;
}
return;
}
static void generate_subkey(struct crypto_cipher *tfm, u8 *k1, u8 *k2)
{
u8 l[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
u8 const_rb[AES_BLOCK_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87};
u8 const_zero[AES_BLOCK_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
crypto_cipher_encrypt_one(tfm, l, const_zero);
if ((l[0] & 0x80) == 0) { /* If MSB(l) = 0, then k1 = l << 1 */
leftshift_onebit(l, k1);
} else { /* Else k1 = ( l << 1 ) (+) Rb */
leftshift_onebit(l, tmp);
xor_128(tmp, const_rb, k1);
}
if ((k1[0] & 0x80) == 0) {
leftshift_onebit(k1, k2);
} else {
leftshift_onebit(k1, tmp);
xor_128(tmp, const_rb, k2);
}
}
static inline void padding(u8 *lastb, u8 *pad, u16 length)
{
u8 j;
/* original last block */
for (j = 0; j < AES_BLOCK_SIZE; j++) {
if (j < length)
pad[j] = lastb[j];
else if (j == length)
pad[j] = 0x80;
else
pad[j] = 0x00;
}
}
void wcnss_wlan_cmac_calc_mic(struct crypto_cipher *tfm, u8 *m,
u16 length, u8 *mac)
{
u8 x[AES_BLOCK_SIZE], y[AES_BLOCK_SIZE];
u8 m_last[AES_BLOCK_SIZE], padded[AES_BLOCK_SIZE];
u8 k1[AES_KEYSIZE_128], k2[AES_KEYSIZE_128];
int cmpBlk;
int i, nBlocks = (length + 15)/AES_BLOCK_SIZE;
generate_subkey(tfm, k1, k2);
if (nBlocks == 0) {
nBlocks = 1;
cmpBlk = 0;
} else {
cmpBlk = ((length % AES_BLOCK_SIZE) == 0) ? 1 : 0;
}
if (cmpBlk) { /* Last block is complete block */
xor_128(&m[AES_BLOCK_SIZE * (nBlocks - 1)], k1, m_last);
} else { /* Last block is not complete block */
padding(&m[AES_BLOCK_SIZE * (nBlocks - 1)], padded,
length % AES_BLOCK_SIZE);
xor_128(padded, k2, m_last);
}
for (i = 0; i < AES_BLOCK_SIZE; i++)
x[i] = 0;
for (i = 0; i < (nBlocks - 1); i++) {
xor_128(x, &m[AES_BLOCK_SIZE * i], y); /* y = Mi (+) x */
crypto_cipher_encrypt_one(tfm, x, y); /* x = AES-128(KEY, y) */
}
xor_128(x, m_last, y);
crypto_cipher_encrypt_one(tfm, x, y);
memcpy(mac, x, CMAC_TLEN);
}
EXPORT_SYMBOL(wcnss_wlan_cmac_calc_mic);

View file

@ -0,0 +1,721 @@
/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/rpm-smd-regulator.h>
#include <linux/wcnss_wlan.h>
#include <linux/semaphore.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/clk.h>
static void __iomem *msm_wcnss_base;
static LIST_HEAD(power_on_lock_list);
static DEFINE_MUTEX(list_lock);
static DEFINE_SEMAPHORE(wcnss_power_on_lock);
static int auto_detect;
static int is_power_on;
#define RIVA_PMU_OFFSET 0x28
#define RIVA_SPARE_OFFSET 0x0b4
#define PRONTO_SPARE_OFFSET 0x1088
#define NVBIN_DLND_BIT BIT(25)
#define PRONTO_IRIS_REG_READ_OFFSET 0x1134
#define PRONTO_IRIS_REG_CHIP_ID 0x04
/* IRIS card chip ID's */
#define WCN3660 0x0200
#define WCN3660A 0x0300
#define WCN3660B 0x0400
#define WCN3620 0x5111
#define WCN3620A 0x5112
#define WCN3610 0x9101
#define WCN3610V1 0x9110
#define WCNSS_PMU_CFG_IRIS_XO_CFG BIT(3)
#define WCNSS_PMU_CFG_IRIS_XO_EN BIT(4)
#define WCNSS_PMU_CFG_IRIS_XO_CFG_STS BIT(6) /* 1: in progress, 0: done */
#define WCNSS_PMU_CFG_IRIS_RESET BIT(7)
#define WCNSS_PMU_CFG_IRIS_RESET_STS BIT(8) /* 1: in progress, 0: done */
#define WCNSS_PMU_CFG_IRIS_XO_READ BIT(9)
#define WCNSS_PMU_CFG_IRIS_XO_READ_STS BIT(10)
#define WCNSS_PMU_CFG_IRIS_XO_MODE 0x6
#define WCNSS_PMU_CFG_IRIS_XO_MODE_48 (3 << 1)
#define VREG_NULL_CONFIG 0x0000
#define VREG_GET_REGULATOR_MASK 0x0001
#define VREG_SET_VOLTAGE_MASK 0x0002
#define VREG_OPTIMUM_MODE_MASK 0x0004
#define VREG_ENABLE_MASK 0x0008
#define VDD_PA "qcom,iris-vddpa"
#define WCNSS_INVALID_IRIS_REG 0xbaadbaad
struct vregs_info {
const char * const name;
int state;
struct regulator *regulator;
};
/* IRIS regulators for Pronto hardware */
static struct vregs_info iris_vregs_pronto[] = {
{"qcom,iris-vddxo", VREG_NULL_CONFIG, NULL},
{"qcom,iris-vddrfa", VREG_NULL_CONFIG, NULL},
{"qcom,iris-vddpa", VREG_NULL_CONFIG, NULL},
{"qcom,iris-vdddig", VREG_NULL_CONFIG, NULL},
};
/* WCNSS regulators for Pronto hardware */
static struct vregs_info pronto_vregs[] = {
{"qcom,pronto-vddmx", VREG_NULL_CONFIG, NULL},
{"qcom,pronto-vddcx", VREG_NULL_CONFIG, NULL},
{"qcom,pronto-vddpx", VREG_NULL_CONFIG, NULL},
};
struct host_driver {
char name[20];
struct list_head list;
};
enum {
IRIS_3660, /* also 3660A and 3680 */
IRIS_3620,
IRIS_3610
};
int xo_auto_detect(u32 reg)
{
reg >>= 30;
switch (reg) {
case IRIS_3660:
return WCNSS_XO_48MHZ;
case IRIS_3620:
return WCNSS_XO_19MHZ;
case IRIS_3610:
return WCNSS_XO_19MHZ;
default:
return WCNSS_XO_INVALID;
}
}
int wcnss_get_iris_name(char *iris_name)
{
struct wcnss_wlan_config *cfg = NULL;
int iris_id;
cfg = wcnss_get_wlan_config();
if (cfg) {
iris_id = cfg->iris_id;
iris_id = iris_id >> 16;
} else {
return 1;
}
switch (iris_id) {
case WCN3660:
memcpy(iris_name, "WCN3660", sizeof("WCN3660"));
break;
case WCN3660A:
memcpy(iris_name, "WCN3660A", sizeof("WCN3660A"));
break;
case WCN3660B:
memcpy(iris_name, "WCN3660B", sizeof("WCN3660B"));
break;
case WCN3620:
memcpy(iris_name, "WCN3620", sizeof("WCN3620"));
break;
case WCN3620A:
memcpy(iris_name, "WCN3620A", sizeof("WCN3620A"));
break;
case WCN3610:
memcpy(iris_name, "WCN3610", sizeof("WCN3610"));
break;
case WCN3610V1:
memcpy(iris_name, "WCN3610V1", sizeof("WCN3610V1"));
break;
default:
return 1;
}
return 0;
}
EXPORT_SYMBOL(wcnss_get_iris_name);
int validate_iris_chip_id(u32 reg)
{
int iris_id;
iris_id = reg >> 16;
switch (iris_id) {
case WCN3660:
case WCN3660A:
case WCN3660B:
case WCN3620:
case WCN3620A:
case WCN3610:
case WCN3610V1:
return 0;
default:
return 1;
}
}
void wcnss_iris_reset(u32 reg, void __iomem *pmu_conf_reg)
{
/* Reset IRIS */
reg |= WCNSS_PMU_CFG_IRIS_RESET;
writel_relaxed(reg, pmu_conf_reg);
/* Wait for PMU_CFG.iris_reg_reset_sts */
while (readl_relaxed(pmu_conf_reg) &
WCNSS_PMU_CFG_IRIS_RESET_STS)
cpu_relax();
/* Reset iris reset bit */
reg &= ~WCNSS_PMU_CFG_IRIS_RESET;
writel_relaxed(reg, pmu_conf_reg);
}
static int
configure_iris_xo(struct device *dev,
struct wcnss_wlan_config *cfg,
int on, int *iris_xo_set)
{
u32 reg = 0, i = 0;
u32 iris_reg = WCNSS_INVALID_IRIS_REG;
int rc = 0;
int pmu_offset = 0;
int spare_offset = 0;
void __iomem *pmu_conf_reg;
void __iomem *spare_reg;
void __iomem *iris_read_reg;
struct clk *clk;
struct clk *clk_rf = NULL;
bool use_48mhz_xo;
use_48mhz_xo = cfg->use_48mhz_xo;
if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
pmu_offset = PRONTO_PMU_OFFSET;
spare_offset = PRONTO_SPARE_OFFSET;
clk = clk_get(dev, "xo");
if (IS_ERR(clk)) {
pr_err("Couldn't get xo clock\n");
return PTR_ERR(clk);
}
} else {
pmu_offset = RIVA_PMU_OFFSET;
spare_offset = RIVA_SPARE_OFFSET;
clk = clk_get(dev, "cxo");
if (IS_ERR(clk)) {
pr_err("Couldn't get cxo clock\n");
return PTR_ERR(clk);
}
}
if (on) {
msm_wcnss_base = cfg->msm_wcnss_base;
if (!msm_wcnss_base) {
pr_err("ioremap wcnss physical failed\n");
goto fail;
}
/* Enable IRIS XO */
rc = clk_prepare_enable(clk);
if (rc) {
pr_err("clk enable failed\n");
goto fail;
}
/* NV bit is set to indicate that platform driver is capable
* of doing NV download.
*/
pr_debug("wcnss: Indicate NV bin download\n");
spare_reg = msm_wcnss_base + spare_offset;
reg = readl_relaxed(spare_reg);
reg |= NVBIN_DLND_BIT;
writel_relaxed(reg, spare_reg);
pmu_conf_reg = msm_wcnss_base + pmu_offset;
writel_relaxed(0, pmu_conf_reg);
reg = readl_relaxed(pmu_conf_reg);
reg |= WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP |
WCNSS_PMU_CFG_IRIS_XO_EN;
writel_relaxed(reg, pmu_conf_reg);
if (wcnss_xo_auto_detect_enabled()) {
iris_read_reg = msm_wcnss_base +
PRONTO_IRIS_REG_READ_OFFSET;
iris_reg = readl_relaxed(iris_read_reg);
}
wcnss_iris_reset(reg, pmu_conf_reg);
if (iris_reg != WCNSS_INVALID_IRIS_REG) {
iris_reg &= 0xffff;
iris_reg |= PRONTO_IRIS_REG_CHIP_ID;
writel_relaxed(iris_reg, iris_read_reg);
do {
/* Iris read */
reg = readl_relaxed(pmu_conf_reg);
reg |= WCNSS_PMU_CFG_IRIS_XO_READ;
writel_relaxed(reg, pmu_conf_reg);
/* Wait for PMU_CFG.iris_reg_read_sts */
while (readl_relaxed(pmu_conf_reg) &
WCNSS_PMU_CFG_IRIS_XO_READ_STS)
cpu_relax();
iris_reg = readl_relaxed(iris_read_reg);
pr_info("wcnss: IRIS Reg: %08x\n", iris_reg);
if (validate_iris_chip_id(iris_reg) && i >= 4) {
pr_info("wcnss: IRIS Card absent/invalid\n");
auto_detect = WCNSS_XO_INVALID;
/* Reset iris read bit */
reg &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
/* Clear XO_MODE[b2:b1] bits.
* Clear implies 19.2 MHz TCXO
*/
reg &= ~(WCNSS_PMU_CFG_IRIS_XO_MODE);
goto xo_configure;
} else if (!validate_iris_chip_id(iris_reg)) {
pr_debug("wcnss: IRIS Card is present\n");
break;
}
reg &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
writel_relaxed(reg, pmu_conf_reg);
wcnss_iris_reset(reg, pmu_conf_reg);
} while (i++ < 5);
auto_detect = xo_auto_detect(iris_reg);
/* Reset iris read bit */
reg &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
} else if (wcnss_xo_auto_detect_enabled())
/* Default to 48 MHZ */
auto_detect = WCNSS_XO_48MHZ;
else
auto_detect = WCNSS_XO_INVALID;
cfg->iris_id = iris_reg;
/* Clear XO_MODE[b2:b1] bits. Clear implies 19.2 MHz TCXO */
reg &= ~(WCNSS_PMU_CFG_IRIS_XO_MODE);
if ((use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
|| auto_detect == WCNSS_XO_48MHZ) {
reg |= WCNSS_PMU_CFG_IRIS_XO_MODE_48;
if (iris_xo_set)
*iris_xo_set = WCNSS_XO_48MHZ;
}
xo_configure:
writel_relaxed(reg, pmu_conf_reg);
wcnss_iris_reset(reg, pmu_conf_reg);
/* Start IRIS XO configuration */
reg |= WCNSS_PMU_CFG_IRIS_XO_CFG;
writel_relaxed(reg, pmu_conf_reg);
/* Wait for XO configuration to finish */
while (readl_relaxed(pmu_conf_reg) &
WCNSS_PMU_CFG_IRIS_XO_CFG_STS)
cpu_relax();
/* Stop IRIS XO configuration */
reg &= ~(WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP |
WCNSS_PMU_CFG_IRIS_XO_CFG);
writel_relaxed(reg, pmu_conf_reg);
clk_disable_unprepare(clk);
if ((!use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
|| auto_detect == WCNSS_XO_19MHZ) {
clk_rf = clk_get(dev, "rf_clk");
if (IS_ERR(clk_rf)) {
pr_err("Couldn't get rf_clk\n");
goto fail;
}
rc = clk_prepare_enable(clk_rf);
if (rc) {
pr_err("clk_rf enable failed\n");
goto fail;
}
if (iris_xo_set)
*iris_xo_set = WCNSS_XO_19MHZ;
}
} else if ((!use_48mhz_xo && auto_detect == WCNSS_XO_INVALID)
|| auto_detect == WCNSS_XO_19MHZ) {
clk_rf = clk_get(dev, "rf_clk");
if (IS_ERR(clk_rf)) {
pr_err("Couldn't get rf_clk\n");
goto fail;
}
clk_disable_unprepare(clk_rf);
}
/* Add some delay for XO to settle */
msleep(20);
fail:
clk_put(clk);
if (clk_rf != NULL)
clk_put(clk_rf);
return rc;
}
/* Helper routine to turn off all WCNSS & IRIS vregs */
static void wcnss_vregs_off(struct vregs_info regulators[], uint size,
struct vregs_level *voltage_level)
{
int i, rc = 0;
struct wcnss_wlan_config *cfg;
cfg = wcnss_get_wlan_config();
if (!cfg) {
pr_err("Failed to get WLAN configuration\n");
return;
}
/* Regulators need to be turned off in the reverse order */
for (i = (size-1); i >= 0; i--) {
if (regulators[i].state == VREG_NULL_CONFIG)
continue;
/* Remove PWM mode */
if (regulators[i].state & VREG_OPTIMUM_MODE_MASK) {
rc = regulator_set_optimum_mode(
regulators[i].regulator, 0);
if (rc < 0)
pr_err("regulator_set_optimum_mode(%s) failed (%d)\n",
regulators[i].name, rc);
}
/* Set voltage to lowest level */
if (regulators[i].state & VREG_SET_VOLTAGE_MASK) {
if (cfg->is_pronto_vadc) {
if (cfg->vbatt < WCNSS_VBATT_THRESHOLD &&
!memcmp(regulators[i].name,
VDD_PA, sizeof(VDD_PA))) {
voltage_level[i].max_voltage =
WCNSS_VBATT_LOW;
}
}
rc = regulator_set_voltage(regulators[i].regulator,
voltage_level[i].low_power_min,
voltage_level[i].max_voltage);
if (rc)
pr_err("regulator_set_voltage(%s) failed (%d)\n",
regulators[i].name, rc);
}
/* Disable regulator */
if (regulators[i].state & VREG_ENABLE_MASK) {
rc = regulator_disable(regulators[i].regulator);
if (rc < 0)
pr_err("vreg %s disable failed (%d)\n",
regulators[i].name, rc);
}
/* Free the regulator source */
if (regulators[i].state & VREG_GET_REGULATOR_MASK)
regulator_put(regulators[i].regulator);
regulators[i].state = VREG_NULL_CONFIG;
}
}
/* Common helper routine to turn on all WCNSS & IRIS vregs */
static int wcnss_vregs_on(struct device *dev,
struct vregs_info regulators[], uint size,
struct vregs_level *voltage_level)
{
int i, rc = 0, reg_cnt;
struct wcnss_wlan_config *cfg;
cfg = wcnss_get_wlan_config();
if (!cfg) {
pr_err("Failed to get WLAN configuration\n");
return -EINVAL;
}
for (i = 0; i < size; i++) {
/* Get regulator source */
regulators[i].regulator =
regulator_get(dev, regulators[i].name);
if (IS_ERR(regulators[i].regulator)) {
rc = PTR_ERR(regulators[i].regulator);
pr_err("regulator get of %s failed (%d)\n",
regulators[i].name, rc);
goto fail;
}
regulators[i].state |= VREG_GET_REGULATOR_MASK;
reg_cnt = regulator_count_voltages(regulators[i].regulator);
/* Set voltage to nominal. Exclude swtiches e.g. LVS */
if ((voltage_level[i].nominal_min ||
voltage_level[i].max_voltage) && (reg_cnt > 0)) {
if (cfg->is_pronto_vadc) {
if (cfg->vbatt < WCNSS_VBATT_THRESHOLD &&
!memcmp(regulators[i].name,
VDD_PA, sizeof(VDD_PA))) {
voltage_level[i].nominal_min =
WCNSS_VBATT_INITIAL;
voltage_level[i].max_voltage =
WCNSS_VBATT_LOW;
}
}
rc = regulator_set_voltage(regulators[i].regulator,
voltage_level[i].nominal_min,
voltage_level[i].max_voltage);
if (rc) {
pr_err("regulator_set_voltage(%s) failed (%d)\n",
regulators[i].name, rc);
goto fail;
}
regulators[i].state |= VREG_SET_VOLTAGE_MASK;
}
/* Vote for PWM/PFM mode if needed */
if (voltage_level[i].uA_load && (reg_cnt > 0)) {
rc = regulator_set_optimum_mode(regulators[i].regulator,
voltage_level[i].uA_load);
if (rc < 0) {
pr_err("regulator_set_optimum_mode(%s) failed (%d)\n",
regulators[i].name, rc);
goto fail;
}
regulators[i].state |= VREG_OPTIMUM_MODE_MASK;
}
/* Enable the regulator */
rc = regulator_enable(regulators[i].regulator);
if (rc) {
pr_err("vreg %s enable failed (%d)\n",
regulators[i].name, rc);
goto fail;
}
regulators[i].state |= VREG_ENABLE_MASK;
}
return rc;
fail:
wcnss_vregs_off(regulators, size, voltage_level);
return rc;
}
static void wcnss_iris_vregs_off(enum wcnss_hw_type hw_type,
struct wcnss_wlan_config *cfg)
{
switch (hw_type) {
case WCNSS_PRONTO_HW:
wcnss_vregs_off(iris_vregs_pronto,
ARRAY_SIZE(iris_vregs_pronto),
cfg->iris_vlevel);
break;
default:
pr_err("%s invalid hardware %d\n", __func__, hw_type);
}
}
static int wcnss_iris_vregs_on(struct device *dev,
enum wcnss_hw_type hw_type,
struct wcnss_wlan_config *cfg)
{
int ret = -1;
switch (hw_type) {
case WCNSS_PRONTO_HW:
ret = wcnss_vregs_on(dev, iris_vregs_pronto,
ARRAY_SIZE(iris_vregs_pronto),
cfg->iris_vlevel);
break;
default:
pr_err("%s invalid hardware %d\n", __func__, hw_type);
}
return ret;
}
static void wcnss_core_vregs_off(enum wcnss_hw_type hw_type,
struct wcnss_wlan_config *cfg)
{
switch (hw_type) {
case WCNSS_PRONTO_HW:
wcnss_vregs_off(pronto_vregs,
ARRAY_SIZE(pronto_vregs), cfg->pronto_vlevel);
break;
default:
pr_err("%s invalid hardware %d\n", __func__, hw_type);
}
}
static int wcnss_core_vregs_on(struct device *dev,
enum wcnss_hw_type hw_type,
struct wcnss_wlan_config *cfg)
{
int ret = -1;
switch (hw_type) {
case WCNSS_PRONTO_HW:
ret = wcnss_vregs_on(dev, pronto_vregs,
ARRAY_SIZE(pronto_vregs),
cfg->pronto_vlevel);
break;
default:
pr_err("%s invalid hardware %d\n", __func__, hw_type);
}
return ret;
}
int wcnss_wlan_power(struct device *dev,
struct wcnss_wlan_config *cfg,
enum wcnss_opcode on, int *iris_xo_set)
{
int rc = 0;
enum wcnss_hw_type hw_type = wcnss_hardware_type();
down(&wcnss_power_on_lock);
if (on) {
/* RIVA regulator settings */
rc = wcnss_core_vregs_on(dev, hw_type,
cfg);
if (rc)
goto fail_wcnss_on;
/* IRIS regulator settings */
rc = wcnss_iris_vregs_on(dev, hw_type,
cfg);
if (rc)
goto fail_iris_on;
/* Configure IRIS XO */
rc = configure_iris_xo(dev, cfg,
WCNSS_WLAN_SWITCH_ON, iris_xo_set);
if (rc)
goto fail_iris_xo;
is_power_on = true;
} else if (is_power_on) {
is_power_on = false;
configure_iris_xo(dev, cfg,
WCNSS_WLAN_SWITCH_OFF, NULL);
wcnss_iris_vregs_off(hw_type, cfg);
wcnss_core_vregs_off(hw_type, cfg);
}
up(&wcnss_power_on_lock);
return rc;
fail_iris_xo:
wcnss_iris_vregs_off(hw_type, cfg);
fail_iris_on:
wcnss_core_vregs_off(hw_type, cfg);
fail_wcnss_on:
up(&wcnss_power_on_lock);
return rc;
}
EXPORT_SYMBOL(wcnss_wlan_power);
/*
* During SSR WCNSS should not be 'powered on' until all the host drivers
* finish their shutdown routines. Host drivers use below APIs to
* synchronize power-on. WCNSS will not be 'powered on' until all the
* requests(to lock power-on) are freed.
*/
int wcnss_req_power_on_lock(char *driver_name)
{
struct host_driver *node;
if (!driver_name)
goto err;
node = kmalloc(sizeof(struct host_driver), GFP_KERNEL);
if (!node)
goto err;
strlcpy(node->name, driver_name, sizeof(node->name));
mutex_lock(&list_lock);
/* Lock when the first request is added */
if (list_empty(&power_on_lock_list))
down(&wcnss_power_on_lock);
list_add(&node->list, &power_on_lock_list);
mutex_unlock(&list_lock);
return 0;
err:
return -EINVAL;
}
EXPORT_SYMBOL(wcnss_req_power_on_lock);
int wcnss_free_power_on_lock(char *driver_name)
{
int ret = -1;
struct host_driver *node;
mutex_lock(&list_lock);
list_for_each_entry(node, &power_on_lock_list, list) {
if (!strncmp(node->name, driver_name, sizeof(node->name))) {
list_del(&node->list);
kfree(node);
ret = 0;
break;
}
}
/* unlock when the last host driver frees the lock */
if (list_empty(&power_on_lock_list))
up(&wcnss_power_on_lock);
mutex_unlock(&list_lock);
return ret;
}
EXPORT_SYMBOL(wcnss_free_power_on_lock);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __QCOM_WCNSS_DEVICE__H
#define __QCOM_WCNSS_DEVICE__H
struct qcom_wcnss_opts {
bool has_48mhz_xo;
};
#endif /* __QCOM_WCNSS_DEVICE__H */

View file

@ -0,0 +1,41 @@
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __QCOM_WLAN_SECIF_H__
#define __QCOM_WLAN_SECIF_H__
#include <crypto/hash.h>
#define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */
/*
* Prototypes for WLAN Security Interface Functions
*/
extern struct crypto_ahash *
wcnss_wlan_crypto_alloc_ahash(const char *alg_name, u32 type, u32 mask);
extern int wcnss_wlan_crypto_ahash_digest(struct ahash_request *req);
extern void wcnss_wlan_crypto_free_ahash(struct crypto_ahash *tfm);
extern int wcnss_wlan_crypto_ahash_setkey(struct crypto_ahash *tfm,
const u8 *key, unsigned int keylen);
extern struct crypto_ablkcipher *
wcnss_wlan_crypto_alloc_ablkcipher(const char *alg_name, u32 type, u32 mask);
extern void wcnss_wlan_ablkcipher_request_free(struct ablkcipher_request *req);
extern void wcnss_wlan_crypto_free_cipher(struct crypto_cipher *tfm);
extern void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm);
extern struct crypto_cipher *
wcnss_wlan_crypto_alloc_cipher(const char *alg_name, u32 type, u32 mask);
extern void wcnss_wlan_cmac_calc_mic(struct crypto_cipher *tfm, u8 *m,
u16 length, u8 *mac);
#endif /* __QCOM_WLAN_SECIF_H__ */

158
include/linux/wcnss_wlan.h Normal file
View file

@ -0,0 +1,158 @@
/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _WCNSS_WLAN_H_
#define _WCNSS_WLAN_H_
#include <linux/device.h>
#define IRIS_REGULATORS 4
#define PRONTO_REGULATORS 3
enum wcnss_opcode {
WCNSS_WLAN_SWITCH_OFF = 0,
WCNSS_WLAN_SWITCH_ON,
};
enum wcnss_hw_type {
WCNSS_RIVA_HW = 0,
WCNSS_PRONTO_HW,
};
struct vregs_level {
int nominal_min;
int low_power_min;
int max_voltage;
int uA_load;
};
struct wcnss_wlan_config {
int use_48mhz_xo;
int is_pronto_vadc;
int is_pronto_v3;
void __iomem *msm_wcnss_base;
int iris_id;
int vbatt;
struct vregs_level pronto_vlevel[PRONTO_REGULATORS];
struct vregs_level iris_vlevel[IRIS_REGULATORS];
};
enum {
WCNSS_XO_48MHZ = 1,
WCNSS_XO_19MHZ,
WCNSS_XO_INVALID,
};
enum {
WCNSS_WLAN_DATA2,
WCNSS_WLAN_DATA1,
WCNSS_WLAN_DATA0,
WCNSS_WLAN_SET,
WCNSS_WLAN_CLK,
WCNSS_WLAN_MAX_GPIO,
};
#define WCNSS_VBATT_THRESHOLD 3500000
#define WCNSS_VBATT_GUARD 20000
#define WCNSS_VBATT_HIGH 3700000
#define WCNSS_VBATT_LOW 3300000
#define WCNSS_VBATT_INITIAL 3000000
#define WCNSS_WLAN_IRQ_INVALID -1
#define HAVE_WCNSS_SUSPEND_RESUME_NOTIFY 1
#define HAVE_WCNSS_RESET_INTR 1
#define HAVE_WCNSS_CAL_DOWNLOAD 1
#define HAVE_CBC_DONE 1
#define HAVE_WCNSS_RX_BUFF_COUNT 1
#define WLAN_MAC_ADDR_SIZE (6)
#define WLAN_RF_REG_ADDR_START_OFFSET 0x3
#define WLAN_RF_REG_DATA_START_OFFSET 0xf
#define WLAN_RF_READ_REG_CMD 0x3
#define WLAN_RF_WRITE_REG_CMD 0x2
#define WLAN_RF_READ_CMD_MASK 0x3fff
#define WLAN_RF_CLK_WAIT_CYCLE 2
#define WLAN_RF_PREPARE_CMD_DATA 5
#define WLAN_RF_READ_DATA 6
#define WLAN_RF_DATA_LEN 3
#define WLAN_RF_DATA0_SHIFT 0
#define WLAN_RF_DATA1_SHIFT 1
#define WLAN_RF_DATA2_SHIFT 2
#define PRONTO_PMU_OFFSET 0x1004
#define WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP BIT(5)
struct device *wcnss_wlan_get_device(void);
void wcnss_get_monotonic_boottime(struct timespec *ts);
struct resource *wcnss_wlan_get_memory_map(struct device *dev);
int wcnss_wlan_get_dxe_tx_irq(struct device *dev);
int wcnss_wlan_get_dxe_rx_irq(struct device *dev);
void wcnss_wlan_register_pm_ops(struct device *dev,
const struct dev_pm_ops *pm_ops);
void wcnss_wlan_unregister_pm_ops(struct device *dev,
const struct dev_pm_ops *pm_ops);
void wcnss_register_thermal_mitigation(struct device *dev,
void (*tm_notify)(struct device *dev, int));
void wcnss_unregister_thermal_mitigation(
void (*tm_notify)(struct device *dev, int));
struct platform_device *wcnss_get_platform_device(void);
struct wcnss_wlan_config *wcnss_get_wlan_config(void);
void wcnss_set_iris_xo_mode(int iris_xo_mode_set);
int wcnss_wlan_power(struct device *dev,
struct wcnss_wlan_config *cfg,
enum wcnss_opcode opcode,
int *iris_xo_mode_set);
int wcnss_req_power_on_lock(char *driver_name);
int wcnss_free_power_on_lock(char *driver_name);
unsigned int wcnss_get_serial_number(void);
int wcnss_get_wlan_mac_address(char mac_addr[WLAN_MAC_ADDR_SIZE]);
void wcnss_allow_suspend(void);
void wcnss_prevent_suspend(void);
int wcnss_hardware_type(void);
void *wcnss_prealloc_get(unsigned int size);
int wcnss_prealloc_put(void *ptr);
void wcnss_reset_fiq(bool clk_chk_en);
void wcnss_suspend_notify(void);
void wcnss_resume_notify(void);
void wcnss_riva_log_debug_regs(void);
void wcnss_pronto_log_debug_regs(void);
int wcnss_is_hw_pronto_ver3(void);
int wcnss_device_ready(void);
bool wcnss_cbc_complete(void);
int wcnss_device_is_shutdown(void);
void wcnss_riva_dump_pmic_regs(void);
int wcnss_xo_auto_detect_enabled(void);
u32 wcnss_get_wlan_rx_buff_count(void);
int wcnss_wlan_iris_xo_mode(void);
void wcnss_flush_work(struct work_struct *work);
void wcnss_flush_delayed_work(struct delayed_work *dwork);
void wcnss_init_work(struct work_struct *work , void *callbackptr);
void wcnss_init_delayed_work(struct delayed_work *dwork , void *callbackptr);
int wcnss_get_iris_name(char *iris_version);
#ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
void wcnss_log_debug_regs_on_bite(void);
#else
static inline void wcnss_log_debug_regs_on_bite(void)
{
}
#endif
int wcnss_set_wlan_unsafe_channel(
u16 *unsafe_ch_list, u16 ch_count);
int wcnss_get_wlan_unsafe_channel(
u16 *unsafe_ch_list, u16 buffer_size,
u16 *ch_count);
#define wcnss_wlan_get_drvdata(dev) dev_get_drvdata(dev)
#define wcnss_wlan_set_drvdata(dev, data) dev_set_drvdata((dev), (data))
/* WLAN driver uses these names */
#define req_riva_power_on_lock(name) wcnss_req_power_on_lock(name)
#define free_riva_power_on_lock(name) wcnss_free_power_on_lock(name)
#endif /* _WCNSS_WLAN_H_ */