Blame view
drivers/bootcount/bootcount_syscon.c
4.45 KB
c50b21b70 bootcount: add a ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Vaisala Oyj. All rights reserved. */ #include <common.h> #include <bootcount.h> #include <dm.h> #include <dm/device_compat.h> #include <linux/ioport.h> #include <regmap.h> #include <syscon.h> #define BYTES_TO_BITS(bytes) ((bytes) << 3) #define GEN_REG_MASK(val_size, val_addr) \ (GENMASK(BYTES_TO_BITS(val_size) - 1, 0) \ << (!!((val_addr) == 0x02) * BYTES_TO_BITS(2))) #define GET_DEFAULT_VALUE(val_size) \ (CONFIG_SYS_BOOTCOUNT_MAGIC >> \ (BYTES_TO_BITS((sizeof(u32) - (val_size))))) /** * struct bootcount_syscon_priv - driver's private data * * @regmap: syscon regmap * @reg_addr: register address used to store the bootcount value * @size: size of the bootcount value (2 or 4 bytes) * @magic: magic used to validate/save the bootcount value * @magic_mask: magic value bitmask * @reg_mask: mask used to identify the location of the bootcount value * in the register when 2 bytes length is used * @shift: value used to extract the botcount value from the register */ struct bootcount_syscon_priv { struct regmap *regmap; fdt_addr_t reg_addr; fdt_size_t size; u32 magic; u32 magic_mask; u32 reg_mask; int shift; }; static int bootcount_syscon_set(struct udevice *dev, const u32 val) { struct bootcount_syscon_priv *priv = dev_get_priv(dev); u32 regval; if ((val & priv->magic_mask) != 0) return -EINVAL; regval = (priv->magic & priv->magic_mask) | (val & ~priv->magic_mask); if (priv->size == 2) { regval &= 0xffff; regval |= (regval & 0xffff) << BYTES_TO_BITS(priv->size); } debug("%s: Prepare to write reg value: 0x%08x with register mask: 0x%08x ", __func__, regval, priv->reg_mask); return regmap_update_bits(priv->regmap, priv->reg_addr, priv->reg_mask, regval); } static int bootcount_syscon_get(struct udevice *dev, u32 *val) { struct bootcount_syscon_priv *priv = dev_get_priv(dev); u32 regval; int ret; ret = regmap_read(priv->regmap, priv->reg_addr, ®val); if (ret) return ret; regval &= priv->reg_mask; regval >>= priv->shift; if ((regval & priv->magic_mask) == (priv->magic & priv->magic_mask)) { *val = regval & ~priv->magic_mask; } else { dev_err(dev, "%s: Invalid bootcount magic ", __func__); return -EINVAL; } debug("%s: Read bootcount value: 0x%08x from regval: 0x%08x ", __func__, *val, regval); return 0; } static int bootcount_syscon_of_to_plat(struct udevice *dev) { struct bootcount_syscon_priv *priv = dev_get_priv(dev); fdt_addr_t bootcount_offset; fdt_size_t reg_size; priv->regmap = syscon_regmap_lookup_by_phandle(dev, "syscon"); if (IS_ERR(priv->regmap)) { dev_err(dev, "%s: Unable to find regmap (%ld) ", __func__, PTR_ERR(priv->regmap)); return PTR_ERR(priv->regmap); } priv->reg_addr = dev_read_addr_size_name(dev, "syscon_reg", ®_size); if (priv->reg_addr == FDT_ADDR_T_NONE) { dev_err(dev, "%s: syscon_reg address not found ", __func__); return -EINVAL; } if (reg_size != 4) { |
05ec89914 bootcount: fix pr... |
115 116 117 |
dev_err(dev, "%s: Unsupported register size: %pa ", __func__, ®_size); |
c50b21b70 bootcount: add a ... |
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
return -EINVAL; } bootcount_offset = dev_read_addr_size_name(dev, "offset", &priv->size); if (bootcount_offset == FDT_ADDR_T_NONE) { dev_err(dev, "%s: offset configuration not found ", __func__); return -EINVAL; } if (bootcount_offset + priv->size > reg_size) { dev_err(dev, "%s: Bootcount value doesn't fit in the reserved space ", __func__); return -EINVAL; } if (priv->size != 2 && priv->size != 4) { dev_err(dev, "%s: Driver supports only 2 and 4 bytes bootcount size ", __func__); return -EINVAL; } priv->magic = GET_DEFAULT_VALUE(priv->size); priv->magic_mask = GENMASK(BYTES_TO_BITS(priv->size) - 1, BYTES_TO_BITS(priv->size >> 1)); priv->shift = !!(bootcount_offset == 0x02) * BYTES_TO_BITS(priv->size); priv->reg_mask = GEN_REG_MASK(priv->size, bootcount_offset); return 0; } static const struct bootcount_ops bootcount_syscon_ops = { .get = bootcount_syscon_get, .set = bootcount_syscon_set, }; static const struct udevice_id bootcount_syscon_ids[] = { { .compatible = "u-boot,bootcount-syscon" }, {} }; U_BOOT_DRIVER(bootcount_syscon) = { .name = "bootcount-syscon", .id = UCLASS_BOOTCOUNT, .of_to_plat = bootcount_syscon_of_to_plat, .priv_auto = sizeof(struct bootcount_syscon_priv), .of_match = bootcount_syscon_ids, .ops = &bootcount_syscon_ops, }; |