/* Copyright (c) 2016-2019, 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 #include #include #include #include "main.h" #include "debug.h" static struct cnss_vreg_cfg cnss_vreg_list[] = { {"vdd-wlan-core", 1300000, 1300000, 0, 0}, {"vdd-wlan-io", 1800000, 1800000, 0, 0}, {"vdd-wlan-xtal-aon", 0, 0, 0, 0}, {"vdd-wlan-xtal", 1800000, 1800000, 0, 2}, {"vdd-wlan", 0, 0, 0, 0}, {"vdd-wlan-aon", 1055000, 1055000, 0, 0}, {"vdd-wlan-rfa1", 1350000, 1350000, 0, 0}, {"vdd-wlan-rfa2", 2040000, 2040000, 0, 0}, {"vdd-wlan-rfa3", 1900000, 1900000, 0, 0}, {"vdd-wlan-ctrl1", 0, 0, 0, 0}, {"vdd-wlan-ctrl2", 0, 0, 0, 0}, {"vdd-wlan-sp2t", 2700000, 2700000, 0, 0}, {"wlan-ant-switch", 2700000, 2700000, 20000, 0}, {"wlan-soc-swreg", 1200000, 1200000, 0, 0}, {"vdd-wlan-en", 0, 0, 0, 10}, }; #define CNSS_VREG_INFO_SIZE ARRAY_SIZE(cnss_vreg_list) #define MAX_PROP_SIZE 32 #define BOOTSTRAP_GPIO "qcom,enable-bootstrap-gpio" #define BOOTSTRAP_ACTIVE "bootstrap_active" #define WLAN_EN_GPIO "wlan-en-gpio" #define WLAN_EN_ACTIVE "wlan_en_active" #define WLAN_EN_SLEEP "wlan_en_sleep" #define WLAN_VREGS_PROP "wlan_vregs" #define BOOTSTRAP_DELAY 1000 #define WLAN_ENABLE_DELAY 1000 /* For converged dt node, get the required vregs from property 'wlan_vregs', * which is string array; if the property is present but no value is set, * means no additional wlan verg is required. * For non-converged dt, go through all vregs in static array 'cnss_vreg_list'. */ int cnss_get_vreg(struct cnss_plat_data *plat_priv) { int ret = 0; int i; struct cnss_vreg_info *vreg; struct device *dev; struct regulator *reg; const __be32 *prop; char prop_name[MAX_PROP_SIZE] = {0}; int len, id_n; struct device_node *dt_node; if (!list_empty(&plat_priv->vreg_list) && !plat_priv->is_converged_dt) { cnss_pr_dbg("Vregs have already been updated\n"); return 0; } dev = &plat_priv->plat_dev->dev; dt_node = (plat_priv->dev_node ? plat_priv->dev_node : dev->of_node); if (plat_priv->is_converged_dt) { id_n = of_property_count_strings(dt_node, WLAN_VREGS_PROP); if (id_n <= 0) { if (id_n == -ENODATA) { cnss_pr_dbg("No additional vregs for: %s:%lx\n", dt_node->name, plat_priv->device_id); return 0; } cnss_pr_err("property %s is invalid or missed: %s:%lx\n", WLAN_VREGS_PROP, dt_node->name, plat_priv->device_id); return -EINVAL; } } else { id_n = CNSS_VREG_INFO_SIZE; } for (i = 0; i < id_n; i++) { vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); if (!vreg) { ret = -ENOMEM; goto out; } if (plat_priv->is_converged_dt) { ret = of_property_read_string_index(dt_node, WLAN_VREGS_PROP, i, &vreg->cfg.name); if (ret) { devm_kfree(dev, vreg); cnss_pr_err("Failed to read vreg ids\n"); goto out; } } else { memcpy(&vreg->cfg, &cnss_vreg_list[i], sizeof(vreg->cfg)); } reg = devm_regulator_get_optional(dev, vreg->cfg.name); if (IS_ERR(reg)) { ret = PTR_ERR(reg); if (ret == -ENODEV) { devm_kfree(dev, vreg); continue; } else if (ret == -EPROBE_DEFER) cnss_pr_info("EPROBE_DEFER for regulator: %s\n", vreg->cfg.name); else cnss_pr_err("Failed to get regulator %s, err = %d\n", vreg->cfg.name, ret); devm_kfree(dev, vreg); goto out; } vreg->reg = reg; snprintf(prop_name, MAX_PROP_SIZE, "qcom,%s-info", vreg->cfg.name); prop = of_get_property(dt_node, prop_name, &len); if (!prop || len != (4 * sizeof(__be32))) { cnss_pr_dbg("Property %s %s, use default\n", prop_name, prop ? "invalid format" : "doesn't exist"); } else { vreg->cfg.min_uv = be32_to_cpup(&prop[0]); vreg->cfg.max_uv = be32_to_cpup(&prop[1]); vreg->cfg.load_ua = be32_to_cpup(&prop[2]); vreg->cfg.delay_us = be32_to_cpup(&prop[3]); } list_add_tail(&vreg->list, &plat_priv->vreg_list); cnss_pr_dbg("Got regulator: %s, min_uv: %u, max_uv: %u, load_ua: %u, delay_us: %u\n", vreg->cfg.name, vreg->cfg.min_uv, vreg->cfg.max_uv, vreg->cfg.load_ua, vreg->cfg.delay_us); } return 0; out: return ret; } void cnss_put_vreg(struct cnss_plat_data *plat_priv) { struct device *dev; struct cnss_vreg_info *vreg; dev = &plat_priv->plat_dev->dev; while (!list_empty(&plat_priv->vreg_list)) { vreg = list_first_entry(&plat_priv->vreg_list, struct cnss_vreg_info, list); list_del(&vreg->list); if (IS_ERR_OR_NULL(vreg->reg)) continue; cnss_pr_dbg("Put regulator: %s\n", vreg->cfg.name); devm_regulator_put(vreg->reg); devm_kfree(dev, vreg); } } static int cnss_vreg_on(struct cnss_plat_data *plat_priv) { int ret = 0; struct cnss_vreg_info *vreg; if (!plat_priv) { cnss_pr_err("plat_priv is NULL!\n"); return -ENODEV; } list_for_each_entry(vreg, &plat_priv->vreg_list, list) { if (IS_ERR_OR_NULL(vreg->reg)) continue; if (vreg->enabled) { cnss_pr_dbg("Regulator %s is already enabled\n", vreg->cfg.name); continue; } cnss_pr_dbg("Regulator %s is being enabled\n", vreg->cfg.name); if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) { ret = regulator_set_voltage(vreg->reg, vreg->cfg.min_uv, vreg->cfg.max_uv); if (ret) { cnss_pr_err("Failed to set voltage for regulator %s, min_uv: %u, max_uv: %u, err = %d\n", vreg->cfg.name, vreg->cfg.min_uv, vreg->cfg.max_uv, ret); break; } } if (vreg->cfg.load_ua) { ret = regulator_set_load(vreg->reg, vreg->cfg.load_ua); if (ret < 0) { cnss_pr_err("Failed to set load for regulator %s, load: %u, err = %d\n", vreg->cfg.name, vreg->cfg.load_ua, ret); break; } } if (vreg->cfg.delay_us) udelay(vreg->cfg.delay_us); ret = regulator_enable(vreg->reg); if (ret) { cnss_pr_err("Failed to enable regulator %s, err = %d\n", vreg->cfg.name, ret); break; } vreg->enabled = true; } if (!ret) return 0; list_for_each_entry_continue_reverse(vreg, &plat_priv->vreg_list, list) { if (IS_ERR_OR_NULL(vreg->reg) || !vreg->enabled) continue; regulator_disable(vreg->reg); if (vreg->cfg.load_ua) regulator_set_load(vreg->reg, 0); if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) regulator_set_voltage(vreg->reg, 0, vreg->cfg.max_uv); vreg->enabled = false; } return ret; } static int cnss_vreg_off(struct cnss_plat_data *plat_priv) { int ret = 0; struct cnss_vreg_info *vreg; if (!plat_priv) { cnss_pr_err("plat_priv is NULL!\n"); return -ENODEV; } list_for_each_entry_reverse(vreg, &plat_priv->vreg_list, list) { if (IS_ERR_OR_NULL(vreg->reg)) continue; if (!vreg->enabled) { cnss_pr_dbg("Regulator %s is already disabled\n", vreg->cfg.name); continue; } cnss_pr_dbg("Regulator %s is being disabled\n", vreg->cfg.name); ret = regulator_disable(vreg->reg); if (ret) cnss_pr_err("Failed to disable regulator %s, err = %d\n", vreg->cfg.name, ret); if (vreg->cfg.load_ua) { ret = regulator_set_load(vreg->reg, 0); if (ret < 0) cnss_pr_err("Failed to set load for regulator %s, err = %d\n", vreg->cfg.name, ret); } if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) { ret = regulator_set_voltage(vreg->reg, 0, vreg->cfg.max_uv); if (ret) cnss_pr_err("Failed to set voltage for regulator %s, err = %d\n", vreg->cfg.name, ret); } vreg->enabled = false; } return ret; } int cnss_get_pinctrl(struct cnss_plat_data *plat_priv) { int ret = 0; struct device *dev; struct cnss_pinctrl_info *pinctrl_info; dev = &plat_priv->plat_dev->dev; pinctrl_info = &plat_priv->pinctrl_info; pinctrl_info->pinctrl = devm_pinctrl_get(dev); if (IS_ERR_OR_NULL(pinctrl_info->pinctrl)) { ret = PTR_ERR(pinctrl_info->pinctrl); cnss_pr_err("Failed to get pinctrl, err = %d\n", ret); goto out; } if (of_find_property(dev->of_node, BOOTSTRAP_GPIO, NULL)) { pinctrl_info->bootstrap_active = pinctrl_lookup_state(pinctrl_info->pinctrl, BOOTSTRAP_ACTIVE); if (IS_ERR_OR_NULL(pinctrl_info->bootstrap_active)) { ret = PTR_ERR(pinctrl_info->bootstrap_active); cnss_pr_err("Failed to get bootstrap active state, err = %d\n", ret); goto out; } } if (of_find_property(dev->of_node, WLAN_EN_GPIO, NULL)) { pinctrl_info->wlan_en_active = pinctrl_lookup_state(pinctrl_info->pinctrl, WLAN_EN_ACTIVE); if (IS_ERR_OR_NULL(pinctrl_info->wlan_en_active)) { ret = PTR_ERR(pinctrl_info->wlan_en_active); cnss_pr_err("Failed to get wlan_en active state, err = %d\n", ret); goto out; } pinctrl_info->wlan_en_sleep = pinctrl_lookup_state(pinctrl_info->pinctrl, WLAN_EN_SLEEP); if (IS_ERR_OR_NULL(pinctrl_info->wlan_en_sleep)) { ret = PTR_ERR(pinctrl_info->wlan_en_sleep); cnss_pr_err("Failed to get wlan_en sleep state, err = %d\n", ret); goto out; } } return 0; out: return ret; } void cnss_put_pinctrl(struct cnss_plat_data *plat_priv) { struct pinctrl *pinctrl; pinctrl = plat_priv->pinctrl_info.pinctrl; if (IS_ERR_OR_NULL(pinctrl)) return; devm_pinctrl_put(pinctrl); memset(&plat_priv->pinctrl_info, 0, sizeof(plat_priv->pinctrl_info)); } static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, bool state) { int ret = 0; struct cnss_pinctrl_info *pinctrl_info; if (!plat_priv) { cnss_pr_err("plat_priv is NULL!\n"); ret = -ENODEV; goto out; } pinctrl_info = &plat_priv->pinctrl_info; if (state) { if (pinctrl_info->activated) { cnss_pr_dbg("Pinctrl is already activated\n"); goto out; } if (!IS_ERR_OR_NULL(pinctrl_info->bootstrap_active)) { ret = pinctrl_select_state( pinctrl_info->pinctrl, pinctrl_info->bootstrap_active); if (ret) { cnss_pr_err("Failed to select bootstrap active state, err = %d\n", ret); goto out; } udelay(BOOTSTRAP_DELAY); } if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_active)) { ret = pinctrl_select_state( pinctrl_info->pinctrl, pinctrl_info->wlan_en_active); if (ret) { cnss_pr_err("Failed to select wlan_en active state, err = %d\n", ret); goto out; } udelay(WLAN_ENABLE_DELAY); } pinctrl_info->activated = true; } else { if (!pinctrl_info->activated) { cnss_pr_dbg("Pinctrl is already de-activated\n"); goto out; } if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_sleep)) { ret = pinctrl_select_state(pinctrl_info->pinctrl, pinctrl_info->wlan_en_sleep); if (ret) { cnss_pr_err("Failed to select wlan_en sleep state, err = %d\n", ret); goto out; } } pinctrl_info->activated = false; } return 0; out: return ret; } int cnss_power_on_device(struct cnss_plat_data *plat_priv) { int ret = 0; ret = cnss_vreg_on(plat_priv); if (ret) { cnss_pr_err("Failed to turn on vreg, err = %d\n", ret); goto out; } ret = cnss_select_pinctrl_state(plat_priv, true); if (ret) { cnss_pr_err("Failed to select pinctrl state, err = %d\n", ret); goto vreg_off; } return 0; vreg_off: cnss_vreg_off(plat_priv); out: return ret; } void cnss_power_off_device(struct cnss_plat_data *plat_priv) { cnss_select_pinctrl_state(plat_priv, false); cnss_vreg_off(plat_priv); } void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv) { unsigned long pin_status = 0; set_bit(CNSS_WLAN_EN, &pin_status); set_bit(CNSS_PCIE_TXN, &pin_status); set_bit(CNSS_PCIE_TXP, &pin_status); set_bit(CNSS_PCIE_RXN, &pin_status); set_bit(CNSS_PCIE_RXP, &pin_status); set_bit(CNSS_PCIE_REFCLKN, &pin_status); set_bit(CNSS_PCIE_REFCLKP, &pin_status); set_bit(CNSS_PCIE_RST, &pin_status); plat_priv->pin_result.host_pin_result = pin_status; } /* If it's converged dt, get device specific regulators and enable them. */ int cnss_dev_specific_power_on(struct cnss_plat_data *plat_priv) { int ret; if (!plat_priv->is_converged_dt) return 0; ret = cnss_get_vreg(plat_priv); if (ret) return ret; return cnss_power_on_device(plat_priv); }