index bdb08645c8a5dab5b5fc0808876462805d89c270..38e96b56863af306082a99e560a538577e6201a2 100644 (file)
#include <linux/sched.h>
#include <linux/suspend.h>
#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <plat/prcm.h>
#include <plat/mailbox.h>
#include "pm.h"
#include "pm33xx.h"
+#include "control.h"
#include "clockdomain.h"
#include "powerdomain.h"
static suspend_state_t suspend_state = PM_SUSPEND_ON;
static struct device *mpu_dev;
+static struct device *i2c_dev;
static struct omap_mbox *m3_mbox;
static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm;
static struct clockdomain *gfx_l3_clkdm, *gfx_l4ls_clkdm;
+static struct am33xx_padconf lp_padconf;
+static int gmii_sel;
+
static int core_suspend_stat = -1;
static int m3_state = M3_STATE_UNKNOWN;
static DECLARE_COMPLETION(a8_m3_sync);
+#define VOLT_TOLERANCE 12500 /* 12.5mV */
+
+struct suspend_opp_change {
+ char *clk_name;
+ char *reg_name;
+ unsigned long freq1; /* Frequency in suspend mode */
+ unsigned long freq2; /* Frequency in active mode */
+ unsigned int volt1; /* Voltage in suspend mode */
+ unsigned int volt2; /* Voltage in active mode */
+ struct clk *clk;
+ struct regulator *reg;
+};
+
+static struct suspend_opp_change opp[] = {
+ {
+ .clk_name = "dpll_mpu_ck",
+ .reg_name = "vdd_mpu",
+ .freq1 = 275000000,
+ .volt1 = 965000,
+ },
+};
+
+static void save_padconf(void)
+{
+ lp_padconf.mii1_col = readl(AM33XX_CTRL_REGADDR(0x0908));
+ lp_padconf.mii1_crs = readl(AM33XX_CTRL_REGADDR(0x090c));
+ lp_padconf.mii1_rxerr = readl(AM33XX_CTRL_REGADDR(0x0910));
+ lp_padconf.mii1_txen = readl(AM33XX_CTRL_REGADDR(0x0914));
+ lp_padconf.mii1_rxdv = readl(AM33XX_CTRL_REGADDR(0x0918));
+ lp_padconf.mii1_txd3 = readl(AM33XX_CTRL_REGADDR(0x091c));
+ lp_padconf.mii1_txd2 = readl(AM33XX_CTRL_REGADDR(0x0920));
+ lp_padconf.mii1_txd1 = readl(AM33XX_CTRL_REGADDR(0x0924));
+ lp_padconf.mii1_txd0 = readl(AM33XX_CTRL_REGADDR(0x0928));
+ lp_padconf.mii1_txclk = readl(AM33XX_CTRL_REGADDR(0x092c));
+ lp_padconf.mii1_rxclk = readl(AM33XX_CTRL_REGADDR(0x0930));
+ lp_padconf.mii1_rxd3 = readl(AM33XX_CTRL_REGADDR(0x0934));
+ lp_padconf.mii1_rxd2 = readl(AM33XX_CTRL_REGADDR(0x0938));
+ lp_padconf.mii1_rxd1 = readl(AM33XX_CTRL_REGADDR(0x093c));
+ lp_padconf.mii1_rxd0 = readl(AM33XX_CTRL_REGADDR(0x0940));
+ lp_padconf.rmii1_refclk = readl(AM33XX_CTRL_REGADDR(0x0944));
+ lp_padconf.mdio_data = readl(AM33XX_CTRL_REGADDR(0x0948));
+ lp_padconf.mdio_clk = readl(AM33XX_CTRL_REGADDR(0x094c));
+ gmii_sel = readl(AM33XX_CTRL_REGADDR(0x0650));
+}
+
+static void restore_padconf(void)
+{
+ writel(lp_padconf.mii1_col, AM33XX_CTRL_REGADDR(0x0908));
+ writel(lp_padconf.mii1_crs, AM33XX_CTRL_REGADDR(0x090c));
+ writel(lp_padconf.mii1_rxerr, AM33XX_CTRL_REGADDR(0x0910));
+ writel(lp_padconf.mii1_txen, AM33XX_CTRL_REGADDR(0x0914));
+ writel(lp_padconf.mii1_rxdv, AM33XX_CTRL_REGADDR(0x0918));
+ writel(lp_padconf.mii1_txd3, AM33XX_CTRL_REGADDR(0x091c));
+ writel(lp_padconf.mii1_txd2, AM33XX_CTRL_REGADDR(0x0920));
+ writel(lp_padconf.mii1_txd1, AM33XX_CTRL_REGADDR(0x0924));
+ writel(lp_padconf.mii1_txd0, AM33XX_CTRL_REGADDR(0x0928));
+ writel(lp_padconf.mii1_txclk, AM33XX_CTRL_REGADDR(0x092c));
+ writel(lp_padconf.mii1_rxclk, AM33XX_CTRL_REGADDR(0x0930));
+ writel(lp_padconf.mii1_rxd3, AM33XX_CTRL_REGADDR(0x0934));
+ writel(lp_padconf.mii1_rxd2, AM33XX_CTRL_REGADDR(0x0938));
+ writel(lp_padconf.mii1_rxd1, AM33XX_CTRL_REGADDR(0x093c));
+ writel(lp_padconf.mii1_rxd0, AM33XX_CTRL_REGADDR(0x0940));
+ writel(lp_padconf.rmii1_refclk, AM33XX_CTRL_REGADDR(0x0944));
+ writel(lp_padconf.mdio_data, AM33XX_CTRL_REGADDR(0x0948));
+ writel(lp_padconf.mdio_clk, AM33XX_CTRL_REGADDR(0x094c));
+ writel(gmii_sel, AM33XX_CTRL_REGADDR(0x0650));
+}
+
+
+static int am33xx_pm_prepare_late(void)
+{
+ int ret = 0;
+
+ save_padconf();
+
+ if(!opp[0].clk || !opp[0].reg || !i2c_dev) {
+ pr_debug("Not attempting OPP change in suspend\n");
+ return ret;
+ }
+
+ opp[0].freq2 = clk_get_rate(opp[0].clk);
+ ret = clk_set_rate(opp[0].clk, opp[0].freq1);
+ if (ret) {
+ pr_debug("Failed to set %s frequency to %luMHz\n",
+ opp[0].clk_name, opp[0].freq1/1000000);
+ } else {
+ opp[0].volt2 = regulator_get_voltage(opp[0].reg);
+ ret = regulator_set_voltage(opp[0].reg, opp[0].volt1,
+ opp[0].volt1 + VOLT_TOLERANCE);
+ if (ret)
+ pr_debug("Failed to set %s voltage to %dmV\n",
+ opp[0].reg_name, opp[0].volt1/1000);
+ }
+
+ return ret;
+}
+
+static void am33xx_pm_finish(void)
+{
+ int ret = 0;
+
+ if(!opp[0].clk || !opp[0].reg || !i2c_dev) {
+ pr_debug("Not attempting OPP change in resume\n");
+ goto padconf;
+ }
+
+ ret = regulator_set_voltage(opp[0].reg, opp[0].volt2,
+ opp[0].volt2 + VOLT_TOLERANCE);
+ if (ret) {
+ pr_warning("Failed to restore %s voltage to %dmV\n",
+ opp[0].reg_name, opp[0].volt2/1000);
+ } else {
+ ret = clk_set_rate(opp[0].clk, opp[0].freq2);
+ if (ret)
+ pr_warning("Failed to restore %s frequency to %luMHz\n",
+ opp[0].clk_name, opp[0].freq2/1000000);
+ }
+
+padconf:
+ restore_padconf();
+}
+
static int am33xx_do_sram_idle(long unsigned int state)
{
am33xx_do_wfi_sram();
disable_hlt();
+ if(i2c_dev)
+ pm_runtime_forbid(i2c_dev);
+
am33xx_lp_ipc.resume_addr = DS_RESUME_ADDR;
am33xx_lp_ipc.sleep_mode = DS_MODE;
am33xx_lp_ipc.ipc_data1 = DS_IPC_DEFAULT;
ret = omap_mbox_msg_send(m3_mbox, 0xABCDABCD);
if (!ret) {
pr_debug("Message sent for resetting M3 state machine\n");
+ if (!wait_for_completion_timeout(&a8_m3_sync, msecs_to_jiffies(5000)))
+ pr_err("A8<->CM3 sync failure\n");
} else {
pr_debug("Could not reset M3 state machine!!!\n");
m3_state = M3_STATE_UNKNOWN;
am33xx_m3_state_machine_reset();
+ if(i2c_dev)
+ pm_runtime_allow(i2c_dev);
+
enable_hlt();
return;
.end = am33xx_pm_end,
.enter = am33xx_pm_enter,
.valid = suspend_valid_only_mem,
+ .prepare = am33xx_pm_prepare_late,
+ .finish = am33xx_pm_finish,
};
int am33xx_ipc_cmd(struct a8_wkup_m3_ipc_data *data)
ret = -1;
goto clear_old_status;
} else {
- pr_info("Status = %0x\n", status);
+ pr_info("Something went wrong :(\nStatus = %0x\n", status);
ret = -1;
}
omap_mbox_msg_rx_flush(m3_mbox);
if (m3_mbox->ops->ack_irq)
m3_mbox->ops->ack_irq(m3_mbox, IRQ_RX);
+ complete(&a8_m3_sync);
} else if (m3_state == M3_STATE_MSG_FOR_LP) {
/* Read back the MBOX and disable the interrupt to M3 since we are going down */
omap_mbox_msg_rx_flush(m3_mbox);
goto err5;
}
+ pr_info("Trying to load am335x-pm-firmware.bin\n");
+
/* Now try to load the firware */
- ret = request_firmware(&firmware, "cm3-firmware.bin", mpu_dev);
+ ret = request_firmware(&firmware, "am335x-pm-firmware.bin", mpu_dev);
if (ret < 0) {
dev_err(mpu_dev, "request_firmware failed\n");
goto err6;
exit:
return ret;
}
-#endif /* CONFIG_SUSPEND */
-
-/*
- * Push the minimal suspend-resume code to SRAM
- */
-void am33xx_push_sram_idle(void)
-{
- am33xx_do_wfi_sram = omap_sram_push(am33xx_do_wfi, am33xx_do_wfi_sz);
-}
/*
* Enable hardware supervised mode for all clockdomains if it's
clkdm_sleep(clkdm);
return 0;
}
+#endif /* CONFIG_SUSPEND */
+
+/*
+ * Push the minimal suspend-resume code to SRAM
+ */
+void am33xx_push_sram_idle(void)
+{
+ am33xx_do_wfi_sram = omap_sram_push(am33xx_do_wfi, am33xx_do_wfi_sz);
+}
static int __init am33xx_pm_init(void)
{
- int ret;
+ int ret, i;
if (!cpu_is_am33xx())
return -ENODEV;
return -EINVAL;
}
+ i2c_dev = omap_device_get_by_hwmod_name("i2c1");
+
+ if (!i2c_dev) {
+ pr_warning("%s: unable to get the i2c device\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(opp); i++) {
+ opp[i].clk = clk_get(NULL, opp[i].clk_name);
+ if (IS_ERR(opp[i].clk))
+ pr_warning("Could not get %s clock\n", opp[i].clk_name);
+
+ if (opp[i].reg_name) {
+ opp[i].reg = regulator_get(NULL, opp[i].reg_name);
+ if (IS_ERR(opp[i].reg))
+ pr_warning("Could not get %s regulator\n",
+ opp[i].reg_name);
+ }
+ }
+
ret = wkup_m3_init();
if (ret) {