xref: /illumos-gate/usr/src/uts/common/io/sdcard/impl/sda_cmd.c (revision f2b90c3c415ff04d4adb3a54242822b41d74bfd9)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * SD card common framework.  This module provides most of the common
28  * functionality so that SecureDigital host adapters and client devices
29  * (such as the sdcard driver) can share common code.
30  *
31  * NB that this file contains a fair bit of non-DDI compliant code.
32  * But writing a nexus driver would be impossible to do with only DDI
33  * compliant interfaces.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/kmem.h>
38 #include <sys/sysmacros.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 #include <sys/sunndi.h>
42 #include <sys/sdcard/sda_impl.h>
43 
44 /*
45  * Types and Structures.
46  */
47 
48 typedef struct sda_cmd_impl {
49 	struct sda_cmd	c_public;
50 
51 	/*
52 	 * Implementation private stuff.
53 	 */
54 	sda_slot_t	*c_slot;
55 	kmutex_t	c_lock;
56 	kcondvar_t	c_cv;
57 	list_node_t	c_list;
58 	sda_err_t	c_errno;
59 
60 	sda_index_t	c_acmd;		/* saved acmd */
61 	sda_rtype_t	c_artype;	/* saved rtype */
62 	uint32_t	c_aarg;		/* saved argument */
63 
64 	void		(*c_done)(struct sda_cmd *);
65 	void		*c_private;
66 } sda_cmd_impl_t;
67 
68 #define	c_index		c_public.sc_index
69 #define	c_argument	c_public.sc_argument
70 #define	c_rtype		c_public.sc_rtype
71 #define	c_response	c_public.sc_response
72 #define	c_blksz		c_public.sc_blksz
73 #define	c_nblks		c_public.sc_nblks
74 #define	c_resid		c_public.sc_resid
75 #define	c_flags		c_public.sc_flags
76 #define	c_ndmac		c_public.sc_ndmac
77 #define	c_dmacs		c_public.sc_dmacs
78 #define	c_kvaddr	c_public.sc_kvaddr
79 
80 /*
81  * Local Prototypes.
82  */
83 
84 static void sda_cmd_wait(sda_cmd_t *);
85 static int sda_cmd_ctor(void *, void *, int);
86 static void sda_cmd_dtor(void *, void *);
87 
88 /*
89  * Static Variables.
90  */
91 
92 static kmem_cache_t *sda_cmd_cache;
93 
94 /*
95  * Macros.
96  */
97 
98 #define	CIP(cmdp)	((sda_cmd_impl_t *)(void *)cmdp)
99 
100 /*
101  * Implementation.
102  */
103 
104 void
105 sda_cmd_init(void)
106 {
107 	sda_cmd_cache = kmem_cache_create("sda_cmd_cache",
108 	    sizeof (struct sda_cmd_impl), 0, sda_cmd_ctor, sda_cmd_dtor,
109 	    NULL, NULL, NULL, 0);
110 }
111 
112 void
113 sda_cmd_fini(void)
114 {
115 	kmem_cache_destroy(sda_cmd_cache);
116 }
117 
118 void
119 sda_cmd_list_init(list_t *list)
120 {
121 	list_create(list, sizeof (struct sda_cmd_impl),
122 	    offsetof(struct sda_cmd_impl, c_list));
123 }
124 
125 void
126 sda_cmd_list_fini(list_t *list)
127 {
128 	list_destroy(list);
129 }
130 
131 /*ARGSUSED1*/
132 int
133 sda_cmd_ctor(void *cbuf, void *arg, int kmflags)
134 {
135 	sda_cmd_impl_t	*c = cbuf;
136 
137 	mutex_init(&c->c_lock, NULL, MUTEX_DRIVER, NULL);
138 	cv_init(&c->c_cv, NULL, CV_DRIVER, NULL);
139 	return (0);
140 }
141 
142 /*ARGSUSED1*/
143 void
144 sda_cmd_dtor(void *cbuf, void *arg)
145 {
146 	sda_cmd_impl_t	*c = cbuf;
147 
148 	cv_destroy(&c->c_cv);
149 	mutex_destroy(&c->c_lock);
150 }
151 
152 void *
153 sda_cmd_data(sda_cmd_t *cmdp)
154 {
155 	return (CIP(cmdp)->c_private);
156 }
157 
158 sda_err_t
159 sda_cmd_errno(sda_cmd_t *cmdp)
160 {
161 	return (CIP(cmdp)->c_errno);
162 }
163 
164 void
165 sda_cmd_notify(sda_cmd_t *cmdp, uint16_t flags, sda_err_t errno)
166 {
167 	sda_cmd_impl_t	*c = CIP(cmdp);
168 
169 	/*
170 	 * Now we need to make sure that we wake anyone waiting on this
171 	 * command to complete, if it is complete.
172 	 */
173 	mutex_enter(&c->c_lock);
174 	c->c_flags &= ~(flags);
175 	/*
176 	 * Don't overwrite an earlier error.
177 	 */
178 	if (c->c_errno == SDA_EOK) {
179 		c->c_errno = errno;
180 	}
181 	if ((c->c_flags & (SDA_CMDF_BUSY | SDA_CMDF_DAT)) == 0) {
182 
183 		if (c->c_done != NULL) {
184 			mutex_exit(&c->c_lock);
185 			c->c_done(cmdp);
186 		} else {
187 			cv_broadcast(&c->c_cv);
188 			mutex_exit(&c->c_lock);
189 		}
190 	} else {
191 		mutex_exit(&c->c_lock);
192 	}
193 }
194 
195 void
196 sda_cmd_wait(sda_cmd_t *cmdp)
197 {
198 	sda_cmd_impl_t	*c = CIP(cmdp);
199 
200 	mutex_enter(&c->c_lock);
201 	while ((c->c_flags & (SDA_CMDF_BUSY | SDA_CMDF_DAT)) != 0)
202 		cv_wait(&c->c_cv, &c->c_lock);
203 	mutex_exit(&c->c_lock);
204 }
205 
206 void
207 sda_cmd_submit(sda_slot_t *slot, sda_cmd_t *cmdp, void (*done)(sda_cmd_t *))
208 {
209 	sda_cmd_impl_t	*c = CIP(cmdp);
210 	sda_err_t	errno = 0;
211 
212 	mutex_enter(&c->c_lock);
213 	c->c_done = done;
214 	c->c_flags |= SDA_CMDF_BUSY;
215 	mutex_exit(&c->c_lock);
216 
217 	sda_slot_enter(slot);
218 
219 	/* checks for cases where the slot can't accept the command */
220 	if (slot->s_failed) {
221 		errno = SDA_EFAULT;
222 	}
223 	if (!slot->s_inserted) {
224 		errno = SDA_ENODEV;
225 	}
226 	if (errno != SDA_EOK) {
227 		sda_slot_exit(slot);
228 		/* fail it synchronously */
229 		sda_cmd_notify(cmdp, SDA_CMDF_DAT | SDA_CMDF_BUSY, errno);
230 		return;
231 	}
232 
233 	/* Initialization commands go to the head of the class */
234 	if (c->c_flags & SDA_CMDF_INIT) {
235 		list_insert_head(&slot->s_cmdlist, c);
236 	} else {
237 		list_insert_tail(&slot->s_cmdlist, c);
238 	}
239 	sda_slot_exit(slot);
240 
241 	sda_slot_wakeup(slot);
242 }
243 
244 void
245 sda_cmd_resubmit_acmd(sda_slot_t *slot, sda_cmd_t *cmdp)
246 {
247 	sda_cmd_impl_t	*c = CIP(cmdp);
248 
249 	ASSERT(sda_slot_owned(slot));
250 
251 	c->c_index = c->c_acmd;
252 	c->c_argument = c->c_aarg;
253 	c->c_rtype = c->c_artype;
254 	c->c_acmd = 0;
255 
256 	list_insert_head(&slot->s_cmdlist, c);
257 }
258 
259 sda_cmd_t *
260 sda_cmd_alloc(sda_slot_t *slot, sda_index_t index, uint32_t argument,
261     sda_rtype_t rtype, void *data, int kmflag)
262 {
263 	sda_cmd_impl_t	*c;
264 
265 	c = kmem_cache_alloc(sda_cmd_cache, kmflag);
266 	if (c == NULL) {
267 		return (NULL);
268 	}
269 	c->c_index = index;
270 	c->c_rtype = rtype;
271 	c->c_argument = argument;
272 	c->c_resid = 0;
273 	c->c_nblks = 0;
274 	c->c_blksz = 0;
275 
276 	c->c_kvaddr = 0;
277 	c->c_ndmac = 0;
278 	c->c_dmacs = NULL;
279 	c->c_flags = 0;
280 
281 	c->c_slot = slot;
282 	c->c_errno = SDA_EOK;
283 	c->c_done = NULL;
284 	c->c_private = data;
285 	c->c_acmd = 0;
286 
287 	return (&(c->c_public));
288 }
289 
290 sda_cmd_t *
291 sda_cmd_alloc_acmd(sda_slot_t *slot, sda_index_t index, uint32_t argument,
292     sda_rtype_t rtype, void *data, int kmflag)
293 {
294 	sda_cmd_impl_t	*c;
295 
296 	c = kmem_cache_alloc(sda_cmd_cache, kmflag);
297 	if (c == NULL) {
298 		return (NULL);
299 	}
300 	c->c_index = CMD_APP_CMD;
301 	c->c_argument = index == ACMD_SD_SEND_OCR ? 0 : slot->s_rca << 16;
302 	c->c_rtype = R1;
303 	c->c_acmd = index;
304 	c->c_artype = rtype;
305 	c->c_aarg = argument;
306 	c->c_resid = 0;
307 	c->c_nblks = 0;
308 	c->c_blksz = 0;
309 
310 	c->c_kvaddr = 0;
311 	c->c_ndmac = 0;
312 	c->c_dmacs = NULL;
313 	c->c_flags = 0;
314 
315 	c->c_slot = slot;
316 	c->c_errno = SDA_EOK;
317 	c->c_done = NULL;
318 	c->c_private = data;
319 
320 	return (&(c->c_public));
321 }
322 
323 void
324 sda_cmd_free(sda_cmd_t *cmdp)
325 {
326 	kmem_cache_free(sda_cmd_cache, cmdp);
327 }
328 
329 sda_err_t
330 sda_cmd_exec(sda_slot_t *slot, sda_cmd_t *cmdp, uint32_t *resp)
331 {
332 	int		errno;
333 
334 	if ((cmdp->sc_rtype & Rb) || (cmdp->sc_nblks != 0)) {
335 		cmdp->sc_flags |= SDA_CMDF_DAT;
336 	}
337 	sda_cmd_submit(slot, cmdp,  NULL);
338 
339 	sda_cmd_wait(cmdp);
340 
341 	if (resp != NULL) {
342 		switch (cmdp->sc_rtype) {
343 		case R0:
344 			break;
345 		case R2:
346 			resp[0] = cmdp->sc_response[0];
347 			resp[1] = cmdp->sc_response[1];
348 			resp[2] = cmdp->sc_response[2];
349 			resp[3] = cmdp->sc_response[3];
350 			break;
351 		default:
352 			resp[0] = cmdp->sc_response[0];
353 			break;
354 		}
355 	}
356 
357 	errno = CIP(cmdp)->c_errno;
358 
359 	return (errno);
360 }
361