Skip to content

Commit a3fe8f1

Browse files
committed
mfd: Add support for CPU power states reporting in the Flipper One MCU driver
Signed-off-by: Alexey Charkov <alchark@flipper.net>
1 parent 98cbe90 commit a3fe8f1

3 files changed

Lines changed: 121 additions & 4 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
What: /sys/bus/i2c/devices/<mcu_device>/cpustate
2+
Date: July 2026
3+
KernelVersion: 7.1
4+
Contact: Alexey Charkov <alchark@flipper.net>
5+
Description: (RW) Reports the CPU/system power state to the Flipper One MCU
6+
so it can present meaningful on-device feedback to the user.
7+
8+
Reading returns the last logical state name known to the driver.
9+
10+
Writing lets userspace announce transitions that the kernel
11+
cannot observe on its own. Only the following values are
12+
accepted; any other value returns -EINVAL:
13+
14+
- ``online``: boot is complete and userspace is up. This is a
15+
sticky state and is restored automatically after resuming
16+
from suspend.
17+
- ``suspend-request``: userspace is preparing to suspend (for
18+
example, running its pre-suspend hooks). Announced before the
19+
kernel actually enters suspend.
20+
- ``reboot-request``: userspace has begun a reboot and is
21+
stopping services. Announced before the kernel starts its
22+
own shutdown sequence.
23+
- ``poweroff-request``: userspace has begun a power-off and is
24+
stopping services. Announced before the kernel starts its
25+
own shutdown sequence.
26+
27+
The ``*-request`` states are transient announcements and are not
28+
restored after resume.
29+
30+
Other states (bootloader, kernel-init, suspend, shutting-down,
31+
powered-off) are managed by the kernel and cannot be written.
32+
33+
Format: %s.

drivers/mfd/flipper-one-mcu.c

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,87 @@ static const struct mfd_cell cells[] = {
118118
"flipper,one-typec"),
119119
};
120120

121+
static int fomcu_set_cpustate(struct fomcu_device *ddata,
122+
enum fomcu_cpu_states state)
123+
{
124+
int ret;
125+
126+
ret = regmap_write(ddata->regmap, FOMCU_REG_CPUSTATE, state);
127+
if (ret)
128+
return ret;
129+
130+
ddata->cpustate = state;
131+
return 0;
132+
}
133+
134+
static const char * const fomcu_state_names[FOMCU_CPUSTATE_NUM_STATES] = {
135+
[FOMCU_CPUSTATE_UNKNOWN] = "unknown",
136+
[FOMCU_CPUSTATE_BOOTLOADER] = "bootloader",
137+
[FOMCU_CPUSTATE_KERNEL_INIT] = "kernel-init",
138+
[FOMCU_CPUSTATE_ONLINE] = "online",
139+
[FOMCU_CPUSTATE_SUSPEND_REQ] = "suspend-request",
140+
[FOMCU_CPUSTATE_SUSPEND] = "suspend",
141+
[FOMCU_CPUSTATE_REBOOT_REQ] = "reboot-request",
142+
[FOMCU_CPUSTATE_POWEROFF_REQ] = "poweroff-request",
143+
[FOMCU_CPUSTATE_SHUTTING_DOWN] = "shutting-down",
144+
[FOMCU_CPUSTATE_POWERED_OFF] = "powered-off",
145+
};
146+
147+
/* States userspace is permitted to report through the cpustate sysfs file. */
148+
static const enum fomcu_cpu_states fomcu_user_writable_states[] = {
149+
FOMCU_CPUSTATE_ONLINE,
150+
FOMCU_CPUSTATE_SUSPEND_REQ,
151+
FOMCU_CPUSTATE_REBOOT_REQ,
152+
FOMCU_CPUSTATE_POWEROFF_REQ,
153+
};
154+
155+
static ssize_t cpustate_show(struct device *dev,
156+
struct device_attribute *attr, char *buf)
157+
{
158+
struct fomcu_device *ddata = dev_get_drvdata(dev);
159+
160+
return sysfs_emit(buf, "%s\n", fomcu_state_names[ddata->cpustate]);
161+
}
162+
163+
static ssize_t cpustate_store(struct device *dev,
164+
struct device_attribute *attr,
165+
const char *buf, size_t count)
166+
{
167+
struct fomcu_device *ddata = dev_get_drvdata(dev);
168+
enum fomcu_cpu_states state;
169+
int i, ret;
170+
171+
for (i = 0; i < ARRAY_SIZE(fomcu_user_writable_states); i++) {
172+
state = fomcu_user_writable_states[i];
173+
if (sysfs_streq(buf, fomcu_state_names[state]))
174+
break;
175+
}
176+
177+
if (i == ARRAY_SIZE(fomcu_user_writable_states))
178+
return -EINVAL;
179+
180+
/*
181+
* Only ONLINE is a sticky state that resume should restore. The
182+
* *-request states are transient announcements of an imminent
183+
* transition, so they must not become the resume restore-point
184+
* (otherwise a suspend-request/suspend/resume cycle would come back
185+
* as "suspend-request" instead of "online").
186+
*/
187+
if (state == FOMCU_CPUSTATE_ONLINE)
188+
ret = fomcu_set_cpustate(ddata, state);
189+
else
190+
ret = regmap_write(ddata->regmap, FOMCU_REG_CPUSTATE, state);
191+
192+
return ret ? : count;
193+
}
194+
static DEVICE_ATTR_RW(cpustate);
195+
196+
static struct attribute *fomcu_attrs[] = {
197+
&dev_attr_cpustate.attr,
198+
NULL,
199+
};
200+
ATTRIBUTE_GROUPS(fomcu);
201+
121202
static int fomcu_reboot_notify(struct notifier_block *nb,
122203
unsigned long action, void *data)
123204
{
@@ -198,8 +279,7 @@ static int fomcu_probe(struct i2c_client *client)
198279
* Signal that early kernel init has reached this driver. Userspace is
199280
* expected to move the MCU to FOMCU_CPUSTATE_ONLINE once boot completes.
200281
*/
201-
ddata->cpustate = FOMCU_CPUSTATE_KERNEL_INIT;
202-
return regmap_write(ddata->regmap, FOMCU_REG_CPUSTATE, ddata->cpustate);
282+
return fomcu_set_cpustate(ddata, FOMCU_CPUSTATE_KERNEL_INIT);
203283
}
204284

205285
static int fomcu_suspend(struct device *dev)
@@ -240,6 +320,7 @@ static struct i2c_driver fomcu_driver = {
240320
.name = "flipper-one-mcu",
241321
.of_match_table = fomcu_of_match,
242322
.pm = pm_sleep_ptr(&fomcu_pm_ops),
323+
.dev_groups = fomcu_groups,
243324
},
244325
.probe = fomcu_probe,
245326
.id_table = fomcu_i2c_ids,

include/linux/mfd/flipper-one-mcu.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ enum fomcu_cpu_states {
3131
FOMCU_CPUSTATE_BOOTLOADER, /* set by the MCU/bootloader itself */
3232
FOMCU_CPUSTATE_KERNEL_INIT, /* this driver has probed */
3333
FOMCU_CPUSTATE_ONLINE, /* userspace up / resumed from suspend */
34-
FOMCU_CPUSTATE_SUSPEND, /* about to suspend */
35-
FOMCU_CPUSTATE_SHUTTING_DOWN, /* reboot or power-off in progress */
34+
FOMCU_CPUSTATE_SUSPEND_REQ, /* userspace preparing to suspend */
35+
FOMCU_CPUSTATE_SUSPEND, /* kernel about to suspend */
36+
FOMCU_CPUSTATE_REBOOT_REQ, /* userspace preparing to reboot */
37+
FOMCU_CPUSTATE_POWEROFF_REQ, /* userspace preparing to power off */
38+
FOMCU_CPUSTATE_SHUTTING_DOWN, /* kernel reboot/power-off in progress */
3639
FOMCU_CPUSTATE_POWERED_OFF, /* safe to cut power to the PMIC */
3740
FOMCU_CPUSTATE_NUM_STATES
3841
};

0 commit comments

Comments
 (0)