Merge "cnss: Provide API to CLD Driver to control SPDT GPIO"

This commit is contained in:
Linux Build Service Account 2016-08-26 22:22:54 -07:00 committed by Gerrit - the friendly Code Review server
commit fad851b94a
6 changed files with 528 additions and 28 deletions

View file

@ -29,6 +29,9 @@ Optional properties:
has exclusive access to UART.
- vdd-wlan-io-supply: phandle to the 1.8V IO regulator for QCA6174
- vdd-wlan-xtal-supply: phandle to the 1.8V XTAL regulator for QCA6174
- vdd-wlan-xtal-aon-supply: phandle to the LDO-4 regulator. This is needed
on platforms where XTAL regulator depends on
always on regulator in VDDmin.
- vdd-wlan-core-supply: phandle to the 1.3V CORE regulator for QCA6174
- vdd-wlan-sp2t-supply: phandle to the 2.7V SP2T regulator for QCA6174
- qcom,wlan-smmu-iova-address: I/O virtual address range as <start length>
@ -39,6 +42,8 @@ Optional properties:
- reg-names: Names of the memory regions defined in reg entry
- wlan-bootstrap-gpio: WLAN_BOOTSTRAP GPIO signal specified by QCA6174
which should be drived depending on platforms
- qcom,is-dual-wifi-enabled: Boolean property to control wlan enable(wlan-en)
gpio on dual-wifi platforms.
Example:

View file

@ -382,3 +382,45 @@ int cnss_common_set_wlan_mac_address(
return ret;
}
EXPORT_SYMBOL(cnss_common_set_wlan_mac_address);
int cnss_power_up(struct device *dev)
{
int ret;
switch (cnss_get_dev_bus_type(dev)) {
case CNSS_BUS_PCI:
ret = cnss_pcie_power_up(dev);
break;
case CNSS_BUS_SDIO:
ret = cnss_sdio_power_up(dev);
break;
default:
pr_err("%s: Invalid Bus Type\n", __func__);
ret = -EINVAL;
break;
}
return ret;
}
EXPORT_SYMBOL(cnss_power_up);
int cnss_power_down(struct device *dev)
{
int ret;
switch (cnss_get_dev_bus_type(dev)) {
case CNSS_BUS_PCI:
ret = cnss_pcie_power_down(dev);
break;
case CNSS_BUS_SDIO:
ret = cnss_sdio_power_down(dev);
break;
default:
pr_err("%s: Invalid Bus Type\n", __func__);
ret = -EINVAL;
break;
}
return ret;
}
EXPORT_SYMBOL(cnss_power_down);

View file

@ -37,4 +37,8 @@ int cnss_sdio_set_wlan_mac_address(const u8 *in, uint32_t len);
u8 *cnss_pci_get_wlan_mac_address(uint32_t *num);
u8 *cnss_sdio_get_wlan_mac_address(uint32_t *num);
int cnss_sdio_power_up(struct device *dev);
int cnss_sdio_power_down(struct device *dev);
int cnss_pcie_power_up(struct device *dev);
int cnss_pcie_power_down(struct device *dev);
#endif /* _NET_CNSS_COMMON_H_ */

View file

@ -109,6 +109,7 @@ static struct cnss_fw_files FW_FILES_DEFAULT = {
#define WLAN_VREG_NAME "vdd-wlan"
#define WLAN_VREG_IO_NAME "vdd-wlan-io"
#define WLAN_VREG_XTAL_NAME "vdd-wlan-xtal"
#define WLAN_VREG_XTAL_AON_NAME "vdd-wlan-xtal-aon"
#define WLAN_VREG_CORE_NAME "vdd-wlan-core"
#define WLAN_VREG_SP2T_NAME "vdd-wlan-sp2t"
#define WLAN_SWREG_NAME "wlan-soc-swreg"
@ -181,6 +182,7 @@ struct cnss_wlan_vreg_info {
struct regulator *ant_switch;
struct regulator *wlan_reg_io;
struct regulator *wlan_reg_xtal;
struct regulator *wlan_reg_xtal_aon;
struct regulator *wlan_reg_core;
struct regulator *wlan_reg_sp2t;
bool state;
@ -221,6 +223,10 @@ struct index_file {
u8 file_name[13];
};
struct cnss_dual_wifi {
bool is_dual_wifi_enabled;
};
/**
* struct wlan_mac_addr - Structure to hold WLAN MAC Address
* @mac_addr: MAC address
@ -298,6 +304,7 @@ static struct cnss_data {
int wlan_bootstrap_gpio;
atomic_t auto_suspended;
bool monitor_wake_intr;
struct cnss_dual_wifi dual_wifi_info;
} *penv;
static unsigned int pcie_link_down_panic;
@ -334,6 +341,15 @@ static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info)
}
}
if (vreg_info->wlan_reg_xtal_aon) {
ret = regulator_enable(vreg_info->wlan_reg_xtal_aon);
if (ret) {
pr_err("%s: wlan_reg_xtal_aon enable failed\n",
__func__);
goto error_enable_reg_xtal_aon;
}
}
if (vreg_info->wlan_reg_xtal) {
ret = regulator_enable(vreg_info->wlan_reg_xtal);
if (ret) {
@ -382,6 +398,9 @@ error_enable_reg_sp2t:
if (vreg_info->wlan_reg_xtal)
regulator_disable(vreg_info->wlan_reg_xtal);
error_enable_reg_xtal:
if (vreg_info->wlan_reg_xtal_aon)
regulator_disable(vreg_info->wlan_reg_xtal_aon);
error_enable_reg_xtal_aon:
if (vreg_info->wlan_reg_io)
regulator_disable(vreg_info->wlan_reg_io);
error_enable_reg_io:
@ -433,6 +452,15 @@ static int cnss_wlan_vreg_off(struct cnss_wlan_vreg_info *vreg_info)
}
}
if (vreg_info->wlan_reg_xtal_aon) {
ret = regulator_disable(vreg_info->wlan_reg_xtal_aon);
if (ret) {
pr_err("%s: wlan_reg_xtal_aon disable failed\n",
__func__);
goto error_disable;
}
}
if (vreg_info->wlan_reg_io) {
ret = regulator_disable(vreg_info->wlan_reg_io);
if (ret) {
@ -553,6 +581,11 @@ static void cnss_wlan_gpio_set(struct cnss_wlan_gpio_info *info, bool state)
return;
}
if (state == WLAN_EN_LOW && penv->dual_wifi_info.is_dual_wifi_enabled) {
pr_debug("%s Dual WiFi enabled\n", __func__);
return;
}
gpio_set_value(info->num, state);
info->state = state;
@ -583,6 +616,89 @@ static int cnss_pinctrl_init(struct cnss_wlan_gpio_info *gpio_info,
return ret;
}
static void cnss_disable_xtal_ldo(struct platform_device *pdev)
{
struct cnss_wlan_vreg_info *info = &penv->vreg_info;
if (info->wlan_reg_xtal) {
regulator_disable(info->wlan_reg_xtal);
regulator_put(info->wlan_reg_xtal);
}
if (info->wlan_reg_xtal_aon) {
regulator_disable(info->wlan_reg_xtal_aon);
regulator_put(info->wlan_reg_xtal_aon);
}
}
static int cnss_enable_xtal_ldo(struct platform_device *pdev)
{
int ret = 0;
struct cnss_wlan_vreg_info *info = &penv->vreg_info;
if (!of_get_property(pdev->dev.of_node,
WLAN_VREG_XTAL_AON_NAME "-supply", NULL))
goto enable_xtal;
info->wlan_reg_xtal_aon = regulator_get(&pdev->dev,
WLAN_VREG_XTAL_AON_NAME);
if (IS_ERR(info->wlan_reg_xtal_aon)) {
ret = PTR_ERR(info->wlan_reg_xtal_aon);
pr_err("%s: XTAL AON Regulator get failed err:%d\n", __func__,
ret);
return ret;
}
ret = regulator_enable(info->wlan_reg_xtal_aon);
if (ret) {
pr_err("%s: VREG_XTAL_ON enable failed\n", __func__);
goto end;
}
enable_xtal:
if (!of_get_property(pdev->dev.of_node,
WLAN_VREG_XTAL_NAME "-supply", NULL))
goto out_disable_xtal_aon;
info->wlan_reg_xtal = regulator_get(&pdev->dev, WLAN_VREG_XTAL_NAME);
if (IS_ERR(info->wlan_reg_xtal)) {
ret = PTR_ERR(info->wlan_reg_xtal);
pr_err("%s XTAL Regulator get failed err:%d\n", __func__, ret);
goto out_disable_xtal_aon;
}
ret = regulator_set_voltage(info->wlan_reg_xtal, WLAN_VREG_XTAL_MIN,
WLAN_VREG_XTAL_MAX);
if (ret) {
pr_err("%s: Set wlan_vreg_xtal failed!\n", __func__);
goto out_put_xtal;
}
ret = regulator_enable(info->wlan_reg_xtal);
if (ret) {
pr_err("%s: Enable wlan_vreg_xtal failed!\n", __func__);
goto out_put_xtal;
}
return 0;
out_put_xtal:
if (info->wlan_reg_xtal)
regulator_put(info->wlan_reg_xtal);
out_disable_xtal_aon:
if (info->wlan_reg_xtal_aon)
regulator_disable(info->wlan_reg_xtal_aon);
end:
if (info->wlan_reg_xtal_aon)
regulator_put(info->wlan_reg_xtal_aon);
return ret;
}
static int cnss_wlan_get_resources(struct platform_device *pdev)
{
int ret = 0;
@ -660,27 +776,8 @@ static int cnss_wlan_get_resources(struct platform_device *pdev)
}
}
if (of_get_property(pdev->dev.of_node,
WLAN_VREG_XTAL_NAME"-supply", NULL)) {
vreg_info->wlan_reg_xtal =
regulator_get(&pdev->dev, WLAN_VREG_XTAL_NAME);
if (!IS_ERR(vreg_info->wlan_reg_xtal)) {
ret = regulator_set_voltage(vreg_info->wlan_reg_xtal,
WLAN_VREG_XTAL_MIN, WLAN_VREG_XTAL_MAX);
if (ret) {
pr_err("%s: Set wlan_vreg_xtal failed!\n",
__func__);
goto err_reg_xtal_set;
}
ret = regulator_enable(vreg_info->wlan_reg_xtal);
if (ret) {
pr_err("%s: Enable wlan_vreg_xtal failed!\n",
__func__);
goto err_reg_xtal_enable;
}
}
}
if (cnss_enable_xtal_ldo(pdev))
goto err_reg_xtal_enable;
if (of_get_property(pdev->dev.of_node,
WLAN_VREG_SP2T_NAME"-supply", NULL)) {
@ -850,13 +947,10 @@ err_reg_sp2t_enable:
err_reg_sp2t_set:
if (vreg_info->wlan_reg_sp2t)
regulator_put(vreg_info->wlan_reg_sp2t);
if (vreg_info->wlan_reg_xtal)
regulator_disable(vreg_info->wlan_reg_xtal);
cnss_disable_xtal_ldo(pdev);
err_reg_xtal_enable:
err_reg_xtal_set:
if (vreg_info->wlan_reg_xtal)
regulator_put(vreg_info->wlan_reg_xtal);
if (vreg_info->wlan_reg_io)
regulator_disable(vreg_info->wlan_reg_io);
@ -901,6 +995,8 @@ static void cnss_wlan_release_resources(void)
regulator_put(vreg_info->wlan_reg_sp2t);
if (vreg_info->wlan_reg_xtal)
regulator_put(vreg_info->wlan_reg_xtal);
if (vreg_info->wlan_reg_xtal_aon)
regulator_put(vreg_info->wlan_reg_xtal_aon);
if (vreg_info->wlan_reg_io)
regulator_put(vreg_info->wlan_reg_io);
regulator_put(vreg_info->wlan_reg);
@ -1864,6 +1960,13 @@ void cnss_schedule_recovery_work(void)
}
EXPORT_SYMBOL(cnss_schedule_recovery_work);
static inline void __cnss_disable_irq(void *data)
{
struct pci_dev *pdev = data;
disable_irq(pdev->irq);
}
void cnss_pci_events_cb(struct msm_pcie_notify *notify)
{
unsigned long flags;
@ -1887,6 +1990,7 @@ void cnss_pci_events_cb(struct msm_pcie_notify *notify)
spin_unlock_irqrestore(&pci_link_down_lock, flags);
pr_err("PCI link down, schedule recovery\n");
__cnss_disable_irq(notify->user);
schedule_work(&cnss_pci_recovery_work);
break;
@ -2813,6 +2917,9 @@ static int cnss_probe(struct platform_device *pdev)
penv->subsys_handle = subsystem_get(penv->subsysdesc.name);
if (of_property_read_bool(dev->of_node, "qcom,is-dual-wifi-enabled"))
penv->dual_wifi_info.is_dual_wifi_enabled = true;
if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic",
&ramdump_size) == 0) {
penv->ramdump_addr = dma_alloc_coherent(&pdev->dev,
@ -3364,6 +3471,326 @@ void cnss_runtime_exit(struct device *dev)
pm_runtime_set_active(dev);
}
EXPORT_SYMBOL(cnss_runtime_exit);
static void __cnss_set_pcie_monitor_intr(struct device *dev, bool val)
{
penv->monitor_wake_intr = val;
}
static void __cnss_set_auto_suspend(struct device *dev, int val)
{
atomic_set(&penv->auto_suspended, val);
}
static int __cnss_resume_link(struct device *dev, u32 flags)
{
int ret;
struct pci_dev *pdev = to_pci_dev(dev);
u8 bus_num = cnss_get_pci_dev_bus_number(pdev);
ret = cnss_msm_pcie_pm_control(MSM_PCIE_RESUME, bus_num, pdev, flags);
if (ret)
pr_err("%s: PCIe link resume failed with flags:%d bus_num:%d\n",
__func__, flags, bus_num);
penv->pcie_link_state = PCIE_LINK_UP;
return ret;
}
static int __cnss_suspend_link(struct device *dev, u32 flags)
{
struct pci_dev *pdev = to_pci_dev(dev);
u8 bus_num = cnss_get_pci_dev_bus_number(pdev);
int ret;
if (!penv->pcie_link_state)
return 0;
ret = cnss_msm_pcie_pm_control(MSM_PCIE_SUSPEND, bus_num, pdev, flags);
if (ret) {
pr_err("%s: Failed to suspend link\n", __func__);
return ret;
}
penv->pcie_link_state = PCIE_LINK_DOWN;
return ret;
}
static int __cnss_pcie_recover_config(struct device *dev)
{
int ret;
ret = cnss_msm_pcie_recover_config(to_pci_dev(dev));
if (ret)
pr_err("%s: PCIe Recover config failed\n", __func__);
return ret;
}
static int __cnss_event_reg(struct device *dev)
{
int ret;
struct msm_pcie_register_event *event_reg;
event_reg = &penv->event_reg;
event_reg->events = MSM_PCIE_EVENT_LINKDOWN |
MSM_PCIE_EVENT_WAKEUP;
event_reg->user = to_pci_dev(dev);
event_reg->mode = MSM_PCIE_TRIGGER_CALLBACK;
event_reg->callback = cnss_pci_events_cb;
event_reg->options = MSM_PCIE_CONFIG_NO_RECOVERY;
ret = cnss_msm_pcie_register_event(event_reg);
if (ret)
pr_err("%s: PCIe event register failed! %d\n", __func__, ret);
return ret;
}
static void __cnss_event_dereg(struct device *dev)
{
cnss_msm_pcie_deregister_event(&penv->event_reg);
}
static struct pci_dev *__cnss_get_pcie_dev(struct device *dev)
{
int ret;
struct pci_dev *pdev = penv->pdev;
if (pdev)
return pdev;
ret = pci_register_driver(&cnss_wlan_pci_driver);
if (ret) {
pr_err("%s: pci re-registration failed\n", __func__);
return NULL;
}
pdev = penv->pdev;
return pdev;
}
static int __cnss_pcie_power_up(struct device *dev)
{
struct cnss_wlan_vreg_info *vreg_info;
struct cnss_wlan_gpio_info *gpio_info;
int ret;
vreg_info = &penv->vreg_info;
gpio_info = &penv->gpio_info;
ret = cnss_wlan_vreg_set(vreg_info, VREG_ON);
if (ret) {
pr_err("%s: WLAN VREG ON Failed\n", __func__);
return ret;
}
msleep(POWER_ON_DELAY);
if (penv->wlan_bootstrap_gpio > 0) {
gpio_set_value(penv->wlan_bootstrap_gpio, WLAN_BOOTSTRAP_HIGH);
msleep(WLAN_BOOTSTRAP_DELAY);
}
cnss_wlan_gpio_set(gpio_info, WLAN_EN_HIGH);
msleep(WLAN_ENABLE_DELAY);
return 0;
}
static int __cnss_pcie_power_down(struct device *dev)
{
struct cnss_wlan_vreg_info *vreg_info;
struct cnss_wlan_gpio_info *gpio_info;
int ret;
vreg_info = &penv->vreg_info;
gpio_info = &penv->gpio_info;
cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
if (penv->wlan_bootstrap_gpio > 0)
gpio_set_value(penv->wlan_bootstrap_gpio, WLAN_BOOTSTRAP_LOW);
ret = cnss_wlan_vreg_set(vreg_info, VREG_OFF);
if (ret)
pr_err("%s: Failed to turn off 3.3V regulator\n", __func__);
return ret;
}
static int __cnss_suspend_link_state(struct device *dev)
{
int ret;
struct pci_dev *pdev = to_pci_dev(dev);
int link_ind;
if (!penv->pcie_link_state) {
pr_debug("%s: Link is already suspended\n", __func__);
return 0;
}
link_ind = penv->pcie_link_down_ind;
if (!link_ind)
pci_save_state(pdev);
penv->saved_state = link_ind ? NULL : cnss_pci_store_saved_state(pdev);
ret = link_ind ? __cnss_suspend_link(dev, PM_OPTIONS_SUSPEND_LINK_DOWN)
: __cnss_suspend_link(dev, PM_OPTIONS);
if (ret) {
pr_err("%s: Link Suspend failed in state:%s\n", __func__,
link_ind ? "LINK_DOWN" : "LINK_ACTIVE");
return ret;
}
penv->pcie_link_state = PCIE_LINK_DOWN;
return 0;
}
static int __cnss_restore_pci_config_space(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret = 0;
if (penv->saved_state)
ret = cnss_pci_load_and_free_saved_state(pdev,
&penv->saved_state);
pci_restore_state(pdev);
return ret;
}
static int __cnss_resume_link_state(struct device *dev)
{
int ret;
int link_ind;
if (penv->pcie_link_state) {
pr_debug("%s: Link is already in active state\n", __func__);
return 0;
}
link_ind = penv->pcie_link_down_ind;
ret = link_ind ? __cnss_resume_link(dev, PM_OPTIONS_RESUME_LINK_DOWN) :
__cnss_resume_link(dev, PM_OPTIONS);
if (ret) {
pr_err("%s: Resume Link failed in link state:%s\n", __func__,
link_ind ? "LINK_DOWN" : "LINK_ACTIVE");
return ret;
}
penv->pcie_link_state = PCIE_LINK_UP;
ret = link_ind ? __cnss_pcie_recover_config(dev) :
__cnss_restore_pci_config_space(dev);
if (ret) {
pr_err("%s: Link Recovery Config Failed link_state:%s\n",
__func__, link_ind ? "LINK_DOWN" : "LINK_ACTIVE");
penv->pcie_link_state = PCIE_LINK_DOWN;
return ret;
}
penv->pcie_link_down_ind = false;
return ret;
}
int cnss_pcie_power_up(struct device *dev)
{
int ret;
struct pci_dev *pdev;
if (!penv) {
pr_err("%s: platform data is NULL\n", __func__);
return -ENODEV;
}
ret = __cnss_pcie_power_up(dev);
if (ret) {
pr_err("%s: Power UP Failed\n", __func__);
return ret;
}
pdev = __cnss_get_pcie_dev(dev);
if (!pdev) {
pr_err("%s: PCIe Dev is NULL\n", __func__);
goto power_down;
}
ret = __cnss_event_reg(dev);
if (ret)
pr_err("%s: PCIe event registration failed\n", __func__);
ret = __cnss_resume_link_state(dev);
if (ret) {
pr_err("%s: Link Bring Up Failed\n", __func__);
goto event_dereg;
}
__cnss_set_pcie_monitor_intr(dev, true);
return ret;
event_dereg:
__cnss_event_dereg(dev);
power_down:
__cnss_pcie_power_down(dev);
pr_err("%s: Device Power Up Failed Fatal Error!\n", __func__);
return ret;
}
static void __cnss_vote_bus_width(struct device *dev, uint32_t option)
{
if (penv->bus_client)
msm_bus_scale_client_update_request(penv->bus_client, option);
}
int cnss_pcie_power_down(struct device *dev)
{
int ret;
struct pci_dev *pdev = to_pci_dev(dev);
if (!penv) {
pr_err("%s: Invalid Platform data\n", __func__);
return -ENODEV;
}
if (!pdev) {
pr_err("%s: Invalid Pdev, Cut Power to device\n", __func__);
__cnss_pcie_power_down(dev);
return -ENODEV;
}
__cnss_vote_bus_width(dev, CNSS_BUS_WIDTH_NONE);
__cnss_event_dereg(dev);
ret = __cnss_suspend_link_state(dev);
if (ret) {
pr_err("%s: Suspend Link failed\n", __func__);
return ret;
}
__cnss_set_pcie_monitor_intr(dev, false);
__cnss_set_auto_suspend(dev, 0);
ret = __cnss_pcie_power_down(dev);
if (ret)
pr_err("%s: Power Down Failed\n", __func__);
return ret;
}
module_init(cnss_initialize);
module_exit(cnss_exit);

View file

@ -49,8 +49,6 @@
#define CNSS_DUMP_NAME "CNSS_WLAN"
#define CNSS_PINCTRL_SLEEP_STATE "sleep"
#define CNSS_PINCTRL_ACTIVE_STATE "active"
#define PINCTRL_SLEEP 0
#define PINCTRL_ACTIVE 1
struct cnss_sdio_regulator {
struct regulator *wlan_io;
@ -673,6 +671,15 @@ static int cnss_set_pinctrl_state(struct cnss_sdio_data *pdata, bool state)
pinctrl_select_state(info->pinctrl, info->sleep);
}
int cnss_sdio_configure_spdt(bool state)
{
if (!cnss_pdata)
return -ENODEV;
return cnss_set_pinctrl_state(cnss_pdata, state);
}
EXPORT_SYMBOL(cnss_sdio_configure_spdt);
/**
* cnss_sdio_wlan_register_driver() - cnss wlan register API
* @driver: sdio wlan driver interface from wlan driver.
@ -1168,6 +1175,16 @@ u8 *cnss_sdio_get_wlan_mac_address(uint32_t *num)
return NULL;
}
int cnss_sdio_power_down(struct device *dev)
{
return 0;
}
int cnss_sdio_power_up(struct device *dev)
{
return 0;
}
static const struct of_device_id cnss_sdio_dt_match[] = {
{.compatible = "qcom,cnss_sdio"},
{}

View file

@ -19,6 +19,8 @@
#ifdef CONFIG_CNSS
#define CNSS_MAX_FILE_NAME 20
#define PINCTRL_SLEEP 0
#define PINCTRL_ACTIVE 1
enum cnss_bus_width_type {
CNSS_BUS_WIDTH_NONE,
@ -242,4 +244,7 @@ extern void cnss_common_schedule_recovery_work(struct device *dev);
extern int cnss_common_set_wlan_mac_address(struct device *dev, const u8 *in,
uint32_t len);
extern u8 *cnss_common_get_wlan_mac_address(struct device *dev, uint32_t *num);
extern int cnss_power_up(struct device *dev);
extern int cnss_power_down(struct device *dev);
extern int cnss_sdio_configure_spdt(bool state);
#endif /* _NET_CNSS_H_ */