1010#include <linux/mfd/core.h>
1111#include <linux/mfd/flipper-one-mcu.h>
1212#include <linux/module.h>
13+ #include <linux/pm.h>
14+ #include <linux/reboot.h>
1315#include <linux/regmap.h>
1416
1517static const struct regmap_range fomcu_writeable_reg_ranges [] = {
18+ regmap_reg_range (FOMCU_REG_CPUSTATE , FOMCU_REG_CPUSTATE ),
1619 regmap_reg_range (FOMCU_REG_INTMSK_INPUT ,
1720 FOMCU_REG_INPUT_BTNS - 1 ),
1821 regmap_reg_range (FOMCU_REG_LEDS_BR_LINK ,
@@ -115,6 +118,34 @@ static const struct mfd_cell cells[] = {
115118 "flipper,one-typec" ),
116119};
117120
121+ static int fomcu_reboot_notify (struct notifier_block * nb ,
122+ unsigned long action , void * data )
123+ {
124+ struct fomcu_device * ddata =
125+ container_of (nb , struct fomcu_device , reboot_nb );
126+
127+ /* Runs before device_shutdown() for both reboot and power-off */
128+ regmap_write (ddata -> regmap , FOMCU_REG_CPUSTATE ,
129+ FOMCU_CPUSTATE_SHUTTING_DOWN );
130+
131+ return NOTIFY_DONE ;
132+ }
133+
134+ static int fomcu_power_off (struct sys_off_data * data )
135+ {
136+ struct fomcu_device * ddata = data -> cb_data ;
137+
138+ /*
139+ * Runs after device_shutdown(), just before the machine is actually
140+ * powered off. Only reached on power-off, not on reboot, so this is
141+ * where we tell the MCU it may cut power to the PMIC.
142+ */
143+ regmap_write (ddata -> regmap , FOMCU_REG_CPUSTATE ,
144+ FOMCU_CPUSTATE_POWERED_OFF );
145+
146+ return NOTIFY_DONE ;
147+ }
148+
118149static int fomcu_probe (struct i2c_client * client )
119150{
120151 struct regmap_irq_chip_data * irq_data ;
@@ -149,9 +180,49 @@ static int fomcu_probe(struct i2c_client *client)
149180 return dev_err_probe (& client -> dev , ret ,
150181 "Failed to register child devices\n" );
151182
152- return ret ;
183+ ddata -> reboot_nb .notifier_call = fomcu_reboot_notify ;
184+ ret = devm_register_reboot_notifier (& client -> dev , & ddata -> reboot_nb );
185+ if (ret )
186+ return dev_err_probe (& client -> dev , ret ,
187+ "Failed to register reboot notifier\n" );
188+
189+ ret = devm_register_sys_off_handler (& client -> dev ,
190+ SYS_OFF_MODE_POWER_OFF_PREPARE ,
191+ SYS_OFF_PRIO_DEFAULT ,
192+ fomcu_power_off , ddata );
193+ if (ret )
194+ return dev_err_probe (& client -> dev , ret ,
195+ "Failed to register power-off handler\n" );
196+
197+ /*
198+ * Signal that early kernel init has reached this driver. Userspace is
199+ * expected to move the MCU to FOMCU_CPUSTATE_ONLINE once boot completes.
200+ */
201+ ddata -> cpustate = FOMCU_CPUSTATE_KERNEL_INIT ;
202+ return regmap_write (ddata -> regmap , FOMCU_REG_CPUSTATE , ddata -> cpustate );
203+ }
204+
205+ static int fomcu_suspend (struct device * dev )
206+ {
207+ struct fomcu_device * ddata = dev_get_drvdata (dev );
208+
209+ /*
210+ * Leave ddata->cpustate untouched so that resume can restore whatever
211+ * state we were in before suspending (KERNEL_INIT if userspace had not
212+ * come up yet, ONLINE otherwise).
213+ */
214+ return regmap_write (ddata -> regmap , FOMCU_REG_CPUSTATE , FOMCU_CPUSTATE_SUSPEND );
153215}
154216
217+ static int fomcu_resume (struct device * dev )
218+ {
219+ struct fomcu_device * ddata = dev_get_drvdata (dev );
220+
221+ return regmap_write (ddata -> regmap , FOMCU_REG_CPUSTATE , ddata -> cpustate );
222+ }
223+
224+ static DEFINE_SIMPLE_DEV_PM_OPS (fomcu_pm_ops , fomcu_suspend , fomcu_resume ) ;
225+
155226static const struct i2c_device_id fomcu_i2c_ids [] = {
156227 { "flipper-one-mcu" },
157228 {}
@@ -168,6 +239,7 @@ static struct i2c_driver fomcu_driver = {
168239 .driver = {
169240 .name = "flipper-one-mcu" ,
170241 .of_match_table = fomcu_of_match ,
242+ .pm = pm_sleep_ptr (& fomcu_pm_ops ),
171243 },
172244 .probe = fomcu_probe ,
173245 .id_table = fomcu_i2c_ids ,
0 commit comments