Skip to content

Commit eae813b

Browse files
digetxrafaeljw
authored andcommitted
soc/tegra: pmc: Use sys-off handler API to power off Nexus 7 properly
Nexus 7 Android tablet can be turned off using a special bootloader command which is conveyed to bootloader by putting magic value into the special scratch register and then rebooting normally. This power-off method should be invoked if USB cable is connected. Bootloader then will display battery status and power off the device. This behaviour is borrowed from downstream kernel and matches user expectations, otherwise it looks like device got hung during power-off and it may wake up on USB disconnect. Switch PMC driver to sys-off handler API, which provides drivers with chained power-off callbacks functionality that is required for powering-off devices properly. It also brings resource-managed API for the restart handler registration that makes PMC driver code cleaner. Signed-off-by: Dmitry Osipenko <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 5b71808 commit eae813b

File tree

1 file changed

+62
-25
lines changed

1 file changed

+62
-25
lines changed

drivers/soc/tegra/pmc.c

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <linux/platform_device.h>
4040
#include <linux/pm_domain.h>
4141
#include <linux/pm_opp.h>
42+
#include <linux/power_supply.h>
4243
#include <linux/reboot.h>
4344
#include <linux/regmap.h>
4445
#include <linux/reset.h>
@@ -108,6 +109,7 @@
108109
#define PMC_USB_DEBOUNCE_DEL 0xec
109110
#define PMC_USB_AO 0xf0
110111

112+
#define PMC_SCRATCH37 0x130
111113
#define PMC_SCRATCH41 0x140
112114

113115
#define PMC_WAKE2_MASK 0x160
@@ -1099,23 +1101,39 @@ static struct notifier_block tegra_pmc_reboot_notifier = {
10991101
.notifier_call = tegra_pmc_reboot_notify,
11001102
};
11011103

1102-
static int tegra_pmc_restart_notify(struct notifier_block *this,
1103-
unsigned long action, void *data)
1104+
static void tegra_pmc_restart(void)
11041105
{
11051106
u32 value;
11061107

11071108
/* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */
11081109
value = tegra_pmc_readl(pmc, PMC_CNTRL);
11091110
value |= PMC_CNTRL_MAIN_RST;
11101111
tegra_pmc_writel(pmc, value, PMC_CNTRL);
1112+
}
1113+
1114+
static int tegra_pmc_restart_handler(struct sys_off_data *data)
1115+
{
1116+
tegra_pmc_restart();
11111117

11121118
return NOTIFY_DONE;
11131119
}
11141120

1115-
static struct notifier_block tegra_pmc_restart_handler = {
1116-
.notifier_call = tegra_pmc_restart_notify,
1117-
.priority = 128,
1118-
};
1121+
static int tegra_pmc_power_off_handler(struct sys_off_data *data)
1122+
{
1123+
/*
1124+
* Reboot Nexus 7 into special bootloader mode if USB cable is
1125+
* connected in order to display battery status and power off.
1126+
*/
1127+
if (of_machine_is_compatible("asus,grouper") &&
1128+
power_supply_is_system_supplied()) {
1129+
const u32 go_to_charger_mode = 0xa5a55a5a;
1130+
1131+
tegra_pmc_writel(pmc, go_to_charger_mode, PMC_SCRATCH37);
1132+
tegra_pmc_restart();
1133+
}
1134+
1135+
return NOTIFY_DONE;
1136+
}
11191137

11201138
static int powergate_show(struct seq_file *s, void *data)
11211139
{
@@ -2877,6 +2895,42 @@ static int tegra_pmc_probe(struct platform_device *pdev)
28772895
pmc->clk = NULL;
28782896
}
28792897

2898+
/*
2899+
* PMC should be last resort for restarting since it soft-resets
2900+
* CPU without resetting everything else.
2901+
*/
2902+
err = devm_register_reboot_notifier(&pdev->dev,
2903+
&tegra_pmc_reboot_notifier);
2904+
if (err) {
2905+
dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
2906+
err);
2907+
return err;
2908+
}
2909+
2910+
err = devm_register_sys_off_handler(&pdev->dev,
2911+
SYS_OFF_MODE_RESTART,
2912+
SYS_OFF_PRIO_LOW,
2913+
tegra_pmc_restart_handler, NULL);
2914+
if (err) {
2915+
dev_err(&pdev->dev, "failed to register sys-off handler: %d\n",
2916+
err);
2917+
return err;
2918+
}
2919+
2920+
/*
2921+
* PMC should be primary power-off method if it soft-resets CPU,
2922+
* asking bootloader to shutdown hardware.
2923+
*/
2924+
err = devm_register_sys_off_handler(&pdev->dev,
2925+
SYS_OFF_MODE_POWER_OFF,
2926+
SYS_OFF_PRIO_FIRMWARE,
2927+
tegra_pmc_power_off_handler, NULL);
2928+
if (err) {
2929+
dev_err(&pdev->dev, "failed to register sys-off handler: %d\n",
2930+
err);
2931+
return err;
2932+
}
2933+
28802934
/*
28812935
* PCLK clock rate can't be retrieved using CLK API because it
28822936
* causes lockup if CPU enters LP2 idle state from some other
@@ -2908,28 +2962,13 @@ static int tegra_pmc_probe(struct platform_device *pdev)
29082962
goto cleanup_sysfs;
29092963
}
29102964

2911-
err = devm_register_reboot_notifier(&pdev->dev,
2912-
&tegra_pmc_reboot_notifier);
2913-
if (err) {
2914-
dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
2915-
err);
2916-
goto cleanup_debugfs;
2917-
}
2918-
2919-
err = register_restart_handler(&tegra_pmc_restart_handler);
2920-
if (err) {
2921-
dev_err(&pdev->dev, "unable to register restart handler, %d\n",
2922-
err);
2923-
goto cleanup_debugfs;
2924-
}
2925-
29262965
err = tegra_pmc_pinctrl_init(pmc);
29272966
if (err)
2928-
goto cleanup_restart_handler;
2967+
goto cleanup_debugfs;
29292968

29302969
err = tegra_pmc_regmap_init(pmc);
29312970
if (err < 0)
2932-
goto cleanup_restart_handler;
2971+
goto cleanup_debugfs;
29332972

29342973
err = tegra_powergate_init(pmc, pdev->dev.of_node);
29352974
if (err < 0)
@@ -2952,8 +2991,6 @@ static int tegra_pmc_probe(struct platform_device *pdev)
29522991

29532992
cleanup_powergates:
29542993
tegra_powergate_remove_all(pdev->dev.of_node);
2955-
cleanup_restart_handler:
2956-
unregister_restart_handler(&tegra_pmc_restart_handler);
29572994
cleanup_debugfs:
29582995
debugfs_remove(pmc->debugfs);
29592996
cleanup_sysfs:

0 commit comments

Comments
 (0)