Merge tag 'efi-2020-07-rc6' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
[oweals/u-boot.git] / drivers / bootcount / bootcount.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2010-2012
4  * Stefan Roese, DENX Software Engineering, sr@denx.de.
5  */
6
7 #include <bootcount.h>
8 #include <cpu_func.h>
9 #include <asm/cache.h>
10 #include <linux/compiler.h>
11
12 #if !defined(CONFIG_DM_BOOTCOUNT)
13 /* Now implement the generic default functions */
14 __weak void bootcount_store(ulong a)
15 {
16         void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR;
17         uintptr_t flush_start = rounddown(CONFIG_SYS_BOOTCOUNT_ADDR,
18                                           CONFIG_SYS_CACHELINE_SIZE);
19         uintptr_t flush_end;
20
21 #if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD)
22         raw_bootcount_store(reg, (CONFIG_SYS_BOOTCOUNT_MAGIC & 0xffff0000) | a);
23
24         flush_end = roundup(CONFIG_SYS_BOOTCOUNT_ADDR + 4,
25                             CONFIG_SYS_CACHELINE_SIZE);
26 #else
27         raw_bootcount_store(reg, a);
28         raw_bootcount_store(reg + 4, CONFIG_SYS_BOOTCOUNT_MAGIC);
29
30         flush_end = roundup(CONFIG_SYS_BOOTCOUNT_ADDR + 8,
31                             CONFIG_SYS_CACHELINE_SIZE);
32 #endif /* defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD */
33         flush_dcache_range(flush_start, flush_end);
34 }
35
36 __weak ulong bootcount_load(void)
37 {
38         void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR;
39
40 #if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD)
41         u32 tmp = raw_bootcount_load(reg);
42
43         if ((tmp & 0xffff0000) != (CONFIG_SYS_BOOTCOUNT_MAGIC & 0xffff0000))
44                 return 0;
45         else
46                 return (tmp & 0x0000ffff);
47 #else
48         if (raw_bootcount_load(reg + 4) != CONFIG_SYS_BOOTCOUNT_MAGIC)
49                 return 0;
50         else
51                 return raw_bootcount_load(reg);
52 #endif /* defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD) */
53 }
54 #else
55 #include <dm.h>
56
57 /*
58  * struct bootcount_mem_priv - private bootcount mem driver data
59  *
60  * @base: base address used for bootcounter
61  * @singleword: if true use only one 32 bit word for bootcounter
62  */
63 struct bootcount_mem_priv {
64         phys_addr_t base;
65         bool singleword;
66 };
67
68 static int bootcount_mem_get(struct udevice *dev, u32 *a)
69 {
70         struct bootcount_mem_priv *priv = dev_get_priv(dev);
71         void *reg = (void *)priv->base;
72         u32 magic = CONFIG_SYS_BOOTCOUNT_MAGIC;
73
74         if (priv->singleword) {
75                 u32 tmp = raw_bootcount_load(reg);
76
77                 if ((tmp & 0xffff0000) != (magic & 0xffff0000))
78                         return -ENODEV;
79
80                 *a = (tmp & 0x0000ffff);
81         } else {
82                 if (raw_bootcount_load(reg + 4) != magic)
83                         return -ENODEV;
84
85                 *a = raw_bootcount_load(reg);
86         }
87
88         return 0;
89 };
90
91 static int bootcount_mem_set(struct udevice *dev, const u32 a)
92 {
93         struct bootcount_mem_priv *priv = dev_get_priv(dev);
94         void *reg = (void *)priv->base;
95         u32 magic = CONFIG_SYS_BOOTCOUNT_MAGIC;
96         uintptr_t flush_start = rounddown(priv->base,
97                                           CONFIG_SYS_CACHELINE_SIZE);
98         uintptr_t flush_end;
99
100         if (priv->singleword) {
101                 raw_bootcount_store(reg, (magic & 0xffff0000) | a);
102                 flush_end = roundup(priv->base + 4,
103                                     CONFIG_SYS_CACHELINE_SIZE);
104         } else {
105                 raw_bootcount_store(reg, a);
106                 raw_bootcount_store(reg + 4, magic);
107                 flush_end = roundup(priv->base + 8,
108                                     CONFIG_SYS_CACHELINE_SIZE);
109         }
110         flush_dcache_range(flush_start, flush_end);
111
112         return 0;
113 };
114
115 static const struct bootcount_ops bootcount_mem_ops = {
116         .get = bootcount_mem_get,
117         .set = bootcount_mem_set,
118 };
119
120 static int bootcount_mem_probe(struct udevice *dev)
121 {
122         struct bootcount_mem_priv *priv = dev_get_priv(dev);
123
124         priv->base = (phys_addr_t)dev_read_addr(dev);
125         if (dev_read_bool(dev, "single-word"))
126                 priv->singleword = true;
127
128         return 0;
129 }
130
131 static const struct udevice_id bootcount_mem_ids[] = {
132         { .compatible = "u-boot,bootcount" },
133         { }
134 };
135
136 U_BOOT_DRIVER(bootcount_mem) = {
137         .name   = "bootcount-mem",
138         .id     = UCLASS_BOOTCOUNT,
139         .priv_auto_alloc_size = sizeof(struct bootcount_mem_priv),
140         .probe  = bootcount_mem_probe,
141         .of_match = bootcount_mem_ids,
142         .ops    = &bootcount_mem_ops,
143 };
144 #endif