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