blob: 2b82d89c7dd8338a572819a22ea97d0f7f7a2280 [file] [log] [blame]
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (C) 2024 MediaTek Inc. All rights reserved.
*
* Helper for resetting boot count of A/B boot systems
*
* Author: Weijie Gao <weijie.gao@mediatek.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/arm-smccc.h>
#define RBC "reset_boot_count"
#define MTK_SIP_READ_NONRST_REG 0xC2000550
#define MTK_SIP_WRITE_NONRST_REG 0xC2000551
static struct proc_dir_entry *rbc_entry;
static bool dual_boot_get_boot_count(u32 *retslot, u32 *retcnt)
{
struct arm_smccc_res res = {0};
u32 val, slot;
s8 neg, pos;
arm_smccc_smc(MTK_SIP_READ_NONRST_REG, 0, 0, 0, 0, 0, 0, 0, &res);
val = (u32)res.a0;
/* slot: val[31..24] = -slot, val[23..16] = slot */
pos = (val >> 16) & 0xff;
neg = (val >> 24) & 0xff;
if (!(pos >= 0 && neg <= 0 && pos + neg == 0)) {
pr_debug("slot of boot count is invalid\n");
goto err;
}
slot = pos;
/* count: val[15..8] = -count, val[7..0] = count */
pos = val & 0xff;
neg = (val >> 8) & 0xff;
if (!(pos >= 0 && neg <= 0 && pos + neg == 0)) {
pr_debug("count of boot count is invalid\n");
goto err;
}
if (retslot)
*retslot = slot;
if (retcnt)
*retcnt = pos;
return true;
err:
if (retslot)
*retslot = 0;
if (retcnt)
*retcnt = 0;
return false;
}
static void dual_boot_set_boot_count(u32 slot, u32 count)
{
struct arm_smccc_res res = {0};
u32 val;
s32 neg;
if (slot > 127 || count > 127)
return;
neg = -count;
val = count | ((neg << 8) & 0xff00);
neg = -slot;
val = val | ((uint32_t)slot << 16) | ((neg << 24) & 0xff000000);
arm_smccc_smc(MTK_SIP_WRITE_NONRST_REG, 0, val, 0, 0, 0, 0, 0, &res);
}
static int rbc_display(struct seq_file *seq, void *v)
{
return 0;
}
static int rbc_open(struct inode *inode, struct file *file)
{
return single_open(file, rbc_display, inode->i_private);
}
static ssize_t rbc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
u32 slot;
dual_boot_get_boot_count(&slot, NULL);
dual_boot_set_boot_count(slot, 0);
pr_info("Boot count reset\n");
return count;
}
static const struct file_operations rbc_fops =
{
.open = rbc_open,
.read = seq_read,
.write = rbc_write,
.llseek = seq_lseek,
.release = single_release,
};
static int __init rbc_init(void)
{
rbc_entry = proc_create(RBC, 0200, NULL, &rbc_fops);
if (!rbc_entry)
pr_err("failed to create proc entry " RBC);
return 0;
}
static void __exit rbc_exit(void)
{
remove_proc_entry(RBC, NULL);
}
module_init(rbc_init);
module_exit(rbc_exit);
MODULE_AUTHOR("Weijie Gao <weijie.gao@mediatek.com>");
MODULE_DESCRIPTION("Kernel module for resetting boot count of A/B boot systems");
MODULE_LICENSE("BSD");