1 // SPDX-License-Identifier: GPL-2.0
3 * CZ.NIC's Turris Omnia MCU system off and RTC wakeup driver
5 * This is not a true RTC driver (in the sense that it does not provide a
6 * real-time clock), rather the MCU implements a wakeup from powered off state
7 * at a specified time relative to MCU boot, and we expose this feature via RTC
8 * alarm, so that it can be used via the rtcwake command, which is the standard
9 * Linux command for this.
11 * 2024 by Marek BehĂșn <kabel@kernel.org>
14 #include <linux/crc32.h>
15 #include <linux/delay.h>
16 #include <linux/device.h>
17 #include <linux/err.h>
18 #include <linux/i2c.h>
19 #include <linux/kstrtox.h>
20 #include <linux/reboot.h>
21 #include <linux/rtc.h>
22 #include <linux/sysfs.h>
23 #include <linux/types.h>
25 #include <linux/turris-omnia-mcu-interface.h>
26 #include "turris-omnia-mcu.h"
28 static int omnia_get_uptime_wakeup(const struct i2c_client
*client
, u32
*uptime
,
34 err
= omnia_cmd_read(client
, OMNIA_CMD_GET_UPTIME_AND_WAKEUP
, reply
,
40 *uptime
= le32_to_cpu(reply
[0]);
43 *wakeup
= le32_to_cpu(reply
[1]);
48 static int omnia_read_time(struct device
*dev
, struct rtc_time
*tm
)
53 err
= omnia_get_uptime_wakeup(to_i2c_client(dev
), &uptime
, NULL
);
57 rtc_time64_to_tm(uptime
, tm
);
62 static int omnia_read_alarm(struct device
*dev
, struct rtc_wkalrm
*alrm
)
64 struct i2c_client
*client
= to_i2c_client(dev
);
65 struct omnia_mcu
*mcu
= i2c_get_clientdata(client
);
69 err
= omnia_get_uptime_wakeup(client
, NULL
, &wakeup
);
73 alrm
->enabled
= !!wakeup
;
74 rtc_time64_to_tm(wakeup
?: mcu
->rtc_alarm
, &alrm
->time
);
79 static int omnia_set_alarm(struct device
*dev
, struct rtc_wkalrm
*alrm
)
81 struct i2c_client
*client
= to_i2c_client(dev
);
82 struct omnia_mcu
*mcu
= i2c_get_clientdata(client
);
84 mcu
->rtc_alarm
= rtc_tm_to_time64(&alrm
->time
);
87 return omnia_cmd_write_u32(client
, OMNIA_CMD_SET_WAKEUP
,
93 static int omnia_alarm_irq_enable(struct device
*dev
, unsigned int enabled
)
95 struct i2c_client
*client
= to_i2c_client(dev
);
96 struct omnia_mcu
*mcu
= i2c_get_clientdata(client
);
98 return omnia_cmd_write_u32(client
, OMNIA_CMD_SET_WAKEUP
,
99 enabled
? mcu
->rtc_alarm
: 0);
102 static const struct rtc_class_ops omnia_rtc_ops
= {
103 .read_time
= omnia_read_time
,
104 .read_alarm
= omnia_read_alarm
,
105 .set_alarm
= omnia_set_alarm
,
106 .alarm_irq_enable
= omnia_alarm_irq_enable
,
109 static int omnia_power_off(struct sys_off_data
*data
)
111 struct omnia_mcu
*mcu
= data
->cb_data
;
117 if (mcu
->front_button_poweron
)
118 arg
= OMNIA_CMD_POWER_OFF_POWERON_BUTTON
;
122 cmd
[0] = OMNIA_CMD_POWER_OFF
;
123 put_unaligned_le16(OMNIA_CMD_POWER_OFF_MAGIC
, &cmd
[1]);
124 put_unaligned_le16(arg
, &cmd
[3]);
127 * Although all values from and to MCU are passed in little-endian, the
128 * MCU's CRC unit uses big-endian CRC32 polynomial (0x04c11db7), so we
129 * need to use crc32_be() here.
131 tmp
= cpu_to_be32(get_unaligned_le32(&cmd
[1]));
132 put_unaligned_le32(crc32_be(~0, (void *)&tmp
, sizeof(tmp
)), &cmd
[5]);
134 err
= omnia_cmd_write(mcu
->client
, cmd
, sizeof(cmd
));
136 dev_err(&mcu
->client
->dev
,
137 "Unable to send the poweroff command: %d\n", err
);
142 static int omnia_restart(struct sys_off_data
*data
)
144 struct omnia_mcu
*mcu
= data
->cb_data
;
148 cmd
[0] = OMNIA_CMD_GENERAL_CONTROL
;
150 if (reboot_mode
== REBOOT_HARD
)
151 cmd
[1] = cmd
[2] = OMNIA_CTL_HARD_RST
;
153 cmd
[1] = cmd
[2] = OMNIA_CTL_LIGHT_RST
;
155 err
= omnia_cmd_write(mcu
->client
, cmd
, sizeof(cmd
));
157 dev_err(&mcu
->client
->dev
,
158 "Unable to send the restart command: %d\n", err
);
161 * MCU needs a little bit to process the I2C command, otherwise it will
162 * do a light reset based on SOC SYSRES_OUT pin.
169 static ssize_t
front_button_poweron_show(struct device
*dev
,
170 struct device_attribute
*a
, char *buf
)
172 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
174 return sysfs_emit(buf
, "%d\n", mcu
->front_button_poweron
);
177 static ssize_t
front_button_poweron_store(struct device
*dev
,
178 struct device_attribute
*a
,
179 const char *buf
, size_t count
)
181 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
185 err
= kstrtobool(buf
, &val
);
189 mcu
->front_button_poweron
= val
;
193 static DEVICE_ATTR_RW(front_button_poweron
);
195 static struct attribute
*omnia_mcu_poweroff_attrs
[] = {
196 &dev_attr_front_button_poweron
.attr
,
200 static umode_t
poweroff_attrs_visible(struct kobject
*kobj
, struct attribute
*a
,
203 struct device
*dev
= kobj_to_dev(kobj
);
204 struct omnia_mcu
*mcu
= dev_get_drvdata(dev
);
206 if (mcu
->features
& OMNIA_FEAT_POWEROFF_WAKEUP
)
212 const struct attribute_group omnia_mcu_poweroff_group
= {
213 .attrs
= omnia_mcu_poweroff_attrs
,
214 .is_visible
= poweroff_attrs_visible
,
217 int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu
*mcu
)
219 struct device
*dev
= &mcu
->client
->dev
;
222 /* MCU restart is always available */
223 err
= devm_register_sys_off_handler(dev
, SYS_OFF_MODE_RESTART
,
224 SYS_OFF_PRIO_FIRMWARE
,
227 return dev_err_probe(dev
, err
,
228 "Cannot register system restart handler\n");
231 * Poweroff and wakeup are available only if POWEROFF_WAKEUP feature is
234 if (!(mcu
->features
& OMNIA_FEAT_POWEROFF_WAKEUP
))
237 err
= devm_register_sys_off_handler(dev
, SYS_OFF_MODE_POWER_OFF
,
238 SYS_OFF_PRIO_FIRMWARE
,
239 omnia_power_off
, mcu
);
241 return dev_err_probe(dev
, err
,
242 "Cannot register system power off handler\n");
244 mcu
->rtcdev
= devm_rtc_allocate_device(dev
);
245 if (IS_ERR(mcu
->rtcdev
))
246 return dev_err_probe(dev
, PTR_ERR(mcu
->rtcdev
),
247 "Cannot allocate RTC device\n");
249 mcu
->rtcdev
->ops
= &omnia_rtc_ops
;
250 mcu
->rtcdev
->range_max
= U32_MAX
;
251 set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY
, mcu
->rtcdev
->features
);
253 err
= devm_rtc_register_device(mcu
->rtcdev
);
255 return dev_err_probe(dev
, err
, "Cannot register RTC device\n");
257 mcu
->front_button_poweron
= true;