1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2019 Collabora
4 * (C) Copyright 2019 GE
5 */
6
7#include <common.h>
8#include <bootcount.h>
9#include <dm.h>
10#include <spi_flash.h>
11
12static const u8 bootcount_magic = 0xbc;
13
14struct bootcount_spi_flash_priv {
15	struct udevice *spi_flash;
16	u32 offset;
17};
18
19static int bootcount_spi_flash_update(struct udevice *dev, u32 offset, u32 len, const void *buf)
20{
21	struct spi_flash *flash = dev_get_uclass_priv(dev);
22	u32 sector_size = flash->sector_size;
23	u32 sector_offset = offset % sector_size;
24	u32 sector = offset - sector_offset;
25	int err = 0;
26
27	/* code only supports updating a single sector */
28	if (sector_offset + len > sector_size)
29		return -ENOSYS;
30
31	u8 *buffer = malloc(sector_size);
32	if (!buffer)
33		return -ENOMEM;
34
35	err = spi_flash_read_dm(dev, sector, sector_size, buffer);
36	if (err < 0)
37		goto out;
38
39	memcpy(buffer + sector_offset, buf, len);
40
41	err = spi_flash_erase_dm(dev, sector, sector_size);
42	if (err < 0)
43		goto out;
44
45	err = spi_flash_write_dm(dev, sector, sector_size, buffer);
46	if (err < 0)
47		goto out;
48
49out:
50	free(buffer);
51	return err;
52}
53
54static int bootcount_spi_flash_set(struct udevice *dev, const u32 a)
55{
56	struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
57	const u16 val = bootcount_magic << 8 | (a & 0xff);
58
59	if (bootcount_spi_flash_update(priv->spi_flash, priv->offset, 2, &val) < 0) {
60		debug("%s: write failed\n", __func__);
61		return -EIO;
62	}
63
64	return 0;
65}
66
67static int bootcount_spi_flash_get(struct udevice *dev, u32 *a)
68{
69	struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
70	u16 val;
71
72	if (spi_flash_read_dm(priv->spi_flash, priv->offset, 2, &val) < 0) {
73		debug("%s: read failed\n", __func__);
74		return -EIO;
75	}
76
77	if (val >> 8 == bootcount_magic) {
78		*a = val & 0xff;
79		return 0;
80	}
81
82	debug("%s: bootcount magic does not match on %04x\n", __func__, val);
83	return -EIO;
84}
85
86static int bootcount_spi_flash_probe(struct udevice *dev)
87{
88	struct ofnode_phandle_args phandle_args;
89	struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
90	struct udevice *spi_flash;
91
92	if (dev_read_phandle_with_args(dev, "spi-flash", NULL, 0, 0, &phandle_args)) {
93		debug("%s: spi-flash backing device not specified\n", dev->name);
94		return -ENOENT;
95	}
96
97	if (uclass_get_device_by_ofnode(UCLASS_SPI_FLASH, phandle_args.node, &spi_flash)) {
98		debug("%s: could not get backing device\n", dev->name);
99		return -ENODEV;
100	}
101
102	priv->spi_flash = spi_flash;
103	priv->offset = dev_read_u32_default(dev, "offset", 0);
104
105	return 0;
106}
107
108static const struct bootcount_ops bootcount_spi_flash_ops = {
109	.get = bootcount_spi_flash_get,
110	.set = bootcount_spi_flash_set,
111};
112
113static const struct udevice_id bootcount_spi_flash_ids[] = {
114	{ .compatible = "u-boot,bootcount-spi-flash" },
115	{ }
116};
117
118U_BOOT_DRIVER(bootcount_spi_flash) = {
119	.name	= "bootcount-spi-flash",
120	.id	= UCLASS_BOOTCOUNT,
121	.priv_auto	= sizeof(struct bootcount_spi_flash_priv),
122	.probe	= bootcount_spi_flash_probe,
123	.of_match = bootcount_spi_flash_ids,
124	.ops	= &bootcount_spi_flash_ops,
125};
126