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