1 /* $NetBSD: ld_sdmmc.c,v 1.3 2009/05/29 22:27:40 nonaka Exp $ */
4 * Copyright (c) 2008 KIYOHARA Takashi
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: ld_sdmmc.c,v 1.3 2009/05/29 22:27:40 nonaka Exp $");
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
42 #include <sys/callout.h>
43 #include <sys/endian.h>
46 #include <sys/kthread.h>
51 #include <uvm/uvm_extern.h>
53 #include <dev/ldvar.h>
55 #include <dev/sdmmc/sdmmcvar.h>
58 #define DPRINTF(s) printf s
60 #define DPRINTF(s) /**/
63 struct ld_sdmmc_softc
;
65 struct ld_sdmmc_task
{
66 struct sdmmc_task task
;
68 struct ld_sdmmc_softc
*task_sc
;
70 callout_t task_callout
;
73 struct ld_sdmmc_softc
{
74 struct ld_softc sc_ld
;
77 struct sdmmc_function
*sc_sf
;
78 struct ld_sdmmc_task sc_task
;
81 static int ld_sdmmc_match(device_t
, cfdata_t
, void *);
82 static void ld_sdmmc_attach(device_t
, device_t
, void *);
83 static int ld_sdmmc_detach(device_t
, int);
85 static int ld_sdmmc_dump(struct ld_softc
*, void *, int, int);
86 static int ld_sdmmc_start(struct ld_softc
*, struct buf
*);
88 static void ld_sdmmc_doattach(void *);
89 static void ld_sdmmc_dobio(void *);
90 static void ld_sdmmc_timeout(void *);
92 CFATTACH_DECL_NEW(ld_sdmmc
, sizeof(struct ld_sdmmc_softc
),
93 ld_sdmmc_match
, ld_sdmmc_attach
, ld_sdmmc_detach
, NULL
);
98 ld_sdmmc_match(device_t parent
, cfdata_t match
, void *aux
)
100 struct sdmmc_softc
*sdmsc
= device_private(parent
);
102 if (ISSET(sdmsc
->sc_flags
, SMF_MEM_MODE
))
109 ld_sdmmc_attach(device_t parent
, device_t self
, void *aux
)
111 struct ld_sdmmc_softc
*sc
= device_private(self
);
112 struct sdmmc_attach_args
*sa
= aux
;
113 struct ld_softc
*ld
= &sc
->sc_ld
;
121 callout_init(&sc
->sc_task
.task_callout
, CALLOUT_MPSAFE
);
123 sc
->sc_hwunit
= 0; /* always 0? */
126 ld
->sc_flags
= LDF_ENABLED
;
127 ld
->sc_secperunit
= sc
->sc_sf
->csd
.capacity
;
128 ld
->sc_secsize
= SDMMC_SECTOR_SIZE
;
129 ld
->sc_maxxfer
= MAXPHYS
;
130 ld
->sc_maxqueuecnt
= 1;
131 ld
->sc_dump
= ld_sdmmc_dump
;
132 ld
->sc_start
= ld_sdmmc_start
;
135 * It is avoided that the error occurs when the card attaches it,
136 * when wedge is supported.
138 if (kthread_create(PRI_NONE
, KTHREAD_MPSAFE
, NULL
,
139 ld_sdmmc_doattach
, sc
, &lwp
, "%sattach", device_xname(self
))) {
140 aprint_error_dev(self
, "couldn't create thread\n");
145 ld_sdmmc_doattach(void *arg
)
147 struct ld_sdmmc_softc
*sc
= (struct ld_sdmmc_softc
*)arg
;
148 struct ld_softc
*ld
= &sc
->sc_ld
;
155 ld_sdmmc_detach(device_t dev
, int flags
)
157 struct ld_sdmmc_softc
*sc
= device_private(dev
);
158 struct ld_softc
*ld
= &sc
->sc_ld
;
161 if ((rv
= ldbegindetach(ld
, flags
)) != 0)
169 ld_sdmmc_start(struct ld_softc
*ld
, struct buf
*bp
)
171 struct ld_sdmmc_softc
*sc
= device_private(ld
->sc_dv
);
172 struct ld_sdmmc_task
*task
= &sc
->sc_task
;
176 sdmmc_init_task(&task
->task
, ld_sdmmc_dobio
, task
);
178 callout_reset(&task
->task_callout
, hz
, ld_sdmmc_timeout
, task
);
179 sdmmc_add_task(sc
->sc_sf
->sc
, &task
->task
);
185 ld_sdmmc_dobio(void *arg
)
187 struct ld_sdmmc_task
*task
= (struct ld_sdmmc_task
*)arg
;
188 struct ld_sdmmc_softc
*sc
= task
->task_sc
;
189 struct buf
*bp
= task
->task_bp
;
192 callout_stop(&task
->task_callout
);
197 DPRINTF(("%s: I/O operation (dir=%s, blkno=0x%jx, bcnt=0x%x)\n",
198 device_xname(sc
->sc_ld
.sc_dv
), bp
->b_flags
& B_READ
? "IN" : "OUT",
199 bp
->b_rawblkno
, bp
->b_bcount
));
201 /* is everything done in terms of blocks? */
202 if (bp
->b_rawblkno
>= sc
->sc_sf
->csd
.capacity
) {
203 /* trying to read or write past end of device */
204 DPRINTF(("%s: blkno exceeds capacity 0x%x\n",
205 device_xname(sc
->sc_ld
.sc_dv
), sc
->sc_sf
->csd
.capacity
));
206 bp
->b_error
= EIO
; /* XXX */
207 bp
->b_resid
= bp
->b_bcount
;
208 lddone(&sc
->sc_ld
, bp
);
213 if (bp
->b_flags
& B_READ
)
214 error
= sdmmc_mem_read_block(sc
->sc_sf
, bp
->b_rawblkno
,
215 bp
->b_data
, bp
->b_bcount
);
217 error
= sdmmc_mem_write_block(sc
->sc_sf
, bp
->b_rawblkno
,
218 bp
->b_data
, bp
->b_bcount
);
220 DPRINTF(("%s: error %d\n", device_xname(sc
->sc_ld
.sc_dv
),
222 bp
->b_error
= EIO
; /* XXXX */
223 bp
->b_resid
= bp
->b_bcount
;
229 lddone(&sc
->sc_ld
, bp
);
233 ld_sdmmc_timeout(void *arg
)
235 struct ld_sdmmc_task
*task
= (struct ld_sdmmc_task
*)arg
;
236 struct ld_sdmmc_softc
*sc
= task
->task_sc
;
237 struct buf
*bp
= task
->task_bp
;
241 if (!sdmmc_task_pending(&task
->task
)) {
245 bp
->b_error
= EIO
; /* XXXX */
246 bp
->b_resid
= bp
->b_bcount
;
247 sdmmc_del_task(&task
->task
);
250 lddone(&sc
->sc_ld
, bp
);
254 ld_sdmmc_dump(struct ld_softc
*ld
, void *data
, int blkno
, int blkcnt
)
256 struct ld_sdmmc_softc
*sc
= device_private(ld
->sc_dv
);
258 return sdmmc_mem_write_block(sc
->sc_sf
, blkno
, data
,
259 blkcnt
* ld
->sc_secsize
);