blob: 59e7ddb02d652342abebed6ca8efd47baea99264 [file] [log] [blame]
/*
*
* MNH PWR APIs
* Copyright (c) 2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
*/
/* #define DEBUG */
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/interrupt.h>
#include <linux/msm_pcie.h>
#include <linux/regulator/consumer.h>
#include "mnh-pcie.h"
#include "mnh-pwr.h"
#include "mnh-sm.h"
#define MNH_PCIE_RC_INDEX 0
#define MNH_PCIE_VENDOR_ID 0x8086
#define MNH_PCIE_DEVICE_ID 0x3140
#define DISABLE_PCIE_L1_MASK 0xFFFFFFFD
#define PCIE20_CAP_LINKCTRLSTATUS 0x80
#define PM_OPT_SUSPEND MSM_PCIE_CONFIG_LINKDOWN
#define PM_OPT_RESUME MSM_PCIE_CONFIG_NO_CFG_FREE
struct mnh_pwr_data {
struct platform_device *pdev;
struct device *dev;
/* regulators */
struct regulator *asr_supply;
struct regulator *sdsr_supply;
struct regulator *sdldo_supply;
struct regulator *ioldo_supply;
struct notifier_block asr_nb;
struct notifier_block sdsr_nb;
struct notifier_block ioldo_nb;
struct notifier_block sdldo_nb;
/* clocks */
struct clk *ref_clk;
struct clk *sleep_clk;
bool ref_clk_enabled;
bool sleep_clk_enabled;
/* pins */
struct gpio_desc *soc_pwr_good_pin;
/* pcie device */
struct pci_dev *pcidev;
struct pci_saved_state *pristine_state;
struct msm_pcie_register_event pci_link_event;
struct work_struct shutdown_work;
bool pcie_failure;
enum mnh_pwr_state state;
struct mutex lock;
};
static struct mnh_pwr_data *mnh_pwr;
static void mnh_pwr_shutdown_work(struct work_struct *data)
{
dev_err(mnh_pwr->dev, "%s: begin emergency power down\n", __func__);
mnh_pwr_set_state(MNH_PWR_S4);
mnh_sm_pwr_error_cb();
}
static int mnh_pwr_asr_notifier_cb(struct notifier_block *nb,
unsigned long event, void *cookie)
{
dev_dbg(mnh_pwr->dev, "%s: received event %ld\n", __func__, event);
mnh_pwr->pcie_failure = true;
/* force emergency shutdown if regulator output has failed */
if (event == REGULATOR_EVENT_FAIL) {
dev_err(mnh_pwr->dev,
"%s: asr supply has failed, forcing shutdown\n",
__func__);
if (mnh_pwr->state != MNH_PWR_S4)
schedule_work(&mnh_pwr->shutdown_work);
}
return 0;
}
static int mnh_pwr_sdsr_notifier_cb(struct notifier_block *nb,
unsigned long event, void *cookie)
{
dev_dbg(mnh_pwr->dev, "%s: received event %ld\n", __func__, event);
mnh_pwr->pcie_failure = true;
/* force emergency shutdown if regulator output has failed */
if (event == REGULATOR_EVENT_FAIL) {
dev_err(mnh_pwr->dev,
"%s: sdsr supply has failed, forcing shutdown\n",
__func__);
if (mnh_pwr->state != MNH_PWR_S4)
schedule_work(&mnh_pwr->shutdown_work);
}
return 0;
}
static int mnh_pwr_ioldo_notifier_cb(struct notifier_block *nb,
unsigned long event, void *cookie)
{
dev_dbg(mnh_pwr->dev, "%s: received event %ld\n", __func__, event);
mnh_pwr->pcie_failure = true;
/* force emergency shutdown if regulator output has failed */
if (event == REGULATOR_EVENT_FAIL) {
dev_err(mnh_pwr->dev,
"%s: ioldo supply has failed, forcing shutdown\n",
__func__);
if (mnh_pwr->state != MNH_PWR_S4)
schedule_work(&mnh_pwr->shutdown_work);
}
return 0;
}
static int mnh_pwr_sdldo_notifier_cb(struct notifier_block *nb,
unsigned long event, void *cookie)
{
dev_dbg(mnh_pwr->dev, "%s: received event %ld\n", __func__, event);
mnh_pwr->pcie_failure = true;
/* force emergency shutdown if regulator output has failed */
if (event == REGULATOR_EVENT_FAIL) {
dev_err(mnh_pwr->dev,
"%s: sdldo supply has failed, forcing shutdown\n",
__func__);
if (mnh_pwr->state != MNH_PWR_S4)
schedule_work(&mnh_pwr->shutdown_work);
}
return 0;
}
void mnh_pwr_pcie_link_state_cb(struct msm_pcie_notify *notify)
{
struct mnh_pwr_data *mnh_pwr = notify->data;
switch (notify->event) {
case MSM_PCIE_EVENT_LINKDOWN:
dev_err(mnh_pwr->dev,
"%s: PCIe link is down, forcing power down\n",
__func__);
mnh_pwr->pcie_failure = true;
/* force emergency shutdown */
schedule_work(&mnh_pwr->shutdown_work);
break;
default:
dev_err(mnh_pwr->dev,
"%s: received invalid pcie link state event (%d)\n",
__func__, notify->event);
break;
}
}
static int mnh_pwr_pcie_enumerate(void)
{
struct pci_dev *pcidev = NULL;
int ret;
/* enumerate PCIE */
ret = msm_pcie_enumerate(MNH_PCIE_RC_INDEX);
if (ret < 0) {
dev_err(mnh_pwr->dev, "%s: pcie enumeration failed (%d)\n",
__func__, ret);
return ret;
}
/* search for PCIE device in our domain */
do {
pcidev = pci_get_device(MNH_PCIE_VENDOR_ID, MNH_PCIE_DEVICE_ID, pcidev);
if (!pcidev)
break;
if (pci_domain_nr(pcidev->bus) == MNH_PCIE_RC_INDEX)
break;
} while (true);
if (!pcidev) {
dev_err(mnh_pwr->dev, "%s: could not find mnh device\n",
__func__);
return -ENODEV;
}
/* save current state in pcidev */
ret = pci_save_state(pcidev);
if (ret) {
dev_err(mnh_pwr->dev, "%s: pci_save_state failed (%d)\n",
__func__, ret);
pci_dev_put(pcidev);
return ret;
}
/* store saved state so we can recall it after resume */
mnh_pwr->pristine_state = pci_store_saved_state(pcidev);
if (!mnh_pwr->pristine_state) {
dev_err(mnh_pwr->dev,
"%s: pci_store_saved_state failed\n",
__func__);
pci_dev_put(pcidev);
return ret;
}
/* save device to driver struct */
mnh_pwr->pcidev = pcidev;
return 0;
}
static int mnh_pwr_pcie_suspend(void)
{
struct pci_dev *pcidev = mnh_pwr->pcidev;
int ret;
if (!pcidev)
return -ENODEV;
/* suspend the driver state */
ret = mnh_pci_suspend();
if (ret) {
dev_err(mnh_pwr->dev, "%s: mnh_pci_suspend failed (%d)\n",
__func__, ret);
}
if (mnh_pwr->pcie_failure) {
/* call the platform driver to update link status */
ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
pcidev, NULL,
PM_OPT_SUSPEND | MSM_PCIE_CONFIG_NO_CFG_RESTORE);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: msm_pcie_pm_control(suspend) failed (%d)\n",
__func__, ret);
return ret;
}
mnh_pwr->pcie_failure = false;
} else {
/* prepare the root complex and endpoint for going to suspend */
ret = pci_prepare_to_sleep(pcidev);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: pci_prepare_to_sleep failed (%d)\n",
__func__, ret);
}
/* call the platform driver to suspend PCIe link */
ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
pcidev, NULL, PM_OPT_SUSPEND);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: msm_pcie_pm_control(suspend) failed (%d)\n",
__func__, ret);
return ret;
}
}
return 0;
}
static int mnh_pwr_pcie_resume(void)
{
struct pci_dev *pcidev = mnh_pwr->pcidev;
int ret;
/* check for valid pcidev */
if (!pcidev) {
/* enumerate pci device */
ret = mnh_pwr_pcie_enumerate();
if (ret) {
dev_err(mnh_pwr->dev,
"%s: mnh_pwr_pcie_enumerate failed (%d)\n",
__func__, ret);
return ret;
}
/* save the newly enumerated device to the local copy */
pcidev = mnh_pwr->pcidev;
/* register for link down events so we can handle them */
mnh_pwr->pci_link_event.events = MSM_PCIE_EVENT_LINKDOWN;
mnh_pwr->pci_link_event.user = pcidev;
mnh_pwr->pci_link_event.callback = mnh_pwr_pcie_link_state_cb;
mnh_pwr->pci_link_event.notify.data = mnh_pwr;
ret = msm_pcie_register_event(&mnh_pwr->pci_link_event);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: msm_pcie_register_event failed (%d)\n",
__func__, ret);
}
} else {
ret = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number,
pcidev, NULL, PM_OPT_RESUME);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: msm_pcie_pm_control(resume) failed (%d)\n",
__func__, ret);
return ret;
}
/* prepare the root complex and endpoint */
ret = pci_back_from_sleep(pcidev);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: pci_back_from_sleep failed (%d)\n",
__func__, ret);
goto fail_pcie_resume_awake;
}
/* load the saved state in the device buffer */
ret = pci_load_saved_state(pcidev, mnh_pwr->pristine_state);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: pci_load_saved_state failed (%d)\n",
__func__, ret);
goto fail_pcie_resume_load_state;
}
/* apply the saved state to the device */
pci_restore_state(pcidev);
/* resume the driver state */
ret = mnh_pci_resume();
if (ret) {
dev_err(mnh_pwr->dev, "%s: mnh_pci_resume failed (%d)\n",
__func__, ret);
goto fail_pcie_resume_mnh_init;
}
}
mnh_pwr->pcie_failure = false;
return 0;
fail_pcie_resume_mnh_init:
fail_pcie_resume_load_state:
pci_prepare_to_sleep(pcidev);
fail_pcie_resume_awake:
msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number, pcidev,
NULL, PM_OPT_SUSPEND);
return ret;
}
static int mnh_pwr_down(void)
{
int ret;
if ((mnh_pwr->state != MNH_PWR_S3) &&
(mnh_sm_get_boot_mode() == MNH_BOOT_MODE_PCIE)) {
/* suspend pcie link */
ret = mnh_pwr_pcie_suspend();
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to suspend pcie link (%d)\n",
__func__, ret);
goto fail_pwr_down_pcie;
}
} else {
/* assert reset */
msm_pcie_set_reset(MNH_PCIE_RC_INDEX, true);
}
/* deassert soc_pwr_good */
gpiod_set_value_cansleep(mnh_pwr->soc_pwr_good_pin, 0);
/* disable clocks */
if (mnh_pwr->ref_clk_enabled) {
clk_disable_unprepare(mnh_pwr->ref_clk);
mnh_pwr->ref_clk_enabled = false;
}
if (mnh_pwr->sleep_clk_enabled) {
clk_disable_unprepare(mnh_pwr->sleep_clk);
mnh_pwr->sleep_clk_enabled = false;
}
/* disable supplies: sdsr -> asr -> ioldo -> sdldo */
if (regulator_is_enabled(mnh_pwr->sdsr_supply)) {
ret = regulator_disable(mnh_pwr->sdsr_supply);
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to disable sdsr (%d)\n",
__func__, ret);
goto fail_pwr_down_sdsr;
}
}
if (regulator_is_enabled(mnh_pwr->asr_supply)) {
ret = regulator_disable(mnh_pwr->asr_supply);
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to disable asr (%d)\n",
__func__, ret);
goto fail_pwr_down_asr;
}
}
if (regulator_is_enabled(mnh_pwr->ioldo_supply)) {
ret = regulator_disable(mnh_pwr->ioldo_supply);
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to disable ioldo (%d)\n",
__func__, ret);
goto fail_pwr_down_ioldo;
}
}
if (regulator_is_enabled(mnh_pwr->sdldo_supply)) {
ret = regulator_disable(mnh_pwr->sdldo_supply);
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to disable sdldo (%d)\n",
__func__, ret);
goto fail_pwr_down_sdldo;
}
}
mnh_pwr->state = MNH_PWR_S4;
return 0;
fail_pwr_down_pcie:
gpiod_set_value_cansleep(mnh_pwr->soc_pwr_good_pin, 0);
if (mnh_pwr->sleep_clk_enabled) {
clk_disable_unprepare(mnh_pwr->sleep_clk);
mnh_pwr->sleep_clk_enabled = false;
}
if (mnh_pwr->ref_clk_enabled) {
clk_disable_unprepare(mnh_pwr->ref_clk);
mnh_pwr->ref_clk_enabled = false;
}
if (regulator_is_enabled(mnh_pwr->sdsr_supply))
regulator_disable(mnh_pwr->sdsr_supply);
fail_pwr_down_sdsr:
if (regulator_is_enabled(mnh_pwr->asr_supply))
regulator_disable(mnh_pwr->asr_supply);
fail_pwr_down_asr:
if (regulator_is_enabled(mnh_pwr->ioldo_supply))
regulator_disable(mnh_pwr->ioldo_supply);
fail_pwr_down_ioldo:
if (regulator_is_enabled(mnh_pwr->sdldo_supply))
regulator_disable(mnh_pwr->sdldo_supply);
fail_pwr_down_sdldo:
dev_err(mnh_pwr->dev,
"%s: force shutdown because of powerdown failure (%d)\n",
__func__, ret);
mnh_pwr->state = MNH_PWR_S4;
return ret;
}
static int mnh_pwr_suspend(void)
{
int ret;
if (mnh_sm_get_boot_mode() == MNH_BOOT_MODE_PCIE) {
/* suspend pcie link */
ret = mnh_pwr_pcie_suspend();
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to suspend pcie link (%d)\n",
__func__, ret);
goto fail_pwr_suspend_pcie;
}
} else {
/* assert reset */
msm_pcie_set_reset(MNH_PCIE_RC_INDEX, true);
}
/* deassert soc_pwr_good */
gpiod_set_value_cansleep(mnh_pwr->soc_pwr_good_pin, 0);
/* disable clocks */
if (mnh_pwr->ref_clk_enabled) {
clk_disable_unprepare(mnh_pwr->ref_clk);
mnh_pwr->ref_clk_enabled = false;
}
if (mnh_pwr->sleep_clk_enabled) {
clk_disable_unprepare(mnh_pwr->sleep_clk);
mnh_pwr->sleep_clk_enabled = false;
}
/* disable core supplies */
ret = regulator_disable(mnh_pwr->asr_supply);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: failed to disable asr (%d)\n", __func__, ret);
goto fail_pwr_suspend_regulators;
}
regulator_disable(mnh_pwr->ioldo_supply);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: failed to disable ioldo (%d)\n", __func__, ret);
goto fail_pwr_suspend_regulators;
}
mnh_pwr->state = MNH_PWR_S3;
return 0;
fail_pwr_suspend_pcie:
gpiod_set_value_cansleep(mnh_pwr->soc_pwr_good_pin, 0);
if (mnh_pwr->sleep_clk_enabled) {
clk_disable_unprepare(mnh_pwr->sleep_clk);
mnh_pwr->sleep_clk_enabled = false;
}
if (mnh_pwr->ref_clk_enabled) {
clk_disable_unprepare(mnh_pwr->ref_clk);
mnh_pwr->ref_clk_enabled = false;
}
fail_pwr_suspend_regulators:
if (regulator_is_enabled(mnh_pwr->sdsr_supply))
regulator_disable(mnh_pwr->sdsr_supply);
if (regulator_is_enabled(mnh_pwr->asr_supply))
regulator_disable(mnh_pwr->asr_supply);
if (regulator_is_enabled(mnh_pwr->ioldo_supply))
regulator_disable(mnh_pwr->ioldo_supply);
if (regulator_is_enabled(mnh_pwr->sdldo_supply))
regulator_disable(mnh_pwr->sdldo_supply);
dev_err(mnh_pwr->dev,
"%s: force shutdown because of suspend failure (%d)\n",
__func__, ret);
mnh_pwr->state = MNH_PWR_S4;
return ret;
}
static int mnh_pwr_partial(void)
{
int ret;
/* disable DRAM core supply */
if (regulator_is_enabled(mnh_pwr->sdsr_supply)) {
ret = regulator_disable(mnh_pwr->sdsr_supply);
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to disable sdsr (%d)\n",
__func__, ret);
}
}
/* disable DRAM I/O supply */
if (regulator_is_enabled(mnh_pwr->sdldo_supply)) {
ret = regulator_disable(mnh_pwr->sdldo_supply);
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to disable sdldo (%d)\n",
__func__, ret);
}
}
mnh_pwr->state = MNH_PWR_S1;
return 0;
}
static int mnh_pwr_up(enum mnh_pwr_state next_state)
{
int ret;
/* enable supplies */
/* sdldo -> ioldo -> asr -> sdsr */
if (mnh_pwr->state == MNH_PWR_S4) {
ret = regulator_enable(mnh_pwr->sdldo_supply);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: failed to enable sdldo (%d)\n",
__func__, ret);
goto fail_pwr_up_regulators;
}
}
ret = regulator_enable(mnh_pwr->ioldo_supply);
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to enable ioldo (%d)\n",
__func__, ret);
goto fail_pwr_up_regulators;
}
ret = regulator_enable(mnh_pwr->asr_supply);
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to enable ioldo (%d)\n",
__func__, ret);
goto fail_pwr_up_regulators;
}
if (mnh_pwr->state == MNH_PWR_S4) {
ret = regulator_enable(mnh_pwr->sdsr_supply);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: failed to enable sdsr (%d)\n",
__func__, ret);
goto fail_pwr_up_regulators;
}
}
/* turn on clocks */
if (!mnh_pwr->ref_clk_enabled) {
ret = clk_prepare_enable(mnh_pwr->ref_clk);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: failed to enable ref clk (%d)\n",
__func__, ret);
goto fail_pwr_up_ref_clk;
}
mnh_pwr->ref_clk_enabled = true;
}
if (!mnh_pwr->sleep_clk_enabled) {
ret = clk_prepare_enable(mnh_pwr->sleep_clk);
if (ret) {
dev_err(mnh_pwr->dev,
"%s: failed to enable sleep clk (%d)\n",
__func__, ret);
goto fail_pwr_up_sleep_clk;
}
mnh_pwr->sleep_clk_enabled = true;
}
/* assert soc_pwr_good */
gpiod_set_value_cansleep(mnh_pwr->soc_pwr_good_pin, 1);
/* give the PLLs some time to initialize */
udelay(60);
if (mnh_sm_get_boot_mode() == MNH_BOOT_MODE_PCIE) {
/* resume pcie link */
ret = mnh_pwr_pcie_resume();
if (ret) {
dev_err(mnh_pwr->dev, "%s: failed to resume pcie link (%d)\n",
__func__, ret);
goto fail_pwr_up_pcie;
}
} else {
/* deassert reset */
msm_pcie_set_reset(MNH_PCIE_RC_INDEX, false);
}
mnh_pwr->state = next_state;
return 0;
fail_pwr_up_pcie:
gpiod_set_value_cansleep(mnh_pwr->soc_pwr_good_pin, 0);
if (mnh_pwr->sleep_clk_enabled) {
clk_disable_unprepare(mnh_pwr->sleep_clk);
mnh_pwr->sleep_clk_enabled = false;
}
fail_pwr_up_sleep_clk:
if (mnh_pwr->ref_clk_enabled) {
clk_disable_unprepare(mnh_pwr->ref_clk);
mnh_pwr->ref_clk_enabled = false;
}
fail_pwr_up_ref_clk:
fail_pwr_up_regulators:
if (regulator_is_enabled(mnh_pwr->sdsr_supply))
regulator_disable(mnh_pwr->sdsr_supply);
if (regulator_is_enabled(mnh_pwr->asr_supply))
regulator_disable(mnh_pwr->asr_supply);
if (regulator_is_enabled(mnh_pwr->ioldo_supply))
regulator_disable(mnh_pwr->ioldo_supply);
if (regulator_is_enabled(mnh_pwr->sdldo_supply))
regulator_disable(mnh_pwr->sdldo_supply);
dev_err(mnh_pwr->dev,
"%s: force shutdown because of power up failure (%d)\n",
__func__, ret);
mnh_pwr->state = MNH_PWR_S4;
return ret;
}
static int mnh_pwr_get_resources(void)
{
struct platform_device *pdev = mnh_pwr->pdev;
struct device *dev = mnh_pwr->dev;
int ret;
/* request supplies */
mnh_pwr->asr_supply = devm_regulator_get(&pdev->dev, "bcm15602_asr");
if (IS_ERR(mnh_pwr->asr_supply)) {
dev_err(dev, "%s: failed to get asr supply (%ld)\n",
__func__, PTR_ERR(mnh_pwr->asr_supply));
return PTR_ERR(mnh_pwr->asr_supply);
}
mnh_pwr->sdsr_supply = devm_regulator_get(&pdev->dev, "bcm15602_sdsr");
if (IS_ERR(mnh_pwr->sdsr_supply)) {
dev_err(dev, "%s: failed to get sdsr supply (%ld)\n",
__func__, PTR_ERR(mnh_pwr->sdsr_supply));
return PTR_ERR(mnh_pwr->sdsr_supply);
}
mnh_pwr->ioldo_supply = devm_regulator_get(&pdev->dev,
"bcm15602_ioldo");
if (IS_ERR(mnh_pwr->ioldo_supply)) {
dev_err(dev, "%s: failed to get ioldo supply (%ld)\n",
__func__, PTR_ERR(mnh_pwr->ioldo_supply));
return PTR_ERR(mnh_pwr->ioldo_supply);
}
mnh_pwr->sdldo_supply = devm_regulator_get(&pdev->dev,
"bcm15602_sdldo");
if (IS_ERR(mnh_pwr->sdldo_supply)) {
dev_err(dev, "%s: failed to get sdldo supply (%ld)\n",
__func__, PTR_ERR(mnh_pwr->sdldo_supply));
return PTR_ERR(mnh_pwr->sdldo_supply);
}
/* register the notifier for each of the supplies */
mnh_pwr->asr_nb.notifier_call = mnh_pwr_asr_notifier_cb;
ret = devm_regulator_register_notifier(mnh_pwr->asr_supply,
&mnh_pwr->asr_nb);
if (ret) {
dev_err(dev,
"%s: failed to register notifier block for asr supply (%d)\n",
__func__, ret);
return ret;
}
mnh_pwr->sdsr_nb.notifier_call = mnh_pwr_sdsr_notifier_cb;
ret = devm_regulator_register_notifier(mnh_pwr->sdsr_supply,
&mnh_pwr->sdsr_nb);
if (ret) {
dev_err(dev,
"%s: failed to register notifier block for sdsr supply (%d)\n",
__func__, ret);
return ret;
}
mnh_pwr->ioldo_nb.notifier_call = mnh_pwr_ioldo_notifier_cb;
ret = devm_regulator_register_notifier(mnh_pwr->ioldo_supply,
&mnh_pwr->ioldo_nb);
if (ret) {
dev_err(dev,
"%s: failed to register notifier block for ioldo supply (%d)\n",
__func__, ret);
return ret;
}
mnh_pwr->sdldo_nb.notifier_call = mnh_pwr_sdldo_notifier_cb;
ret = devm_regulator_register_notifier(mnh_pwr->sdldo_supply,
&mnh_pwr->sdldo_nb);
if (ret) {
dev_err(dev,
"%s: failed to register notifier block for sdldo supply (%d)\n",
__func__, ret);
return ret;
}
/* request gpio descriptors */
mnh_pwr->soc_pwr_good_pin = devm_gpiod_get(&pdev->dev, "soc-pwr-good",
GPIOD_OUT_LOW);
if (IS_ERR(mnh_pwr->soc_pwr_good_pin)) {
dev_err(dev, "%s: could not get soc_pwr_good gpio (%ld)\n",
__func__, PTR_ERR(mnh_pwr->soc_pwr_good_pin));
return PTR_ERR(mnh_pwr->soc_pwr_good_pin);
}
/* request clocks */
mnh_pwr->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
if (IS_ERR(mnh_pwr->ref_clk)) {
dev_err(dev, "%s: could not get ref clk (%ld)\n", __func__,
PTR_ERR(mnh_pwr->ref_clk));
return PTR_ERR(mnh_pwr->ref_clk);
}
mnh_pwr->sleep_clk = devm_clk_get(&pdev->dev, "sleep_clk");
if (IS_ERR(mnh_pwr->sleep_clk)) {
dev_err(dev, "%s: could not get sleep clk (%ld)\n", __func__,
PTR_ERR(mnh_pwr->sleep_clk));
return PTR_ERR(mnh_pwr->sleep_clk);
}
return 0;
}
int mnh_pwr_set_state(enum mnh_pwr_state system_state)
{
int ret = 0;
enum mnh_pwr_state curr_state = mnh_pwr_get_state();
dev_dbg(mnh_pwr->dev, "%s req: %d, current: %d\n", __func__,
system_state, curr_state);
mutex_lock(&mnh_pwr->lock);
if (system_state != mnh_pwr->state) {
switch (system_state) {
case MNH_PWR_S0:
ret = mnh_pwr_up(system_state);
break;
case MNH_PWR_S1:
ret = mnh_pwr_partial();
break;
case MNH_PWR_S3:
ret = mnh_pwr_suspend();
break;
case MNH_PWR_S4:
ret = mnh_pwr_down();
break;
default:
dev_err(mnh_pwr->dev, "%s: invalid state %d\n",
__func__, system_state);
ret = -EINVAL;
break;
}
if (ret)
dev_err(mnh_pwr->dev,
"%s: state transition failed (%d)\n",
__func__, ret);
else
dev_dbg(mnh_pwr->dev, "%s done with state: %d\n",
__func__, system_state);
} else {
dev_dbg(mnh_pwr->dev, "%s: no state change needed\n", __func__);
}
mutex_unlock(&mnh_pwr->lock);
return ret;
}
EXPORT_SYMBOL_GPL(mnh_pwr_set_state);
enum mnh_pwr_state mnh_pwr_get_state(void)
{
enum mnh_pwr_state curr_state;
mutex_lock(&mnh_pwr->lock);
curr_state = mnh_pwr->state;
mutex_unlock(&mnh_pwr->lock);
return curr_state;
}
EXPORT_SYMBOL_GPL(mnh_pwr_get_state);
int mnh_pwr_init(struct platform_device *pdev, struct device *dev)
{
int ret;
/* allocate memory for mnh_pwr_data struct */
mnh_pwr = devm_kzalloc(dev, sizeof(struct mnh_pwr_data), GFP_KERNEL);
if (!mnh_pwr)
return -ENOMEM;
/* save a local copy of the device struct */
mnh_pwr->pdev = pdev;
mnh_pwr->dev = dev;
/* initialize power state to powered down */
mnh_pwr->state = MNH_PWR_S4;
/* get platform resources */
ret = mnh_pwr_get_resources();
if (ret) {
dev_err(dev, "%s: failed to get platform resources (%d)\n",
__func__, ret);
return ret;
}
/* initialize some structures */
INIT_WORK(&mnh_pwr->shutdown_work, mnh_pwr_shutdown_work);
mutex_init(&mnh_pwr->lock);
/* power on the device to enumerate PCIe */
ret = mnh_pwr_up(MNH_PWR_S0);
if (ret) {
dev_err(dev, "%s: failed initial power up (%d)", __func__, ret);
return ret;
}
/* power down the device */
ret = mnh_pwr_down();
if (ret) {
dev_err(dev, "%s: failed initial power down (%d)", __func__,
ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL(mnh_pwr_init);