261 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * CZ.NIC's Turris Omnia MCU system off and RTC wakeup driver4 *5 * This is not a true RTC driver (in the sense that it does not provide a6 * real-time clock), rather the MCU implements a wakeup from powered off state7 * at a specified time relative to MCU boot, and we expose this feature via RTC8 * alarm, so that it can be used via the rtcwake command, which is the standard9 * Linux command for this.10 *11 * 2024 by Marek Behún <kabel@kernel.org>12 */13 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>24 25#include <linux/turris-omnia-mcu-interface.h>26#include "turris-omnia-mcu.h"27 28static int omnia_get_uptime_wakeup(const struct i2c_client *client, u32 *uptime,29 u32 *wakeup)30{31 __le32 reply[2];32 int err;33 34 err = omnia_cmd_read(client, OMNIA_CMD_GET_UPTIME_AND_WAKEUP, reply,35 sizeof(reply));36 if (err)37 return err;38 39 if (uptime)40 *uptime = le32_to_cpu(reply[0]);41 42 if (wakeup)43 *wakeup = le32_to_cpu(reply[1]);44 45 return 0;46}47 48static int omnia_read_time(struct device *dev, struct rtc_time *tm)49{50 u32 uptime;51 int err;52 53 err = omnia_get_uptime_wakeup(to_i2c_client(dev), &uptime, NULL);54 if (err)55 return err;56 57 rtc_time64_to_tm(uptime, tm);58 59 return 0;60}61 62static int omnia_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)63{64 struct i2c_client *client = to_i2c_client(dev);65 struct omnia_mcu *mcu = i2c_get_clientdata(client);66 u32 wakeup;67 int err;68 69 err = omnia_get_uptime_wakeup(client, NULL, &wakeup);70 if (err)71 return err;72 73 alrm->enabled = !!wakeup;74 rtc_time64_to_tm(wakeup ?: mcu->rtc_alarm, &alrm->time);75 76 return 0;77}78 79static int omnia_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)80{81 struct i2c_client *client = to_i2c_client(dev);82 struct omnia_mcu *mcu = i2c_get_clientdata(client);83 84 mcu->rtc_alarm = rtc_tm_to_time64(&alrm->time);85 86 if (alrm->enabled)87 return omnia_cmd_write_u32(client, OMNIA_CMD_SET_WAKEUP,88 mcu->rtc_alarm);89 90 return 0;91}92 93static int omnia_alarm_irq_enable(struct device *dev, unsigned int enabled)94{95 struct i2c_client *client = to_i2c_client(dev);96 struct omnia_mcu *mcu = i2c_get_clientdata(client);97 98 return omnia_cmd_write_u32(client, OMNIA_CMD_SET_WAKEUP,99 enabled ? mcu->rtc_alarm : 0);100}101 102static 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,107};108 109static int omnia_power_off(struct sys_off_data *data)110{111 struct omnia_mcu *mcu = data->cb_data;112 __be32 tmp;113 u8 cmd[9];114 u16 arg;115 int err;116 117 if (mcu->front_button_poweron)118 arg = OMNIA_CMD_POWER_OFF_POWERON_BUTTON;119 else120 arg = 0;121 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]);125 126 /*127 * Although all values from and to MCU are passed in little-endian, the128 * MCU's CRC unit uses big-endian CRC32 polynomial (0x04c11db7), so we129 * need to use crc32_be() here.130 */131 tmp = cpu_to_be32(get_unaligned_le32(&cmd[1]));132 put_unaligned_le32(crc32_be(~0, (void *)&tmp, sizeof(tmp)), &cmd[5]);133 134 err = omnia_cmd_write(mcu->client, cmd, sizeof(cmd));135 if (err)136 dev_err(&mcu->client->dev,137 "Unable to send the poweroff command: %d\n", err);138 139 return NOTIFY_DONE;140}141 142static int omnia_restart(struct sys_off_data *data)143{144 struct omnia_mcu *mcu = data->cb_data;145 u8 cmd[3];146 int err;147 148 cmd[0] = OMNIA_CMD_GENERAL_CONTROL;149 150 if (reboot_mode == REBOOT_HARD)151 cmd[1] = cmd[2] = OMNIA_CTL_HARD_RST;152 else153 cmd[1] = cmd[2] = OMNIA_CTL_LIGHT_RST;154 155 err = omnia_cmd_write(mcu->client, cmd, sizeof(cmd));156 if (err)157 dev_err(&mcu->client->dev,158 "Unable to send the restart command: %d\n", err);159 160 /*161 * MCU needs a little bit to process the I2C command, otherwise it will162 * do a light reset based on SOC SYSRES_OUT pin.163 */164 mdelay(1);165 166 return NOTIFY_DONE;167}168 169static ssize_t front_button_poweron_show(struct device *dev,170 struct device_attribute *a, char *buf)171{172 struct omnia_mcu *mcu = dev_get_drvdata(dev);173 174 return sysfs_emit(buf, "%d\n", mcu->front_button_poweron);175}176 177static ssize_t front_button_poweron_store(struct device *dev,178 struct device_attribute *a,179 const char *buf, size_t count)180{181 struct omnia_mcu *mcu = dev_get_drvdata(dev);182 bool val;183 int err;184 185 err = kstrtobool(buf, &val);186 if (err)187 return err;188 189 mcu->front_button_poweron = val;190 191 return count;192}193static DEVICE_ATTR_RW(front_button_poweron);194 195static struct attribute *omnia_mcu_poweroff_attrs[] = {196 &dev_attr_front_button_poweron.attr,197 NULL198};199 200static umode_t poweroff_attrs_visible(struct kobject *kobj, struct attribute *a,201 int n)202{203 struct device *dev = kobj_to_dev(kobj);204 struct omnia_mcu *mcu = dev_get_drvdata(dev);205 206 if (mcu->features & OMNIA_FEAT_POWEROFF_WAKEUP)207 return a->mode;208 209 return 0;210}211 212const struct attribute_group omnia_mcu_poweroff_group = {213 .attrs = omnia_mcu_poweroff_attrs,214 .is_visible = poweroff_attrs_visible,215};216 217int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu)218{219 struct device *dev = &mcu->client->dev;220 int err;221 222 /* MCU restart is always available */223 err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART,224 SYS_OFF_PRIO_FIRMWARE,225 omnia_restart, mcu);226 if (err)227 return dev_err_probe(dev, err,228 "Cannot register system restart handler\n");229 230 /*231 * Poweroff and wakeup are available only if POWEROFF_WAKEUP feature is232 * present.233 */234 if (!(mcu->features & OMNIA_FEAT_POWEROFF_WAKEUP))235 return 0;236 237 err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF,238 SYS_OFF_PRIO_FIRMWARE,239 omnia_power_off, mcu);240 if (err)241 return dev_err_probe(dev, err,242 "Cannot register system power off handler\n");243 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");248 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);252 253 err = devm_rtc_register_device(mcu->rtcdev);254 if (err)255 return dev_err_probe(dev, err, "Cannot register RTC device\n");256 257 mcu->front_button_poweron = true;258 259 return 0;260}261