| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: spi.c,v 1.17 2021/04/24 23:36:59 thorpej Exp $ */ | | 1 | /* $NetBSD: spi.c,v 1.17.2.1 2021/05/18 23:48:16 thorpej Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2006 Urbana-Champaign Independent Media Center. | | 4 | * Copyright (c) 2006 Urbana-Champaign Independent Media Center. |
5 | * Copyright (c) 2006 Garrett D'Amore. | | 5 | * Copyright (c) 2006 Garrett D'Amore. |
6 | * All rights reserved. | | 6 | * All rights reserved. |
7 | * | | 7 | * |
8 | * Portions of this code were written by Garrett D'Amore for the | | 8 | * Portions of this code were written by Garrett D'Amore for the |
9 | * Champaign-Urbana Community Wireless Network Project. | | 9 | * Champaign-Urbana Community Wireless Network Project. |
10 | * | | 10 | * |
11 | * Redistribution and use in source and binary forms, with or | | 11 | * Redistribution and use in source and binary forms, with or |
12 | * without modification, are permitted provided that the following | | 12 | * without modification, are permitted provided that the following |
13 | * conditions are met: | | 13 | * conditions are met: |
14 | * 1. Redistributions of source code must retain the above copyright | | 14 | * 1. Redistributions of source code must retain the above copyright |
| @@ -32,52 +32,54 @@ | | | @@ -32,52 +32,54 @@ |
32 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 32 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
33 | * ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT | | 33 | * ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT |
34 | * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT, | | 34 | * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT, |
35 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | | 35 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
36 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | | 36 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
37 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | 37 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
38 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | | 38 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
39 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 39 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
40 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | | 40 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
41 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | 41 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
42 | */ | | 42 | */ |
43 | | | 43 | |
44 | #include <sys/cdefs.h> | | 44 | #include <sys/cdefs.h> |
45 | __KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.17 2021/04/24 23:36:59 thorpej Exp $"); | | 45 | __KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.17.2.1 2021/05/18 23:48:16 thorpej Exp $"); |
46 | | | 46 | |
47 | #include "locators.h" | | 47 | #include "locators.h" |
48 | | | 48 | |
49 | #include <sys/param.h> | | 49 | #include <sys/param.h> |
50 | #include <sys/systm.h> | | 50 | #include <sys/systm.h> |
51 | #include <sys/device.h> | | 51 | #include <sys/device.h> |
52 | #include <sys/conf.h> | | 52 | #include <sys/conf.h> |
53 | #include <sys/malloc.h> | | 53 | #include <sys/kmem.h> |
54 | #include <sys/mutex.h> | | 54 | #include <sys/mutex.h> |
55 | #include <sys/condvar.h> | | 55 | #include <sys/condvar.h> |
56 | #include <sys/errno.h> | | 56 | #include <sys/errno.h> |
57 | | | 57 | |
58 | #include <dev/spi/spivar.h> | | 58 | #include <dev/spi/spivar.h> |
59 | #include <dev/spi/spi_io.h> | | 59 | #include <dev/spi/spi_io.h> |
60 | | | 60 | |
61 | #include "ioconf.h" | | 61 | #include "ioconf.h" |
62 | #include "locators.h" | | 62 | #include "locators.h" |
63 | | | 63 | |
64 | struct spi_softc { | | 64 | struct spi_softc { |
| | | 65 | device_t sc_dev; |
65 | struct spi_controller sc_controller; | | 66 | struct spi_controller sc_controller; |
66 | int sc_mode; | | 67 | int sc_mode; |
67 | int sc_speed; | | 68 | int sc_speed; |
68 | int sc_slave; | | 69 | int sc_slave; |
69 | int sc_nslaves; | | 70 | int sc_nslaves; |
70 | struct spi_handle *sc_slaves; | | 71 | struct spi_handle *sc_slaves; |
| | | 72 | kmutex_t sc_slave_state_lock; |
71 | kmutex_t sc_lock; | | 73 | kmutex_t sc_lock; |
72 | kcondvar_t sc_cv; | | 74 | kcondvar_t sc_cv; |
73 | int sc_flags; | | 75 | int sc_flags; |
74 | #define SPIC_BUSY 1 | | 76 | #define SPIC_BUSY 1 |
75 | }; | | 77 | }; |
76 | | | 78 | |
77 | static dev_type_open(spi_open); | | 79 | static dev_type_open(spi_open); |
78 | static dev_type_close(spi_close); | | 80 | static dev_type_close(spi_close); |
79 | static dev_type_ioctl(spi_ioctl); | | 81 | static dev_type_ioctl(spi_ioctl); |
80 | | | 82 | |
81 | const struct cdevsw spi_cdevsw = { | | 83 | const struct cdevsw spi_cdevsw = { |
82 | .d_open = spi_open, | | 84 | .d_open = spi_open, |
83 | .d_close = spi_close, | | 85 | .d_close = spi_close, |
| @@ -87,33 +89,34 @@ const struct cdevsw spi_cdevsw = { | | | @@ -87,33 +89,34 @@ const struct cdevsw spi_cdevsw = { |
87 | .d_stop = nostop, | | 89 | .d_stop = nostop, |
88 | .d_tty = notty, | | 90 | .d_tty = notty, |
89 | .d_poll = nopoll, | | 91 | .d_poll = nopoll, |
90 | .d_mmap = nommap, | | 92 | .d_mmap = nommap, |
91 | .d_kqfilter = nokqfilter, | | 93 | .d_kqfilter = nokqfilter, |
92 | .d_discard = nodiscard, | | 94 | .d_discard = nodiscard, |
93 | .d_flag = D_OTHER | | 95 | .d_flag = D_OTHER |
94 | }; | | 96 | }; |
95 | | | 97 | |
96 | /* | | 98 | /* |
97 | * SPI slave device. We have one of these per slave. | | 99 | * SPI slave device. We have one of these per slave. |
98 | */ | | 100 | */ |
99 | struct spi_handle { | | 101 | struct spi_handle { |
100 | struct spi_softc *sh_sc; | | 102 | struct spi_softc *sh_sc; /* static */ |
101 | struct spi_controller *sh_controller; | | 103 | struct spi_controller *sh_controller; /* static */ |
102 | int sh_slave; | | 104 | int sh_slave; /* static */ |
103 | int sh_mode; | | 105 | int sh_mode; /* locked by owning child */ |
104 | int sh_speed; | | 106 | int sh_speed; /* locked by owning child */ |
105 | int sh_flags; | | 107 | int sh_flags; /* ^^ slave_state_lock ^^ */ |
106 | #define SPIH_ATTACHED 1 | | 108 | #define SPIH_ATTACHED __BIT(0) |
| | | 109 | #define SPIH_DIRECT __BIT(1) |
107 | }; | | 110 | }; |
108 | | | 111 | |
109 | #define SPI_MAXDATA 4096 | | 112 | #define SPI_MAXDATA 4096 |
110 | | | 113 | |
111 | /* | | 114 | /* |
112 | * API for bus drivers. | | 115 | * API for bus drivers. |
113 | */ | | 116 | */ |
114 | | | 117 | |
115 | int | | 118 | int |
116 | spibus_print(void *aux, const char *pnp) | | 119 | spibus_print(void *aux, const char *pnp) |
117 | { | | 120 | { |
118 | | | 121 | |
119 | if (pnp != NULL) | | 122 | if (pnp != NULL) |
| @@ -121,214 +124,219 @@ spibus_print(void *aux, const char *pnp) | | | @@ -121,214 +124,219 @@ spibus_print(void *aux, const char *pnp) |
121 | | | 124 | |
122 | return (UNCONF); | | 125 | return (UNCONF); |
123 | } | | 126 | } |
124 | | | 127 | |
125 | | | 128 | |
126 | static int | | 129 | static int |
127 | spi_match(device_t parent, cfdata_t cf, void *aux) | | 130 | spi_match(device_t parent, cfdata_t cf, void *aux) |
128 | { | | 131 | { |
129 | | | 132 | |
130 | return 1; | | 133 | return 1; |
131 | } | | 134 | } |
132 | | | 135 | |
133 | static int | | 136 | static int |
134 | spi_print(void *aux, const char *pnp) | | 137 | spi_print_direct(void *aux, const char *pnp) |
135 | { | | 138 | { |
136 | struct spi_attach_args *sa = aux; | | 139 | struct spi_attach_args *sa = aux; |
137 | | | 140 | |
138 | if (sa->sa_handle->sh_slave != -1) | | 141 | if (pnp != NULL) { |
| | | 142 | aprint_normal("%s%s%s%s at %s slave %d", |
| | | 143 | sa->sa_name ? sa->sa_name : "(unknown)", |
| | | 144 | sa->sa_clist ? " (" : "", |
| | | 145 | sa->sa_clist ? sa->sa_clist : "", |
| | | 146 | sa->sa_clist ? ")" : "", |
| | | 147 | pnp, sa->sa_handle->sh_slave); |
| | | 148 | } else { |
139 | aprint_normal(" slave %d", sa->sa_handle->sh_slave); | | 149 | aprint_normal(" slave %d", sa->sa_handle->sh_slave); |
| | | 150 | } |
140 | | | 151 | |
141 | return (UNCONF); | | 152 | return UNCONF; |
142 | } | | 153 | } |
143 | | | 154 | |
144 | static int | | 155 | static int |
145 | spi_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) | | 156 | spi_print(void *aux, const char *pnp) |
146 | { | | 157 | { |
147 | struct spi_softc *sc = device_private(parent); | | 158 | struct spi_attach_args *sa = aux; |
148 | struct spi_attach_args sa; | | | |
149 | int addr; | | | |
150 | | | | |
151 | addr = cf->cf_loc[SPICF_SLAVE]; | | | |
152 | if ((addr < 0) || (addr >= sc->sc_controller.sct_nslaves)) { | | | |
153 | return -1; | | | |
154 | } | | | |
155 | | | | |
156 | memset(&sa, 0, sizeof sa); | | | |
157 | sa.sa_handle = &sc->sc_slaves[addr]; | | | |
158 | if (ISSET(sa.sa_handle->sh_flags, SPIH_ATTACHED)) | | | |
159 | return -1; | | | |
160 | | | 159 | |
161 | if (config_probe(parent, cf, &sa)) { | | 160 | aprint_normal(" slave %d", sa->sa_handle->sh_slave); |
162 | SET(sa.sa_handle->sh_flags, SPIH_ATTACHED); | | | |
163 | config_attach(parent, cf, &sa, spi_print, CFARG_EOL); | | | |
164 | } | | | |
165 | | | 161 | |
166 | return 0; | | 162 | return UNCONF; |
167 | } | | 163 | } |
168 | | | 164 | |
169 | /* | | 165 | /* |
170 | * XXX this is the same as i2c_fill_compat. It could be refactored into a | | 166 | * Direct and indrect for SPI are pretty similar, so we can collapse |
171 | * common fill_compat function with pointers to compat & ncompat instead | | 167 | * them into a single function. |
172 | * of attach_args as the first parameter. | | | |
173 | */ | | 168 | */ |
174 | static void | | 169 | static void |
175 | spi_fill_compat(struct spi_attach_args *sa, const char *compat, size_t len, | | 170 | spi_attach_child(struct spi_softc *sc, struct spi_attach_args *sa, |
176 | char **buffer) | | 171 | int chip_select, cfdata_t cf) |
177 | { | | 172 | { |
178 | int count, i; | | 173 | struct spi_handle *sh; |
179 | const char *c, *start, **ptr; | | 174 | device_t newdev = NULL; |
| | | 175 | bool is_direct = cf == NULL; |
| | | 176 | const int skip_flags = is_direct ? SPIH_ATTACHED |
| | | 177 | : (SPIH_ATTACHED | SPIH_DIRECT); |
| | | 178 | const int claim_flags = skip_flags ^ SPIH_DIRECT; |
| | | 179 | int locs[SPICF_NLOCS] = { 0 }; |
180 | | | 180 | |
181 | *buffer = NULL; | | 181 | if (chip_select < 0 || |
182 | for (i = count = 0, c = compat; i < len; i++, c++) | | 182 | chip_select >= sc->sc_controller.sct_nslaves) { |
183 | if (*c == 0) | | | |
184 | count++; | | | |
185 | count += 2; | | | |
186 | ptr = malloc(sizeof(char*)*count, M_TEMP, M_WAITOK); | | | |
187 | if (!ptr) | | | |
188 | return; | | 183 | return; |
| | | 184 | } |
189 | | | 185 | |
190 | for (i = count = 0, start = c = compat; i < len; i++, c++) { | | 186 | sh = &sc->sc_slaves[chip_select]; |
191 | if (*c == 0) { | | 187 | |
192 | ptr[count++] = start; | | 188 | mutex_enter(&sc->sc_slave_state_lock); |
193 | start = c + 1; | | 189 | if (ISSET(sh->sh_flags, skip_flags)) { |
194 | } | | 190 | mutex_exit(&sc->sc_slave_state_lock); |
| | | 191 | return; |
195 | } | | 192 | } |
196 | if (start < compat + len) { | | 193 | |
197 | /* last string not 0 terminated */ | | 194 | /* Keep others off of this chip select. */ |
198 | size_t l = c - start; | | 195 | SET(sh->sh_flags, claim_flags); |
199 | *buffer = malloc(l + 1, M_TEMP, M_WAITOK); | | 196 | mutex_exit(&sc->sc_slave_state_lock); |
200 | memcpy(*buffer, start, l); | | 197 | |
201 | (*buffer)[l] = 0; | | 198 | locs[SPICF_SLAVE] = chip_select; |
202 | ptr[count++] = *buffer; | | 199 | sa->sa_handle = sh; |
| | | 200 | |
| | | 201 | if (is_direct) { |
| | | 202 | newdev = config_found(sc->sc_dev, sa, spi_print_direct, |
| | | 203 | /* CFARG_SUBMATCH, config_stdsubmatch, XXX */ |
| | | 204 | CFARG_LOCATORS, locs, |
| | | 205 | CFARG_DEVHANDLE, sa->sa_devhandle, |
| | | 206 | CFARG_EOL); |
| | | 207 | } else { |
| | | 208 | if (config_probe(sc->sc_dev, cf, &sa)) { |
| | | 209 | newdev = config_attach(sc->sc_dev, cf, &sa, spi_print, |
| | | 210 | CFARG_LOCATORS, locs, |
| | | 211 | CFARG_EOL); |
| | | 212 | } |
203 | } | | 213 | } |
204 | ptr[count] = NULL; | | | |
205 | | | 214 | |
206 | sa->sa_compat = ptr; | | 215 | if (newdev == NULL) { |
207 | sa->sa_ncompat = count; | | 216 | /* |
| | | 217 | * Clear our claim on this chip select (yes, just |
| | | 218 | * the ATTACHED flag; we want to keep indirects off |
| | | 219 | * of chip selects for which there is a device tree |
| | | 220 | * node). |
| | | 221 | */ |
| | | 222 | mutex_enter(&sc->sc_slave_state_lock); |
| | | 223 | CLR(sh->sh_flags, SPIH_ATTACHED); |
| | | 224 | mutex_exit(&sc->sc_slave_state_lock); |
| | | 225 | } |
208 | } | | 226 | } |
209 | | | 227 | |
210 | static void | | 228 | static int |
211 | spi_direct_attach_child_devices(device_t parent, struct spi_softc *sc, | | 229 | spi_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) |
212 | prop_array_t child_devices) | | | |
213 | { | | 230 | { |
214 | unsigned int count; | | 231 | struct spi_softc *sc = device_private(parent); |
215 | prop_dictionary_t child; | | | |
216 | prop_data_t cdata; | | | |
217 | uint32_t slave; | | | |
218 | uint64_t cookie; | | | |
219 | struct spi_attach_args sa; | | 232 | struct spi_attach_args sa; |
220 | int loc[SPICF_NLOCS]; | | | |
221 | char *buf; | | | |
222 | int i; | | | |
223 | | | | |
224 | memset(loc, 0, sizeof loc); | | | |
225 | count = prop_array_count(child_devices); | | | |
226 | for (i = 0; i < count; i++) { | | | |
227 | child = prop_array_get(child_devices, i); | | | |
228 | if (!child) | | | |
229 | continue; | | | |
230 | if (!prop_dictionary_get_uint32(child, "slave", &slave)) | | | |
231 | continue; | | | |
232 | if(slave >= sc->sc_controller.sct_nslaves) | | | |
233 | continue; | | | |
234 | if (!prop_dictionary_get_uint64(child, "cookie", &cookie)) | | | |
235 | continue; | | | |
236 | if (!(cdata = prop_dictionary_get(child, "compatible"))) | | | |
237 | continue; | | | |
238 | loc[SPICF_SLAVE] = slave; | | | |
239 | | | | |
240 | memset(&sa, 0, sizeof sa); | | | |
241 | sa.sa_handle = &sc->sc_slaves[i]; | | | |
242 | sa.sa_prop = child; | | | |
243 | sa.sa_cookie = cookie; | | | |
244 | if (ISSET(sa.sa_handle->sh_flags, SPIH_ATTACHED)) | | | |
245 | continue; | | | |
246 | SET(sa.sa_handle->sh_flags, SPIH_ATTACHED); | | | |
247 | | | | |
248 | buf = NULL; | | | |
249 | spi_fill_compat(&sa, | | | |
250 | prop_data_value(cdata), | | | |
251 | prop_data_size(cdata), &buf); | | | |
252 | config_found(parent, &sa, spi_print, | | | |
253 | CFARG_LOCATORS, loc, | | | |
254 | CFARG_EOL); | | | |
255 | | | 233 | |
256 | if (sa.sa_compat) | | 234 | if (cf->cf_loc[SPICF_SLAVE] == SPICF_SLAVE_DEFAULT) { |
257 | free(sa.sa_compat, M_TEMP); | | 235 | /* No wildcards for indirect on SPI. */ |
258 | if (buf) | | 236 | return 0; |
259 | free(buf, M_TEMP); | | | |
260 | } | | 237 | } |
| | | 238 | |
| | | 239 | memset(&sa, 0, sizeof(sa)); |
| | | 240 | spi_attach_child(sc, &sa, cf->cf_loc[SPICF_SLAVE], cf); |
| | | 241 | |
| | | 242 | return 0; |
| | | 243 | } |
| | | 244 | |
| | | 245 | static bool |
| | | 246 | spi_enumerate_devices_callback(device_t self, |
| | | 247 | struct spi_enumerate_devices_args *args) |
| | | 248 | { |
| | | 249 | struct spi_softc *sc = device_private(self); |
| | | 250 | |
| | | 251 | spi_attach_child(sc, args->sa, args->chip_select, NULL); |
| | | 252 | |
| | | 253 | return true; /* keep enumerating */ |
261 | } | | 254 | } |
262 | | | 255 | |
263 | int | | 256 | int |
264 | spi_compatible_match(const struct spi_attach_args *sa, const cfdata_t cf, | | 257 | spi_compatible_match(const struct spi_attach_args *sa, const cfdata_t cf, |
265 | const struct device_compatible_entry *compats) | | 258 | const struct device_compatible_entry *compats) |
266 | { | | 259 | { |
267 | if (sa->sa_ncompat > 0) | | 260 | if (sa->sa_clist != NULL) { |
268 | return device_compatible_match(sa->sa_compat, sa->sa_ncompat, | | 261 | return device_compatible_match_strlist(sa->sa_clist, |
269 | compats); | | 262 | sa->sa_clist_size, compats); |
| | | 263 | } |
270 | | | 264 | |
| | | 265 | /* |
| | | 266 | * In this case, we're using indirect configuration, but SPI |
| | | 267 | * has no real addressing system, and we've filtered out |
| | | 268 | * wildcarded chip selects in spi_search(), so we have no |
| | | 269 | * choice but to trust the user-specified config. |
| | | 270 | */ |
271 | return 1; | | 271 | return 1; |
272 | } | | 272 | } |
273 | | | 273 | |
274 | /* | | | |
275 | * API for device drivers. | | | |
276 | * | | | |
277 | * We provide wrapper routines to decouple the ABI for the SPI | | | |
278 | * device drivers from the ABI for the SPI bus drivers. | | | |
279 | */ | | | |
280 | static void | | 274 | static void |
281 | spi_attach(device_t parent, device_t self, void *aux) | | 275 | spi_attach(device_t parent, device_t self, void *aux) |
282 | { | | 276 | { |
283 | struct spi_softc *sc = device_private(self); | | 277 | struct spi_softc *sc = device_private(self); |
284 | struct spibus_attach_args *sba = aux; | | 278 | struct spibus_attach_args *sba = aux; |
285 | int i; | | 279 | int i; |
286 | | | 280 | |
| | | 281 | sc->sc_dev = self; |
| | | 282 | |
287 | aprint_naive(": SPI bus\n"); | | 283 | aprint_naive(": SPI bus\n"); |
288 | aprint_normal(": SPI bus\n"); | | 284 | aprint_normal(": SPI bus\n"); |
289 | | | 285 | |
290 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); | | 286 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); |
| | | 287 | mutex_init(&sc->sc_slave_state_lock, MUTEX_DEFAULT, IPL_NONE); |
291 | cv_init(&sc->sc_cv, "spictl"); | | 288 | cv_init(&sc->sc_cv, "spictl"); |
292 | | | 289 | |
293 | sc->sc_controller = *sba->sba_controller; | | 290 | sc->sc_controller = *sba->sba_controller; |
294 | sc->sc_nslaves = sba->sba_controller->sct_nslaves; | | 291 | sc->sc_nslaves = sba->sba_controller->sct_nslaves; |
| | | 292 | |
295 | /* allocate slave structures */ | | 293 | /* allocate slave structures */ |
296 | sc->sc_slaves = malloc(sizeof (struct spi_handle) * sc->sc_nslaves, | | 294 | sc->sc_slaves = kmem_zalloc(sizeof(*sc->sc_slaves) * sc->sc_nslaves, |
297 | M_DEVBUF, M_WAITOK | M_ZERO); | | 295 | KM_SLEEP); |
298 | | | 296 | |
299 | sc->sc_speed = 0; | | 297 | sc->sc_speed = 0; |
300 | sc->sc_mode = -1; | | 298 | sc->sc_mode = -1; |
301 | sc->sc_slave = -1; | | 299 | sc->sc_slave = -1; |
302 | | | 300 | |
303 | /* | | 301 | /* |
304 | * Initialize slave handles | | 302 | * Initialize slave handles |
305 | */ | | 303 | */ |
306 | for (i = 0; i < sc->sc_nslaves; i++) { | | 304 | for (i = 0; i < sc->sc_nslaves; i++) { |
307 | sc->sc_slaves[i].sh_slave = i; | | 305 | sc->sc_slaves[i].sh_slave = i; |
308 | sc->sc_slaves[i].sh_sc = sc; | | 306 | sc->sc_slaves[i].sh_sc = sc; |
309 | sc->sc_slaves[i].sh_controller = &sc->sc_controller; | | 307 | sc->sc_slaves[i].sh_controller = &sc->sc_controller; |
310 | } | | 308 | } |
311 | | | 309 | |
312 | /* First attach devices known to be present via fdt */ | | 310 | /* |
313 | if (sba->sba_child_devices) { | | 311 | * Attempt to enumerate the devices on the bus using the |
314 | spi_direct_attach_child_devices(self, sc, sba->sba_child_devices); | | 312 | * platform device tree. |
315 | } | | 313 | */ |
| | | 314 | struct spi_attach_args sa = { 0 }; |
| | | 315 | struct spi_enumerate_devices_args enumargs = { |
| | | 316 | .sa = &sa, |
| | | 317 | .callback = spi_enumerate_devices_callback, |
| | | 318 | }; |
| | | 319 | device_call(self, "spi-enumerate-devices", &enumargs); |
| | | 320 | |
316 | /* Then do any other devices the user may have manually wired */ | | 321 | /* Then do any other devices the user may have manually wired */ |
317 | config_search(self, NULL, | | 322 | config_search(self, NULL, |
318 | CFARG_SEARCH, spi_search, | | 323 | CFARG_SEARCH, spi_search, |
319 | CFARG_EOL); | | 324 | CFARG_EOL); |
320 | } | | 325 | } |
321 | | | 326 | |
| | | 327 | CFATTACH_DECL_NEW(spi, sizeof(struct spi_softc), |
| | | 328 | spi_match, spi_attach, NULL, NULL); |
| | | 329 | |
322 | static int | | 330 | static int |
323 | spi_open(dev_t dev, int flag, int fmt, lwp_t *l) | | 331 | spi_open(dev_t dev, int flag, int fmt, lwp_t *l) |
324 | { | | 332 | { |
325 | struct spi_softc *sc = device_lookup_private(&spi_cd, minor(dev)); | | 333 | struct spi_softc *sc = device_lookup_private(&spi_cd, minor(dev)); |
326 | | | 334 | |
327 | if (sc == NULL) | | 335 | if (sc == NULL) |
328 | return ENXIO; | | 336 | return ENXIO; |
329 | | | 337 | |
330 | return 0; | | 338 | return 0; |
331 | } | | 339 | } |
332 | | | 340 | |
333 | static int | | 341 | static int |
334 | spi_close(dev_t dev, int flag, int fmt, lwp_t *l) | | 342 | spi_close(dev_t dev, int flag, int fmt, lwp_t *l) |
| @@ -365,64 +373,68 @@ spi_ioctl(dev_t dev, u_long cmd, void *d | | | @@ -365,64 +373,68 @@ spi_ioctl(dev_t dev, u_long cmd, void *d |
365 | if (sit->sit_addr < 0 || sit->sit_addr >= sc->sc_nslaves) { | | 373 | if (sit->sit_addr < 0 || sit->sit_addr >= sc->sc_nslaves) { |
366 | error = EINVAL; | | 374 | error = EINVAL; |
367 | break; | | 375 | break; |
368 | } | | 376 | } |
369 | if ((sit->sit_send && sit->sit_sendlen == 0) | | 377 | if ((sit->sit_send && sit->sit_sendlen == 0) |
370 | || (sit->sit_recv && sit->sit_recv == 0)) { | | 378 | || (sit->sit_recv && sit->sit_recv == 0)) { |
371 | error = EINVAL; | | 379 | error = EINVAL; |
372 | break; | | 380 | break; |
373 | } | | 381 | } |
374 | sh = &sc->sc_slaves[sit->sit_addr]; | | 382 | sh = &sc->sc_slaves[sit->sit_addr]; |
375 | sbuf = rbuf = NULL; | | 383 | sbuf = rbuf = NULL; |
376 | error = 0; | | 384 | error = 0; |
377 | if (sit->sit_send && sit->sit_sendlen <= SPI_MAXDATA) { | | 385 | if (sit->sit_send && sit->sit_sendlen <= SPI_MAXDATA) { |
378 | sbuf = malloc(sit->sit_sendlen, M_DEVBUF, M_WAITOK); | | 386 | sbuf = kmem_alloc(sit->sit_sendlen, KM_SLEEP); |
379 | error = copyin(sit->sit_send, sbuf, sit->sit_sendlen); | | 387 | error = copyin(sit->sit_send, sbuf, sit->sit_sendlen); |
380 | } | | 388 | } |
381 | if (sit->sit_recv && sit->sit_recvlen <= SPI_MAXDATA) { | | 389 | if (sit->sit_recv && sit->sit_recvlen <= SPI_MAXDATA) { |
382 | rbuf = malloc(sit->sit_recvlen, M_DEVBUF, M_WAITOK); | | 390 | rbuf = kmem_alloc(sit->sit_recvlen, KM_SLEEP); |
383 | } | | 391 | } |
384 | if (error == 0) { | | 392 | if (error == 0) { |
385 | if (sbuf && rbuf) | | 393 | if (sbuf && rbuf) |
386 | error = spi_send_recv(sh, | | 394 | error = spi_send_recv(sh, |
387 | sit->sit_sendlen, sbuf, | | 395 | sit->sit_sendlen, sbuf, |
388 | sit->sit_recvlen, rbuf); | | 396 | sit->sit_recvlen, rbuf); |
389 | else if (sbuf) | | 397 | else if (sbuf) |
390 | error = spi_send(sh, | | 398 | error = spi_send(sh, |
391 | sit->sit_sendlen, sbuf); | | 399 | sit->sit_sendlen, sbuf); |
392 | else if (rbuf) | | 400 | else if (rbuf) |
393 | error = spi_recv(sh, | | 401 | error = spi_recv(sh, |
394 | sit->sit_recvlen, rbuf); | | 402 | sit->sit_recvlen, rbuf); |
395 | } | | 403 | } |
396 | if (rbuf) { | | 404 | if (rbuf) { |
397 | if (error == 0) | | 405 | if (error == 0) |
398 | error = copyout(rbuf, sit->sit_recv, | | 406 | error = copyout(rbuf, sit->sit_recv, |
399 | sit->sit_recvlen); | | 407 | sit->sit_recvlen); |
400 | free(rbuf, M_DEVBUF); | | 408 | kmem_free(rbuf, sit->sit_recvlen); |
401 | } | | 409 | } |
402 | if (sbuf) { | | 410 | if (sbuf) { |
403 | free(sbuf, M_DEVBUF); | | 411 | kmem_free(sbuf, sit->sit_sendlen); |
404 | } | | 412 | } |
405 | break; | | 413 | break; |
406 | default: | | 414 | default: |
407 | error = ENODEV; | | 415 | error = ENODEV; |
408 | break; | | 416 | break; |
409 | } | | 417 | } |
410 | | | 418 | |
411 | return error; | | 419 | return error; |
412 | } | | 420 | } |
413 | | | 421 | |
414 | CFATTACH_DECL_NEW(spi, sizeof(struct spi_softc), | | 422 | /* |
415 | spi_match, spi_attach, NULL, NULL); | | 423 | * API for device drivers. |
| | | 424 | * |
| | | 425 | * We provide wrapper routines to decouple the ABI for the SPI |
| | | 426 | * device drivers from the ABI for the SPI bus drivers. |
| | | 427 | */ |
416 | | | 428 | |
417 | /* | | 429 | /* |
418 | * Configure. This should be the first thing that the SPI driver | | 430 | * Configure. This should be the first thing that the SPI driver |
419 | * should do, to configure which mode (e.g. SPI_MODE_0, which is the | | 431 | * should do, to configure which mode (e.g. SPI_MODE_0, which is the |
420 | * same as Philips Microwire mode), and speed. If the bus driver | | 432 | * same as Philips Microwire mode), and speed. If the bus driver |
421 | * cannot run fast enough, then it should just configure the fastest | | 433 | * cannot run fast enough, then it should just configure the fastest |
422 | * mode that it can support. If the bus driver cannot run slow | | 434 | * mode that it can support. If the bus driver cannot run slow |
423 | * enough, then the device is incompatible and an error should be | | 435 | * enough, then the device is incompatible and an error should be |
424 | * returned. | | 436 | * returned. |
425 | */ | | 437 | */ |
426 | int | | 438 | int |
427 | spi_configure(struct spi_handle *sh, int mode, int speed) | | 439 | spi_configure(struct spi_handle *sh, int mode, int speed) |
428 | { | | 440 | { |
| @@ -652,14 +664,13 @@ spi_send_recv(struct spi_handle *sh, int | | | @@ -652,14 +664,13 @@ spi_send_recv(struct spi_handle *sh, int |
652 | spi_chunk_init(&chunk2, rcnt, NULL, rcv); | | 664 | spi_chunk_init(&chunk2, rcnt, NULL, rcv); |
653 | spi_transfer_add(&trans, &chunk1); | | 665 | spi_transfer_add(&trans, &chunk1); |
654 | spi_transfer_add(&trans, &chunk2); | | 666 | spi_transfer_add(&trans, &chunk2); |
655 | | | 667 | |
656 | /* enqueue it and wait for it to complete */ | | 668 | /* enqueue it and wait for it to complete */ |
657 | spi_transfer(sh, &trans); | | 669 | spi_transfer(sh, &trans); |
658 | spi_wait(&trans); | | 670 | spi_wait(&trans); |
659 | | | 671 | |
660 | if (trans.st_flags & SPI_F_ERROR) | | 672 | if (trans.st_flags & SPI_F_ERROR) |
661 | return trans.st_errno; | | 673 | return trans.st_errno; |
662 | | | 674 | |
663 | return 0; | | 675 | return 0; |
664 | } | | 676 | } |
665 | | | | |