bootcounter: add DM support for memory based bootcounter
[oweals/u-boot.git] / drivers / bootcount / bootcount.c
index 7a6d03dcca3b8268ddf84b70c8da588c96a22486..655dfaf59c48a3a1259978ed9cac3b2a007ca332 100644 (file)
@@ -8,6 +8,7 @@
 #include <cpu_func.h>
 #include <linux/compiler.h>
 
+#if !defined(CONFIG_DM_BOOTCOUNT)
 /* Now implement the generic default functions */
 __weak void bootcount_store(ulong a)
 {
@@ -49,3 +50,94 @@ __weak ulong bootcount_load(void)
                return raw_bootcount_load(reg);
 #endif /* defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD) */
 }
+#else
+#include <dm.h>
+
+/*
+ * struct bootcount_mem_priv - private bootcount mem driver data
+ *
+ * @base: base address used for bootcounter
+ * @singleword: if true use only one 32 bit word for bootcounter
+ */
+struct bootcount_mem_priv {
+       phys_addr_t base;
+       bool singleword;
+};
+
+static int bootcount_mem_get(struct udevice *dev, u32 *a)
+{
+       struct bootcount_mem_priv *priv = dev_get_priv(dev);
+       void *reg = (void *)priv->base;
+       u32 magic = CONFIG_SYS_BOOTCOUNT_MAGIC;
+
+       if (priv->singleword) {
+               u32 tmp = raw_bootcount_load(reg);
+
+               if ((tmp & 0xffff0000) != (magic & 0xffff0000))
+                       return -ENODEV;
+
+               *a = (tmp & 0x0000ffff);
+       } else {
+               if (raw_bootcount_load(reg + 4) != magic)
+                       return -ENODEV;
+
+               *a = raw_bootcount_load(reg);
+       }
+
+       return 0;
+};
+
+static int bootcount_mem_set(struct udevice *dev, const u32 a)
+{
+       struct bootcount_mem_priv *priv = dev_get_priv(dev);
+       void *reg = (void *)priv->base;
+       u32 magic = CONFIG_SYS_BOOTCOUNT_MAGIC;
+       uintptr_t flush_start = rounddown(priv->base,
+                                         CONFIG_SYS_CACHELINE_SIZE);
+       uintptr_t flush_end;
+
+       if (priv->singleword) {
+               raw_bootcount_store(reg, (magic & 0xffff0000) | a);
+               flush_end = roundup(priv->base + 4,
+                                   CONFIG_SYS_CACHELINE_SIZE);
+       } else {
+               raw_bootcount_store(reg, a);
+               raw_bootcount_store(reg + 4, magic);
+               flush_end = roundup(priv->base + 8,
+                                   CONFIG_SYS_CACHELINE_SIZE);
+       }
+       flush_dcache_range(flush_start, flush_end);
+
+       return 0;
+};
+
+static const struct bootcount_ops bootcount_mem_ops = {
+       .get = bootcount_mem_get,
+       .set = bootcount_mem_set,
+};
+
+static int bootcount_mem_probe(struct udevice *dev)
+{
+       struct bootcount_mem_priv *priv = dev_get_priv(dev);
+
+       priv->base = (phys_addr_t)dev_read_addr(dev);
+       if (dev_read_bool(dev, "single-word"))
+               priv->singleword = true;
+
+       return 0;
+}
+
+static const struct udevice_id bootcount_mem_ids[] = {
+       { .compatible = "u-boot,bootcount" },
+       { }
+};
+
+U_BOOT_DRIVER(bootcount_mem) = {
+       .name   = "bootcount-mem",
+       .id     = UCLASS_BOOTCOUNT,
+       .priv_auto_alloc_size = sizeof(struct bootcount_mem_priv),
+       .probe  = bootcount_mem_probe,
+       .of_match = bootcount_mem_ids,
+       .ops    = &bootcount_mem_ops,
+};
+#endif