| @@ -1,775 +1,774 @@ | | | @@ -1,775 +1,774 @@ |
1 | /* $NetBSD: cac.c,v 1.61.10.2 2021/03/22 16:23:45 thorpej Exp $ */ | | 1 | /* $NetBSD: cac.c,v 1.61.10.3 2021/03/28 20:30:14 thorpej Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2000, 2006, 2007 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2000, 2006, 2007 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Andrew Doran. | | 8 | * by Andrew Doran. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. | | 29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | /* | | 32 | /* |
33 | * Driver for Compaq array controllers. | | 33 | * Driver for Compaq array controllers. |
34 | */ | | 34 | */ |
35 | | | 35 | |
36 | #include <sys/cdefs.h> | | 36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(0, "$NetBSD: cac.c,v 1.61.10.2 2021/03/22 16:23:45 thorpej Exp $"); | | 37 | __KERNEL_RCSID(0, "$NetBSD: cac.c,v 1.61.10.3 2021/03/28 20:30:14 thorpej Exp $"); |
38 | | | 38 | |
39 | #if defined(_KERNEL_OPT) | | 39 | #if defined(_KERNEL_OPT) |
40 | #include "bio.h" | | 40 | #include "bio.h" |
41 | #endif | | 41 | #endif |
42 | | | 42 | |
43 | #include <sys/param.h> | | 43 | #include <sys/param.h> |
44 | #include <sys/systm.h> | | 44 | #include <sys/systm.h> |
45 | #include <sys/kernel.h> | | 45 | #include <sys/kernel.h> |
46 | #include <sys/device.h> | | 46 | #include <sys/device.h> |
47 | #include <sys/queue.h> | | 47 | #include <sys/queue.h> |
48 | #include <sys/proc.h> | | 48 | #include <sys/proc.h> |
49 | #include <sys/buf.h> | | 49 | #include <sys/buf.h> |
50 | #include <sys/endian.h> | | 50 | #include <sys/endian.h> |
51 | #include <sys/malloc.h> | | 51 | #include <sys/malloc.h> |
52 | #include <sys/pool.h> | | 52 | #include <sys/pool.h> |
53 | #include <sys/module.h> | | 53 | #include <sys/module.h> |
54 | #include <sys/bswap.h> | | 54 | #include <sys/bswap.h> |
55 | #include <sys/bus.h> | | 55 | #include <sys/bus.h> |
56 | | | 56 | |
57 | #include <dev/ic/cacreg.h> | | 57 | #include <dev/ic/cacreg.h> |
58 | #include <dev/ic/cacvar.h> | | 58 | #include <dev/ic/cacvar.h> |
59 | | | 59 | |
60 | #if NBIO > 0 | | 60 | #if NBIO > 0 |
61 | #include <dev/biovar.h> | | 61 | #include <dev/biovar.h> |
62 | #endif /* NBIO > 0 */ | | 62 | #endif /* NBIO > 0 */ |
63 | | | 63 | |
64 | #include "ioconf.h" | | 64 | #include "ioconf.h" |
65 | #include "locators.h" | | 65 | #include "locators.h" |
66 | | | 66 | |
67 | static struct cac_ccb *cac_ccb_alloc(struct cac_softc *, int); | | 67 | static struct cac_ccb *cac_ccb_alloc(struct cac_softc *, int); |
68 | static void cac_ccb_done(struct cac_softc *, struct cac_ccb *); | | 68 | static void cac_ccb_done(struct cac_softc *, struct cac_ccb *); |
69 | static void cac_ccb_free(struct cac_softc *, struct cac_ccb *); | | 69 | static void cac_ccb_free(struct cac_softc *, struct cac_ccb *); |
70 | static int cac_ccb_poll(struct cac_softc *, struct cac_ccb *, int); | | 70 | static int cac_ccb_poll(struct cac_softc *, struct cac_ccb *, int); |
71 | static int cac_ccb_start(struct cac_softc *, struct cac_ccb *); | | 71 | static int cac_ccb_start(struct cac_softc *, struct cac_ccb *); |
72 | static int cac_print(void *, const char *); | | 72 | static int cac_print(void *, const char *); |
73 | static void cac_shutdown(void *); | | 73 | static void cac_shutdown(void *); |
74 | | | 74 | |
75 | static struct cac_ccb *cac_l0_completed(struct cac_softc *); | | 75 | static struct cac_ccb *cac_l0_completed(struct cac_softc *); |
76 | static int cac_l0_fifo_full(struct cac_softc *); | | 76 | static int cac_l0_fifo_full(struct cac_softc *); |
77 | static void cac_l0_intr_enable(struct cac_softc *, int); | | 77 | static void cac_l0_intr_enable(struct cac_softc *, int); |
78 | static int cac_l0_intr_pending(struct cac_softc *); | | 78 | static int cac_l0_intr_pending(struct cac_softc *); |
79 | static void cac_l0_submit(struct cac_softc *, struct cac_ccb *); | | 79 | static void cac_l0_submit(struct cac_softc *, struct cac_ccb *); |
80 | | | 80 | |
81 | static void *cac_sdh; /* shutdown hook */ | | 81 | static void *cac_sdh; /* shutdown hook */ |
82 | | | 82 | |
83 | #if NBIO > 0 | | 83 | #if NBIO > 0 |
84 | int cac_ioctl(device_t, u_long, void *); | | 84 | int cac_ioctl(device_t, u_long, void *); |
85 | int cac_ioctl_vol(struct cac_softc *, struct bioc_vol *); | | 85 | int cac_ioctl_vol(struct cac_softc *, struct bioc_vol *); |
86 | int cac_create_sensors(struct cac_softc *); | | 86 | int cac_create_sensors(struct cac_softc *); |
87 | void cac_sensor_refresh(struct sysmon_envsys *, envsys_data_t *); | | 87 | void cac_sensor_refresh(struct sysmon_envsys *, envsys_data_t *); |
88 | #endif /* NBIO > 0 */ | | 88 | #endif /* NBIO > 0 */ |
89 | | | 89 | |
90 | const struct cac_linkage cac_l0 = { | | 90 | const struct cac_linkage cac_l0 = { |
91 | cac_l0_completed, | | 91 | cac_l0_completed, |
92 | cac_l0_fifo_full, | | 92 | cac_l0_fifo_full, |
93 | cac_l0_intr_enable, | | 93 | cac_l0_intr_enable, |
94 | cac_l0_intr_pending, | | 94 | cac_l0_intr_pending, |
95 | cac_l0_submit | | 95 | cac_l0_submit |
96 | }; | | 96 | }; |
97 | | | 97 | |
98 | /* | | 98 | /* |
99 | * Initialise our interface to the controller. | | 99 | * Initialise our interface to the controller. |
100 | */ | | 100 | */ |
101 | int | | 101 | int |
102 | cac_init(struct cac_softc *sc, const char *intrstr, int startfw) | | 102 | cac_init(struct cac_softc *sc, const char *intrstr, int startfw) |
103 | { | | 103 | { |
104 | struct cac_controller_info cinfo; | | 104 | struct cac_controller_info cinfo; |
105 | int error, rseg, size, i; | | 105 | int error, rseg, size, i; |
106 | bus_dma_segment_t seg; | | 106 | bus_dma_segment_t seg; |
107 | struct cac_ccb *ccb; | | 107 | struct cac_ccb *ccb; |
108 | char firm[8]; | | 108 | char firm[8]; |
109 | | | 109 | |
110 | if (intrstr != NULL) | | 110 | if (intrstr != NULL) |
111 | aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr); | | 111 | aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr); |
112 | | | 112 | |
113 | SIMPLEQ_INIT(&sc->sc_ccb_free); | | 113 | SIMPLEQ_INIT(&sc->sc_ccb_free); |
114 | SIMPLEQ_INIT(&sc->sc_ccb_queue); | | 114 | SIMPLEQ_INIT(&sc->sc_ccb_queue); |
115 | mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM); | | 115 | mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM); |
116 | cv_init(&sc->sc_ccb_cv, "cacccb"); | | 116 | cv_init(&sc->sc_ccb_cv, "cacccb"); |
117 | | | 117 | |
118 | size = sizeof(struct cac_ccb) * CAC_MAX_CCBS; | | 118 | size = sizeof(struct cac_ccb) * CAC_MAX_CCBS; |
119 | | | 119 | |
120 | if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1, | | 120 | if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1, |
121 | &rseg, BUS_DMA_NOWAIT)) != 0) { | | 121 | &rseg, BUS_DMA_NOWAIT)) != 0) { |
122 | aprint_error_dev(sc->sc_dev, "unable to allocate CCBs, error = %d\n", | | 122 | aprint_error_dev(sc->sc_dev, "unable to allocate CCBs, error = %d\n", |
123 | error); | | 123 | error); |
124 | return (-1); | | 124 | return (-1); |
125 | } | | 125 | } |
126 | | | 126 | |
127 | if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size, | | 127 | if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size, |
128 | (void **)&sc->sc_ccbs, | | 128 | (void **)&sc->sc_ccbs, |
129 | BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { | | 129 | BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { |
130 | aprint_error_dev(sc->sc_dev, "unable to map CCBs, error = %d\n", | | 130 | aprint_error_dev(sc->sc_dev, "unable to map CCBs, error = %d\n", |
131 | error); | | 131 | error); |
132 | return (-1); | | 132 | return (-1); |
133 | } | | 133 | } |
134 | | | 134 | |
135 | if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, | | 135 | if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, |
136 | BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) { | | 136 | BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) { |
137 | aprint_error_dev(sc->sc_dev, "unable to create CCB DMA map, error = %d\n", | | 137 | aprint_error_dev(sc->sc_dev, "unable to create CCB DMA map, error = %d\n", |
138 | error); | | 138 | error); |
139 | return (-1); | | 139 | return (-1); |
140 | } | | 140 | } |
141 | | | 141 | |
142 | if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs, | | 142 | if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs, |
143 | size, NULL, BUS_DMA_NOWAIT)) != 0) { | | 143 | size, NULL, BUS_DMA_NOWAIT)) != 0) { |
144 | aprint_error_dev(sc->sc_dev, "unable to load CCB DMA map, error = %d\n", | | 144 | aprint_error_dev(sc->sc_dev, "unable to load CCB DMA map, error = %d\n", |
145 | error); | | 145 | error); |
146 | return (-1); | | 146 | return (-1); |
147 | } | | 147 | } |
148 | | | 148 | |
149 | sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr; | | 149 | sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr; |
150 | memset(sc->sc_ccbs, 0, size); | | 150 | memset(sc->sc_ccbs, 0, size); |
151 | ccb = (struct cac_ccb *)sc->sc_ccbs; | | 151 | ccb = (struct cac_ccb *)sc->sc_ccbs; |
152 | | | 152 | |
153 | for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) { | | 153 | for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) { |
154 | /* Create the DMA map for this CCB's data */ | | 154 | /* Create the DMA map for this CCB's data */ |
155 | error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER, | | 155 | error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER, |
156 | CAC_SG_SIZE, CAC_MAX_XFER, 0, | | 156 | CAC_SG_SIZE, CAC_MAX_XFER, 0, |
157 | BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, | | 157 | BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, |
158 | &ccb->ccb_dmamap_xfer); | | 158 | &ccb->ccb_dmamap_xfer); |
159 | | | 159 | |
160 | if (error) { | | 160 | if (error) { |
161 | aprint_error_dev(sc->sc_dev, "can't create ccb dmamap (%d)\n", | | 161 | aprint_error_dev(sc->sc_dev, "can't create ccb dmamap (%d)\n", |
162 | error); | | 162 | error); |
163 | break; | | 163 | break; |
164 | } | | 164 | } |
165 | | | 165 | |
166 | ccb->ccb_flags = 0; | | 166 | ccb->ccb_flags = 0; |
167 | ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb); | | 167 | ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb); |
168 | SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain); | | 168 | SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain); |
169 | } | | 169 | } |
170 | | | 170 | |
171 | /* Start firmware background tasks, if needed. */ | | 171 | /* Start firmware background tasks, if needed. */ |
172 | if (startfw) { | | 172 | if (startfw) { |
173 | if (cac_cmd(sc, CAC_CMD_START_FIRMWARE, &cinfo, sizeof(cinfo), | | 173 | if (cac_cmd(sc, CAC_CMD_START_FIRMWARE, &cinfo, sizeof(cinfo), |
174 | 0, 0, CAC_CCB_DATA_IN, NULL)) { | | 174 | 0, 0, CAC_CCB_DATA_IN, NULL)) { |
175 | aprint_error_dev(sc->sc_dev, "CAC_CMD_START_FIRMWARE failed\n"); | | 175 | aprint_error_dev(sc->sc_dev, "CAC_CMD_START_FIRMWARE failed\n"); |
176 | return (-1); | | 176 | return (-1); |
177 | } | | 177 | } |
178 | } | | 178 | } |
179 | | | 179 | |
180 | if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0, | | 180 | if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0, |
181 | CAC_CCB_DATA_IN, NULL)) { | | 181 | CAC_CCB_DATA_IN, NULL)) { |
182 | aprint_error_dev(sc->sc_dev, "CAC_CMD_GET_CTRL_INFO failed\n"); | | 182 | aprint_error_dev(sc->sc_dev, "CAC_CMD_GET_CTRL_INFO failed\n"); |
183 | return (-1); | | 183 | return (-1); |
184 | } | | 184 | } |
185 | | | 185 | |
186 | strlcpy(firm, cinfo.firm_rev, 4+1); | | 186 | strlcpy(firm, cinfo.firm_rev, 4+1); |
187 | printf("%s: %d channels, firmware <%s>\n", device_xname(sc->sc_dev), | | 187 | printf("%s: %d channels, firmware <%s>\n", device_xname(sc->sc_dev), |
188 | cinfo.scsi_chips, firm); | | 188 | cinfo.scsi_chips, firm); |
189 | | | 189 | |
190 | /* Limit number of units to size of our sc_unitmask */ | | 190 | /* Limit number of units to size of our sc_unitmask */ |
191 | sc->sc_nunits = cinfo.num_drvs; | | 191 | sc->sc_nunits = cinfo.num_drvs; |
192 | if (sc->sc_nunits > sizeof(sc->sc_unitmask) * NBBY) | | 192 | if (sc->sc_nunits > sizeof(sc->sc_unitmask) * NBBY) |
193 | sc->sc_nunits = sizeof(sc->sc_unitmask) * NBBY; | | 193 | sc->sc_nunits = sizeof(sc->sc_unitmask) * NBBY; |
194 | | | 194 | |
195 | /* Attach our units */ | | 195 | /* Attach our units */ |
196 | sc->sc_unitmask = 0; | | 196 | sc->sc_unitmask = 0; |
197 | cac_rescan(sc->sc_dev, NULL, NULL); | | 197 | cac_rescan(sc->sc_dev, NULL, NULL); |
198 | | | 198 | |
199 | /* Set our `shutdownhook' before we start any device activity. */ | | 199 | /* Set our `shutdownhook' before we start any device activity. */ |
200 | if (cac_sdh == NULL) | | 200 | if (cac_sdh == NULL) |
201 | cac_sdh = shutdownhook_establish(cac_shutdown, NULL); | | 201 | cac_sdh = shutdownhook_establish(cac_shutdown, NULL); |
202 | | | 202 | |
203 | mutex_enter(&sc->sc_mutex); | | 203 | mutex_enter(&sc->sc_mutex); |
204 | (*sc->sc_cl.cl_intr_enable)(sc, CAC_INTR_ENABLE); | | 204 | (*sc->sc_cl.cl_intr_enable)(sc, CAC_INTR_ENABLE); |
205 | mutex_exit(&sc->sc_mutex); | | 205 | mutex_exit(&sc->sc_mutex); |
206 | | | 206 | |
207 | #if NBIO > 0 | | 207 | #if NBIO > 0 |
208 | if (bio_register(sc->sc_dev, cac_ioctl) != 0) | | 208 | if (bio_register(sc->sc_dev, cac_ioctl) != 0) |
209 | aprint_error_dev(sc->sc_dev, "controller registration failed"); | | 209 | aprint_error_dev(sc->sc_dev, "controller registration failed"); |
210 | else | | 210 | else |
211 | sc->sc_ioctl = cac_ioctl; | | 211 | sc->sc_ioctl = cac_ioctl; |
212 | if (cac_create_sensors(sc) != 0) | | 212 | if (cac_create_sensors(sc) != 0) |
213 | aprint_error_dev(sc->sc_dev, "unable to create sensors\n"); | | 213 | aprint_error_dev(sc->sc_dev, "unable to create sensors\n"); |
214 | #endif | | 214 | #endif |
215 | | | 215 | |
216 | return (0); | | 216 | return (0); |
217 | } | | 217 | } |
218 | | | 218 | |
219 | int | | 219 | int |
220 | cac_rescan(device_t self, const char *attr, const int *flags) | | 220 | cac_rescan(device_t self, const char *attr, const int *locs) |
221 | { | | 221 | { |
222 | struct cac_softc *sc; | | 222 | struct cac_softc *sc; |
223 | struct cac_attach_args caca; | | 223 | struct cac_attach_args caca; |
224 | int locs[CACCF_NLOCS]; | | 224 | int mlocs[CACCF_NLOCS]; |
225 | int i; | | 225 | int i; |
226 | | | 226 | |
227 | sc = device_private(self); | | 227 | sc = device_private(self); |
228 | for (i = 0; i < sc->sc_nunits; i++) { | | 228 | for (i = 0; i < sc->sc_nunits; i++) { |
229 | if (sc->sc_unitmask & (1 << i)) | | 229 | if (sc->sc_unitmask & (1 << i)) |
230 | continue; | | 230 | continue; |
231 | caca.caca_unit = i; | | 231 | caca.caca_unit = i; |
232 | | | 232 | |
233 | locs[CACCF_UNIT] = i; | | 233 | mlocs[CACCF_UNIT] = i; |
234 | | | 234 | |
235 | if (config_found(self, &caca, cac_print, | | 235 | if (config_found(self, &caca, cac_print, |
236 | CFARG_SUBMATCH, config_stdsubmatch, | | 236 | CFARG_SUBMATCH, config_stdsubmatch, |
237 | CFARG_IATTR, attr, | | 237 | CFARG_LOCATORS, mlocs, |
238 | CFARG_LOCATORS, locs, | | | |
239 | CFARG_EOL) != NULL) | | 238 | CFARG_EOL) != NULL) |
240 | sc->sc_unitmask |= 1 << i; | | 239 | sc->sc_unitmask |= 1 << i; |
241 | } | | 240 | } |
242 | return 0; | | 241 | return 0; |
243 | } | | 242 | } |
244 | | | 243 | |
245 | /* | | 244 | /* |
246 | * Shut down all `cac' controllers. | | 245 | * Shut down all `cac' controllers. |
247 | */ | | 246 | */ |
248 | static void | | 247 | static void |
249 | cac_shutdown(void *cookie) | | 248 | cac_shutdown(void *cookie) |
250 | { | | 249 | { |
251 | struct cac_softc *sc; | | 250 | struct cac_softc *sc; |
252 | u_int8_t tbuf[512]; | | 251 | u_int8_t tbuf[512]; |
253 | int i; | | 252 | int i; |
254 | | | 253 | |
255 | for (i = 0; i < cac_cd.cd_ndevs; i++) { | | 254 | for (i = 0; i < cac_cd.cd_ndevs; i++) { |
256 | if ((sc = device_lookup_private(&cac_cd, i)) == NULL) | | 255 | if ((sc = device_lookup_private(&cac_cd, i)) == NULL) |
257 | continue; | | 256 | continue; |
258 | memset(tbuf, 0, sizeof(tbuf)); | | 257 | memset(tbuf, 0, sizeof(tbuf)); |
259 | tbuf[0] = 1; | | 258 | tbuf[0] = 1; |
260 | cac_cmd(sc, CAC_CMD_FLUSH_CACHE, tbuf, sizeof(tbuf), 0, 0, | | 259 | cac_cmd(sc, CAC_CMD_FLUSH_CACHE, tbuf, sizeof(tbuf), 0, 0, |
261 | CAC_CCB_DATA_OUT, NULL); | | 260 | CAC_CCB_DATA_OUT, NULL); |
262 | } | | 261 | } |
263 | } | | 262 | } |
264 | | | 263 | |
265 | /* | | 264 | /* |
266 | * Print autoconfiguration message for a sub-device. | | 265 | * Print autoconfiguration message for a sub-device. |
267 | */ | | 266 | */ |
268 | static int | | 267 | static int |
269 | cac_print(void *aux, const char *pnp) | | 268 | cac_print(void *aux, const char *pnp) |
270 | { | | 269 | { |
271 | struct cac_attach_args *caca; | | 270 | struct cac_attach_args *caca; |
272 | | | 271 | |
273 | caca = (struct cac_attach_args *)aux; | | 272 | caca = (struct cac_attach_args *)aux; |
274 | | | 273 | |
275 | if (pnp != NULL) | | 274 | if (pnp != NULL) |
276 | aprint_normal("block device at %s", pnp); | | 275 | aprint_normal("block device at %s", pnp); |
277 | aprint_normal(" unit %d", caca->caca_unit); | | 276 | aprint_normal(" unit %d", caca->caca_unit); |
278 | return (UNCONF); | | 277 | return (UNCONF); |
279 | } | | 278 | } |
280 | | | 279 | |
281 | /* | | 280 | /* |
282 | * Handle an interrupt from the controller: process finished CCBs and | | 281 | * Handle an interrupt from the controller: process finished CCBs and |
283 | * dequeue any waiting CCBs. | | 282 | * dequeue any waiting CCBs. |
284 | */ | | 283 | */ |
285 | int | | 284 | int |
286 | cac_intr(void *cookie) | | 285 | cac_intr(void *cookie) |
287 | { | | 286 | { |
288 | struct cac_softc *sc; | | 287 | struct cac_softc *sc; |
289 | struct cac_ccb *ccb; | | 288 | struct cac_ccb *ccb; |
290 | int rv; | | 289 | int rv; |
291 | | | 290 | |
292 | sc = cookie; | | 291 | sc = cookie; |
293 | | | 292 | |
294 | mutex_enter(&sc->sc_mutex); | | 293 | mutex_enter(&sc->sc_mutex); |
295 | | | 294 | |
296 | if ((*sc->sc_cl.cl_intr_pending)(sc)) { | | 295 | if ((*sc->sc_cl.cl_intr_pending)(sc)) { |
297 | while ((ccb = (*sc->sc_cl.cl_completed)(sc)) != NULL) { | | 296 | while ((ccb = (*sc->sc_cl.cl_completed)(sc)) != NULL) { |
298 | cac_ccb_done(sc, ccb); | | 297 | cac_ccb_done(sc, ccb); |
299 | cac_ccb_start(sc, NULL); | | 298 | cac_ccb_start(sc, NULL); |
300 | } | | 299 | } |
301 | rv = 1; | | 300 | rv = 1; |
302 | } else | | 301 | } else |
303 | rv = 0; | | 302 | rv = 0; |
304 | | | 303 | |
305 | mutex_exit(&sc->sc_mutex); | | 304 | mutex_exit(&sc->sc_mutex); |
306 | | | 305 | |
307 | return (rv); | | 306 | return (rv); |
308 | } | | 307 | } |
309 | | | 308 | |
310 | /* | | 309 | /* |
311 | * Execute a [polled] command. | | 310 | * Execute a [polled] command. |
312 | */ | | 311 | */ |
313 | int | | 312 | int |
314 | cac_cmd(struct cac_softc *sc, int command, void *data, int datasize, | | 313 | cac_cmd(struct cac_softc *sc, int command, void *data, int datasize, |
315 | int drive, int blkno, int flags, struct cac_context *context) | | 314 | int drive, int blkno, int flags, struct cac_context *context) |
316 | { | | 315 | { |
317 | struct cac_ccb *ccb; | | 316 | struct cac_ccb *ccb; |
318 | struct cac_sgb *sgb; | | 317 | struct cac_sgb *sgb; |
319 | int i, rv, size, nsegs; | | 318 | int i, rv, size, nsegs; |
320 | | | 319 | |
321 | size = 0; | | 320 | size = 0; |
322 | | | 321 | |
323 | if ((ccb = cac_ccb_alloc(sc, 1)) == NULL) { | | 322 | if ((ccb = cac_ccb_alloc(sc, 1)) == NULL) { |
324 | aprint_error_dev(sc->sc_dev, "unable to alloc CCB"); | | 323 | aprint_error_dev(sc->sc_dev, "unable to alloc CCB"); |
325 | return (EAGAIN); | | 324 | return (EAGAIN); |
326 | } | | 325 | } |
327 | | | 326 | |
328 | if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { | | 327 | if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { |
329 | bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer, | | 328 | bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer, |
330 | (void *)data, datasize, NULL, BUS_DMA_NOWAIT | | | 329 | (void *)data, datasize, NULL, BUS_DMA_NOWAIT | |
331 | BUS_DMA_STREAMING | ((flags & CAC_CCB_DATA_IN) ? | | 330 | BUS_DMA_STREAMING | ((flags & CAC_CCB_DATA_IN) ? |
332 | BUS_DMA_READ : BUS_DMA_WRITE)); | | 331 | BUS_DMA_READ : BUS_DMA_WRITE)); |
333 | | | 332 | |
334 | bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, datasize, | | 333 | bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, datasize, |
335 | (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD : | | 334 | (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD : |
336 | BUS_DMASYNC_PREWRITE); | | 335 | BUS_DMASYNC_PREWRITE); |
337 | | | 336 | |
338 | sgb = ccb->ccb_seg; | | 337 | sgb = ccb->ccb_seg; |
339 | nsegs = uimin(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE); | | 338 | nsegs = uimin(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE); |
340 | | | 339 | |
341 | for (i = 0; i < nsegs; i++, sgb++) { | | 340 | for (i = 0; i < nsegs; i++, sgb++) { |
342 | size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len; | | 341 | size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len; |
343 | sgb->length = | | 342 | sgb->length = |
344 | htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len); | | 343 | htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len); |
345 | sgb->addr = | | 344 | sgb->addr = |
346 | htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr); | | 345 | htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr); |
347 | } | | 346 | } |
348 | } else { | | 347 | } else { |
349 | size = datasize; | | 348 | size = datasize; |
350 | nsegs = 0; | | 349 | nsegs = 0; |
351 | } | | 350 | } |
352 | | | 351 | |
353 | ccb->ccb_hdr.drive = drive; | | 352 | ccb->ccb_hdr.drive = drive; |
354 | ccb->ccb_hdr.priority = 0; | | 353 | ccb->ccb_hdr.priority = 0; |
355 | ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) + | | 354 | ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) + |
356 | sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2); | | 355 | sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2); |
357 | | | 356 | |
358 | ccb->ccb_req.next = 0; | | 357 | ccb->ccb_req.next = 0; |
359 | ccb->ccb_req.error = 0; | | 358 | ccb->ccb_req.error = 0; |
360 | ccb->ccb_req.reserved = 0; | | 359 | ccb->ccb_req.reserved = 0; |
361 | ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE)); | | 360 | ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE)); |
362 | ccb->ccb_req.command = command; | | 361 | ccb->ccb_req.command = command; |
363 | ccb->ccb_req.sgcount = nsegs; | | 362 | ccb->ccb_req.sgcount = nsegs; |
364 | ccb->ccb_req.blkno = htole32(blkno); | | 363 | ccb->ccb_req.blkno = htole32(blkno); |
365 | | | 364 | |
366 | ccb->ccb_flags = flags; | | 365 | ccb->ccb_flags = flags; |
367 | ccb->ccb_datasize = size; | | 366 | ccb->ccb_datasize = size; |
368 | | | 367 | |
369 | mutex_enter(&sc->sc_mutex); | | 368 | mutex_enter(&sc->sc_mutex); |
370 | | | 369 | |
371 | if (context == NULL) { | | 370 | if (context == NULL) { |
372 | memset(&ccb->ccb_context, 0, sizeof(struct cac_context)); | | 371 | memset(&ccb->ccb_context, 0, sizeof(struct cac_context)); |
373 | | | 372 | |
374 | /* Synchronous commands musn't wait. */ | | 373 | /* Synchronous commands musn't wait. */ |
375 | if ((*sc->sc_cl.cl_fifo_full)(sc)) { | | 374 | if ((*sc->sc_cl.cl_fifo_full)(sc)) { |
376 | cac_ccb_free(sc, ccb); | | 375 | cac_ccb_free(sc, ccb); |
377 | rv = EAGAIN; | | 376 | rv = EAGAIN; |
378 | } else { | | 377 | } else { |
379 | #ifdef DIAGNOSTIC | | 378 | #ifdef DIAGNOSTIC |
380 | ccb->ccb_flags |= CAC_CCB_ACTIVE; | | 379 | ccb->ccb_flags |= CAC_CCB_ACTIVE; |
381 | #endif | | 380 | #endif |
382 | (*sc->sc_cl.cl_submit)(sc, ccb); | | 381 | (*sc->sc_cl.cl_submit)(sc, ccb); |
383 | rv = cac_ccb_poll(sc, ccb, 2000); | | 382 | rv = cac_ccb_poll(sc, ccb, 2000); |
384 | cac_ccb_free(sc, ccb); | | 383 | cac_ccb_free(sc, ccb); |
385 | } | | 384 | } |
386 | } else { | | 385 | } else { |
387 | memcpy(&ccb->ccb_context, context, sizeof(struct cac_context)); | | 386 | memcpy(&ccb->ccb_context, context, sizeof(struct cac_context)); |
388 | (void)cac_ccb_start(sc, ccb); | | 387 | (void)cac_ccb_start(sc, ccb); |
389 | rv = 0; | | 388 | rv = 0; |
390 | } | | 389 | } |
391 | | | 390 | |
392 | mutex_exit(&sc->sc_mutex); | | 391 | mutex_exit(&sc->sc_mutex); |
393 | return (rv); | | 392 | return (rv); |
394 | } | | 393 | } |
395 | | | 394 | |
396 | /* | | 395 | /* |
397 | * Wait for the specified CCB to complete. | | 396 | * Wait for the specified CCB to complete. |
398 | */ | | 397 | */ |
399 | static int | | 398 | static int |
400 | cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo) | | 399 | cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo) |
401 | { | | 400 | { |
402 | struct cac_ccb *ccb; | | 401 | struct cac_ccb *ccb; |
403 | | | 402 | |
404 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 403 | KASSERT(mutex_owned(&sc->sc_mutex)); |
405 | | | 404 | |
406 | timo *= 1000; | | 405 | timo *= 1000; |
407 | | | 406 | |
408 | do { | | 407 | do { |
409 | for (; timo != 0; timo--) { | | 408 | for (; timo != 0; timo--) { |
410 | ccb = (*sc->sc_cl.cl_completed)(sc); | | 409 | ccb = (*sc->sc_cl.cl_completed)(sc); |
411 | if (ccb != NULL) | | 410 | if (ccb != NULL) |
412 | break; | | 411 | break; |
413 | DELAY(1); | | 412 | DELAY(1); |
414 | } | | 413 | } |
415 | | | 414 | |
416 | if (timo == 0) { | | 415 | if (timo == 0) { |
417 | printf("%s: timeout\n", device_xname(sc->sc_dev)); | | 416 | printf("%s: timeout\n", device_xname(sc->sc_dev)); |
418 | return (EBUSY); | | 417 | return (EBUSY); |
419 | } | | 418 | } |
420 | cac_ccb_done(sc, ccb); | | 419 | cac_ccb_done(sc, ccb); |
421 | } while (ccb != wantccb); | | 420 | } while (ccb != wantccb); |
422 | | | 421 | |
423 | return (0); | | 422 | return (0); |
424 | } | | 423 | } |
425 | | | 424 | |
426 | /* | | 425 | /* |
427 | * Enqueue the specified command (if any) and attempt to start all enqueued | | 426 | * Enqueue the specified command (if any) and attempt to start all enqueued |
428 | * commands. | | 427 | * commands. |
429 | */ | | 428 | */ |
430 | static int | | 429 | static int |
431 | cac_ccb_start(struct cac_softc *sc, struct cac_ccb *ccb) | | 430 | cac_ccb_start(struct cac_softc *sc, struct cac_ccb *ccb) |
432 | { | | 431 | { |
433 | | | 432 | |
434 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 433 | KASSERT(mutex_owned(&sc->sc_mutex)); |
435 | | | 434 | |
436 | if (ccb != NULL) | | 435 | if (ccb != NULL) |
437 | SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain); | | 436 | SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain); |
438 | | | 437 | |
439 | while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) { | | 438 | while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) { |
440 | if ((*sc->sc_cl.cl_fifo_full)(sc)) | | 439 | if ((*sc->sc_cl.cl_fifo_full)(sc)) |
441 | return (EAGAIN); | | 440 | return (EAGAIN); |
442 | SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb_chain); | | 441 | SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb_chain); |
443 | #ifdef DIAGNOSTIC | | 442 | #ifdef DIAGNOSTIC |
444 | ccb->ccb_flags |= CAC_CCB_ACTIVE; | | 443 | ccb->ccb_flags |= CAC_CCB_ACTIVE; |
445 | #endif | | 444 | #endif |
446 | (*sc->sc_cl.cl_submit)(sc, ccb); | | 445 | (*sc->sc_cl.cl_submit)(sc, ccb); |
447 | } | | 446 | } |
448 | | | 447 | |
449 | return (0); | | 448 | return (0); |
450 | } | | 449 | } |
451 | | | 450 | |
452 | /* | | 451 | /* |
453 | * Process a finished CCB. | | 452 | * Process a finished CCB. |
454 | */ | | 453 | */ |
455 | static void | | 454 | static void |
456 | cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb) | | 455 | cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb) |
457 | { | | 456 | { |
458 | device_t dv; | | 457 | device_t dv; |
459 | void *context; | | 458 | void *context; |
460 | int error; | | 459 | int error; |
461 | | | 460 | |
462 | error = 0; | | 461 | error = 0; |
463 | | | 462 | |
464 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 463 | KASSERT(mutex_owned(&sc->sc_mutex)); |
465 | | | 464 | |
466 | #ifdef DIAGNOSTIC | | 465 | #ifdef DIAGNOSTIC |
467 | if ((ccb->ccb_flags & CAC_CCB_ACTIVE) == 0) | | 466 | if ((ccb->ccb_flags & CAC_CCB_ACTIVE) == 0) |
468 | panic("cac_ccb_done: CCB not active"); | | 467 | panic("cac_ccb_done: CCB not active"); |
469 | ccb->ccb_flags &= ~CAC_CCB_ACTIVE; | | 468 | ccb->ccb_flags &= ~CAC_CCB_ACTIVE; |
470 | #endif | | 469 | #endif |
471 | | | 470 | |
472 | if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { | | 471 | if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { |
473 | bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, | | 472 | bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, |
474 | ccb->ccb_datasize, ccb->ccb_flags & CAC_CCB_DATA_IN ? | | 473 | ccb->ccb_datasize, ccb->ccb_flags & CAC_CCB_DATA_IN ? |
475 | BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); | | 474 | BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); |
476 | bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap_xfer); | | 475 | bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap_xfer); |
477 | } | | 476 | } |
478 | | | 477 | |
479 | error = ccb->ccb_req.error; | | 478 | error = ccb->ccb_req.error; |
480 | if (ccb->ccb_context.cc_handler != NULL) { | | 479 | if (ccb->ccb_context.cc_handler != NULL) { |
481 | dv = ccb->ccb_context.cc_dv; | | 480 | dv = ccb->ccb_context.cc_dv; |
482 | context = ccb->ccb_context.cc_context; | | 481 | context = ccb->ccb_context.cc_context; |
483 | cac_ccb_free(sc, ccb); | | 482 | cac_ccb_free(sc, ccb); |
484 | (*ccb->ccb_context.cc_handler)(dv, context, error); | | 483 | (*ccb->ccb_context.cc_handler)(dv, context, error); |
485 | } else { | | 484 | } else { |
486 | if ((error & CAC_RET_SOFT_ERROR) != 0) | | 485 | if ((error & CAC_RET_SOFT_ERROR) != 0) |
487 | aprint_error_dev(sc->sc_dev, "soft error; array may be degraded\n"); | | 486 | aprint_error_dev(sc->sc_dev, "soft error; array may be degraded\n"); |
488 | if ((error & CAC_RET_HARD_ERROR) != 0) | | 487 | if ((error & CAC_RET_HARD_ERROR) != 0) |
489 | aprint_error_dev(sc->sc_dev, "hard error\n"); | | 488 | aprint_error_dev(sc->sc_dev, "hard error\n"); |
490 | if ((error & CAC_RET_CMD_REJECTED) != 0) { | | 489 | if ((error & CAC_RET_CMD_REJECTED) != 0) { |
491 | error = 1; | | 490 | error = 1; |
492 | aprint_error_dev(sc->sc_dev, "invalid request\n"); | | 491 | aprint_error_dev(sc->sc_dev, "invalid request\n"); |
493 | } | | 492 | } |
494 | } | | 493 | } |
495 | } | | 494 | } |
496 | | | 495 | |
497 | /* | | 496 | /* |
498 | * Allocate a CCB. | | 497 | * Allocate a CCB. |
499 | */ | | 498 | */ |
500 | static struct cac_ccb * | | 499 | static struct cac_ccb * |
501 | cac_ccb_alloc(struct cac_softc *sc, int nosleep) | | 500 | cac_ccb_alloc(struct cac_softc *sc, int nosleep) |
502 | { | | 501 | { |
503 | struct cac_ccb *ccb; | | 502 | struct cac_ccb *ccb; |
504 | | | 503 | |
505 | mutex_enter(&sc->sc_mutex); | | 504 | mutex_enter(&sc->sc_mutex); |
506 | | | 505 | |
507 | for (;;) { | | 506 | for (;;) { |
508 | if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) { | | 507 | if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) { |
509 | SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_chain); | | 508 | SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_chain); |
510 | break; | | 509 | break; |
511 | } | | 510 | } |
512 | if (nosleep) { | | 511 | if (nosleep) { |
513 | ccb = NULL; | | 512 | ccb = NULL; |
514 | break; | | 513 | break; |
515 | } | | 514 | } |
516 | cv_wait(&sc->sc_ccb_cv, &sc->sc_mutex); | | 515 | cv_wait(&sc->sc_ccb_cv, &sc->sc_mutex); |
517 | } | | 516 | } |
518 | | | 517 | |
519 | mutex_exit(&sc->sc_mutex); | | 518 | mutex_exit(&sc->sc_mutex); |
520 | return (ccb); | | 519 | return (ccb); |
521 | } | | 520 | } |
522 | | | 521 | |
523 | /* | | 522 | /* |
524 | * Put a CCB onto the freelist. | | 523 | * Put a CCB onto the freelist. |
525 | */ | | 524 | */ |
526 | static void | | 525 | static void |
527 | cac_ccb_free(struct cac_softc *sc, struct cac_ccb *ccb) | | 526 | cac_ccb_free(struct cac_softc *sc, struct cac_ccb *ccb) |
528 | { | | 527 | { |
529 | | | 528 | |
530 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 529 | KASSERT(mutex_owned(&sc->sc_mutex)); |
531 | | | 530 | |
532 | ccb->ccb_flags = 0; | | 531 | ccb->ccb_flags = 0; |
533 | if (SIMPLEQ_EMPTY(&sc->sc_ccb_free)) | | 532 | if (SIMPLEQ_EMPTY(&sc->sc_ccb_free)) |
534 | cv_signal(&sc->sc_ccb_cv); | | 533 | cv_signal(&sc->sc_ccb_cv); |
535 | SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); | | 534 | SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); |
536 | } | | 535 | } |
537 | | | 536 | |
538 | /* | | 537 | /* |
539 | * Board specific linkage shared between multiple bus types. | | 538 | * Board specific linkage shared between multiple bus types. |
540 | */ | | 539 | */ |
541 | | | 540 | |
542 | static int | | 541 | static int |
543 | cac_l0_fifo_full(struct cac_softc *sc) | | 542 | cac_l0_fifo_full(struct cac_softc *sc) |
544 | { | | 543 | { |
545 | | | 544 | |
546 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 545 | KASSERT(mutex_owned(&sc->sc_mutex)); |
547 | | | 546 | |
548 | return (cac_inl(sc, CAC_REG_CMD_FIFO) == 0); | | 547 | return (cac_inl(sc, CAC_REG_CMD_FIFO) == 0); |
549 | } | | 548 | } |
550 | | | 549 | |
551 | static void | | 550 | static void |
552 | cac_l0_submit(struct cac_softc *sc, struct cac_ccb *ccb) | | 551 | cac_l0_submit(struct cac_softc *sc, struct cac_ccb *ccb) |
553 | { | | 552 | { |
554 | | | 553 | |
555 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 554 | KASSERT(mutex_owned(&sc->sc_mutex)); |
556 | | | 555 | |
557 | bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, | | 556 | bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, |
558 | (char *)ccb - (char *)sc->sc_ccbs, | | 557 | (char *)ccb - (char *)sc->sc_ccbs, |
559 | sizeof(struct cac_ccb), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); | | 558 | sizeof(struct cac_ccb), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
560 | cac_outl(sc, CAC_REG_CMD_FIFO, ccb->ccb_paddr); | | 559 | cac_outl(sc, CAC_REG_CMD_FIFO, ccb->ccb_paddr); |
561 | } | | 560 | } |
562 | | | 561 | |
563 | static struct cac_ccb * | | 562 | static struct cac_ccb * |
564 | cac_l0_completed(struct cac_softc *sc) | | 563 | cac_l0_completed(struct cac_softc *sc) |
565 | { | | 564 | { |
566 | struct cac_ccb *ccb; | | 565 | struct cac_ccb *ccb; |
567 | paddr_t off; | | 566 | paddr_t off; |
568 | | | 567 | |
569 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 568 | KASSERT(mutex_owned(&sc->sc_mutex)); |
570 | | | 569 | |
571 | if ((off = cac_inl(sc, CAC_REG_DONE_FIFO)) == 0) | | 570 | if ((off = cac_inl(sc, CAC_REG_DONE_FIFO)) == 0) |
572 | return (NULL); | | 571 | return (NULL); |
573 | | | 572 | |
574 | if ((off & 3) != 0) | | 573 | if ((off & 3) != 0) |
575 | aprint_error_dev(sc->sc_dev, "failed command list returned: %lx\n", | | 574 | aprint_error_dev(sc->sc_dev, "failed command list returned: %lx\n", |
576 | (long)off); | | 575 | (long)off); |
577 | | | 576 | |
578 | off = (off & ~3) - sc->sc_ccbs_paddr; | | 577 | off = (off & ~3) - sc->sc_ccbs_paddr; |
579 | ccb = (struct cac_ccb *)((char *)sc->sc_ccbs + off); | | 578 | ccb = (struct cac_ccb *)((char *)sc->sc_ccbs + off); |
580 | | | 579 | |
581 | bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off, sizeof(struct cac_ccb), | | 580 | bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off, sizeof(struct cac_ccb), |
582 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); | | 581 | BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
583 | | | 582 | |
584 | if ((off & 3) != 0 && ccb->ccb_req.error == 0) | | 583 | if ((off & 3) != 0 && ccb->ccb_req.error == 0) |
585 | ccb->ccb_req.error = CAC_RET_CMD_REJECTED; | | 584 | ccb->ccb_req.error = CAC_RET_CMD_REJECTED; |
586 | | | 585 | |
587 | return (ccb); | | 586 | return (ccb); |
588 | } | | 587 | } |
589 | | | 588 | |
590 | static int | | 589 | static int |
591 | cac_l0_intr_pending(struct cac_softc *sc) | | 590 | cac_l0_intr_pending(struct cac_softc *sc) |
592 | { | | 591 | { |
593 | | | 592 | |
594 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 593 | KASSERT(mutex_owned(&sc->sc_mutex)); |
595 | | | 594 | |
596 | return (cac_inl(sc, CAC_REG_INTR_PENDING) & CAC_INTR_ENABLE); | | 595 | return (cac_inl(sc, CAC_REG_INTR_PENDING) & CAC_INTR_ENABLE); |
597 | } | | 596 | } |
598 | | | 597 | |
599 | static void | | 598 | static void |
600 | cac_l0_intr_enable(struct cac_softc *sc, int state) | | 599 | cac_l0_intr_enable(struct cac_softc *sc, int state) |
601 | { | | 600 | { |
602 | | | 601 | |
603 | KASSERT(mutex_owned(&sc->sc_mutex)); | | 602 | KASSERT(mutex_owned(&sc->sc_mutex)); |
604 | | | 603 | |
605 | cac_outl(sc, CAC_REG_INTR_MASK, | | 604 | cac_outl(sc, CAC_REG_INTR_MASK, |
606 | state ? CAC_INTR_ENABLE : CAC_INTR_DISABLE); | | 605 | state ? CAC_INTR_ENABLE : CAC_INTR_DISABLE); |
607 | } | | 606 | } |
608 | | | 607 | |
609 | #if NBIO > 0 | | 608 | #if NBIO > 0 |
610 | const int cac_level[] = { 0, 4, 1, 5, 51, 7 }; | | 609 | const int cac_level[] = { 0, 4, 1, 5, 51, 7 }; |
611 | const int cac_stat[] = { BIOC_SVONLINE, BIOC_SVOFFLINE, BIOC_SVOFFLINE, | | 610 | const int cac_stat[] = { BIOC_SVONLINE, BIOC_SVOFFLINE, BIOC_SVOFFLINE, |
612 | BIOC_SVDEGRADED, BIOC_SVREBUILD, BIOC_SVREBUILD, BIOC_SVDEGRADED, | | 611 | BIOC_SVDEGRADED, BIOC_SVREBUILD, BIOC_SVREBUILD, BIOC_SVDEGRADED, |
613 | BIOC_SVDEGRADED, BIOC_SVINVALID, BIOC_SVINVALID, BIOC_SVBUILDING, | | 612 | BIOC_SVDEGRADED, BIOC_SVINVALID, BIOC_SVINVALID, BIOC_SVBUILDING, |
614 | BIOC_SVOFFLINE, BIOC_SVBUILDING }; | | 613 | BIOC_SVOFFLINE, BIOC_SVBUILDING }; |
615 | | | 614 | |
616 | int | | 615 | int |
617 | cac_ioctl(device_t dev, u_long cmd, void *addr) | | 616 | cac_ioctl(device_t dev, u_long cmd, void *addr) |
618 | { | | 617 | { |
619 | struct cac_softc *sc = device_private(dev); | | 618 | struct cac_softc *sc = device_private(dev); |
620 | struct bioc_inq *bi; | | 619 | struct bioc_inq *bi; |
621 | struct bioc_disk *bd; | | 620 | struct bioc_disk *bd; |
622 | cac_lock_t lock; | | 621 | cac_lock_t lock; |
623 | int error = 0; | | 622 | int error = 0; |
624 | | | 623 | |
625 | lock = CAC_LOCK(sc); | | 624 | lock = CAC_LOCK(sc); |
626 | switch (cmd) { | | 625 | switch (cmd) { |
627 | case BIOCINQ: | | 626 | case BIOCINQ: |
628 | bi = (struct bioc_inq *)addr; | | 627 | bi = (struct bioc_inq *)addr; |
629 | strlcpy(bi->bi_dev, device_xname(sc->sc_dev), sizeof(bi->bi_dev)); | | 628 | strlcpy(bi->bi_dev, device_xname(sc->sc_dev), sizeof(bi->bi_dev)); |
630 | bi->bi_novol = sc->sc_nunits; | | 629 | bi->bi_novol = sc->sc_nunits; |
631 | bi->bi_nodisk = 0; | | 630 | bi->bi_nodisk = 0; |
632 | break; | | 631 | break; |
633 | | | 632 | |
634 | case BIOCVOL: | | 633 | case BIOCVOL: |
635 | error = cac_ioctl_vol(sc, (struct bioc_vol *)addr); | | 634 | error = cac_ioctl_vol(sc, (struct bioc_vol *)addr); |
636 | break; | | 635 | break; |
637 | | | 636 | |
638 | case BIOCDISK: | | 637 | case BIOCDISK: |
639 | case BIOCDISK_NOVOL: | | 638 | case BIOCDISK_NOVOL: |
640 | bd = (struct bioc_disk *)addr; | | 639 | bd = (struct bioc_disk *)addr; |
641 | if (bd->bd_volid > sc->sc_nunits) { | | 640 | if (bd->bd_volid > sc->sc_nunits) { |
642 | error = EINVAL; | | 641 | error = EINVAL; |
643 | break; | | 642 | break; |
644 | } | | 643 | } |
645 | /* No disk information yet */ | | 644 | /* No disk information yet */ |
646 | break; | | 645 | break; |
647 | | | 646 | |
648 | default: | | 647 | default: |
649 | error = EINVAL; | | 648 | error = EINVAL; |
650 | } | | 649 | } |
651 | CAC_UNLOCK(sc, lock); | | 650 | CAC_UNLOCK(sc, lock); |
652 | | | 651 | |
653 | return (error); | | 652 | return (error); |
654 | } | | 653 | } |
655 | | | 654 | |
656 | int | | 655 | int |
657 | cac_ioctl_vol(struct cac_softc *sc, struct bioc_vol *bv) | | 656 | cac_ioctl_vol(struct cac_softc *sc, struct bioc_vol *bv) |
658 | { | | 657 | { |
659 | struct cac_drive_info dinfo; | | 658 | struct cac_drive_info dinfo; |
660 | struct cac_drive_status dstatus; | | 659 | struct cac_drive_status dstatus; |
661 | u_int32_t blks; | | 660 | u_int32_t blks; |
662 | | | 661 | |
663 | if (bv->bv_volid > sc->sc_nunits) { | | 662 | if (bv->bv_volid > sc->sc_nunits) { |
664 | return EINVAL; | | 663 | return EINVAL; |
665 | } | | 664 | } |
666 | if (cac_cmd(sc, CAC_CMD_GET_LOG_DRV_INFO, &dinfo, sizeof(dinfo), | | 665 | if (cac_cmd(sc, CAC_CMD_GET_LOG_DRV_INFO, &dinfo, sizeof(dinfo), |
667 | bv->bv_volid, 0, CAC_CCB_DATA_IN, NULL)) { | | 666 | bv->bv_volid, 0, CAC_CCB_DATA_IN, NULL)) { |
668 | return EIO; | | 667 | return EIO; |
669 | } | | 668 | } |
670 | if (cac_cmd(sc, CAC_CMD_SENSE_DRV_STATUS, &dstatus, sizeof(dstatus), | | 669 | if (cac_cmd(sc, CAC_CMD_SENSE_DRV_STATUS, &dstatus, sizeof(dstatus), |
671 | bv->bv_volid, 0, CAC_CCB_DATA_IN, NULL)) { | | 670 | bv->bv_volid, 0, CAC_CCB_DATA_IN, NULL)) { |
672 | return EIO; | | 671 | return EIO; |
673 | } | | 672 | } |
674 | blks = CAC_GET2(dinfo.ncylinders) * CAC_GET1(dinfo.nheads) * | | 673 | blks = CAC_GET2(dinfo.ncylinders) * CAC_GET1(dinfo.nheads) * |
675 | CAC_GET1(dinfo.nsectors); | | 674 | CAC_GET1(dinfo.nsectors); |
676 | bv->bv_size = (off_t)blks * CAC_GET2(dinfo.secsize); | | 675 | bv->bv_size = (off_t)blks * CAC_GET2(dinfo.secsize); |
677 | bv->bv_level = cac_level[CAC_GET1(dinfo.mirror)]; /*XXX limit check */ | | 676 | bv->bv_level = cac_level[CAC_GET1(dinfo.mirror)]; /*XXX limit check */ |
678 | bv->bv_nodisk = 0; /* XXX */ | | 677 | bv->bv_nodisk = 0; /* XXX */ |
679 | bv->bv_status = 0; /* XXX */ | | 678 | bv->bv_status = 0; /* XXX */ |
680 | bv->bv_percent = -1; | | 679 | bv->bv_percent = -1; |
681 | bv->bv_seconds = 0; | | 680 | bv->bv_seconds = 0; |
682 | if (dstatus.stat < sizeof(cac_stat)/sizeof(cac_stat[0])) | | 681 | if (dstatus.stat < sizeof(cac_stat)/sizeof(cac_stat[0])) |
683 | bv->bv_status = cac_stat[dstatus.stat]; | | 682 | bv->bv_status = cac_stat[dstatus.stat]; |
684 | if (bv->bv_status == BIOC_SVREBUILD || | | 683 | if (bv->bv_status == BIOC_SVREBUILD || |
685 | bv->bv_status == BIOC_SVBUILDING) | | 684 | bv->bv_status == BIOC_SVBUILDING) |
686 | bv->bv_percent = ((blks - CAC_GET4(dstatus.prog)) * 1000ULL) / | | 685 | bv->bv_percent = ((blks - CAC_GET4(dstatus.prog)) * 1000ULL) / |
687 | blks; | | 686 | blks; |
688 | return 0; | | 687 | return 0; |
689 | } | | 688 | } |
690 | | | 689 | |
691 | int | | 690 | int |
692 | cac_create_sensors(struct cac_softc *sc) | | 691 | cac_create_sensors(struct cac_softc *sc) |
693 | { | | 692 | { |
694 | int i; | | 693 | int i; |
695 | int nsensors = sc->sc_nunits; | | 694 | int nsensors = sc->sc_nunits; |
696 | | | 695 | |
697 | sc->sc_sme = sysmon_envsys_create(); | | 696 | sc->sc_sme = sysmon_envsys_create(); |
698 | sc->sc_sensor = malloc(sizeof(envsys_data_t) * nsensors, | | 697 | sc->sc_sensor = malloc(sizeof(envsys_data_t) * nsensors, |
699 | M_DEVBUF, M_WAITOK | M_ZERO); | | 698 | M_DEVBUF, M_WAITOK | M_ZERO); |
700 | for (i = 0; i < nsensors; i++) { | | 699 | for (i = 0; i < nsensors; i++) { |
701 | sc->sc_sensor[i].units = ENVSYS_DRIVE; | | 700 | sc->sc_sensor[i].units = ENVSYS_DRIVE; |
702 | sc->sc_sensor[i].state = ENVSYS_SINVALID; | | 701 | sc->sc_sensor[i].state = ENVSYS_SINVALID; |
703 | sc->sc_sensor[i].value_cur = ENVSYS_DRIVE_EMPTY; | | 702 | sc->sc_sensor[i].value_cur = ENVSYS_DRIVE_EMPTY; |
704 | /* Enable monitoring for drive state changes */ | | 703 | /* Enable monitoring for drive state changes */ |
705 | sc->sc_sensor[i].flags |= ENVSYS_FMONSTCHANGED; | | 704 | sc->sc_sensor[i].flags |= ENVSYS_FMONSTCHANGED; |
706 | /* logical drives */ | | 705 | /* logical drives */ |
707 | snprintf(sc->sc_sensor[i].desc, | | 706 | snprintf(sc->sc_sensor[i].desc, |
708 | sizeof(sc->sc_sensor[i].desc), "%s:%d", | | 707 | sizeof(sc->sc_sensor[i].desc), "%s:%d", |
709 | device_xname(sc->sc_dev), i); | | 708 | device_xname(sc->sc_dev), i); |
710 | if (sysmon_envsys_sensor_attach(sc->sc_sme, | | 709 | if (sysmon_envsys_sensor_attach(sc->sc_sme, |
711 | &sc->sc_sensor[i])) | | 710 | &sc->sc_sensor[i])) |
712 | goto out; | | 711 | goto out; |
713 | } | | 712 | } |
714 | sc->sc_sme->sme_name = device_xname(sc->sc_dev); | | 713 | sc->sc_sme->sme_name = device_xname(sc->sc_dev); |
715 | sc->sc_sme->sme_cookie = sc; | | 714 | sc->sc_sme->sme_cookie = sc; |
716 | sc->sc_sme->sme_refresh = cac_sensor_refresh; | | 715 | sc->sc_sme->sme_refresh = cac_sensor_refresh; |
717 | if (sysmon_envsys_register(sc->sc_sme)) { | | 716 | if (sysmon_envsys_register(sc->sc_sme)) { |
718 | aprint_error_dev(sc->sc_dev, "unable to register with sysmon\n"); | | 717 | aprint_error_dev(sc->sc_dev, "unable to register with sysmon\n"); |
719 | return(1); | | 718 | return(1); |
720 | } | | 719 | } |
721 | return (0); | | 720 | return (0); |
722 | | | 721 | |
723 | out: | | 722 | out: |
724 | free(sc->sc_sensor, M_DEVBUF); | | 723 | free(sc->sc_sensor, M_DEVBUF); |
725 | sysmon_envsys_destroy(sc->sc_sme); | | 724 | sysmon_envsys_destroy(sc->sc_sme); |
726 | return EINVAL; | | 725 | return EINVAL; |
727 | } | | 726 | } |
728 | | | 727 | |
729 | void | | 728 | void |
730 | cac_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) | | 729 | cac_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) |
731 | { | | 730 | { |
732 | struct cac_softc *sc = sme->sme_cookie; | | 731 | struct cac_softc *sc = sme->sme_cookie; |
733 | struct bioc_vol bv; | | 732 | struct bioc_vol bv; |
734 | int s; | | 733 | int s; |
735 | | | 734 | |
736 | if (edata->sensor >= sc->sc_nunits) | | 735 | if (edata->sensor >= sc->sc_nunits) |
737 | return; | | 736 | return; |
738 | | | 737 | |
739 | memset(&bv, 0, sizeof(bv)); | | 738 | memset(&bv, 0, sizeof(bv)); |
740 | bv.bv_volid = edata->sensor; | | 739 | bv.bv_volid = edata->sensor; |
741 | s = splbio(); | | 740 | s = splbio(); |
742 | if (cac_ioctl_vol(sc, &bv)) | | 741 | if (cac_ioctl_vol(sc, &bv)) |
743 | bv.bv_status = BIOC_SVINVALID; | | 742 | bv.bv_status = BIOC_SVINVALID; |
744 | splx(s); | | 743 | splx(s); |
745 | | | 744 | |
746 | bio_vol_to_envsys(edata, &bv); | | 745 | bio_vol_to_envsys(edata, &bv); |
747 | } | | 746 | } |
748 | #endif /* NBIO > 0 */ | | 747 | #endif /* NBIO > 0 */ |
749 | | | 748 | |
750 | MODULE(MODULE_CLASS_DRIVER, cac, NULL); | | 749 | MODULE(MODULE_CLASS_DRIVER, cac, NULL); |
751 | | | 750 | |
752 | #ifdef _MODULE | | 751 | #ifdef _MODULE |
753 | CFDRIVER_DECL(cac, DV_DISK, NULL); | | 752 | CFDRIVER_DECL(cac, DV_DISK, NULL); |
754 | #endif | | 753 | #endif |
755 | | | 754 | |
756 | static int | | 755 | static int |
757 | cac_modcmd(modcmd_t cmd, void *opaque) | | 756 | cac_modcmd(modcmd_t cmd, void *opaque) |
758 | { | | 757 | { |
759 | int error = 0; | | 758 | int error = 0; |
760 | | | 759 | |
761 | #ifdef _MODULE | | 760 | #ifdef _MODULE |
762 | switch (cmd) { | | 761 | switch (cmd) { |
763 | case MODULE_CMD_INIT: | | 762 | case MODULE_CMD_INIT: |
764 | error = config_cfdriver_attach(&cac_cd); | | 763 | error = config_cfdriver_attach(&cac_cd); |
765 | break; | | 764 | break; |
766 | case MODULE_CMD_FINI: | | 765 | case MODULE_CMD_FINI: |
767 | error = config_cfdriver_detach(&cac_cd); | | 766 | error = config_cfdriver_detach(&cac_cd); |
768 | break; | | 767 | break; |
769 | default: | | 768 | default: |
770 | error = ENOTTY; | | 769 | error = ENOTTY; |
771 | break; | | 770 | break; |
772 | } | | 771 | } |
773 | #endif | | 772 | #endif |
774 | return error; | | 773 | return error; |
775 | } | | 774 | } |