bootcount_syscon.c 4.5 KB

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