1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * SD card common framework. This module provides most of the common
27 * functionality so that SecureDigital host adapters and client devices
28 * (such as the sdcard driver) can share common code.
29 */
30
31 #include <sys/types.h>
32 #include <sys/kmem.h>
33 #include <sys/sysmacros.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/sunndi.h>
37 #include <sys/sdcard/sda_impl.h>
38
39 /*
40 * Types and Structures.
41 */
42
43 typedef struct sda_cmd_impl {
44 struct sda_cmd c_public;
45
46 /*
47 * Implementation private stuff.
48 */
49 sda_slot_t *c_slot;
50 kmutex_t c_lock;
51 kcondvar_t c_cv;
52 list_node_t c_list;
53 sda_err_t c_errno;
54
55 sda_index_t c_acmd; /* saved acmd */
56 sda_rtype_t c_artype; /* saved rtype */
57 uint32_t c_aarg; /* saved argument */
58
59 void (*c_done)(struct sda_cmd *);
60 void *c_private;
61 } sda_cmd_impl_t;
62
63 #define c_index c_public.sc_index
64 #define c_argument c_public.sc_argument
65 #define c_rtype c_public.sc_rtype
66 #define c_response c_public.sc_response
67 #define c_blksz c_public.sc_blksz
68 #define c_nblks c_public.sc_nblks
69 #define c_resid c_public.sc_resid
70 #define c_flags c_public.sc_flags
71 #define c_ndmac c_public.sc_ndmac
72 #define c_dmah c_public.sc_dmah
73 #define c_dmac c_public.sc_dmac
74 #define c_kvaddr c_public.sc_kvaddr
75
76 /*
77 * Local Prototypes.
78 */
79
80 static void sda_cmd_wait(sda_cmd_t *);
81 static int sda_cmd_ctor(void *, void *, int);
82 static void sda_cmd_dtor(void *, void *);
83
84 /*
85 * Static Variables.
86 */
87
88 static kmem_cache_t *sda_cmd_cache;
89
90 /*
91 * Macros.
92 */
93
94 #define CIP(cmdp) ((sda_cmd_impl_t *)(void *)cmdp)
95
96 /*
97 * Implementation.
98 */
99
100 void
sda_cmd_init(void)101 sda_cmd_init(void)
102 {
103 sda_cmd_cache = kmem_cache_create("sda_cmd_cache",
104 sizeof (struct sda_cmd_impl), 0, sda_cmd_ctor, sda_cmd_dtor,
105 NULL, NULL, NULL, 0);
106 }
107
108 void
sda_cmd_fini(void)109 sda_cmd_fini(void)
110 {
111 kmem_cache_destroy(sda_cmd_cache);
112 }
113
114 void
sda_cmd_list_init(list_t * list)115 sda_cmd_list_init(list_t *list)
116 {
117 list_create(list, sizeof (struct sda_cmd_impl),
118 offsetof(struct sda_cmd_impl, c_list));
119 }
120
121 void
sda_cmd_list_fini(list_t * list)122 sda_cmd_list_fini(list_t *list)
123 {
124 list_destroy(list);
125 }
126
127 /*ARGSUSED1*/
128 int
sda_cmd_ctor(void * cbuf,void * arg,int kmflags)129 sda_cmd_ctor(void *cbuf, void *arg, int kmflags)
130 {
131 sda_cmd_impl_t *c = cbuf;
132
133 mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL);
134 cv_init(&c->c_cv, NULL, CV_DRIVER, NULL);
135 return (0);
136 }
137
138 /*ARGSUSED1*/
139 void
sda_cmd_dtor(void * cbuf,void * arg)140 sda_cmd_dtor(void *cbuf, void *arg)
141 {
142 sda_cmd_impl_t *c = cbuf;
143
144 cv_destroy(&c->c_cv);
145 mutex_destroy(&c->c_lock);
146 }
147
148 void *
sda_cmd_data(sda_cmd_t * cmdp)149 sda_cmd_data(sda_cmd_t *cmdp)
150 {
151 return (CIP(cmdp)->c_private);
152 }
153
154 sda_err_t
sda_cmd_errno(sda_cmd_t * cmdp)155 sda_cmd_errno(sda_cmd_t *cmdp)
156 {
157 return (CIP(cmdp)->c_errno);
158 }
159
160 void
sda_cmd_notify(sda_cmd_t * cmdp,uint16_t flags,sda_err_t errno)161 sda_cmd_notify(sda_cmd_t *cmdp, uint16_t flags, sda_err_t errno)
162 {
163 sda_cmd_impl_t *c = CIP(cmdp);
164
165 /*
166 * Now we need to make sure that we wake anyone waiting on this
167 * command to complete, if it is complete.
168 */
169 mutex_enter(&c->c_lock);
170 c->c_flags &= ~(flags);
171 /*
172 * Don't overwrite an earlier error.
173 */
174 if (c->c_errno == SDA_EOK) {
175 c->c_errno = errno;
176 }
177 if ((c->c_flags & (SDA_CMDF_BUSY | SDA_CMDF_DAT)) == 0) {
178
179 if (c->c_done != NULL) {
180 mutex_exit(&c->c_lock);
181 c->c_done(cmdp);
182 } else {
183 cv_broadcast(&c->c_cv);
184 mutex_exit(&c->c_lock);
185 }
186 } else {
187 mutex_exit(&c->c_lock);
188 }
189 }
190
191 void
sda_cmd_wait(sda_cmd_t * cmdp)192 sda_cmd_wait(sda_cmd_t *cmdp)
193 {
194 sda_cmd_impl_t *c = CIP(cmdp);
195
196 mutex_enter(&c->c_lock);
197 while ((c->c_flags & (SDA_CMDF_BUSY | SDA_CMDF_DAT)) != 0)
198 cv_wait(&c->c_cv, &c->c_lock);
199 mutex_exit(&c->c_lock);
200 }
201
202 void
sda_cmd_submit(sda_slot_t * slot,sda_cmd_t * cmdp,void (* done)(sda_cmd_t *))203 sda_cmd_submit(sda_slot_t *slot, sda_cmd_t *cmdp, void (*done)(sda_cmd_t *))
204 {
205 sda_cmd_impl_t *c = CIP(cmdp);
206 sda_err_t errno = 0;
207
208 mutex_enter(&c->c_lock);
209 c->c_done = done;
210 c->c_flags |= SDA_CMDF_BUSY;
211 mutex_exit(&c->c_lock);
212
213 sda_slot_enter(slot);
214
215 /* checks for cases where the slot can't accept the command */
216 if (slot->s_failed) {
217 errno = SDA_EFAULT;
218 }
219 if (!slot->s_inserted) {
220 errno = SDA_ENODEV;
221 }
222 if (errno != SDA_EOK) {
223 /*
224 * We have to return failure conditions asynchronously.
225 * What we do in this case is mark the command failed,
226 * and move it to the abortlist so that the slot thread
227 * will execute the failure notification asynchronously.
228 *
229 * NB: using 0 for flags ensures that we don't execute
230 * the notification callback yet, we're just stashing
231 * the errno.
232 */
233 sda_cmd_notify(cmdp, 0, errno);
234 list_insert_tail(&slot->s_abortlist, cmdp);
235
236 } else if (c->c_flags & SDA_CMDF_INIT) {
237 /* Initialization commands go to the head of the class */
238 list_insert_head(&slot->s_cmdlist, c);
239 } else {
240 list_insert_tail(&slot->s_cmdlist, c);
241 }
242 sda_slot_exit(slot);
243
244 sda_slot_wakeup(slot);
245 }
246
247 void
sda_cmd_resubmit_acmd(sda_slot_t * slot,sda_cmd_t * cmdp)248 sda_cmd_resubmit_acmd(sda_slot_t *slot, sda_cmd_t *cmdp)
249 {
250 sda_cmd_impl_t *c = CIP(cmdp);
251
252 ASSERT(sda_slot_owned(slot));
253
254 c->c_index = c->c_acmd;
255 c->c_argument = c->c_aarg;
256 c->c_rtype = c->c_artype;
257 c->c_acmd = 0;
258
259 list_insert_head(&slot->s_cmdlist, c);
260 }
261
262 sda_cmd_t *
sda_cmd_alloc(sda_slot_t * slot,sda_index_t index,uint32_t argument,sda_rtype_t rtype,void * data,int kmflag)263 sda_cmd_alloc(sda_slot_t *slot, sda_index_t index, uint32_t argument,
264 sda_rtype_t rtype, void *data, int kmflag)
265 {
266 sda_cmd_impl_t *c;
267
268 c = kmem_cache_alloc(sda_cmd_cache, kmflag);
269 if (c == NULL) {
270 return (NULL);
271 }
272 c->c_index = index;
273 c->c_rtype = rtype;
274 c->c_argument = argument;
275 c->c_resid = 0;
276 c->c_nblks = 0;
277 c->c_blksz = 0;
278
279 c->c_kvaddr = 0;
280 c->c_dmah = 0;
281 c->c_ndmac = 0;
282 c->c_dmah = NULL;
283 bzero(&c->c_dmac, sizeof (c->c_dmac));
284 c->c_flags = 0;
285
286 c->c_slot = slot;
287 c->c_errno = SDA_EOK;
288 c->c_done = NULL;
289 c->c_private = data;
290 c->c_acmd = 0;
291
292 return (&(c->c_public));
293 }
294
295 sda_cmd_t *
sda_cmd_alloc_acmd(sda_slot_t * slot,sda_index_t index,uint32_t argument,sda_rtype_t rtype,void * data,int kmflag)296 sda_cmd_alloc_acmd(sda_slot_t *slot, sda_index_t index, uint32_t argument,
297 sda_rtype_t rtype, void *data, int kmflag)
298 {
299 sda_cmd_impl_t *c;
300
301 c = kmem_cache_alloc(sda_cmd_cache, kmflag);
302 if (c == NULL) {
303 return (NULL);
304 }
305 c->c_index = CMD_APP_CMD;
306 c->c_argument = index == ACMD_SD_SEND_OCR ? 0 : slot->s_rca << 16;
307 c->c_rtype = R1;
308 c->c_acmd = index;
309 c->c_artype = rtype;
310 c->c_aarg = argument;
311 c->c_resid = 0;
312 c->c_nblks = 0;
313 c->c_blksz = 0;
314
315 c->c_kvaddr = 0;
316 c->c_ndmac = 0;
317 c->c_dmah = NULL;
318 bzero(&c->c_dmac, sizeof (c->c_dmac));
319 c->c_flags = 0;
320
321 c->c_slot = slot;
322 c->c_errno = SDA_EOK;
323 c->c_done = NULL;
324 c->c_private = data;
325
326 return (&(c->c_public));
327 }
328
329 void
sda_cmd_free(sda_cmd_t * cmdp)330 sda_cmd_free(sda_cmd_t *cmdp)
331 {
332 kmem_cache_free(sda_cmd_cache, cmdp);
333 }
334
335 sda_err_t
sda_cmd_exec(sda_slot_t * slot,sda_cmd_t * cmdp,uint32_t * resp)336 sda_cmd_exec(sda_slot_t *slot, sda_cmd_t *cmdp, uint32_t *resp)
337 {
338 int errno;
339
340 if ((cmdp->sc_rtype & Rb) || (cmdp->sc_nblks != 0)) {
341 cmdp->sc_flags |= SDA_CMDF_DAT;
342 }
343 sda_cmd_submit(slot, cmdp, NULL);
344
345 sda_cmd_wait(cmdp);
346
347 if (resp != NULL) {
348 switch (cmdp->sc_rtype) {
349 case R0:
350 break;
351 case R2:
352 resp[0] = cmdp->sc_response[0];
353 resp[1] = cmdp->sc_response[1];
354 resp[2] = cmdp->sc_response[2];
355 resp[3] = cmdp->sc_response[3];
356 break;
357 default:
358 resp[0] = cmdp->sc_response[0];
359 break;
360 }
361 }
362
363 errno = CIP(cmdp)->c_errno;
364
365 return (errno);
366 }
367