xref: /illumos-gate/usr/src/uts/common/io/sdcard/impl/sda_cmd.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
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
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
109 sda_cmd_fini(void)
110 {
111 	kmem_cache_destroy(sda_cmd_cache);
112 }
113 
114 void
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
122 sda_cmd_list_fini(list_t *list)
123 {
124 	list_destroy(list);
125 }
126 
127 /*ARGSUSED1*/
128 int
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
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 *
149 sda_cmd_data(sda_cmd_t *cmdp)
150 {
151 	return (CIP(cmdp)->c_private);
152 }
153 
154 sda_err_t
155 sda_cmd_errno(sda_cmd_t *cmdp)
156 {
157 	return (CIP(cmdp)->c_errno);
158 }
159 
160 void
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
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
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
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 *
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 *
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
330 sda_cmd_free(sda_cmd_t *cmdp)
331 {
332 	kmem_cache_free(sda_cmd_cache, cmdp);
333 }
334 
335 sda_err_t
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