1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2023
4  * Philip Richard Oberfichtner <pro@denx.de>
5  *
6  * Based on previous work from Heiko Schocher (legacy bootcount_i2c.c driver)
7  */
8 
9 #include <bootcount.h>
10 #include <dm.h>
11 #include <i2c.h>
12 
13 #define BC_MAGIC	0x55
14 
15 struct bootcount_i2c_priv {
16 	struct udevice *bcdev;
17 	unsigned int offset;
18 };
19 
bootcount_i2c_set(struct udevice * dev,const u32 val)20 static int bootcount_i2c_set(struct udevice *dev, const u32 val)
21 {
22 	int ret;
23 	struct bootcount_i2c_priv *priv = dev_get_priv(dev);
24 
25 	ret = dm_i2c_reg_write(priv->bcdev, priv->offset, BC_MAGIC);
26 	if (ret < 0)
27 		goto err_exit;
28 
29 	ret = dm_i2c_reg_write(priv->bcdev, priv->offset + 1, val & 0xff);
30 	if (ret < 0)
31 		goto err_exit;
32 
33 	return 0;
34 
35 err_exit:
36 	log_debug("%s: Error writing to I2C device (%d)\n", __func__, ret);
37 	return ret;
38 }
39 
bootcount_i2c_get(struct udevice * dev,u32 * val)40 static int bootcount_i2c_get(struct udevice *dev, u32 *val)
41 {
42 	int ret;
43 	struct bootcount_i2c_priv *priv = dev_get_priv(dev);
44 
45 	ret = dm_i2c_reg_read(priv->bcdev, priv->offset);
46 	if (ret < 0)
47 		goto err_exit;
48 
49 	if ((ret & 0xff) != BC_MAGIC) {
50 		log_debug("%s: Invalid Magic, reset bootcounter.\n", __func__);
51 		*val = 0;
52 		return bootcount_i2c_set(dev, 0);
53 	}
54 
55 	ret = dm_i2c_reg_read(priv->bcdev, priv->offset + 1);
56 	if (ret < 0)
57 		goto err_exit;
58 
59 	*val = ret;
60 	return 0;
61 
62 err_exit:
63 	log_debug("%s: Error reading from I2C device (%d)\n", __func__, ret);
64 	return ret;
65 }
66 
bootcount_i2c_probe(struct udevice * dev)67 static int bootcount_i2c_probe(struct udevice *dev)
68 {
69 	struct bootcount_i2c_priv *priv = dev_get_priv(dev);
70 	int ret;
71 
72 	ret = dev_read_u32(dev, "offset", &priv->offset);
73 	if (ret)
74 		goto exit;
75 
76 	ret = i2c_get_chip_by_phandle(dev, "i2cbcdev", &priv->bcdev);
77 
78 exit:
79 	if (ret)
80 		log_debug("%s failed, ret = %d\n", __func__, ret);
81 
82 	return ret;
83 }
84 
85 static const struct bootcount_ops bootcount_i2c_ops = {
86 	.get = bootcount_i2c_get,
87 	.set = bootcount_i2c_set,
88 };
89 
90 static const struct udevice_id bootcount_i2c_ids[] = {
91 	{ .compatible = "u-boot,bootcount-i2c" },
92 	{ }
93 };
94 
95 U_BOOT_DRIVER(bootcount_i2c) = {
96 	.name		= "bootcount-i2c",
97 	.id		= UCLASS_BOOTCOUNT,
98 	.priv_auto	= sizeof(struct bootcount_i2c_priv),
99 	.probe		= bootcount_i2c_probe,
100 	.of_match	= bootcount_i2c_ids,
101 	.ops		= &bootcount_i2c_ops,
102 };
103