Commit 65b60897a700e63af02ba46249de60ed3f79ca41
Committed by
Tom Rini
1 parent
586d4b010e
Exists in
smarc_8mq_lf_v2020.04
and in
13 other branches
w1: fix data abort if no one wire bus master present
When the "w1 bus" command is used with no bus master present a data abort may occur. This is because uclass_first_device() returns zero, but sets the output struct udevice pointer to NULL in the no device found case. Fix w1_get_bus() to account for this and return an error code as is expected by the callers. Signed-off-by: Martin Fuzzey <martin.fuzzey@flowbird.group> Reviewed-by: Eugen Hristev <eugen.hristev@microchip.com>
Showing 1 changed file with 8 additions and 6 deletions Inline Diff
drivers/w1/w1-uclass.c
1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | 2 | /* |
3 | * | 3 | * |
4 | * Copyright (c) 2015 Free Electrons | 4 | * Copyright (c) 2015 Free Electrons |
5 | * Copyright (c) 2015 NextThing Co. | 5 | * Copyright (c) 2015 NextThing Co. |
6 | * Copyright (c) 2018 Microchip Technology, Inc. | 6 | * Copyright (c) 2018 Microchip Technology, Inc. |
7 | * | 7 | * |
8 | * Maxime Ripard <maxime.ripard@free-electrons.com> | 8 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
9 | * Eugen Hristev <eugen.hristev@microchip.com> | 9 | * Eugen Hristev <eugen.hristev@microchip.com> |
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <common.h> | 13 | #include <common.h> |
14 | #include <dm.h> | 14 | #include <dm.h> |
15 | #include <w1.h> | 15 | #include <w1.h> |
16 | #include <w1-eeprom.h> | 16 | #include <w1-eeprom.h> |
17 | 17 | ||
18 | #include <dm/device-internal.h> | 18 | #include <dm/device-internal.h> |
19 | 19 | ||
20 | #define W1_MATCH_ROM 0x55 | 20 | #define W1_MATCH_ROM 0x55 |
21 | #define W1_SKIP_ROM 0xcc | 21 | #define W1_SKIP_ROM 0xcc |
22 | #define W1_SEARCH 0xf0 | 22 | #define W1_SEARCH 0xf0 |
23 | 23 | ||
24 | struct w1_bus { | 24 | struct w1_bus { |
25 | u64 search_id; | 25 | u64 search_id; |
26 | }; | 26 | }; |
27 | 27 | ||
28 | static int w1_enumerate(struct udevice *bus) | 28 | static int w1_enumerate(struct udevice *bus) |
29 | { | 29 | { |
30 | const struct w1_ops *ops = device_get_ops(bus); | 30 | const struct w1_ops *ops = device_get_ops(bus); |
31 | struct w1_bus *w1 = dev_get_uclass_priv(bus); | 31 | struct w1_bus *w1 = dev_get_uclass_priv(bus); |
32 | u64 last_rn, rn = w1->search_id, tmp64; | 32 | u64 last_rn, rn = w1->search_id, tmp64; |
33 | bool last_device = false; | 33 | bool last_device = false; |
34 | int search_bit, desc_bit = 64; | 34 | int search_bit, desc_bit = 64; |
35 | int last_zero = -1; | 35 | int last_zero = -1; |
36 | u8 triplet_ret = 0; | 36 | u8 triplet_ret = 0; |
37 | int i; | 37 | int i; |
38 | 38 | ||
39 | if (!ops->reset || !ops->write_byte || !ops->triplet) | 39 | if (!ops->reset || !ops->write_byte || !ops->triplet) |
40 | return -ENOSYS; | 40 | return -ENOSYS; |
41 | 41 | ||
42 | while (!last_device) { | 42 | while (!last_device) { |
43 | last_rn = rn; | 43 | last_rn = rn; |
44 | rn = 0; | 44 | rn = 0; |
45 | 45 | ||
46 | /* | 46 | /* |
47 | * Reset bus and all 1-wire device state machines | 47 | * Reset bus and all 1-wire device state machines |
48 | * so they can respond to our requests. | 48 | * so they can respond to our requests. |
49 | * | 49 | * |
50 | * Return 0 - device(s) present, 1 - no devices present. | 50 | * Return 0 - device(s) present, 1 - no devices present. |
51 | */ | 51 | */ |
52 | if (ops->reset(bus)) { | 52 | if (ops->reset(bus)) { |
53 | debug("%s: No devices present on the wire.\n", | 53 | debug("%s: No devices present on the wire.\n", |
54 | __func__); | 54 | __func__); |
55 | break; | 55 | break; |
56 | } | 56 | } |
57 | 57 | ||
58 | /* Start the search */ | 58 | /* Start the search */ |
59 | ops->write_byte(bus, W1_SEARCH); | 59 | ops->write_byte(bus, W1_SEARCH); |
60 | for (i = 0; i < 64; ++i) { | 60 | for (i = 0; i < 64; ++i) { |
61 | /* Determine the direction/search bit */ | 61 | /* Determine the direction/search bit */ |
62 | if (i == desc_bit) | 62 | if (i == desc_bit) |
63 | /* took the 0 path last time, so take the 1 path */ | 63 | /* took the 0 path last time, so take the 1 path */ |
64 | search_bit = 1; | 64 | search_bit = 1; |
65 | else if (i > desc_bit) | 65 | else if (i > desc_bit) |
66 | /* take the 0 path on the next branch */ | 66 | /* take the 0 path on the next branch */ |
67 | search_bit = 0; | 67 | search_bit = 0; |
68 | else | 68 | else |
69 | search_bit = ((last_rn >> i) & 0x1); | 69 | search_bit = ((last_rn >> i) & 0x1); |
70 | 70 | ||
71 | /* Read two bits and write one bit */ | 71 | /* Read two bits and write one bit */ |
72 | triplet_ret = ops->triplet(bus, search_bit); | 72 | triplet_ret = ops->triplet(bus, search_bit); |
73 | 73 | ||
74 | /* quit if no device responded */ | 74 | /* quit if no device responded */ |
75 | if ((triplet_ret & 0x03) == 0x03) | 75 | if ((triplet_ret & 0x03) == 0x03) |
76 | break; | 76 | break; |
77 | 77 | ||
78 | /* If both directions were valid, and we took the 0 path... */ | 78 | /* If both directions were valid, and we took the 0 path... */ |
79 | if (triplet_ret == 0) | 79 | if (triplet_ret == 0) |
80 | last_zero = i; | 80 | last_zero = i; |
81 | 81 | ||
82 | /* extract the direction taken & update the device number */ | 82 | /* extract the direction taken & update the device number */ |
83 | tmp64 = (triplet_ret >> 2); | 83 | tmp64 = (triplet_ret >> 2); |
84 | rn |= (tmp64 << i); | 84 | rn |= (tmp64 << i); |
85 | } | 85 | } |
86 | 86 | ||
87 | /* last device or error, aborting here */ | 87 | /* last device or error, aborting here */ |
88 | if ((triplet_ret & 0x03) == 0x03) | 88 | if ((triplet_ret & 0x03) == 0x03) |
89 | last_device = true; | 89 | last_device = true; |
90 | 90 | ||
91 | if ((triplet_ret & 0x03) != 0x03) { | 91 | if ((triplet_ret & 0x03) != 0x03) { |
92 | if (desc_bit == last_zero || last_zero < 0) { | 92 | if (desc_bit == last_zero || last_zero < 0) { |
93 | last_device = 1; | 93 | last_device = 1; |
94 | w1->search_id = 0; | 94 | w1->search_id = 0; |
95 | } else { | 95 | } else { |
96 | w1->search_id = rn; | 96 | w1->search_id = rn; |
97 | } | 97 | } |
98 | desc_bit = last_zero; | 98 | desc_bit = last_zero; |
99 | 99 | ||
100 | debug("%s: Detected new device 0x%llx (family 0x%x)\n", | 100 | debug("%s: Detected new device 0x%llx (family 0x%x)\n", |
101 | bus->name, rn, (u8)(rn & 0xff)); | 101 | bus->name, rn, (u8)(rn & 0xff)); |
102 | 102 | ||
103 | /* attempt to register as w1-eeprom device */ | 103 | /* attempt to register as w1-eeprom device */ |
104 | w1_eeprom_register_new_device(rn); | 104 | w1_eeprom_register_new_device(rn); |
105 | } | 105 | } |
106 | } | 106 | } |
107 | 107 | ||
108 | return 0; | 108 | return 0; |
109 | } | 109 | } |
110 | 110 | ||
111 | int w1_get_bus(int busnum, struct udevice **busp) | 111 | int w1_get_bus(int busnum, struct udevice **busp) |
112 | { | 112 | { |
113 | int ret, i = 0; | 113 | int ret, i = 0; |
114 | 114 | ||
115 | struct udevice *dev; | 115 | struct udevice *dev; |
116 | 116 | ||
117 | for (ret = uclass_first_device(UCLASS_W1, &dev); | 117 | for (ret = uclass_first_device(UCLASS_W1, &dev); |
118 | !ret; | 118 | dev && !ret; |
119 | uclass_next_device(&dev), i++) { | 119 | ret = uclass_next_device(&dev), i++) { |
120 | if (ret) { | ||
121 | debug("Cannot find w1 bus %d\n", busnum); | ||
122 | return ret; | ||
123 | } | ||
124 | if (i == busnum) { | 120 | if (i == busnum) { |
125 | *busp = dev; | 121 | *busp = dev; |
126 | return 0; | 122 | return 0; |
127 | } | 123 | } |
128 | } | 124 | } |
125 | |||
126 | if (!ret) { | ||
127 | debug("Cannot find w1 bus %d\n", busnum); | ||
128 | ret = -ENODEV; | ||
129 | } | ||
130 | |||
129 | return ret; | 131 | return ret; |
130 | } | 132 | } |
131 | 133 | ||
132 | u8 w1_get_device_family(struct udevice *dev) | 134 | u8 w1_get_device_family(struct udevice *dev) |
133 | { | 135 | { |
134 | struct w1_device *w1 = dev_get_parent_platdata(dev); | 136 | struct w1_device *w1 = dev_get_parent_platdata(dev); |
135 | 137 | ||
136 | return w1->id & 0xff; | 138 | return w1->id & 0xff; |
137 | } | 139 | } |
138 | 140 | ||
139 | int w1_reset_select(struct udevice *dev) | 141 | int w1_reset_select(struct udevice *dev) |
140 | { | 142 | { |
141 | struct w1_device *w1 = dev_get_parent_platdata(dev); | 143 | struct w1_device *w1 = dev_get_parent_platdata(dev); |
142 | struct udevice *bus = dev_get_parent(dev); | 144 | struct udevice *bus = dev_get_parent(dev); |
143 | const struct w1_ops *ops = device_get_ops(bus); | 145 | const struct w1_ops *ops = device_get_ops(bus); |
144 | int i; | 146 | int i; |
145 | 147 | ||
146 | if (!ops->reset || !ops->write_byte) | 148 | if (!ops->reset || !ops->write_byte) |
147 | return -ENOSYS; | 149 | return -ENOSYS; |
148 | 150 | ||
149 | ops->reset(bus); | 151 | ops->reset(bus); |
150 | 152 | ||
151 | ops->write_byte(bus, W1_MATCH_ROM); | 153 | ops->write_byte(bus, W1_MATCH_ROM); |
152 | 154 | ||
153 | for (i = 0; i < sizeof(w1->id); i++) | 155 | for (i = 0; i < sizeof(w1->id); i++) |
154 | ops->write_byte(bus, (w1->id >> (i * 8)) & 0xff); | 156 | ops->write_byte(bus, (w1->id >> (i * 8)) & 0xff); |
155 | 157 | ||
156 | return 0; | 158 | return 0; |
157 | } | 159 | } |
158 | 160 | ||
159 | int w1_read_byte(struct udevice *dev) | 161 | int w1_read_byte(struct udevice *dev) |
160 | { | 162 | { |
161 | struct udevice *bus = dev_get_parent(dev); | 163 | struct udevice *bus = dev_get_parent(dev); |
162 | const struct w1_ops *ops = device_get_ops(bus); | 164 | const struct w1_ops *ops = device_get_ops(bus); |
163 | 165 | ||
164 | if (!ops->read_byte) | 166 | if (!ops->read_byte) |
165 | return -ENOSYS; | 167 | return -ENOSYS; |
166 | 168 | ||
167 | return ops->read_byte(bus); | 169 | return ops->read_byte(bus); |
168 | } | 170 | } |
169 | 171 | ||
170 | int w1_read_buf(struct udevice *dev, u8 *buf, unsigned int count) | 172 | int w1_read_buf(struct udevice *dev, u8 *buf, unsigned int count) |
171 | { | 173 | { |
172 | int i, ret; | 174 | int i, ret; |
173 | 175 | ||
174 | for (i = 0; i < count; i++) { | 176 | for (i = 0; i < count; i++) { |
175 | ret = w1_read_byte(dev); | 177 | ret = w1_read_byte(dev); |
176 | if (ret < 0) | 178 | if (ret < 0) |
177 | return ret; | 179 | return ret; |
178 | 180 | ||
179 | buf[i] = ret & 0xff; | 181 | buf[i] = ret & 0xff; |
180 | } | 182 | } |
181 | 183 | ||
182 | return 0; | 184 | return 0; |
183 | } | 185 | } |
184 | 186 | ||
185 | int w1_write_byte(struct udevice *dev, u8 byte) | 187 | int w1_write_byte(struct udevice *dev, u8 byte) |
186 | { | 188 | { |
187 | struct udevice *bus = dev_get_parent(dev); | 189 | struct udevice *bus = dev_get_parent(dev); |
188 | const struct w1_ops *ops = device_get_ops(bus); | 190 | const struct w1_ops *ops = device_get_ops(bus); |
189 | 191 | ||
190 | if (!ops->write_byte) | 192 | if (!ops->write_byte) |
191 | return -ENOSYS; | 193 | return -ENOSYS; |
192 | 194 | ||
193 | ops->write_byte(bus, byte); | 195 | ops->write_byte(bus, byte); |
194 | 196 | ||
195 | return 0; | 197 | return 0; |
196 | } | 198 | } |
197 | 199 | ||
198 | static int w1_post_probe(struct udevice *bus) | 200 | static int w1_post_probe(struct udevice *bus) |
199 | { | 201 | { |
200 | w1_enumerate(bus); | 202 | w1_enumerate(bus); |
201 | 203 | ||
202 | return 0; | 204 | return 0; |
203 | } | 205 | } |
204 | 206 | ||
205 | int w1_init(void) | 207 | int w1_init(void) |
206 | { | 208 | { |
207 | struct udevice *bus; | 209 | struct udevice *bus; |
208 | struct uclass *uc; | 210 | struct uclass *uc; |
209 | int ret; | 211 | int ret; |
210 | 212 | ||
211 | ret = uclass_get(UCLASS_W1, &uc); | 213 | ret = uclass_get(UCLASS_W1, &uc); |
212 | if (ret) | 214 | if (ret) |
213 | return ret; | 215 | return ret; |
214 | 216 | ||
215 | uclass_foreach_dev(bus, uc) { | 217 | uclass_foreach_dev(bus, uc) { |
216 | ret = device_probe(bus); | 218 | ret = device_probe(bus); |
217 | if (ret == -ENODEV) { /* No such device. */ | 219 | if (ret == -ENODEV) { /* No such device. */ |
218 | printf("W1 controller not available.\n"); | 220 | printf("W1 controller not available.\n"); |
219 | continue; | 221 | continue; |
220 | } | 222 | } |
221 | 223 | ||
222 | if (ret) { /* Other error. */ | 224 | if (ret) { /* Other error. */ |
223 | printf("W1 controller probe failed.\n"); | 225 | printf("W1 controller probe failed.\n"); |
224 | continue; | 226 | continue; |
225 | } | 227 | } |
226 | } | 228 | } |
227 | return 0; | 229 | return 0; |
228 | } | 230 | } |
229 | 231 | ||
230 | UCLASS_DRIVER(w1) = { | 232 | UCLASS_DRIVER(w1) = { |
231 | .name = "w1", | 233 | .name = "w1", |
232 | .id = UCLASS_W1, | 234 | .id = UCLASS_W1, |
233 | .flags = DM_UC_FLAG_SEQ_ALIAS, | 235 | .flags = DM_UC_FLAG_SEQ_ALIAS, |
234 | .per_device_auto_alloc_size = sizeof(struct w1_bus), | 236 | .per_device_auto_alloc_size = sizeof(struct w1_bus), |
235 | .post_probe = w1_post_probe, | 237 | .post_probe = w1_post_probe, |
236 | #if CONFIG_IS_ENABLED(OF_CONTROL) | 238 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
237 | .post_bind = dm_scan_fdt_dev, | 239 | .post_bind = dm_scan_fdt_dev, |