blob: 5a2bd1f9f72c8c2bf67b61eac55a33e207fc16ea [file] [log] [blame]
Sean Anderson77c66292022-05-05 13:11:39 -04001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
4 */
5
6#include <common.h>
7#include <i2c_eeprom.h>
8#include <linker_lists.h>
9#include <misc.h>
10#include <nvmem.h>
11#include <rtc.h>
12#include <dm/device_compat.h>
13#include <dm/ofnode.h>
14#include <dm/read.h>
15#include <dm/uclass.h>
16
17int nvmem_cell_read(struct nvmem_cell *cell, void *buf, size_t size)
18{
19 dev_dbg(cell->nvmem, "%s: off=%u size=%zu\n", __func__, cell->offset, size);
20 if (size != cell->size)
21 return -EINVAL;
22
23 switch (cell->nvmem->driver->id) {
24 case UCLASS_I2C_EEPROM:
25 return i2c_eeprom_read(cell->nvmem, cell->offset, buf, size);
26 case UCLASS_MISC: {
27 int ret = misc_read(cell->nvmem, cell->offset, buf, size);
28
29 if (ret < 0)
30 return ret;
31 if (ret != size)
32 return -EIO;
33 return 0;
34 }
35 case UCLASS_RTC:
36 return dm_rtc_read(cell->nvmem, cell->offset, buf, size);
37 default:
38 return -ENOSYS;
39 }
40}
41
42int nvmem_cell_write(struct nvmem_cell *cell, const void *buf, size_t size)
43{
44 dev_dbg(cell->nvmem, "%s: off=%u size=%zu\n", __func__, cell->offset, size);
45 if (size != cell->size)
46 return -EINVAL;
47
48 switch (cell->nvmem->driver->id) {
49 case UCLASS_I2C_EEPROM:
50 return i2c_eeprom_write(cell->nvmem, cell->offset, buf, size);
51 case UCLASS_MISC: {
52 int ret = misc_write(cell->nvmem, cell->offset, buf, size);
53
54 if (ret < 0)
55 return ret;
56 if (ret != size)
57 return -EIO;
58 return 0;
59 }
60 case UCLASS_RTC:
61 return dm_rtc_write(cell->nvmem, cell->offset, buf, size);
62 default:
63 return -ENOSYS;
64 }
65}
66
67/**
68 * nvmem_get_device() - Get an nvmem device for a cell
69 * @node: ofnode of the nvmem device
70 * @cell: Cell to look up
71 *
72 * Try to find a nvmem-compatible device by going through the nvmem interfaces.
73 *
74 * Return:
75 * * 0 on success
76 * * -ENODEV if we didn't find anything
77 * * A negative error if there was a problem looking up the device
78 */
79static int nvmem_get_device(ofnode node, struct nvmem_cell *cell)
80{
81 int i, ret;
82 enum uclass_id ids[] = {
83 UCLASS_I2C_EEPROM,
84 UCLASS_MISC,
85 UCLASS_RTC,
86 };
87
88 for (i = 0; i < ARRAY_SIZE(ids); i++) {
89 ret = uclass_get_device_by_ofnode(ids[i], node, &cell->nvmem);
90 if (!ret)
91 return 0;
92 if (ret != -ENODEV && ret != -EPFNOSUPPORT)
93 return ret;
94 }
95
96 return -ENODEV;
97}
98
99int nvmem_cell_get_by_index(struct udevice *dev, int index,
100 struct nvmem_cell *cell)
101{
102 fdt_addr_t offset;
103 fdt_size_t size = FDT_SIZE_T_NONE;
104 int ret;
105 struct ofnode_phandle_args args;
106
107 dev_dbg(dev, "%s: index=%d\n", __func__, index);
108
109 ret = dev_read_phandle_with_args(dev, "nvmem-cells", NULL, 0, index,
110 &args);
111 if (ret)
112 return ret;
113
114 ret = nvmem_get_device(ofnode_get_parent(args.node), cell);
115 if (ret)
116 return ret;
117
118 offset = ofnode_get_addr_size_index_notrans(args.node, 0, &size);
119 if (offset == FDT_ADDR_T_NONE || size == FDT_SIZE_T_NONE) {
120 dev_dbg(cell->nvmem, "missing address or size for %s\n",
121 ofnode_get_name(args.node));
122 return -EINVAL;
123 }
124
125 cell->offset = offset;
126 cell->size = size;
127 return 0;
128}
129
130int nvmem_cell_get_by_name(struct udevice *dev, const char *name,
131 struct nvmem_cell *cell)
132{
133 int index;
134
135 dev_dbg(dev, "%s, name=%s\n", __func__, name);
136
137 index = dev_read_stringlist_search(dev, "nvmem-cell-names", name);
138 if (index < 0)
139 return index;
140
141 return nvmem_cell_get_by_index(dev, index, cell);
142}