Added SD/MMC support from OpenBSD.
tested on i386, amd64 at current-users ML by pgoyette@. tested on zaurus by myself.
This commit is contained in:
parent
91d9da05bb
commit
e0297d1ead
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: GENERIC,v 1.241 2009/04/20 20:49:22 cegger Exp $
|
||||
# $NetBSD: GENERIC,v 1.242 2009/04/21 03:00:29 nonaka Exp $
|
||||
#
|
||||
# GENERIC machine description file
|
||||
#
|
||||
|
@ -22,7 +22,7 @@ include "arch/amd64/conf/std.amd64"
|
|||
|
||||
options INCLUDE_CONFIG_FILE # embed config file in kernel binary
|
||||
|
||||
#ident "GENERIC-$Revision: 1.241 $"
|
||||
#ident "GENERIC-$Revision: 1.242 $"
|
||||
|
||||
maxusers 64 # estimated number of users
|
||||
|
||||
|
@ -980,6 +980,9 @@ radio* at bktr?
|
|||
bt3c* at pcmcia? function ? # 3Com 3CRWB6096-A
|
||||
btbc* at pcmcia? function ? # AnyCom BlueCard LSE041/039/139
|
||||
|
||||
# Bluetooth SDIO Controllers
|
||||
sbt* at sdmmc?
|
||||
|
||||
# Bluetooth USB Controllers
|
||||
ubt* at uhub? port ?
|
||||
|
||||
|
@ -988,6 +991,7 @@ bthub* at bcsp?
|
|||
bthub* at bt3c?
|
||||
bthub* at btbc?
|
||||
bthub* at btuart?
|
||||
bthub* at sbt?
|
||||
bthub* at ubt?
|
||||
|
||||
# Bluetooth HID support
|
||||
|
@ -1005,6 +1009,15 @@ wskbd* at btkbd? console ? mux 1
|
|||
btsco* at bthub?
|
||||
|
||||
|
||||
# SD/MMC/SDIO Controller and Device support
|
||||
|
||||
# SD/MMC controller
|
||||
sdhc* at pci? # SD Host Controller
|
||||
sdmmc* at sdhc? # SD/MMC bus
|
||||
|
||||
ld* at sdmmc?
|
||||
|
||||
|
||||
# Mice
|
||||
|
||||
# Middle Digital, Inc. PCI-Weasel serial console board control
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.amd64,v 1.66 2009/04/16 15:34:23 rmind Exp $
|
||||
# $NetBSD: files.amd64,v 1.67 2009/04/21 03:00:29 nonaka Exp $
|
||||
#
|
||||
# new style config file for amd64 architecture
|
||||
#
|
||||
|
@ -172,7 +172,9 @@ include "dev/usb/files.usb"
|
|||
|
||||
include "dev/bluetooth/files.bluetooth"
|
||||
|
||||
include "dev/ieee1394/files.ieee1394"
|
||||
include "dev/sdmmc/files.sdmmc"
|
||||
|
||||
include "dev/ieee1394/files.ieee1394"
|
||||
include "dev/apm/files.apm"
|
||||
include "dev/acpi/files.acpi"
|
||||
file arch/amd64/acpi/acpi_wakeup_low.S acpi
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.pxa2x0,v 1.15 2007/12/03 15:33:19 ad Exp $
|
||||
# $NetBSD: files.pxa2x0,v 1.16 2009/04/21 03:00:29 nonaka Exp $
|
||||
#
|
||||
# Configuration info for Intel PXA2[751]0 CPU support
|
||||
#
|
||||
|
@ -90,5 +90,5 @@ attach pxartc at pxaip
|
|||
file arch/arm/xscale/pxa2x0_rtc.c pxartc
|
||||
|
||||
# MMC controller
|
||||
#device pxamci: sdmmcbus
|
||||
#file arch/arm/xscale/pxa2x0_mci.c pxamci
|
||||
device pxamci: sdmmcbus
|
||||
file arch/arm/xscale/pxa2x0_mci.c pxamci
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,86 @@
|
|||
/* $NetBSD: pxa2x0_mci.h,v 1.1 2009/04/21 03:00:29 nonaka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2006 NONAKA Kimihiro <nonaka@netbsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PXA2X0_MCI_H_
|
||||
#define _PXA2X0_MCI_H_
|
||||
|
||||
#include <sys/bus.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/condvar.h>
|
||||
|
||||
#include <arm/xscale/pxa2x0_dmac.h>
|
||||
|
||||
struct pxamci_tag {
|
||||
void *cookie;
|
||||
|
||||
uint32_t (*get_ocr)(void *);
|
||||
int (*set_power)(void *, uint32_t);
|
||||
int (*card_detect)(void *);
|
||||
int (*write_protect)(void *);
|
||||
};
|
||||
|
||||
struct pxamci_softc {
|
||||
device_t sc_dev;
|
||||
bus_space_tag_t sc_iot;
|
||||
bus_space_handle_t sc_ioh;
|
||||
void *sc_ih;
|
||||
device_t sc_sdmmc;
|
||||
|
||||
struct dmac_xfer *sc_rxdx;
|
||||
bus_dma_segment_t sc_rxdr; /* read descriptor */
|
||||
struct dmac_xfer *sc_txdx;
|
||||
bus_dma_segment_t sc_txdr; /* write descriptor */
|
||||
|
||||
struct pxamci_tag sc_tag;
|
||||
|
||||
u_int sc_caps;
|
||||
#define PMC_CAPS_4BIT (1U << 0)
|
||||
#define PMC_CAPS_NO_DMA (1U << 1)
|
||||
|
||||
u_int sc_flags;
|
||||
#define PMF_CARDINITED (1U << 0)
|
||||
|
||||
struct sdmmc_command * volatile sc_cmd; /* command in progress */
|
||||
|
||||
uint32_t sc_imask;
|
||||
|
||||
uint32_t sc_ocr; /* selected OCR */
|
||||
int sc_maxblklen; /* maximum block length */
|
||||
int sc_buswidth; /* current bus width */
|
||||
|
||||
u_int sc_clkmin;
|
||||
u_int sc_clkmax;
|
||||
u_int sc_clkbase;
|
||||
uint32_t sc_clkrt;
|
||||
};
|
||||
|
||||
int pxamci_attach_sub(device_t, struct pxaip_attach_args *);
|
||||
void pxamci_card_detect_event(struct pxamci_softc *);
|
||||
|
||||
#endif /* _PXA2X0_MCI_H_ */
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: GUMSTIX,v 1.35 2009/03/06 20:31:48 joerg Exp $
|
||||
# $NetBSD: GUMSTIX,v 1.36 2009/04/21 03:00:29 nonaka Exp $
|
||||
#
|
||||
# GUMSTIX -- Gumstix. Inc. gumstix platforms kernel
|
||||
#
|
||||
|
@ -206,13 +206,13 @@ audio* at audiobus?
|
|||
pxaudc0 at pxaip? # USB Device Controller
|
||||
|
||||
# integrated MMC/SD contoller
|
||||
#pxamci0 at pxaip? addr 0x41100000 size 0x48
|
||||
#sdmmc* at pxamci?
|
||||
pxamci0 at pxaip? addr 0x41100000 size 0x48
|
||||
sdmmc* at pxamci?
|
||||
#options PXAMCI_DEBUG
|
||||
#options SDMMC_DEBUG
|
||||
#options SDMMC_DUMP_CSD
|
||||
|
||||
#ld* at sdmmc?
|
||||
ld* at sdmmc?
|
||||
|
||||
# gumstix device support
|
||||
gxio0 at pxaip?
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.gumstix,v 1.12 2008/05/14 01:58:29 matt Exp $
|
||||
# $NetBSD: files.gumstix,v 1.13 2009/04/21 03:00:29 nonaka Exp $
|
||||
#
|
||||
# Gumstix. Inc. Gumstix boards configuration info
|
||||
#
|
||||
|
@ -27,3 +27,9 @@ file arch/evbarm/gumstix/gxpcic.c pxapcic_gxpcic
|
|||
device gxiic: pxaiic, i2cbus
|
||||
attach gxiic at pxaip
|
||||
file arch/evbarm/gumstix/gxiic.c gxiic
|
||||
|
||||
include "dev/sdmmc/files.sdmmc"
|
||||
|
||||
# MMC/SD controller
|
||||
attach pxamci at pxaip with gxmci
|
||||
file arch/evbarm/gumstix/gxmci.c gxmci
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/* $NetBSD: gxmci.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2007 NONAKA Kimihiro <nonaka@netbsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: gxmci.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/intr.h>
|
||||
|
||||
#include <arm/xscale/pxa2x0cpu.h>
|
||||
#include <arm/xscale/pxa2x0reg.h>
|
||||
#include <arm/xscale/pxa2x0var.h>
|
||||
#include <arm/xscale/pxa2x0_gpio.h>
|
||||
#include <arm/xscale/pxa2x0_mci.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmcreg.h>
|
||||
|
||||
#if defined(PXAMCI_DEBUG)
|
||||
#define DPRINTF(s) printf s
|
||||
#else
|
||||
#define DPRINTF(s)
|
||||
#endif
|
||||
|
||||
struct gxmci_softc {
|
||||
struct pxamci_softc sc;
|
||||
};
|
||||
|
||||
static int pxamci_match(device_t, cfdata_t, void *);
|
||||
static void pxamci_attach(device_t, device_t, void *);
|
||||
|
||||
CFATTACH_DECL_NEW(gxmci, sizeof(struct gxmci_softc),
|
||||
pxamci_match, pxamci_attach, NULL, NULL);
|
||||
|
||||
static uint32_t gxmci_get_ocr(void *);
|
||||
static int gxmci_set_power(void *, uint32_t);
|
||||
static int gxmci_card_detect(void *);
|
||||
static int gxmci_write_protect(void *);
|
||||
|
||||
static int
|
||||
pxamci_match(device_t parent, cfdata_t cf, void *aux)
|
||||
{
|
||||
static struct pxa2x0_gpioconf pxa25x_gxmci_gpioconf[] = {
|
||||
{ 8, GPIO_ALT_FN_1_OUT }, /* MMCCS0 */
|
||||
{ 53, GPIO_ALT_FN_1_OUT }, /* MMCLK */
|
||||
{ -1 }
|
||||
};
|
||||
struct pxa2x0_gpioconf *gpioconf;
|
||||
u_int reg;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Check GPIO configuration. If you use these, it is sure already
|
||||
* to have been set by gxio.
|
||||
*/
|
||||
gpioconf =
|
||||
CPU_IS_PXA250 ? pxa25x_gxmci_gpioconf : pxa27x_pxamci_gpioconf;
|
||||
for (i = 0; gpioconf[i].pin != -1; i++) {
|
||||
reg = pxa2x0_gpio_get_function(gpioconf[i].pin);
|
||||
if (GPIO_FN(reg) != GPIO_FN(gpioconf[i].value) ||
|
||||
GPIO_FN_IS_OUT(reg) != GPIO_FN_IS_OUT(gpioconf[i].value))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
pxamci_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct gxmci_softc *sc = device_private(self);
|
||||
struct pxaip_attach_args *pxa = aux;
|
||||
|
||||
sc->sc.sc_tag.cookie = sc;
|
||||
sc->sc.sc_tag.get_ocr = gxmci_get_ocr;
|
||||
sc->sc.sc_tag.set_power = gxmci_set_power;
|
||||
sc->sc.sc_tag.card_detect = gxmci_card_detect;
|
||||
sc->sc.sc_tag.write_protect = gxmci_write_protect;
|
||||
sc->sc.sc_caps = 0;
|
||||
|
||||
if (pxamci_attach_sub(self, pxa)) {
|
||||
aprint_error_dev(self, "unable to attach MMC controller\n");
|
||||
}
|
||||
|
||||
if (!pmf_device_register(self, NULL, NULL)) {
|
||||
aprint_error_dev(self, "couldn't establish power handler\n");
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
gxmci_get_ocr(void *arg)
|
||||
{
|
||||
|
||||
return MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V;
|
||||
}
|
||||
|
||||
static int
|
||||
gxmci_set_power(void *arg, uint32_t ocr)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return non-zero if the card is currently inserted.
|
||||
*/
|
||||
static int
|
||||
gxmci_card_detect(void *arg)
|
||||
{
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return non-zero if the card is currently write-protected.
|
||||
*/
|
||||
static int
|
||||
gxmci_write_protect(void *arg)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: GENERIC,v 1.933 2009/04/20 20:49:21 cegger Exp $
|
||||
# $NetBSD: GENERIC,v 1.934 2009/04/21 03:00:29 nonaka Exp $
|
||||
#
|
||||
# GENERIC machine description file
|
||||
#
|
||||
|
@ -22,7 +22,7 @@ include "arch/i386/conf/std.i386"
|
|||
|
||||
options INCLUDE_CONFIG_FILE # embed config file in kernel binary
|
||||
|
||||
#ident "GENERIC-$Revision: 1.933 $"
|
||||
#ident "GENERIC-$Revision: 1.934 $"
|
||||
|
||||
maxusers 64 # estimated number of users
|
||||
|
||||
|
@ -1240,6 +1240,15 @@ fwip* at ieee1394if? # IP over IEEE1394
|
|||
sbp* at ieee1394if? euihi ? euilo ? # SCSI over IEEE1394
|
||||
|
||||
|
||||
# SD/MMC/SDIO Controller and Device support
|
||||
|
||||
# SD/MMC controller
|
||||
sdhc* at pci? # SD Host Controller
|
||||
sdmmc* at sdhc? # SD/MMC bus
|
||||
|
||||
ld* at sdmmc?
|
||||
|
||||
|
||||
# Audio Devices
|
||||
|
||||
# PCI audio devices
|
||||
|
@ -1356,6 +1365,9 @@ radio* at bktr?
|
|||
bt3c* at pcmcia? function ? # 3Com 3CRWB6096-A
|
||||
btbc* at pcmcia? function ? # AnyCom BlueCard LSE041/039/139
|
||||
|
||||
# Bluetooth SDIO Controller
|
||||
sbt* at sdmmc?
|
||||
|
||||
# Bluetooth USB Controllers
|
||||
ubt* at uhub? port ?
|
||||
|
||||
|
@ -1364,6 +1376,7 @@ bthub* at bcsp?
|
|||
bthub* at bt3c?
|
||||
bthub* at btbc?
|
||||
bthub* at btuart?
|
||||
bthub* at sbt?
|
||||
bthub* at ubt?
|
||||
|
||||
# Bluetooth HID support
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.i386,v 1.348 2009/04/16 15:34:23 rmind Exp $
|
||||
# $NetBSD: files.i386,v 1.349 2009/04/21 03:00:29 nonaka Exp $
|
||||
#
|
||||
# new style config file for i386 architecture
|
||||
#
|
||||
|
@ -467,6 +467,8 @@ include "dev/usb/files.usb"
|
|||
|
||||
include "dev/bluetooth/files.bluetooth"
|
||||
|
||||
include "dev/sdmmc/files.sdmmc"
|
||||
|
||||
include "dev/ieee1394/files.ieee1394"
|
||||
|
||||
include "arch/i386/pnpbios/files.pnpbios"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: GENERIC,v 1.23 2009/03/13 13:55:18 nonaka Exp $
|
||||
# $NetBSD: GENERIC,v 1.24 2009/04/21 03:00:30 nonaka Exp $
|
||||
#
|
||||
# GENERIC machine description file
|
||||
#
|
||||
|
@ -324,15 +324,14 @@ ukphy* at mii? phy ? # generic unknown PHYs
|
|||
urlphy* at mii? phy ? # Realtek RTL8150L internal PHYs
|
||||
|
||||
|
||||
|
||||
# integrated MMC/SD contoller
|
||||
#pxamci0 at pxaip? addr 0x41100000 size 0x48
|
||||
#sdmmc* at pxamci?
|
||||
pxamci0 at pxaip? addr 0x41100000 size 0x48
|
||||
sdmmc* at pxamci?
|
||||
#options PXAMCI_DEBUG
|
||||
#options SDMMC_DEBUG
|
||||
#options SDMMC_DUMP_CSD
|
||||
|
||||
#ld* at sdmmc? # MMC/SD/SDHC card
|
||||
ld* at sdmmc? # MMC/SD/SDHC card
|
||||
|
||||
|
||||
# Pseudo-Devices
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.zaurus,v 1.7 2009/03/11 09:02:04 nonaka Exp $
|
||||
# $NetBSD: files.zaurus,v 1.8 2009/04/21 03:00:30 nonaka Exp $
|
||||
#
|
||||
# Sharp Zaurus specific configuration info
|
||||
#
|
||||
|
@ -16,7 +16,7 @@ include "dev/ata/files.ata"
|
|||
include "dev/i2o/files.i2o"
|
||||
|
||||
# Machine-independent SD/MMC drivers
|
||||
#include "dev/sdmmc/files.sdmmc"
|
||||
include "dev/sdmmc/files.sdmmc"
|
||||
|
||||
# HPC
|
||||
include "arch/hpc/conf/files.hpc"
|
||||
|
@ -98,8 +98,8 @@ attach zusb at pxaip
|
|||
file arch/zaurus/dev/zusb.c zusb
|
||||
|
||||
# MMC/SD controller
|
||||
#attach pxamci at pxaip with zmci
|
||||
#file arch/zaurus/dev/zmci.c zmci
|
||||
attach pxamci at pxaip with zmci
|
||||
file arch/zaurus/dev/zmci.c zmci
|
||||
|
||||
# PCMCIA drivers
|
||||
include "dev/pcmcia/files.pcmcia"
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
/* $NetBSD: zmci.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2006-2008 NONAKA Kimihiro <nonaka@netbsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: zmci.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/pmf.h>
|
||||
|
||||
#include <machine/intr.h>
|
||||
|
||||
#include <arm/xscale/pxa2x0cpu.h>
|
||||
#include <arm/xscale/pxa2x0reg.h>
|
||||
#include <arm/xscale/pxa2x0var.h>
|
||||
#include <arm/xscale/pxa2x0_gpio.h>
|
||||
#include <arm/xscale/pxa2x0_mci.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmcreg.h>
|
||||
|
||||
#include <zaurus/dev/scoopreg.h>
|
||||
#include <zaurus/dev/scoopvar.h>
|
||||
|
||||
#include <zaurus/zaurus/zaurus_reg.h>
|
||||
#include <zaurus/zaurus/zaurus_var.h>
|
||||
|
||||
#if defined(PXAMCI_DEBUG)
|
||||
#define DPRINTF(s) do { printf s; } while (0)
|
||||
#else
|
||||
#define DPRINTF(s) do {} while (0)
|
||||
#endif
|
||||
|
||||
struct zmci_softc {
|
||||
struct pxamci_softc sc;
|
||||
|
||||
void *sc_detect_ih;
|
||||
int sc_detect_pin;
|
||||
int sc_wp_pin;
|
||||
};
|
||||
|
||||
static int pxamci_match(device_t, cfdata_t, void *);
|
||||
static void pxamci_attach(device_t, device_t, void *);
|
||||
|
||||
CFATTACH_DECL_NEW(zmci, sizeof(struct zmci_softc),
|
||||
pxamci_match, pxamci_attach, NULL, NULL);
|
||||
|
||||
static int zmci_intr(void *arg);
|
||||
|
||||
static uint32_t zmci_get_ocr(void *);
|
||||
static int zmci_set_power(void *, uint32_t);
|
||||
static int zmci_card_detect(void *);
|
||||
static int zmci_write_protect(void *);
|
||||
|
||||
static int
|
||||
pxamci_match(device_t parent, cfdata_t cf, void *aux)
|
||||
{
|
||||
|
||||
if (!ZAURUS_ISC3000)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
pxamci_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct zmci_softc *sc = device_private(self);
|
||||
struct pxaip_attach_args *pxa = aux;
|
||||
|
||||
sc->sc.sc_dev = self;
|
||||
|
||||
if (ZAURUS_ISC3000) {
|
||||
sc->sc_detect_pin = C3000_GPIO_SD_DETECT_PIN;
|
||||
sc->sc_wp_pin = C3000_GPIO_SD_WP_PIN;
|
||||
pxa2x0_gpio_set_function(sc->sc_detect_pin, GPIO_IN);
|
||||
pxa2x0_gpio_set_function(sc->sc_wp_pin, GPIO_IN);
|
||||
} else {
|
||||
/* XXX: C7x0/C8x0 */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Establish SD detect interrupt */
|
||||
sc->sc_detect_ih = pxa2x0_gpio_intr_establish(sc->sc_detect_pin,
|
||||
IST_EDGE_BOTH, IPL_BIO, zmci_intr, sc);
|
||||
if (sc->sc_detect_ih == NULL) {
|
||||
aprint_error_dev(self,
|
||||
"unable to establish nSD_DETECT interrupt\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sc->sc.sc_tag.cookie = sc;
|
||||
sc->sc.sc_tag.get_ocr = zmci_get_ocr;
|
||||
sc->sc.sc_tag.set_power = zmci_set_power;
|
||||
sc->sc.sc_tag.card_detect = zmci_card_detect;
|
||||
sc->sc.sc_tag.write_protect = zmci_write_protect;
|
||||
sc->sc.sc_caps = 0;
|
||||
|
||||
if (pxamci_attach_sub(self, pxa)) {
|
||||
aprint_error_dev(self, "unable to attach MMC controller\n");
|
||||
goto free_intr;
|
||||
}
|
||||
|
||||
if (!pmf_device_register(self, NULL, NULL)) {
|
||||
aprint_error_dev(self, "couldn't establish power handler\n");
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
free_intr:
|
||||
pxa2x0_gpio_intr_disestablish(sc->sc_detect_ih);
|
||||
sc->sc_detect_ih = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
zmci_intr(void *arg)
|
||||
{
|
||||
struct zmci_softc *sc = (struct zmci_softc *)arg;
|
||||
|
||||
pxa2x0_gpio_clear_intr(sc->sc_detect_pin);
|
||||
|
||||
pxamci_card_detect_event(&sc->sc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
zmci_get_ocr(void *arg)
|
||||
{
|
||||
|
||||
return MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V;
|
||||
}
|
||||
|
||||
static int
|
||||
zmci_set_power(void *arg, uint32_t ocr)
|
||||
{
|
||||
struct zmci_softc *sc = (struct zmci_softc *)arg;
|
||||
|
||||
if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V)) {
|
||||
scoop_set_sdmmc_power(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ocr != 0) {
|
||||
printf("%s: zmci_set_power(): unsupported OCR (%#x)\n",
|
||||
device_xname(sc->sc.sc_dev), ocr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* power off */
|
||||
scoop_set_sdmmc_power(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return non-zero if the card is currently inserted.
|
||||
*/
|
||||
static int
|
||||
zmci_card_detect(void *arg)
|
||||
{
|
||||
struct zmci_softc *sc = (struct zmci_softc *)arg;
|
||||
|
||||
if (!pxa2x0_gpio_get_bit(sc->sc_detect_pin))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return non-zero if the card is currently write-protected.
|
||||
*/
|
||||
static int
|
||||
zmci_write_protect(void *arg)
|
||||
{
|
||||
struct zmci_softc *sc = (struct zmci_softc *)arg;
|
||||
|
||||
if (pxa2x0_gpio_get_bit(sc->sc_wp_pin))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files,v 1.944 2009/03/12 00:15:07 jmcneill Exp $
|
||||
# $NetBSD: files,v 1.945 2009/04/21 03:00:29 nonaka Exp $
|
||||
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
|
||||
|
||||
version 20090214
|
||||
|
@ -1091,6 +1091,7 @@ define acpibus { } # ACPI "bus" attachment
|
|||
define pcmciabus { [controller = -1], [socket = -1]} # PCMCIA bus attachment
|
||||
define cbbus { } # CardBus attachment
|
||||
define pcmciaslot { } # PCMCIA slot itself
|
||||
define sdmmcbus { } # SD/MMC attachment
|
||||
|
||||
# We need the USB bus controllers here so different busses can
|
||||
# use them in an 'attach-with'.
|
||||
|
@ -1124,6 +1125,10 @@ file dev/ic/sl811hs.c slhci needs-flag
|
|||
define hid
|
||||
file dev/usb/hid.c hid
|
||||
|
||||
# SD Host controller
|
||||
device sdhc: sdmmcbus
|
||||
file dev/sdmmc/sdhc.c sdhc needs-flag
|
||||
|
||||
# Myson MTD803 3-in-1 Fast Ethernet Controller
|
||||
device mtd: arp, ether, ifnet, mii
|
||||
file dev/ic/mtd803.c mtd
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: files.pci,v 1.313 2009/04/20 20:10:23 cegger Exp $
|
||||
# $NetBSD: files.pci,v 1.314 2009/04/21 03:00:29 nonaka Exp $
|
||||
#
|
||||
# Config file and device description for machine-independent PCI code.
|
||||
# Included by ports that need it. Requires that the SCSI files be
|
||||
|
@ -909,6 +909,10 @@ device msk: ether, ifnet, arp, mii
|
|||
attach msk at mskc
|
||||
file dev/pci/if_msk.c mskc | msk
|
||||
|
||||
# SD Host Controller
|
||||
attach sdhc at pci with sdhc_pci
|
||||
file dev/pci/sdhc_pci.c sdhc_pci
|
||||
|
||||
#
|
||||
# Direct Rendering Manager
|
||||
#
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
/* $NetBSD: sdhc_pci.c,v 1.1 2009/04/21 03:00:29 nonaka Exp $ */
|
||||
/* $OpenBSD: sdhc_pci.c,v 1.7 2007/10/30 18:13:45 chl Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: sdhc_pci.c,v 1.1 2009/04/21 03:00:29 nonaka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/pmf.h>
|
||||
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <dev/pci/pcidevs.h>
|
||||
|
||||
#include <dev/sdmmc/sdhcreg.h>
|
||||
#include <dev/sdmmc/sdhcvar.h>
|
||||
#include <dev/sdmmc/sdmmcvar.h>
|
||||
|
||||
/* PCI base address registers */
|
||||
#define SDHC_PCI_BAR_START PCI_MAPREG_START
|
||||
#define SDHC_PCI_BAR_END PCI_MAPREG_END
|
||||
|
||||
/* PCI interface classes */
|
||||
#define SDHC_PCI_INTERFACE_NO_DMA 0x00
|
||||
#define SDHC_PCI_INTERFACE_DMA 0x01
|
||||
#define SDHC_PCI_INTERFACE_VENDOR 0x02
|
||||
|
||||
/*
|
||||
* 8-bit PCI configuration register that tells us how many slots there
|
||||
* are and which BAR entry corresponds to the first slot.
|
||||
*/
|
||||
#define SDHC_PCI_CONF_SLOT_INFO 0x40
|
||||
#define SDHC_PCI_NUM_SLOTS(info) ((((info) >> 4) & 0x7) + 1)
|
||||
#define SDHC_PCI_FIRST_BAR(info) ((info) & 0x7)
|
||||
|
||||
struct sdhc_pci_softc {
|
||||
struct sdhc_softc sc;
|
||||
void *sc_ih;
|
||||
};
|
||||
|
||||
static int sdhc_pci_match(device_t, cfdata_t, void *);
|
||||
static void sdhc_pci_attach(device_t, device_t, void *);
|
||||
|
||||
CFATTACH_DECL_NEW(sdhc_pci, sizeof(struct sdhc_pci_softc),
|
||||
sdhc_pci_match, sdhc_pci_attach, NULL, NULL);
|
||||
|
||||
#ifdef SDHC_DEBUG
|
||||
#define DPRINTF(s) printf s
|
||||
#else
|
||||
#define DPRINTF(s) /**/
|
||||
#endif
|
||||
|
||||
/* XXX */
|
||||
#define PCI_PRODUCT_TI_PCI7XX1_FLASH 0x8033
|
||||
#define PCI_PRODUCT_TI_PCI7XX1_SD 0x8034
|
||||
#define PCI_PRODUCT_ENE_SDCARD 0x0550
|
||||
|
||||
static const struct sdhc_pci_quirk {
|
||||
pci_vendor_id_t vendor;
|
||||
pci_product_id_t product;
|
||||
pci_vendor_id_t subvendor;
|
||||
pci_product_id_t subproduct;
|
||||
u_int function;
|
||||
|
||||
uint32_t flags;
|
||||
#define SDHC_PCI_QUIRK_FORCE_DMA (1U << 0)
|
||||
#define SDHC_PCI_QUIRK_TI_HACK (1U << 1)
|
||||
#define SDHC_PCI_QUIRK_NO_PWR0 (1U << 2)
|
||||
} sdhc_pci_quirk_table[] = {
|
||||
{
|
||||
PCI_VENDOR_TI,
|
||||
PCI_PRODUCT_TI_PCI7XX1_SD,
|
||||
0xffff,
|
||||
0xffff,
|
||||
4,
|
||||
SDHC_PCI_QUIRK_TI_HACK
|
||||
},
|
||||
|
||||
{
|
||||
PCI_VENDOR_ENE,
|
||||
PCI_PRODUCT_ENE_SDCARD,
|
||||
0xffff,
|
||||
0xffff,
|
||||
0,
|
||||
SDHC_PCI_QUIRK_NO_PWR0
|
||||
},
|
||||
};
|
||||
|
||||
static void sdhc_pci_quirk_ti_hack(struct pci_attach_args *);
|
||||
|
||||
static uint32_t
|
||||
sdhc_pci_lookup_quirk_flags(struct pci_attach_args *pa)
|
||||
{
|
||||
const struct sdhc_pci_quirk *q;
|
||||
pcireg_t id;
|
||||
pci_vendor_id_t vendor;
|
||||
pci_product_id_t product;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < __arraycount(sdhc_pci_quirk_table); i++) {
|
||||
q = &sdhc_pci_quirk_table[i];
|
||||
|
||||
if ((PCI_VENDOR(pa->pa_id) == q->vendor)
|
||||
&& (PCI_PRODUCT(pa->pa_id) == q->product)) {
|
||||
if ((q->function != ~0)
|
||||
&& (pa->pa_function != q->function))
|
||||
continue;
|
||||
|
||||
if ((q->subvendor == 0xffff)
|
||||
&& (q->subproduct == 0xffff))
|
||||
return q->flags;
|
||||
|
||||
id = pci_conf_read(pa->pa_pc, pa->pa_tag,
|
||||
PCI_SUBSYS_ID_REG);
|
||||
vendor = PCI_VENDOR(id);
|
||||
product = PCI_PRODUCT(id);
|
||||
|
||||
if ((q->subvendor != 0xffff)
|
||||
&& (q->subproduct != 0xffff)) {
|
||||
if ((vendor == q->subvendor)
|
||||
&& (product == q->subproduct))
|
||||
return q->flags;
|
||||
} else if (q->subvendor != 0xffff) {
|
||||
if (product == q->subproduct)
|
||||
return q->flags;
|
||||
} else {
|
||||
if (vendor == q->subvendor)
|
||||
return q->flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sdhc_pci_match(device_t parent, cfdata_t cf, void *aux)
|
||||
{
|
||||
struct pci_attach_args *pa = aux;
|
||||
|
||||
if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SYSTEM &&
|
||||
PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SYSTEM_SDHC)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sdhc_pci_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct sdhc_pci_softc *sc = device_private(self);
|
||||
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
|
||||
pci_chipset_tag_t pc = pa->pa_pc;
|
||||
pcitag_t tag = pa->pa_tag;
|
||||
pci_intr_handle_t ih;
|
||||
pcireg_t csr;
|
||||
pcireg_t slotinfo;
|
||||
char devinfo[256];
|
||||
char const *intrstr;
|
||||
int nslots;
|
||||
int reg;
|
||||
int cnt;
|
||||
bus_space_tag_t iot;
|
||||
bus_space_handle_t ioh;
|
||||
bus_size_t size;
|
||||
uint32_t flags;
|
||||
|
||||
sc->sc.sc_dev = self;
|
||||
sc->sc.sc_dmat = pa->pa_dmat;
|
||||
sc->sc.sc_host = NULL;
|
||||
|
||||
pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo));
|
||||
aprint_normal(": %s (rev. 0x%02x)\n", devinfo,
|
||||
PCI_REVISION(pa->pa_class));
|
||||
aprint_naive("\n");
|
||||
|
||||
/* Some controllers needs special treatment. */
|
||||
flags = sdhc_pci_lookup_quirk_flags(pa);
|
||||
if (ISSET(flags, SDHC_PCI_QUIRK_TI_HACK))
|
||||
sdhc_pci_quirk_ti_hack(pa);
|
||||
if (ISSET(flags, SDHC_PCI_QUIRK_FORCE_DMA))
|
||||
SET(sc->sc.sc_flags, SDHC_FLAG_FORCE_DMA);
|
||||
if (ISSET(flags, SDHC_PCI_QUIRK_NO_PWR0))
|
||||
SET(sc->sc.sc_flags, SDHC_FLAG_NO_PWR0);
|
||||
|
||||
/*
|
||||
* Map and attach all hosts supported by the host controller.
|
||||
*/
|
||||
slotinfo = pci_conf_read(pc, tag, SDHC_PCI_CONF_SLOT_INFO);
|
||||
nslots = SDHC_PCI_NUM_SLOTS(slotinfo);
|
||||
|
||||
/* Allocate an array big enough to hold all the possible hosts */
|
||||
sc->sc.sc_host = malloc(sizeof(struct sdhc_host *) * nslots,
|
||||
M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (sc->sc.sc_host == NULL) {
|
||||
aprint_error_dev(self, "couldn't alloc memory\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Enable the device. */
|
||||
csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
|
||||
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG,
|
||||
csr | PCI_COMMAND_MASTER_ENABLE);
|
||||
|
||||
/* Map and establish the interrupt. */
|
||||
if (pci_intr_map(pa, &ih)) {
|
||||
aprint_error_dev(self, "couldn't map interrupt\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
intrstr = pci_intr_string(pc, ih);
|
||||
sc->sc_ih = pci_intr_establish(pc, ih, IPL_SDMMC, sdhc_intr, sc);
|
||||
if (sc->sc_ih == NULL) {
|
||||
aprint_error_dev(self, "couldn't establish interrupt\n");
|
||||
goto err;
|
||||
}
|
||||
aprint_normal_dev(self, "interrupting at %s\n", intrstr);
|
||||
|
||||
/* Enable use of DMA if supported by the interface. */
|
||||
if ((PCI_INTERFACE(pa->pa_class) == SDHC_PCI_INTERFACE_DMA))
|
||||
SET(sc->sc.sc_flags, SDHC_FLAG_USE_DMA);
|
||||
|
||||
/* XXX: handle 64-bit BARs */
|
||||
cnt = 0;
|
||||
for (reg = SDHC_PCI_BAR_START + SDHC_PCI_FIRST_BAR(slotinfo) *
|
||||
sizeof(uint32_t);
|
||||
reg < SDHC_PCI_BAR_END && nslots > 0;
|
||||
reg += sizeof(uint32_t), nslots--) {
|
||||
if (pci_mapreg_map(pa, reg, PCI_MAPREG_TYPE_MEM, 0,
|
||||
&iot, &ioh, NULL, &size)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cnt++;
|
||||
if (sdhc_host_found(&sc->sc, iot, ioh, size) != 0) {
|
||||
/* XXX: sc->sc_host leak */
|
||||
aprint_error_dev(self,
|
||||
"couldn't initialize host (0x%x)\n", reg);
|
||||
}
|
||||
}
|
||||
if (cnt == 0) {
|
||||
aprint_error_dev(self, "couldn't map register\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!pmf_device_register1(self, sdhc_suspend, sdhc_resume,
|
||||
sdhc_shutdown)) {
|
||||
aprint_error_dev(self, "couldn't establish powerhook\n");
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
if (sc->sc.sc_host != NULL)
|
||||
free(sc->sc.sc_host, M_DEVBUF);
|
||||
}
|
||||
|
||||
/* TI specific register */
|
||||
#define SDHC_PCI_GENERAL_CTL 0x4c
|
||||
#define MMC_SD_DIS 0x02
|
||||
|
||||
static void
|
||||
sdhc_pci_quirk_ti_hack(struct pci_attach_args *pa)
|
||||
{
|
||||
pci_chipset_tag_t pc = pa->pa_pc;
|
||||
pcitag_t tag;
|
||||
pcireg_t id, reg;
|
||||
|
||||
/* Look at func 3 for the flash device */
|
||||
tag = pci_make_tag(pc, pa->pa_bus, pa->pa_device, 3);
|
||||
id = pci_conf_read(pc, tag, PCI_ID_REG);
|
||||
if (PCI_PRODUCT(id) != PCI_PRODUCT_TI_PCI7XX1_FLASH)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Disable MMC/SD on the flash media controller so the
|
||||
* SD host takes over.
|
||||
*/
|
||||
reg = pci_conf_read(pc, tag, SDHC_PCI_GENERAL_CTL);
|
||||
reg |= MMC_SD_DIS;
|
||||
pci_conf_write(pc, tag, SDHC_PCI_GENERAL_CTL, reg);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# $NetBSD: Makefile.sdmmcdevs,v 1.1 2009/04/21 03:00:30 nonaka Exp $
|
||||
# $OpenBSD: Makefile,v 1.1 2006/06/01 21:15:40 uwe Exp $
|
||||
#
|
||||
# 1.) Change "src/sys/dev/sdmmc/sdmmcdevs".
|
||||
# 2.) Commit "src/sys/dev/sdmmc/sdmmcdevs".
|
||||
# 3.) Execute "make -f Makefile.sdmmcdevs" in "src/sys/dev/sdmmc".
|
||||
# 4.) Commit "src/sys/dev/sdmmc/sdmmcdevs.h".
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
sdmmcdevs.h: sdmmcdevs devlist2h.awk
|
||||
/bin/rm -f sdmmcdevs.h
|
||||
${TOOL_AWK} -f devlist2h.awk sdmmcdevs
|
|
@ -0,0 +1,144 @@
|
|||
#! /usr/bin/awk -f
|
||||
# $NetBSD: devlist2h.awk,v 1.1 2009/04/21 03:00:30 nonaka Exp $
|
||||
# $OpenBSD: devlist2h.awk,v 1.2 2006/06/02 21:16:44 uwe Exp $
|
||||
# NetBSD: devlist2h.awk,v 1.2 1998/07/22 11:47:13 christos Exp
|
||||
#
|
||||
# Copyright (c) 1998, Christos Zoulas
|
||||
# Copyright (c) 1995, 1996 Christopher G. Demetriou
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. All advertising materials mentioning features or use of this software
|
||||
# must display the following acknowledgement:
|
||||
# This product includes software developed by Christopher G. Demetriou.
|
||||
# This product includes software developed by Christos Zoulas
|
||||
# 4. The name of the author(s) may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
function collectline(f, line) {
|
||||
oparen = 0
|
||||
line = ""
|
||||
while (f <= NF) {
|
||||
if ($f == "#") {
|
||||
line = line "("
|
||||
oparen = 1
|
||||
f++
|
||||
continue
|
||||
}
|
||||
if (oparen) {
|
||||
line = line $f
|
||||
if (f < NF)
|
||||
line = line " "
|
||||
f++
|
||||
continue
|
||||
}
|
||||
line = line $f
|
||||
if (f < NF)
|
||||
line = line " "
|
||||
f++
|
||||
}
|
||||
if (oparen)
|
||||
line = line ")"
|
||||
return line
|
||||
}
|
||||
BEGIN {
|
||||
nproducts = nvendors = 0
|
||||
hfile="sdmmcdevs.h"
|
||||
}
|
||||
NR == 1 {
|
||||
VERSION = $0
|
||||
gsub("\\$", "", VERSION)
|
||||
|
||||
printf("/*\t$NetBSD: devlist2h.awk,v 1.1 2009/04/21 03:00:30 nonaka Exp $\t*/\n\n") > hfile
|
||||
printf("/*\n") > hfile
|
||||
printf(" * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.\n") \
|
||||
> hfile
|
||||
printf(" *\n") > hfile
|
||||
printf(" * generated from:\n") > hfile
|
||||
printf(" *\t%s\n", VERSION) > hfile
|
||||
printf(" */\n") > hfile
|
||||
|
||||
next
|
||||
}
|
||||
$1 == "vendor" {
|
||||
nvendors++
|
||||
|
||||
vendorindex[$2] = nvendors; # record index for this name, for later.
|
||||
vendors[nvendors, 1] = $2; # name
|
||||
vendors[nvendors, 2] = $3; # id
|
||||
printf("#define\tSDMMC_VENDOR_%s\t%s\t", vendors[nvendors, 1],
|
||||
vendors[nvendors, 2]) > hfile
|
||||
vendors[nvendors, 3] = collectline(4, line)
|
||||
printf("/* %s */\n", vendors[nvendors, 3]) > hfile
|
||||
next
|
||||
}
|
||||
$1 == "product" {
|
||||
nproducts++
|
||||
|
||||
products[nproducts, 1] = $2; # vendor name
|
||||
products[nproducts, 2] = $3; # product id
|
||||
products[nproducts, 3] = $4; # id
|
||||
|
||||
f = 5;
|
||||
|
||||
if ($4 == "{") {
|
||||
products[nproducts, 3] = "SDMMC_PRODUCT_INVALID"
|
||||
z = "{ "
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (f <= NF) {
|
||||
gsub("&sp", " ", $f)
|
||||
gsub("&tab", "\t", $f)
|
||||
gsub("&nl", "\n", $f)
|
||||
z = z $f " "
|
||||
f++
|
||||
}
|
||||
else {
|
||||
if (i == 3)
|
||||
z = z "NULL "
|
||||
else
|
||||
z = z "NULL, "
|
||||
}
|
||||
}
|
||||
products[nproducts, 4] = z $f
|
||||
f++
|
||||
}
|
||||
else {
|
||||
products[nproducts, 4] = "{ NULL, NULL, NULL, NULL }"
|
||||
}
|
||||
printf("#define\tSDMMC_CIS_%s_%s\t%s\n",
|
||||
products[nproducts, 1], products[nproducts, 2],
|
||||
products[nproducts, 4]) > hfile
|
||||
printf("#define\tSDMMC_PRODUCT_%s_%s\t%s\n", products[nproducts, 1],
|
||||
products[nproducts, 2], products[nproducts, 3]) > hfile
|
||||
|
||||
# products[nproducts, 5] = collectline(f, line)
|
||||
#
|
||||
# printf("#define\tSDMMC_STR_%s_%s\t\"%s\"\n",
|
||||
# products[nproducts, 1], products[nproducts, 2],
|
||||
# products[nproducts, 5]) > hfile
|
||||
|
||||
next
|
||||
}
|
||||
{
|
||||
if ($0 == "")
|
||||
blanklines++
|
||||
print $0 > hfile
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
# $NetBSD: files.sdmmc,v 1.1 2009/04/21 03:00:30 nonaka Exp $
|
||||
# $OpenBSD: files.sdmmc,v 1.2 2006/06/01 21:53:41 uwe Exp $
|
||||
#
|
||||
# Config file and device description for machine-independent SD/MMC code.
|
||||
# Included by ports that need it.
|
||||
|
||||
device sdmmc {}
|
||||
attach sdmmc at sdmmcbus
|
||||
file dev/sdmmc/sdmmc.c sdmmc
|
||||
file dev/sdmmc/sdmmc_cis.c sdmmc
|
||||
file dev/sdmmc/sdmmc_io.c sdmmc
|
||||
file dev/sdmmc/sdmmc_mem.c sdmmc
|
||||
|
||||
attach ld at sdmmc with ld_sdmmc
|
||||
file dev/sdmmc/ld_sdmmc.c ld_sdmmc
|
||||
|
||||
# Bluetooth SDIO cards (Type-A/B)
|
||||
device sbt: btbus, bluetooth
|
||||
attach sbt at sdmmc
|
||||
file dev/sdmmc/sbt.c sbt
|
|
@ -0,0 +1,240 @@
|
|||
/* $NetBSD: ld_sdmmc.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 KIYOHARA Takashi
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ld_sdmmc.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
|
||||
|
||||
#include "rnd.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/bufq.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/callout.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/dkio.h>
|
||||
#include <sys/disk.h>
|
||||
#if NRND > 0
|
||||
#include <sys/rnd.h>
|
||||
#endif
|
||||
|
||||
#include <uvm/uvm_extern.h>
|
||||
|
||||
#include <dev/ldvar.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmcvar.h>
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
#define DPRINTF(s) printf s
|
||||
#else
|
||||
#define DPRINTF(s) /**/
|
||||
#endif
|
||||
|
||||
struct ld_sdmmc_softc;
|
||||
|
||||
struct ld_sdmmc_task {
|
||||
struct sdmmc_task task;
|
||||
|
||||
struct ld_sdmmc_softc *task_sc;
|
||||
struct buf *task_bp;
|
||||
callout_t task_callout;
|
||||
};
|
||||
|
||||
struct ld_sdmmc_softc {
|
||||
struct ld_softc sc_ld;
|
||||
int sc_hwunit;
|
||||
|
||||
struct sdmmc_function *sc_sf;
|
||||
struct ld_sdmmc_task sc_task;
|
||||
};
|
||||
|
||||
static int ld_sdmmc_match(device_t, struct cfdata *, void *);
|
||||
static void ld_sdmmc_attach(device_t, device_t, void *);
|
||||
static int ld_sdmmc_detach(device_t, int);
|
||||
|
||||
static int ld_sdmmc_dump(struct ld_softc *, void *, int, int);
|
||||
static int ld_sdmmc_start(struct ld_softc *, struct buf *);
|
||||
|
||||
static void ld_sdmmc_dobio(void *);
|
||||
static void ld_sdmmc_timeout(void *);
|
||||
|
||||
CFATTACH_DECL_NEW(ld_sdmmc, sizeof(struct ld_sdmmc_softc),
|
||||
ld_sdmmc_match, ld_sdmmc_attach, ld_sdmmc_detach, NULL);
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
ld_sdmmc_match(device_t parent, struct cfdata *match, void *aux)
|
||||
{
|
||||
struct sdmmc_softc *sdmsc = device_private(parent);
|
||||
|
||||
if (ISSET(sdmsc->sc_flags, SMF_MEM_MODE))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
ld_sdmmc_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct ld_sdmmc_softc *sc = device_private(self);
|
||||
struct sdmmc_attach_args *sa = aux;
|
||||
struct ld_softc *ld = &sc->sc_ld;
|
||||
|
||||
ld->sc_dv = self;
|
||||
|
||||
aprint_normal("\n");
|
||||
aprint_naive("\n");
|
||||
|
||||
callout_init(&sc->sc_task.task_callout, CALLOUT_MPSAFE);
|
||||
|
||||
sc->sc_hwunit = 0; /* always 0? */
|
||||
sc->sc_sf = sa->sf;
|
||||
|
||||
ld->sc_flags = LDF_ENABLED;
|
||||
ld->sc_secperunit = sc->sc_sf->csd.capacity;
|
||||
ld->sc_secsize = sc->sc_sf->csd.sector_size;
|
||||
ld->sc_maxxfer = MAXPHYS;
|
||||
ld->sc_maxqueuecnt = 1;
|
||||
ld->sc_dump = ld_sdmmc_dump;
|
||||
ld->sc_start = ld_sdmmc_start;
|
||||
|
||||
ldattach(ld);
|
||||
}
|
||||
|
||||
static int
|
||||
ld_sdmmc_detach(device_t dev, int flags)
|
||||
{
|
||||
struct ld_sdmmc_softc *sc = device_private(dev);
|
||||
struct ld_softc *ld = &sc->sc_ld;
|
||||
int rv;
|
||||
|
||||
if ((rv = ldbegindetach(ld, flags)) != 0)
|
||||
return rv;
|
||||
ldenddetach(ld);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ld_sdmmc_start(struct ld_softc *ld, struct buf *bp)
|
||||
{
|
||||
struct ld_sdmmc_softc *sc = device_private(ld->sc_dv);
|
||||
struct ld_sdmmc_task *task = &sc->sc_task;
|
||||
|
||||
task->task_sc = sc;
|
||||
task->task_bp = bp;
|
||||
sdmmc_init_task(&task->task, ld_sdmmc_dobio, task);
|
||||
|
||||
callout_reset(&task->task_callout, hz, ld_sdmmc_timeout, task);
|
||||
sdmmc_add_task(sc->sc_sf->sc, &task->task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ld_sdmmc_dobio(void *arg)
|
||||
{
|
||||
struct ld_sdmmc_task *task = (struct ld_sdmmc_task *)arg;
|
||||
struct ld_sdmmc_softc *sc = task->task_sc;
|
||||
struct buf *bp = task->task_bp;
|
||||
int error, s;
|
||||
|
||||
callout_stop(&task->task_callout);
|
||||
|
||||
/*
|
||||
* I/O operation
|
||||
*/
|
||||
DPRINTF(("%s: I/O operation (dir=%s, blkno=0x%jx, bcnt=0x%x)\n",
|
||||
device_xname(sc->sc_ld.sc_dv), bp->b_flags & B_READ ? "IN" : "OUT",
|
||||
bp->b_rawblkno, bp->b_bcount));
|
||||
|
||||
/* is everything done in terms of blocks? */
|
||||
if (bp->b_rawblkno >= sc->sc_sf->csd.capacity) {
|
||||
/* trying to read or write past end of device */
|
||||
DPRINTF(("%s: blkno exceeds capacity 0x%x\n",
|
||||
device_xname(sc->sc_ld.sc_dv), sc->sc_sf->csd.capacity));
|
||||
bp->b_error = EIO; /* XXX */
|
||||
bp->b_resid = bp->b_bcount;
|
||||
lddone(&sc->sc_ld, bp);
|
||||
return;
|
||||
}
|
||||
|
||||
s = splbio();
|
||||
if (bp->b_flags & B_READ)
|
||||
error = sdmmc_mem_read_block(sc->sc_sf, bp->b_rawblkno,
|
||||
bp->b_data, bp->b_bcount);
|
||||
else
|
||||
error = sdmmc_mem_write_block(sc->sc_sf, bp->b_rawblkno,
|
||||
bp->b_data, bp->b_bcount);
|
||||
if (error) {
|
||||
DPRINTF(("%s: error %d\n", device_xname(sc->sc_ld.sc_dv),
|
||||
error));
|
||||
bp->b_error = EIO; /* XXXX */
|
||||
bp->b_resid = bp->b_bcount;
|
||||
} else {
|
||||
bp->b_resid = 0;
|
||||
}
|
||||
splx(s);
|
||||
|
||||
lddone(&sc->sc_ld, bp);
|
||||
}
|
||||
|
||||
static void
|
||||
ld_sdmmc_timeout(void *arg)
|
||||
{
|
||||
struct ld_sdmmc_task *task = (struct ld_sdmmc_task *)arg;
|
||||
struct ld_sdmmc_softc *sc = task->task_sc;
|
||||
struct buf *bp = task->task_bp;
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
if (!sdmmc_task_pending(&task->task)) {
|
||||
splx(s);
|
||||
return;
|
||||
}
|
||||
bp->b_error = EIO; /* XXXX */
|
||||
bp->b_resid = bp->b_bcount;
|
||||
sdmmc_del_task(&task->task);
|
||||
splx(s);
|
||||
|
||||
lddone(&sc->sc_ld, bp);
|
||||
}
|
||||
|
||||
static int
|
||||
ld_sdmmc_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt)
|
||||
{
|
||||
struct ld_sdmmc_softc *sc = device_private(ld->sc_dv);
|
||||
|
||||
return sdmmc_mem_write_block(sc->sc_sf, blkno, data,
|
||||
blkcnt * ld->sc_secsize);
|
||||
}
|
|
@ -0,0 +1,583 @@
|
|||
/* $NetBSD: sbt.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sbt.c,v 1.9 2007/06/19 07:59:57 uwe Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Driver for Type-A/B SDIO Bluetooth cards */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: sbt.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mbuf.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <netbt/hci.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmcdevs.h>
|
||||
#include <dev/sdmmc/sdmmcvar.h>
|
||||
|
||||
#define CSR_READ_1(sc, reg) sdmmc_io_read_1((sc)->sc_sf, (reg))
|
||||
#define CSR_WRITE_1(sc, reg, val) sdmmc_io_write_1((sc)->sc_sf, (reg), (val))
|
||||
|
||||
#define SBT_REG_DAT 0x00 /* receiver/transmitter data */
|
||||
#define SBT_REG_RPC 0x10 /* read packet control */
|
||||
#define RPC_PCRRT (1<<0) /* packet read retry */
|
||||
#define SBT_REG_WPC 0x11 /* write packet control */
|
||||
#define WPC_PCWRT (1<<0) /* packet write retry */
|
||||
#define SBT_REG_RC 0x12 /* retry control status/set */
|
||||
#define SBT_REG_ISTAT 0x13 /* interrupt status */
|
||||
#define ISTAT_INTRD (1<<0) /* packet available for read */
|
||||
#define SBT_REG_ICLR 0x13 /* interrupt clear */
|
||||
#define SBT_REG_IENA 0x14 /* interrupt enable */
|
||||
#define SBT_REG_BTMODE 0x20 /* SDIO Bluetooth card mode */
|
||||
#define BTMODE_TYPEB (1<<0) /* 1=Type-B, 0=Type-A */
|
||||
|
||||
#define SBT_PKT_BUFSIZ 65540
|
||||
#define SBT_RXTRY_MAX 5
|
||||
|
||||
struct sbt_softc {
|
||||
device_t sc_dev; /* base device */
|
||||
int sc_flags;
|
||||
struct hci_unit *sc_unit; /* Bluetooth HCI Unit */
|
||||
struct bt_stats sc_stats;
|
||||
struct sdmmc_function *sc_sf; /* SDIO function */
|
||||
int sc_dying; /* shutdown in progress */
|
||||
void *sc_ih;
|
||||
u_char *sc_buf;
|
||||
int sc_rxtry;
|
||||
|
||||
/* transmit queues */
|
||||
MBUFQ_HEAD() sc_cmdq;
|
||||
MBUFQ_HEAD() sc_aclq;
|
||||
MBUFQ_HEAD() sc_scoq;
|
||||
};
|
||||
|
||||
/* sc_flags */
|
||||
#define SBT_XMIT (1 << 0) /* transmit is active */
|
||||
#define SBT_ENABLED (1 << 1) /* device is enabled */
|
||||
|
||||
static int sbt_match(device_t, struct cfdata *, void *);
|
||||
static void sbt_attach(device_t, device_t, void *);
|
||||
static int sbt_detach(device_t, int);
|
||||
|
||||
CFATTACH_DECL_NEW(sbt, sizeof(struct sbt_softc),
|
||||
sbt_match, sbt_attach, sbt_detach, NULL);
|
||||
|
||||
static int sbt_write_packet(struct sbt_softc *, u_char *, size_t);
|
||||
static int sbt_read_packet(struct sbt_softc *, u_char *, size_t *);
|
||||
static void sbt_start(struct sbt_softc *);
|
||||
|
||||
static int sbt_intr(void *);
|
||||
|
||||
static int sbt_enable(device_t);
|
||||
static void sbt_disable(device_t);
|
||||
static void sbt_start_cmd(device_t, struct mbuf *);
|
||||
static void sbt_start_acl(device_t, struct mbuf *);
|
||||
static void sbt_start_sco(device_t, struct mbuf *);
|
||||
static void sbt_stats(device_t, struct bt_stats *, int);
|
||||
|
||||
#undef DPRINTF /* avoid redefine by bluetooth.h */
|
||||
#ifdef SBT_DEBUG
|
||||
int sbt_debug = 1;
|
||||
#define DPRINTF(s) printf s
|
||||
#define DNPRINTF(n, s) do { if ((n) <= sbt_debug) printf s; } while (0)
|
||||
#else
|
||||
#define DPRINTF(s) do {} while (0)
|
||||
#define DNPRINTF(n, s) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define DEVNAME(sc) device_xname((sc)->sc_dev)
|
||||
|
||||
|
||||
/*
|
||||
* Autoconf glue
|
||||
*/
|
||||
|
||||
static const struct sbt_product {
|
||||
uint16_t sp_vendor;
|
||||
uint16_t sp_product;
|
||||
const char *sp_cisinfo[4];
|
||||
} sbt_products[] = {
|
||||
{
|
||||
SDMMC_VENDOR_SOCKETCOM,
|
||||
SDMMC_PRODUCT_SOCKETCOM_BTCARD,
|
||||
SDMMC_CIS_SOCKETCOM_BTCARD
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hci_if sbt_hci = {
|
||||
.enable = sbt_enable,
|
||||
.disable = sbt_disable,
|
||||
.output_cmd = sbt_start_cmd,
|
||||
.output_acl = sbt_start_acl,
|
||||
.output_sco = sbt_start_sco,
|
||||
.get_stats = sbt_stats,
|
||||
.ipl = IPL_TTY, /* XXX */
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
sbt_match(device_t parent, struct cfdata *match, void *aux)
|
||||
{
|
||||
struct sdmmc_attach_args *sa = aux;
|
||||
const struct sbt_product *sp;
|
||||
struct sdmmc_function *sf;
|
||||
int i;
|
||||
|
||||
if (sa->sf == NULL)
|
||||
return 0; /* not SDIO */
|
||||
|
||||
sf = sa->sf->sc->sc_fn0;
|
||||
sp = &sbt_products[0];
|
||||
|
||||
for (i = 0; i < sizeof(sbt_products) / sizeof(sbt_products[0]);
|
||||
i++, sp = &sbt_products[i])
|
||||
if (sp->sp_vendor == sf->cis.manufacturer &&
|
||||
sp->sp_product == sf->cis.product)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sbt_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct sbt_softc *sc = device_private(self);
|
||||
struct sdmmc_attach_args *sa = aux;
|
||||
|
||||
aprint_normal("\n");
|
||||
aprint_naive("\n");
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_sf = sa->sf;
|
||||
MBUFQ_INIT(&sc->sc_cmdq);
|
||||
MBUFQ_INIT(&sc->sc_aclq);
|
||||
MBUFQ_INIT(&sc->sc_scoq);
|
||||
|
||||
(void)sdmmc_io_function_disable(sc->sc_sf);
|
||||
if (sdmmc_io_function_enable(sc->sc_sf)) {
|
||||
printf("%s: function not ready\n", DEVNAME(sc));
|
||||
return;
|
||||
}
|
||||
|
||||
/* It may be Type-B, but we use it only in Type-A mode. */
|
||||
printf("%s: SDIO Bluetooth Type-A\n", DEVNAME(sc));
|
||||
|
||||
sc->sc_buf = malloc(SBT_PKT_BUFSIZ, M_DEVBUF, M_NOWAIT | M_CANFAIL);
|
||||
if (sc->sc_buf == NULL) {
|
||||
printf("%s: can't allocate cmd buffer\n", DEVNAME(sc));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enable the HCI packet transport read interrupt. */
|
||||
CSR_WRITE_1(sc, SBT_REG_IENA, ISTAT_INTRD);
|
||||
|
||||
/* Enable the card interrupt for this function. */
|
||||
sc->sc_ih = sdmmc_intr_establish(parent, sbt_intr, sc, DEVNAME(sc));
|
||||
if (sc->sc_ih == NULL) {
|
||||
printf("%s: can't establish interrupt\n", DEVNAME(sc));
|
||||
return;
|
||||
}
|
||||
sdmmc_intr_enable(sc->sc_sf);
|
||||
|
||||
/*
|
||||
* Attach Bluetooth unit (machine-independent HCI).
|
||||
*/
|
||||
sc->sc_unit = hci_attach(&sbt_hci, self, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
sbt_detach(device_t self, int flags)
|
||||
{
|
||||
struct sbt_softc *sc = (struct sbt_softc *)self;
|
||||
|
||||
sc->sc_dying = 1;
|
||||
|
||||
if (sc->sc_unit) {
|
||||
hci_detach(sc->sc_unit);
|
||||
sc->sc_unit = NULL;
|
||||
}
|
||||
|
||||
if (sc->sc_ih != NULL)
|
||||
sdmmc_intr_disestablish(sc->sc_ih);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Bluetooth HCI packet transport
|
||||
*/
|
||||
|
||||
static int
|
||||
sbt_write_packet(struct sbt_softc *sc, u_char *buf, size_t len)
|
||||
{
|
||||
u_char hdr[3];
|
||||
size_t pktlen;
|
||||
int error = EIO;
|
||||
int retry = 3;
|
||||
|
||||
again:
|
||||
if (retry-- == 0) {
|
||||
DPRINTF(("%s: sbt_write_cmd: giving up\n", DEVNAME(sc)));
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Restart the current packet. */
|
||||
sdmmc_io_write_1(sc->sc_sf, SBT_REG_WPC, WPC_PCWRT);
|
||||
|
||||
/* Write the packet length. */
|
||||
pktlen = len + 3;
|
||||
hdr[0] = pktlen & 0xff;
|
||||
hdr[1] = (pktlen >> 8) & 0xff;
|
||||
hdr[2] = (pktlen >> 16) & 0xff;
|
||||
error = sdmmc_io_write_multi_1(sc->sc_sf, SBT_REG_DAT, hdr, 3);
|
||||
if (error) {
|
||||
DPRINTF(("%s: sbt_write_packet: failed to send length\n",
|
||||
DEVNAME(sc)));
|
||||
goto again;
|
||||
}
|
||||
|
||||
error = sdmmc_io_write_multi_1(sc->sc_sf, SBT_REG_DAT, buf, len);
|
||||
if (error) {
|
||||
DPRINTF(("%s: sbt_write_packet: failed to send packet data\n",
|
||||
DEVNAME(sc)));
|
||||
goto again;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sbt_read_packet(struct sbt_softc *sc, u_char *buf, size_t *lenp)
|
||||
{
|
||||
u_char hdr[3];
|
||||
size_t len;
|
||||
int error;
|
||||
|
||||
error = sdmmc_io_read_multi_1(sc->sc_sf, SBT_REG_DAT, hdr, 3);
|
||||
if (error) {
|
||||
DPRINTF(("%s: sbt_read_packet: failed to read length\n",
|
||||
DEVNAME(sc)));
|
||||
goto out;
|
||||
}
|
||||
len = (hdr[0] | (hdr[1] << 8) | (hdr[2] << 16)) - 3;
|
||||
if (len > *lenp) {
|
||||
DPRINTF(("%s: sbt_read_packet: len %u > %u\n",
|
||||
DEVNAME(sc), len, *lenp));
|
||||
error = ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
DNPRINTF(2,("%s: sbt_read_packet: reading len %u bytes\n",
|
||||
DEVNAME(sc), len));
|
||||
error = sdmmc_io_read_multi_1(sc->sc_sf, SBT_REG_DAT, buf, len);
|
||||
if (error) {
|
||||
DPRINTF(("%s: sbt_read_packet: failed to read packet data\n",
|
||||
DEVNAME(sc)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (error) {
|
||||
if (sc->sc_rxtry >= SBT_RXTRY_MAX) {
|
||||
/* Drop and request the next packet. */
|
||||
sc->sc_rxtry = 0;
|
||||
CSR_WRITE_1(sc, SBT_REG_RPC, 0);
|
||||
} else {
|
||||
/* Request the current packet again. */
|
||||
sc->sc_rxtry++;
|
||||
CSR_WRITE_1(sc, SBT_REG_RPC, RPC_PCRRT);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* acknowledge read packet */
|
||||
CSR_WRITE_1(sc, SBT_REG_RPC, 0);
|
||||
|
||||
*lenp = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handling
|
||||
*/
|
||||
|
||||
static int
|
||||
sbt_intr(void *arg)
|
||||
{
|
||||
struct sbt_softc *sc = arg;
|
||||
struct mbuf *m = NULL;
|
||||
u_int8_t status;
|
||||
size_t len;
|
||||
int s;
|
||||
|
||||
s = splsdmmc();
|
||||
|
||||
status = CSR_READ_1(sc, SBT_REG_ISTAT);
|
||||
CSR_WRITE_1(sc, SBT_REG_ICLR, status);
|
||||
|
||||
if ((status & ISTAT_INTRD) == 0)
|
||||
return 0; /* shared SDIO card interrupt? */
|
||||
|
||||
len = SBT_PKT_BUFSIZ;
|
||||
if (sbt_read_packet(sc, sc->sc_buf, &len) != 0 || len == 0) {
|
||||
DPRINTF(("%s: sbt_intr: read failed\n", DEVNAME(sc)));
|
||||
goto eoi;
|
||||
}
|
||||
|
||||
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
||||
if (m == NULL) {
|
||||
DPRINTF(("%s: sbt_intr: MGETHDR failed\n", DEVNAME(sc)));
|
||||
goto eoi;
|
||||
}
|
||||
|
||||
m->m_pkthdr.len = m->m_len = MHLEN;
|
||||
m_copyback(m, 0, len, sc->sc_buf);
|
||||
if (m->m_pkthdr.len == MAX(MHLEN, len)) {
|
||||
m->m_pkthdr.len = len;
|
||||
m->m_len = MIN(MHLEN, m->m_pkthdr.len);
|
||||
} else {
|
||||
DPRINTF(("%s: sbt_intr: m_copyback failed\n", DEVNAME(sc)));
|
||||
m_free(m);
|
||||
m = NULL;
|
||||
}
|
||||
|
||||
eoi:
|
||||
if (m != NULL) {
|
||||
switch (sc->sc_buf[0]) {
|
||||
case HCI_ACL_DATA_PKT:
|
||||
DNPRINTF(1,("%s: recv ACL packet (%d bytes)\n",
|
||||
DEVNAME(sc), m->m_pkthdr.len));
|
||||
hci_input_acl(sc->sc_unit, m);
|
||||
break;
|
||||
case HCI_SCO_DATA_PKT:
|
||||
DNPRINTF(1,("%s: recv SCO packet (%d bytes)\n",
|
||||
DEVNAME(sc), m->m_pkthdr.len));
|
||||
hci_input_sco(sc->sc_unit, m);
|
||||
break;
|
||||
case HCI_EVENT_PKT:
|
||||
DNPRINTF(1,("%s: recv EVENT packet (%d bytes)\n",
|
||||
DEVNAME(sc), m->m_pkthdr.len));
|
||||
hci_input_event(sc->sc_unit, m);
|
||||
break;
|
||||
default:
|
||||
DPRINTF(("%s: recv 0x%x packet (%d bytes)\n",
|
||||
DEVNAME(sc), sc->sc_buf[0], m->m_pkthdr.len));
|
||||
sc->sc_stats.err_rx++;
|
||||
m_free(m);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
sc->sc_stats.err_rx++;
|
||||
|
||||
splx(s);
|
||||
|
||||
/* Claim this interrupt. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Bluetooth HCI unit functions
|
||||
*/
|
||||
|
||||
static int
|
||||
sbt_enable(device_t self)
|
||||
{
|
||||
struct sbt_softc *sc = device_private(self);
|
||||
int s;
|
||||
|
||||
if (sc->sc_flags & SBT_ENABLED)
|
||||
return 0;
|
||||
|
||||
s = spltty();
|
||||
|
||||
sc->sc_flags |= SBT_ENABLED;
|
||||
sc->sc_flags &= ~SBT_XMIT;
|
||||
|
||||
splx(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sbt_disable(device_t self)
|
||||
{
|
||||
struct sbt_softc *sc = device_private(self);
|
||||
int s;
|
||||
|
||||
if (!(sc->sc_flags & SBT_ENABLED))
|
||||
return;
|
||||
|
||||
s = spltty();
|
||||
|
||||
#ifdef notyet /* XXX */
|
||||
if (sc->sc_rxp) {
|
||||
m_freem(sc->sc_rxp);
|
||||
sc->sc_rxp = NULL;
|
||||
}
|
||||
|
||||
if (sc->sc_txp) {
|
||||
m_freem(sc->sc_txp);
|
||||
sc->sc_txp = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
MBUFQ_DRAIN(&sc->sc_cmdq);
|
||||
MBUFQ_DRAIN(&sc->sc_aclq);
|
||||
MBUFQ_DRAIN(&sc->sc_scoq);
|
||||
|
||||
sc->sc_flags &= ~SBT_ENABLED;
|
||||
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static void
|
||||
sbt_start(struct sbt_softc *sc)
|
||||
{
|
||||
struct mbuf *m;
|
||||
int len;
|
||||
#ifdef SBT_DEBUG
|
||||
const char *what;
|
||||
#endif
|
||||
|
||||
KASSERT((sc->sc_flags & SBT_XMIT) == 0);
|
||||
|
||||
if (sc->sc_dying)
|
||||
return;
|
||||
|
||||
if (MBUFQ_FIRST(&sc->sc_cmdq)) {
|
||||
MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
|
||||
sc->sc_stats.cmd_tx++;
|
||||
#ifdef SBT_DEBUG
|
||||
what = "CMD";
|
||||
#endif
|
||||
goto start;
|
||||
}
|
||||
|
||||
if (MBUFQ_FIRST(&sc->sc_scoq)) {
|
||||
MBUFQ_DEQUEUE(&sc->sc_scoq, m);
|
||||
sc->sc_stats.sco_tx++;
|
||||
#ifdef SBT_DEBUG
|
||||
what = "SCO";
|
||||
#endif
|
||||
goto start;
|
||||
}
|
||||
|
||||
if (MBUFQ_FIRST(&sc->sc_aclq)) {
|
||||
MBUFQ_DEQUEUE(&sc->sc_aclq, m);
|
||||
sc->sc_stats.acl_tx++;
|
||||
#ifdef SBT_DEBUG
|
||||
what = "ACL";
|
||||
#endif
|
||||
goto start;
|
||||
}
|
||||
|
||||
/* Nothing to send */
|
||||
return;
|
||||
|
||||
start:
|
||||
DNPRINTF(1,("%s: xmit %s packet (%d bytes)\n", DEVNAME(sc),
|
||||
what, m->m_pkthdr.len));
|
||||
|
||||
sc->sc_flags |= SBT_XMIT;
|
||||
|
||||
len = m->m_pkthdr.len;
|
||||
m_copydata(m, 0, len, sc->sc_buf);
|
||||
m_freem(m);
|
||||
|
||||
if (sbt_write_packet(sc, sc->sc_buf, len))
|
||||
DPRINTF(("%s: sbt_write_packet failed\n", DEVNAME(sc)));
|
||||
|
||||
sc->sc_flags &= ~SBT_XMIT;
|
||||
}
|
||||
|
||||
static void
|
||||
sbt_start_cmd(device_t self, struct mbuf *m)
|
||||
{
|
||||
struct sbt_softc *sc = device_private(self);
|
||||
int s;
|
||||
|
||||
KASSERT(sc->sc_flags & SBT_ENABLED);
|
||||
|
||||
M_SETCTX(m, NULL);
|
||||
|
||||
s = spltty();
|
||||
|
||||
MBUFQ_ENQUEUE(&sc->sc_cmdq, m);
|
||||
if ((sc->sc_flags & SBT_XMIT) == 0)
|
||||
sbt_start(sc);
|
||||
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static void
|
||||
sbt_start_acl(device_t self, struct mbuf *m)
|
||||
{
|
||||
struct sbt_softc *sc = device_private(self);
|
||||
int s;
|
||||
|
||||
KASSERT(sc->sc_flags & SBT_ENABLED);
|
||||
|
||||
M_SETCTX(m, NULL);
|
||||
|
||||
s = spltty();
|
||||
|
||||
MBUFQ_ENQUEUE(&sc->sc_aclq, m);
|
||||
if ((sc->sc_flags & SBT_XMIT) == 0)
|
||||
sbt_start(sc);
|
||||
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static void
|
||||
sbt_start_sco(device_t self, struct mbuf *m)
|
||||
{
|
||||
struct sbt_softc *sc = device_private(self);
|
||||
int s;
|
||||
|
||||
KASSERT(sc->sc_flags & SBT_ENABLED);
|
||||
|
||||
s = spltty();
|
||||
|
||||
MBUFQ_ENQUEUE(&sc->sc_scoq, m);
|
||||
if ((sc->sc_flags & SBT_XMIT) == 0)
|
||||
sbt_start(sc);
|
||||
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static void
|
||||
sbt_stats(device_t self, struct bt_stats *dest, int flush)
|
||||
{
|
||||
struct sbt_softc *sc = device_private(self);
|
||||
int s;
|
||||
|
||||
s = spltty();
|
||||
|
||||
memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats));
|
||||
|
||||
if (flush)
|
||||
memset(&sc->sc_stats, 0, sizeof(struct bt_stats));
|
||||
|
||||
splx(s);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,181 @@
|
|||
/* $NetBSD: sdhcreg.h,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sdhcreg.h,v 1.4 2006/07/30 17:20:40 fgsch Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDHCREG_H_
|
||||
#define _SDHCREG_H_
|
||||
|
||||
/* Host standard register set */
|
||||
#define SDHC_DMA_ADDR 0x00
|
||||
#define SDHC_BLOCK_SIZE 0x04
|
||||
#define SDHC_BLOCK_COUNT 0x06
|
||||
#define SDHC_BLOCK_COUNT_MAX 512
|
||||
#define SDHC_ARGUMENT 0x08
|
||||
#define SDHC_TRANSFER_MODE 0x0c
|
||||
#define SDHC_MULTI_BLOCK_MODE (1<<5)
|
||||
#define SDHC_READ_MODE (1<<4)
|
||||
#define SDHC_AUTO_CMD12_ENABLE (1<<2)
|
||||
#define SDHC_BLOCK_COUNT_ENABLE (1<<1)
|
||||
#define SDHC_DMA_ENABLE (1<<0)
|
||||
#define SDHC_COMMAND 0x0e
|
||||
/* 14-15 reserved */
|
||||
#define SDHC_COMMAND_INDEX_SHIFT 8
|
||||
#define SDHC_COMMAND_INDEX_MASK 0x3f
|
||||
#define SDHC_COMMAND_TYPE_ABORT (3<<6)
|
||||
#define SDHC_COMMAND_TYPE_RESUME (2<<6)
|
||||
#define SDHC_COMMAND_TYPE_SUSPEND (1<<6)
|
||||
#define SDHC_COMMAND_TYPE_NORMAL (0<<6)
|
||||
#define SDHC_DATA_PRESENT_SELECT (1<<5)
|
||||
#define SDHC_INDEX_CHECK_ENABLE (1<<4)
|
||||
#define SDHC_CRC_CHECK_ENABLE (1<<3)
|
||||
/* 2 reserved */
|
||||
#define SDHC_RESP_LEN_48_CHK_BUSY (3<<0)
|
||||
#define SDHC_RESP_LEN_48 (2<<0)
|
||||
#define SDHC_RESP_LEN_136 (1<<0)
|
||||
#define SDHC_NO_RESPONSE (0<<0)
|
||||
#define SDHC_RESPONSE 0x10 /* - 0x1f */
|
||||
#define SDHC_DATA 0x20
|
||||
#define SDHC_PRESENT_STATE 0x24
|
||||
/* 25-31 reserved */
|
||||
#define SDHC_CMD_LINE_SIGNAL_LEVEL (1<<24)
|
||||
#define SDHC_DAT3_LINE_LEVEL (1<<23)
|
||||
#define SDHC_DAT2_LINE_LEVEL (1<<22)
|
||||
#define SDHC_DAT1_LINE_LEVEL (1<<21)
|
||||
#define SDHC_DAT0_LINE_LEVEL (1<<20)
|
||||
#define SDHC_WRITE_PROTECT_SWITCH (1<<19)
|
||||
#define SDHC_CARD_DETECT_PIN_LEVEL (1<<18)
|
||||
#define SDHC_CARD_STATE_STABLE (1<<17)
|
||||
#define SDHC_CARD_INSERTED (1<<16)
|
||||
/* 12-15 reserved */
|
||||
#define SDHC_BUFFER_READ_ENABLE (1<<11)
|
||||
#define SDHC_BUFFER_WRITE_ENABLE (1<<10)
|
||||
#define SDHC_READ_TRANSFER_ACTIVE (1<<9)
|
||||
#define SDHC_WRITE_TRANSFER_ACTIVE (1<<8)
|
||||
/* 3-7 reserved */
|
||||
#define SDHC_DAT_ACTIVE (1<<2)
|
||||
#define SDHC_CMD_INHIBIT_DAT (1<<1)
|
||||
#define SDHC_CMD_INHIBIT_CMD (1<<0)
|
||||
#define SDHC_CMD_INHIBIT_MASK 0x0003
|
||||
#define SDHC_HOST_CTL 0x28
|
||||
#define SDHC_HIGH_SPEED (1<<2)
|
||||
#define SDHC_4BIT_MODE (1<<1)
|
||||
#define SDHC_LED_ON (1<<0)
|
||||
#define SDHC_POWER_CTL 0x29
|
||||
#define SDHC_VOLTAGE_SHIFT 1
|
||||
#define SDHC_VOLTAGE_MASK 0x07
|
||||
#define SDHC_VOLTAGE_3_3V 0x07
|
||||
#define SDHC_VOLTAGE_3_0V 0x06
|
||||
#define SDHC_VOLTAGE_1_8V 0x05
|
||||
#define SDHC_BUS_POWER (1<<0)
|
||||
#define SDHC_BLOCK_GAP_CTL 0x2a
|
||||
#define SDHC_WAKEUP_CTL 0x2b
|
||||
#define SDHC_CLOCK_CTL 0x2c
|
||||
#define SDHC_SDCLK_DIV_SHIFT 8
|
||||
#define SDHC_SDCLK_DIV_MASK 0xff
|
||||
#define SDHC_SDCLK_ENABLE (1<<2)
|
||||
#define SDHC_INTCLK_STABLE (1<<1)
|
||||
#define SDHC_INTCLK_ENABLE (1<<0)
|
||||
#define SDHC_TIMEOUT_CTL 0x2e
|
||||
#define SDHC_TIMEOUT_MAX 0x0e
|
||||
#define SDHC_SOFTWARE_RESET 0x2f
|
||||
#define SDHC_RESET_MASK 0x5
|
||||
#define SDHC_RESET_DAT (1<<2)
|
||||
#define SDHC_RESET_CMD (1<<1)
|
||||
#define SDHC_RESET_ALL (1<<0)
|
||||
#define SDHC_NINTR_STATUS 0x30
|
||||
#define SDHC_ERROR_INTERRUPT (1<<15)
|
||||
#define SDHC_CARD_INTERRUPT (1<<8)
|
||||
#define SDHC_CARD_REMOVAL (1<<7)
|
||||
#define SDHC_CARD_INSERTION (1<<6)
|
||||
#define SDHC_BUFFER_READ_READY (1<<5)
|
||||
#define SDHC_BUFFER_WRITE_READY (1<<4)
|
||||
#define SDHC_DMA_INTERRUPT (1<<3)
|
||||
#define SDHC_BLOCK_GAP_EVENT (1<<2)
|
||||
#define SDHC_TRANSFER_COMPLETE (1<<1)
|
||||
#define SDHC_COMMAND_COMPLETE (1<<0)
|
||||
#define SDHC_NINTR_STATUS_MASK 0x81ff
|
||||
#define SDHC_EINTR_STATUS 0x32
|
||||
#define SDHC_AUTO_CMD12_ERROR (1<<8)
|
||||
#define SDHC_CURRENT_LIMIT_ERROR (1<<7)
|
||||
#define SDHC_DATA_END_BIT_ERROR (1<<6)
|
||||
#define SDHC_DATA_CRC_ERROR (1<<5)
|
||||
#define SDHC_DATA_TIMEOUT_ERROR (1<<4)
|
||||
#define SDHC_CMD_INDEX_ERROR (1<<3)
|
||||
#define SDHC_CMD_END_BIT_ERROR (1<<2)
|
||||
#define SDHC_CMD_CRC_ERROR (1<<1)
|
||||
#define SDHC_CMD_TIMEOUT_ERROR (1<<0)
|
||||
#define SDHC_EINTR_STATUS_MASK 0x01ff /* excluding vendor signals */
|
||||
#define SDHC_NINTR_STATUS_EN 0x34
|
||||
#define SDHC_EINTR_STATUS_EN 0x36
|
||||
#define SDHC_NINTR_SIGNAL_EN 0x38
|
||||
#define SDHC_NINTR_SIGNAL_MASK 0x01ff
|
||||
#define SDHC_EINTR_SIGNAL_EN 0x3a
|
||||
#define SDHC_EINTR_SIGNAL_MASK 0x01ff /* excluding vendor signals */
|
||||
#define SDHC_CMD12_ERROR_STATUS 0x3c
|
||||
#define SDHC_CAPABILITIES 0x40
|
||||
#define SDHC_VOLTAGE_SUPP_1_8V (1<<26)
|
||||
#define SDHC_VOLTAGE_SUPP_3_0V (1<<25)
|
||||
#define SDHC_VOLTAGE_SUPP_3_3V (1<<24)
|
||||
#define SDHC_DMA_SUPPORT (1<<22)
|
||||
#define SDHC_HIGH_SPEED_SUPP (1<<21)
|
||||
#define SDHC_MAX_BLK_LEN_512 0
|
||||
#define SDHC_MAX_BLK_LEN_1024 1
|
||||
#define SDHC_MAX_BLK_LEN_2048 2
|
||||
#define SDHC_MAX_BLK_LEN_SHIFT 16
|
||||
#define SDHC_MAX_BLK_LEN_MASK 0x3
|
||||
#define SDHC_BASE_FREQ_SHIFT 8
|
||||
#define SDHC_BASE_FREQ_MASK 0x3f
|
||||
#define SDHC_TIMEOUT_FREQ_UNIT (1<<7) /* 0=KHz, 1=MHz */
|
||||
#define SDHC_TIMEOUT_FREQ_SHIFT 0
|
||||
#define SDHC_TIMEOUT_FREQ_MASK 0x1f
|
||||
#define SDHC_MAX_CAPABILITIES 0x48
|
||||
#define SDHC_SLOT_INTR_STATUS 0xfc
|
||||
#define SDHC_HOST_CTL_VERSION 0xfe
|
||||
#define SDHC_SPEC_VERS_SHIFT 0
|
||||
#define SDHC_SPEC_VERS_MASK 0xff
|
||||
#define SDHC_VENDOR_VERS_SHIFT 8
|
||||
#define SDHC_VENDOR_VERS_MASK 0xff
|
||||
|
||||
/* SDHC_CAPABILITIES decoding */
|
||||
#define SDHC_BASE_FREQ_KHZ(cap) \
|
||||
((((cap) >> SDHC_BASE_FREQ_SHIFT) & SDHC_BASE_FREQ_MASK) * 1000)
|
||||
#define SDHC_TIMEOUT_FREQ(cap) \
|
||||
(((cap) >> SDHC_TIMEOUT_FREQ_SHIFT) & SDHC_TIMEOUT_FREQ_MASK)
|
||||
#define SDHC_TIMEOUT_FREQ_KHZ(cap) \
|
||||
(((cap) & SDHC_TIMEOUT_FREQ_UNIT) ? \
|
||||
SDHC_TIMEOUT_FREQ(cap) * 1000: \
|
||||
SDHC_TIMEOUT_FREQ(cap))
|
||||
|
||||
/* SDHC_HOST_CTL_VERSION decoding */
|
||||
#define SDHC_SPEC_VERSION(hcv) \
|
||||
(((hcv) >> SDHC_SPEC_VERS_SHIFT) & SDHC_SPEC_VERS_MASK)
|
||||
#define SDHC_VENDOR_VERSION(hcv) \
|
||||
(((hcv) >> SDHC_VENDOR_VERS_SHIFT) & SDHC_VENDOR_VERS_MASK)
|
||||
|
||||
#define SDHC_PRESENT_STATE_BITS \
|
||||
"\20\31CL\30D3L\27D2L\26D1L\25D0L\24WPS\23CD\22CSS\21CI" \
|
||||
"\14BRE\13BWE\12RTA\11WTA\3DLA\2CID\1CIC"
|
||||
#define SDHC_NINTR_STATUS_BITS \
|
||||
"\20\20ERROR\11CARD\10REMOVAL\7INSERTION\6READ\5WRITE" \
|
||||
"\4DMA\3GAP\2XFER\1CMD"
|
||||
#define SDHC_EINTR_STATUS_BITS \
|
||||
"\20\11ACMD12\10CL\7DEB\6DCRC\5DT\4CI\3CEB\2CCRC\1CT"
|
||||
#define SDHC_CAPABILITIES_BITS \
|
||||
"\20\33Vdd1.8V\32Vdd3.0V\31Vdd3.3V\30SUSPEND\27DMA\26HIGHSPEED"
|
||||
|
||||
#endif /* _SDHCREG_H_ */
|
|
@ -0,0 +1,51 @@
|
|||
/* $NetBSD: sdhcvar.h,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sdhcvar.h,v 1.3 2007/09/06 08:01:01 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDHCVAR_H_
|
||||
#define _SDHCVAR_H_
|
||||
|
||||
#include <sys/bus.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/pmf.h>
|
||||
|
||||
struct sdhc_host;
|
||||
|
||||
struct sdhc_softc {
|
||||
device_t sc_dev;
|
||||
|
||||
struct sdhc_host **sc_host;
|
||||
int sc_nhosts;
|
||||
|
||||
bus_dma_tag_t sc_dmat;
|
||||
|
||||
uint32_t sc_flags;
|
||||
#define SDHC_FLAG_USE_DMA 0x0001
|
||||
#define SDHC_FLAG_FORCE_DMA 0x0002
|
||||
#define SDHC_FLAG_NO_PWR0 0x0004
|
||||
};
|
||||
|
||||
/* Host controller functions called by the attachment driver. */
|
||||
int sdhc_host_found(struct sdhc_softc *, bus_space_tag_t,
|
||||
bus_space_handle_t, bus_size_t);
|
||||
int sdhc_intr(void *);
|
||||
bool sdhc_suspend(device_t dev PMF_FN_ARGS);
|
||||
bool sdhc_resume(device_t dev PMF_FN_ARGS);
|
||||
bool sdhc_shutdown(device_t dev, int flags);
|
||||
|
||||
#endif /* _SDHCVAR_H_ */
|
|
@ -0,0 +1,785 @@
|
|||
/* $NetBSD: sdmmc.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sdmmc.c,v 1.18 2009/01/09 10:58:38 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2007-2009 NONAKA Kimihiro <nonaka@netbsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Host controller independent SD/MMC bus driver based on information
|
||||
* from SanDisk SD Card Product Manual Revision 2.2 (SanDisk), SDIO
|
||||
* Simple Specification Version 1.0 (SDIO) and the Linux "mmc" driver.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmc_ioreg.h>
|
||||
#include <dev/sdmmc/sdmmcchip.h>
|
||||
#include <dev/sdmmc/sdmmcreg.h>
|
||||
#include <dev/sdmmc/sdmmcvar.h>
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
int sdmmcdebug = 1;
|
||||
static void sdmmc_dump_command(struct sdmmc_softc *, struct sdmmc_command *);
|
||||
#define DPRINTF(n,s) do { if ((n) <= sdmmcdebug) printf s; } while (0)
|
||||
#else
|
||||
#define DPRINTF(n,s) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define DEVNAME(sc) SDMMCDEVNAME(sc)
|
||||
|
||||
static int sdmmc_match(device_t, cfdata_t, void *);
|
||||
static void sdmmc_attach(device_t, device_t, void *);
|
||||
static int sdmmc_detach(device_t, int);
|
||||
|
||||
CFATTACH_DECL_NEW(sdmmc, sizeof(struct sdmmc_softc),
|
||||
sdmmc_match, sdmmc_attach, sdmmc_detach, NULL);
|
||||
|
||||
static void sdmmc_doattach(device_t);
|
||||
static void sdmmc_task_thread(void *);
|
||||
static void sdmmc_discover_task(void *);
|
||||
static void sdmmc_card_attach(struct sdmmc_softc *);
|
||||
static void sdmmc_card_detach(struct sdmmc_softc *, int);
|
||||
static int sdmmc_print(void *, const char *);
|
||||
static int sdmmc_enable(struct sdmmc_softc *);
|
||||
static void sdmmc_disable(struct sdmmc_softc *);
|
||||
static int sdmmc_scan(struct sdmmc_softc *);
|
||||
static int sdmmc_init(struct sdmmc_softc *);
|
||||
|
||||
static int
|
||||
sdmmc_match(device_t parent, cfdata_t cf, void *aux)
|
||||
{
|
||||
struct sdmmcbus_attach_args *saa = (struct sdmmcbus_attach_args *)aux;
|
||||
|
||||
if (strcmp(saa->saa_busname, cf->cf_name) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sdmmc_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct sdmmc_softc *sc = device_private(self);
|
||||
struct sdmmcbus_attach_args *saa = (struct sdmmcbus_attach_args *)aux;
|
||||
int error;
|
||||
|
||||
aprint_normal("\n");
|
||||
aprint_naive("\n");
|
||||
|
||||
sc->sc_dev = self;
|
||||
sc->sc_sct = saa->saa_sct;
|
||||
sc->sc_sch = saa->saa_sch;
|
||||
sc->sc_dmat = saa->saa_dmat;
|
||||
sc->sc_clkmin = saa->saa_clkmin;
|
||||
sc->sc_clkmax = saa->saa_clkmax;
|
||||
sc->sc_busclk = sc->sc_clkmax;
|
||||
sc->sc_buswidth = 1;
|
||||
sc->sc_caps = saa->saa_caps;
|
||||
|
||||
if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
|
||||
error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, SDMMC_MAXNSEGS,
|
||||
MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmap);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"couldn't create dma map. (error=%d)\n", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SIMPLEQ_INIT(&sc->sf_head);
|
||||
TAILQ_INIT(&sc->sc_tskq);
|
||||
TAILQ_INIT(&sc->sc_intrq);
|
||||
|
||||
sdmmc_init_task(&sc->sc_discover_task, sdmmc_discover_task, sc);
|
||||
sdmmc_init_task(&sc->sc_intr_task, sdmmc_intr_task, sc);
|
||||
|
||||
mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_SDMMC);
|
||||
mutex_init(&sc->sc_tskq_mtx, MUTEX_DEFAULT, IPL_SDMMC);
|
||||
mutex_init(&sc->sc_discover_task_mtx, MUTEX_DEFAULT, IPL_SDMMC);
|
||||
mutex_init(&sc->sc_intr_task_mtx, MUTEX_DEFAULT, IPL_SDMMC);
|
||||
cv_init(&sc->sc_tskq_cv, "mmctaskq");
|
||||
|
||||
if (!pmf_device_register(self, NULL, NULL)) {
|
||||
aprint_error_dev(self, "couldn't establish power handler\n");
|
||||
}
|
||||
|
||||
SET(sc->sc_flags, SMF_INITED);
|
||||
|
||||
/*
|
||||
* Create the event thread that will attach and detach cards
|
||||
* and perform other lengthy operations.
|
||||
*/
|
||||
config_pending_incr();
|
||||
config_interrupts(self, sdmmc_doattach);
|
||||
}
|
||||
|
||||
static int
|
||||
sdmmc_detach(device_t self, int flags)
|
||||
{
|
||||
struct sdmmc_softc *sc = device_private(self);
|
||||
int error;
|
||||
|
||||
mutex_enter(&sc->sc_tskq_mtx);
|
||||
sc->sc_dying = 1;
|
||||
cv_signal(&sc->sc_tskq_cv);
|
||||
while (sc->sc_tskq_lwp != NULL)
|
||||
cv_wait(&sc->sc_tskq_cv, &sc->sc_tskq_mtx);
|
||||
mutex_exit(&sc->sc_tskq_mtx);
|
||||
|
||||
pmf_device_deregister(self);
|
||||
|
||||
error = config_detach_children(self, flags);
|
||||
if (error)
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sdmmc_doattach(device_t dev)
|
||||
{
|
||||
struct sdmmc_softc *sc = device_private(dev);
|
||||
|
||||
if (kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
|
||||
sdmmc_task_thread, sc, &sc->sc_tskq_lwp, "%s", device_xname(dev))) {
|
||||
aprint_error_dev(dev, "couldn't create task thread\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_add_task(struct sdmmc_softc *sc, struct sdmmc_task *task)
|
||||
{
|
||||
|
||||
mutex_enter(&sc->sc_tskq_mtx);
|
||||
task->onqueue = 1;
|
||||
task->sc = sc;
|
||||
TAILQ_INSERT_TAIL(&sc->sc_tskq, task, next);
|
||||
cv_broadcast(&sc->sc_tskq_cv);
|
||||
mutex_exit(&sc->sc_tskq_mtx);
|
||||
}
|
||||
|
||||
static inline void
|
||||
sdmmc_del_task1(struct sdmmc_softc *sc, struct sdmmc_task *task)
|
||||
{
|
||||
|
||||
TAILQ_REMOVE(&sc->sc_tskq, task, next);
|
||||
task->sc = NULL;
|
||||
task->onqueue = 0;
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_del_task(struct sdmmc_task *task)
|
||||
{
|
||||
struct sdmmc_softc *sc = (struct sdmmc_softc *)task->sc;
|
||||
|
||||
if (sc != NULL) {
|
||||
mutex_enter(&sc->sc_tskq_mtx);
|
||||
sdmmc_del_task1(sc, task);
|
||||
mutex_exit(&sc->sc_tskq_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sdmmc_task_thread(void *arg)
|
||||
{
|
||||
struct sdmmc_softc *sc = (struct sdmmc_softc *)arg;
|
||||
struct sdmmc_task *task;
|
||||
|
||||
sdmmc_discover_task(sc);
|
||||
config_pending_decr();
|
||||
|
||||
mutex_enter(&sc->sc_tskq_mtx);
|
||||
for (;;) {
|
||||
task = TAILQ_FIRST(&sc->sc_tskq);
|
||||
if (task != NULL) {
|
||||
sdmmc_del_task1(sc, task);
|
||||
mutex_exit(&sc->sc_tskq_mtx);
|
||||
(*task->func)(task->arg);
|
||||
mutex_enter(&sc->sc_tskq_mtx);
|
||||
} else {
|
||||
/* Check for the exit condition. */
|
||||
if (sc->sc_dying)
|
||||
break;
|
||||
cv_wait(&sc->sc_tskq_cv, &sc->sc_tskq_mtx);
|
||||
}
|
||||
}
|
||||
/* time to die. */
|
||||
sc->sc_dying = 0;
|
||||
if (ISSET(sc->sc_flags, SMF_CARD_PRESENT))
|
||||
sdmmc_card_detach(sc, DETACH_FORCE);
|
||||
sc->sc_tskq_lwp = NULL;
|
||||
cv_broadcast(&sc->sc_tskq_cv);
|
||||
mutex_exit(&sc->sc_tskq_mtx);
|
||||
kthread_exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_needs_discover(device_t dev)
|
||||
{
|
||||
struct sdmmc_softc *sc = device_private(dev);
|
||||
|
||||
if (!ISSET(sc->sc_flags, SMF_INITED))
|
||||
return;
|
||||
|
||||
mutex_enter(&sc->sc_discover_task_mtx);
|
||||
if (!sdmmc_task_pending(&sc->sc_discover_task))
|
||||
sdmmc_add_task(sc, &sc->sc_discover_task);
|
||||
mutex_exit(&sc->sc_discover_task_mtx);
|
||||
}
|
||||
|
||||
static void
|
||||
sdmmc_discover_task(void *arg)
|
||||
{
|
||||
struct sdmmc_softc *sc = (struct sdmmc_softc *)arg;
|
||||
|
||||
if (sdmmc_chip_card_detect(sc->sc_sct, sc->sc_sch)) {
|
||||
if (!ISSET(sc->sc_flags, SMF_CARD_PRESENT)) {
|
||||
SET(sc->sc_flags, SMF_CARD_PRESENT);
|
||||
sdmmc_card_attach(sc);
|
||||
}
|
||||
} else {
|
||||
if (ISSET(sc->sc_flags, SMF_CARD_PRESENT)) {
|
||||
CLR(sc->sc_flags, SMF_CARD_PRESENT);
|
||||
sdmmc_card_detach(sc, DETACH_FORCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from process context when a card is present.
|
||||
*/
|
||||
static void
|
||||
sdmmc_card_attach(struct sdmmc_softc *sc)
|
||||
{
|
||||
struct sdmmc_function *sf;
|
||||
struct sdmmc_attach_args saa;
|
||||
int error;
|
||||
|
||||
DPRINTF(1,("%s: attach card\n", DEVNAME(sc)));
|
||||
|
||||
CLR(sc->sc_flags, SMF_CARD_ATTACHED);
|
||||
|
||||
/*
|
||||
* Power up the card (or card stack).
|
||||
*/
|
||||
error = sdmmc_enable(sc);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't enable card\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan for I/O functions and memory cards on the bus,
|
||||
* allocating a sdmmc_function structure for each.
|
||||
*/
|
||||
error = sdmmc_scan(sc);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "no functions\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set SD/MMC bus clock.
|
||||
*/
|
||||
#ifdef SDMMC_DEBUG
|
||||
if ((sc->sc_busclk / 1000) != 0) {
|
||||
DPRINTF(1,("%s: bus clock: %u.%03u MHz\n", DEVNAME(sc),
|
||||
sc->sc_busclk / 1000, sc->sc_busclk % 1000));
|
||||
} else {
|
||||
DPRINTF(1,("%s: bus clock: %u KHz\n", DEVNAME(sc),
|
||||
sc->sc_busclk % 1000));
|
||||
}
|
||||
#endif
|
||||
(void)sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, sc->sc_busclk);
|
||||
|
||||
/*
|
||||
* Initialize the I/O functions and memory cards.
|
||||
*/
|
||||
error = sdmmc_init(sc);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "init failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
|
||||
if (ISSET(sc->sc_flags, SMF_IO_MODE) && sf->number < 1)
|
||||
continue;
|
||||
|
||||
memset(&saa, 0, sizeof saa);
|
||||
saa.manufacturer = sf->cis.manufacturer;
|
||||
saa.product = sf->cis.product;
|
||||
saa.sf = sf;
|
||||
|
||||
sf->child =
|
||||
config_found_ia(sc->sc_dev, "sdmmc", &saa, sdmmc_print);
|
||||
}
|
||||
|
||||
SET(sc->sc_flags, SMF_CARD_ATTACHED);
|
||||
return;
|
||||
|
||||
err:
|
||||
sdmmc_card_detach(sc, DETACH_FORCE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from process context with DETACH_* flags from <sys/device.h>
|
||||
* when cards are gone.
|
||||
*/
|
||||
static void
|
||||
sdmmc_card_detach(struct sdmmc_softc *sc, int flags)
|
||||
{
|
||||
struct sdmmc_function *sf, *sfnext;
|
||||
|
||||
DPRINTF(1,("%s: detach card\n", DEVNAME(sc)));
|
||||
|
||||
if (ISSET(sc->sc_flags, SMF_CARD_ATTACHED)) {
|
||||
SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
|
||||
if (sf->child != NULL) {
|
||||
config_detach(sf->child, DETACH_FORCE);
|
||||
sf->child = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
KASSERT(TAILQ_EMPTY(&sc->sc_intrq));
|
||||
|
||||
CLR(sc->sc_flags, SMF_CARD_ATTACHED);
|
||||
}
|
||||
|
||||
/* Power down. */
|
||||
sdmmc_disable(sc);
|
||||
|
||||
/* Free all sdmmc_function structures. */
|
||||
for (sf = SIMPLEQ_FIRST(&sc->sf_head); sf != NULL; sf = sfnext) {
|
||||
sfnext = SIMPLEQ_NEXT(sf, sf_list);
|
||||
sdmmc_function_free(sf);
|
||||
}
|
||||
SIMPLEQ_INIT(&sc->sf_head);
|
||||
sc->sc_function_count = 0;
|
||||
sc->sc_fn0 = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
sdmmc_print(void *aux, const char *pnp)
|
||||
{
|
||||
struct sdmmc_attach_args *sa = aux;
|
||||
struct sdmmc_function *sf = sa->sf;
|
||||
struct sdmmc_cis *cis = &sf->sc->sc_fn0->cis;
|
||||
int i;
|
||||
|
||||
if (pnp) {
|
||||
if (sf->number == 0)
|
||||
return QUIET;
|
||||
|
||||
for (i = 0; i < 4 && cis->cis1_info[i]; i++)
|
||||
printf("%s%s", i ? ", " : "\"", cis->cis1_info[i]);
|
||||
if (i != 0)
|
||||
printf("\"");
|
||||
|
||||
if (cis->manufacturer != SDMMC_VENDOR_INVALID &&
|
||||
cis->product != SDMMC_PRODUCT_INVALID) {
|
||||
printf("%s(", i ? " " : "");
|
||||
if (cis->manufacturer != SDMMC_VENDOR_INVALID)
|
||||
printf("manufacturer 0x%x%s",
|
||||
cis->manufacturer,
|
||||
cis->product == SDMMC_PRODUCT_INVALID ?
|
||||
"" : ", ");
|
||||
if (cis->product != SDMMC_PRODUCT_INVALID)
|
||||
printf("product 0x%x", cis->product);
|
||||
printf(")");
|
||||
}
|
||||
printf("%sat %s", i ? " " : "", pnp);
|
||||
}
|
||||
if (sf->number > 0)
|
||||
printf(" function %d", sf->number);
|
||||
|
||||
if (!pnp) {
|
||||
for (i = 0; i < 3 && cis->cis1_info[i]; i++)
|
||||
printf("%s%s", i ? ", " : " \"", cis->cis1_info[i]);
|
||||
if (i != 0)
|
||||
printf("\"");
|
||||
}
|
||||
return UNCONF;
|
||||
}
|
||||
|
||||
static int
|
||||
sdmmc_enable(struct sdmmc_softc *sc)
|
||||
{
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Calculate the equivalent of the card OCR from the host
|
||||
* capabilities and select the maximum supported bus voltage.
|
||||
*/
|
||||
error = sdmmc_chip_bus_power(sc->sc_sct, sc->sc_sch,
|
||||
sdmmc_chip_host_ocr(sc->sc_sct, sc->sc_sch));
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't supply bus power\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select the minimum clock frequency.
|
||||
*/
|
||||
error = sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, SDMMC_SDCLK_400K);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't supply clock\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* XXX wait for card to power up */
|
||||
sdmmc_delay(100000);
|
||||
|
||||
/* Initialize SD I/O card function(s). */
|
||||
error = sdmmc_io_enable(sc);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/* Initialize SD/MMC memory card(s). */
|
||||
if (ISSET(sc->sc_flags, SMF_MEM_MODE))
|
||||
error = sdmmc_mem_enable(sc);
|
||||
|
||||
out:
|
||||
if (error)
|
||||
sdmmc_disable(sc);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
sdmmc_disable(struct sdmmc_softc *sc)
|
||||
{
|
||||
/* XXX complete commands if card is still present. */
|
||||
|
||||
/* Make sure no card is still selected. */
|
||||
(void)sdmmc_select_card(sc, NULL);
|
||||
|
||||
/* Turn off bus power and clock. */
|
||||
(void)sdmmc_chip_bus_width(sc->sc_sct, sc->sc_sch, 1);
|
||||
(void)sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, SDMMC_SDCLK_OFF);
|
||||
(void)sdmmc_chip_bus_power(sc->sc_sct, sc->sc_sch, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the lowest bus voltage supported by the card and the host.
|
||||
*/
|
||||
int
|
||||
sdmmc_set_bus_power(struct sdmmc_softc *sc, uint32_t host_ocr,
|
||||
uint32_t card_ocr)
|
||||
{
|
||||
uint32_t bit;
|
||||
|
||||
/* Mask off unsupported voltage levels and select the lowest. */
|
||||
DPRINTF(1,("%s: host_ocr=%x ", DEVNAME(sc), host_ocr));
|
||||
host_ocr &= card_ocr;
|
||||
for (bit = 4; bit < 23; bit++) {
|
||||
if (ISSET(host_ocr, (1 << bit))) {
|
||||
host_ocr &= (3 << bit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
DPRINTF(1,("card_ocr=%x new_ocr=%x\n", card_ocr, host_ocr));
|
||||
|
||||
if (host_ocr == 0 ||
|
||||
sdmmc_chip_bus_power(sc->sc_sct, sc->sc_sch, host_ocr) != 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sdmmc_function *
|
||||
sdmmc_function_alloc(struct sdmmc_softc *sc)
|
||||
{
|
||||
struct sdmmc_function *sf;
|
||||
|
||||
sf = malloc(sizeof *sf, M_DEVBUF, M_WAITOK|M_ZERO);
|
||||
if (sf == NULL) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"couldn't alloc memory (sdmmc function)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sf->sc = sc;
|
||||
sf->number = -1;
|
||||
sf->cis.manufacturer = SDMMC_VENDOR_INVALID;
|
||||
sf->cis.product = SDMMC_PRODUCT_INVALID;
|
||||
sf->cis.function = SDMMC_FUNCTION_INVALID;
|
||||
|
||||
return sf;
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_function_free(struct sdmmc_function *sf)
|
||||
{
|
||||
|
||||
free(sf, M_DEVBUF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan for I/O functions and memory cards on the bus, allocating a
|
||||
* sdmmc_function structure for each.
|
||||
*/
|
||||
static int
|
||||
sdmmc_scan(struct sdmmc_softc *sc)
|
||||
{
|
||||
|
||||
/* Scan for I/O functions. */
|
||||
if (ISSET(sc->sc_flags, SMF_IO_MODE))
|
||||
sdmmc_io_scan(sc);
|
||||
|
||||
/* Scan for memory cards on the bus. */
|
||||
if (ISSET(sc->sc_flags, SMF_MEM_MODE))
|
||||
sdmmc_mem_scan(sc);
|
||||
|
||||
/* There should be at least one function now. */
|
||||
if (SIMPLEQ_EMPTY(&sc->sf_head)) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't identify card\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize all the distinguished functions of the card, be it I/O
|
||||
* or memory functions.
|
||||
*/
|
||||
static int
|
||||
sdmmc_init(struct sdmmc_softc *sc)
|
||||
{
|
||||
struct sdmmc_function *sf;
|
||||
|
||||
/* Initialize all identified card functions. */
|
||||
SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
|
||||
if (ISSET(sc->sc_flags, SMF_IO_MODE) &&
|
||||
sdmmc_io_init(sc, sf) != 0) {
|
||||
aprint_error_dev(sc->sc_dev, "i/o init failed\n");
|
||||
}
|
||||
|
||||
if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
|
||||
sdmmc_mem_init(sc, sf) != 0) {
|
||||
aprint_error_dev(sc->sc_dev, "mem init failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Any good functions left after initialization? */
|
||||
SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
|
||||
if (!ISSET(sf->flags, SFF_ERROR))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No, we should probably power down the card. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_delay(u_int usecs)
|
||||
{
|
||||
|
||||
delay(usecs);
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
|
||||
{
|
||||
struct sdmmc_command acmd;
|
||||
int error;
|
||||
|
||||
DPRINTF(1,("sdmmc_app_command: start\n"));
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
memset(&acmd, 0, sizeof(acmd));
|
||||
acmd.c_opcode = MMC_APP_CMD;
|
||||
acmd.c_arg = 0;
|
||||
acmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &acmd);
|
||||
if (error == 0) {
|
||||
if (!ISSET(MMC_R1(acmd.c_resp), MMC_R1_APP_CMD)) {
|
||||
/* Card does not support application commands. */
|
||||
error = ENODEV;
|
||||
} else {
|
||||
error = sdmmc_mmc_command(sc, cmd);
|
||||
}
|
||||
}
|
||||
DPRINTF(1,("sdmmc_app_command: done (error=%d)\n", error));
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute MMC command and data transfers. All interactions with the
|
||||
* host controller to complete the command happen in the context of
|
||||
* the current process.
|
||||
*/
|
||||
int
|
||||
sdmmc_mmc_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
|
||||
{
|
||||
int error;
|
||||
|
||||
DPRINTF(1,("sdmmc_mmc_command: cmd=%#x, arg=%#x, flags=%#x\n",
|
||||
cmd->c_opcode, cmd->c_arg, cmd->c_flags));
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
#if defined(DIAGNOSTIC) || defined(SDMMC_DEBUG)
|
||||
if (cmd->c_data) {
|
||||
if (sc->sc_card == NULL)
|
||||
panic("%s: deselected card\n", DEVNAME(sc));
|
||||
}
|
||||
#endif
|
||||
|
||||
sdmmc_chip_exec_command(sc->sc_sct, sc->sc_sch, cmd);
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
sdmmc_dump_command(sc, cmd);
|
||||
#endif
|
||||
|
||||
error = cmd->c_error;
|
||||
|
||||
DPRINTF(1,("sdmmc_mmc_command: error=%d\n", error));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the "GO IDLE STATE" command.
|
||||
*/
|
||||
void
|
||||
sdmmc_go_idle_state(struct sdmmc_softc *sc)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
|
||||
DPRINTF(1,("sdmmc_go_idle_state\n"));
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_opcode = MMC_GO_IDLE_STATE;
|
||||
cmd.c_flags = SCF_CMD_BC | SCF_RSP_R0;
|
||||
|
||||
(void)sdmmc_mmc_command(sc, &cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve (SD) or set (MMC) the relative card address (RCA).
|
||||
*/
|
||||
int
|
||||
sdmmc_set_relative_addr(struct sdmmc_softc *sc, struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
|
||||
cmd.c_opcode = SD_SEND_RELATIVE_ADDR;
|
||||
cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R6;
|
||||
} else {
|
||||
cmd.c_opcode = MMC_SET_RELATIVE_ADDR;
|
||||
cmd.c_arg = MMC_ARG_RCA(sf->rca);
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
|
||||
}
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (ISSET(sc->sc_flags, SMF_SD_MODE))
|
||||
sf->rca = SD_R6_RCA(cmd.c_resp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_select_card(struct sdmmc_softc *sc, struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
if (sc->sc_card == sf
|
||||
|| (sf && sc->sc_card && sc->sc_card->rca == sf->rca)) {
|
||||
sc->sc_card = sf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_opcode = MMC_SELECT_CARD;
|
||||
cmd.c_arg = (sf == NULL) ? 0 : MMC_ARG_RCA(sf->rca);
|
||||
cmd.c_flags = SCF_CMD_AC | ((sf == NULL) ? SCF_RSP_R0 : SCF_RSP_R1);
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error == 0 || sf == NULL)
|
||||
sc->sc_card = sf;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
static void
|
||||
sdmmc_dump_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
DPRINTF(1,("%s: cmd %u arg=%#x data=%p dlen=%d flags=%#x "
|
||||
"proc=\"%s\" (error %d)\n",
|
||||
DEVNAME(sc), cmd->c_opcode, cmd->c_arg, cmd->c_data,
|
||||
cmd->c_datalen, cmd->c_flags, curproc ? curproc->p_comm : "",
|
||||
cmd->c_error));
|
||||
|
||||
if (cmd->c_error || sdmmcdebug < 1)
|
||||
return;
|
||||
|
||||
aprint_normal_dev(sc->sc_dev, "resp=");
|
||||
if (ISSET(cmd->c_flags, SCF_RSP_136))
|
||||
for (i = 0; i < sizeof cmd->c_resp; i++)
|
||||
aprint_normal("%02x ", ((uint8_t *)cmd->c_resp)[i]);
|
||||
else if (ISSET(cmd->c_flags, SCF_RSP_PRESENT))
|
||||
for (i = 0; i < 4; i++)
|
||||
aprint_normal("%02x ", ((uint8_t *)cmd->c_resp)[i]);
|
||||
aprint_normal("\n");
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,213 @@
|
|||
/* $NetBSD: sdmmc_cis.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sdmmc_cis.c,v 1.1 2006/06/01 21:53:41 uwe Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Routines to decode the Card Information Structure of SD I/O cards */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: sdmmc_cis.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmc_ioreg.h>
|
||||
#include <dev/sdmmc/sdmmcdevs.h>
|
||||
#include <dev/sdmmc/sdmmcvar.h>
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
#define DPRINTF(s) printf s
|
||||
#else
|
||||
#define DPRINTF(s) /**/
|
||||
#endif
|
||||
|
||||
static uint32_t sdmmc_cisptr(struct sdmmc_function *);
|
||||
static uint32_t
|
||||
sdmmc_cisptr(struct sdmmc_function *sf)
|
||||
{
|
||||
uint32_t cisptr = 0;
|
||||
|
||||
/* XXX where is the per-function CIS pointer register? */
|
||||
if (sf->number != 0)
|
||||
return SD_IO_CIS_START;
|
||||
|
||||
/* XXX is the CIS pointer stored in little-endian format? */
|
||||
cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 0) << 0;
|
||||
cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 1) << 8;
|
||||
cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 2) << 16;
|
||||
return cisptr;
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis)
|
||||
{
|
||||
device_t dev = sf->sc->sc_dev;
|
||||
uint32_t reg;
|
||||
uint8_t tplcode;
|
||||
uint8_t tpllen;
|
||||
int start, ch, count;
|
||||
int i;
|
||||
|
||||
memset(cis, 0, sizeof *cis);
|
||||
|
||||
/* XXX read per-function CIS */
|
||||
if (sf->number != 0)
|
||||
return 1;
|
||||
|
||||
reg = sdmmc_cisptr(sf);
|
||||
if (reg < SD_IO_CIS_START ||
|
||||
reg >= (SD_IO_CIS_START + SD_IO_CIS_SIZE - 16)) {
|
||||
aprint_error_dev(dev, "bad CIS ptr %#x\n", reg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
tplcode = sdmmc_io_read_1(sf, reg++);
|
||||
tpllen = sdmmc_io_read_1(sf, reg++);
|
||||
|
||||
if (tplcode == 0xff || tpllen == 0) {
|
||||
if (tplcode != 0xff)
|
||||
aprint_error_dev(dev, "CIS parse error at %d, "
|
||||
"tuple code %#x, length %d\n",
|
||||
reg, tplcode, tpllen);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (tplcode) {
|
||||
case SD_IO_CISTPL_FUNCID:
|
||||
if (tpllen < 2) {
|
||||
aprint_error_dev(dev,
|
||||
"bad CISTPL_FUNCID length\n");
|
||||
reg += tpllen;
|
||||
break;
|
||||
}
|
||||
cis->function = sdmmc_io_read_1(sf, reg);
|
||||
reg += tpllen;
|
||||
break;
|
||||
|
||||
case SD_IO_CISTPL_MANFID:
|
||||
if (tpllen < 4) {
|
||||
aprint_error_dev(dev,
|
||||
"bad CISTPL_MANFID length\n");
|
||||
reg += tpllen;
|
||||
break;
|
||||
}
|
||||
cis->manufacturer = sdmmc_io_read_1(sf, reg++);
|
||||
cis->manufacturer |= sdmmc_io_read_1(sf, reg++) << 8;
|
||||
cis->product = sdmmc_io_read_1(sf, reg++);
|
||||
cis->product |= sdmmc_io_read_1(sf, reg++) << 8;
|
||||
break;
|
||||
|
||||
case SD_IO_CISTPL_VERS_1:
|
||||
if (tpllen < 2) {
|
||||
aprint_error_dev(dev,
|
||||
"CISTPL_VERS_1 too short\n");
|
||||
reg += tpllen;
|
||||
break;
|
||||
}
|
||||
|
||||
cis->cis1_major = sdmmc_io_read_1(sf, reg++);
|
||||
cis->cis1_minor = sdmmc_io_read_1(sf, reg++);
|
||||
|
||||
for (count = 0, start = 0, i = 0;
|
||||
(count < 4) && ((i + 4) < 256); i++) {
|
||||
ch = sdmmc_io_read_1(sf, reg + i);
|
||||
if (ch == 0xff)
|
||||
break;
|
||||
cis->cis1_info_buf[i] = ch;
|
||||
if (ch == 0) {
|
||||
cis->cis1_info[count] =
|
||||
cis->cis1_info_buf + start;
|
||||
start = i + 1;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
reg += tpllen - 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
aprint_error_dev(dev,
|
||||
"unknown tuple code %#x, length %d\n",
|
||||
tplcode, tpllen);
|
||||
reg += tpllen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_print_cis(struct sdmmc_function *sf)
|
||||
{
|
||||
device_t dev = sf->sc->sc_dev;
|
||||
struct sdmmc_cis *cis = &sf->cis;
|
||||
int i;
|
||||
|
||||
printf("%s: CIS version %u.%u\n", device_xname(dev), cis->cis1_major,
|
||||
cis->cis1_minor);
|
||||
|
||||
printf("%s: CIS info: ", device_xname(dev));
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (cis->cis1_info[i] == NULL)
|
||||
break;
|
||||
if (i != 0)
|
||||
aprint_verbose(", ");
|
||||
printf("%s", cis->cis1_info[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("%s: Manufacturer code 0x%x, product 0x%x\n", device_xname(dev),
|
||||
cis->manufacturer, cis->product);
|
||||
|
||||
printf("%s: function %d: ", device_xname(dev), sf->number);
|
||||
switch (sf->cis.function) {
|
||||
case SDMMC_FUNCTION_WLAN:
|
||||
printf("wireless network adapter");
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unknown function (%d)", sf->cis.function);
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_check_cis_quirks(struct sdmmc_function *sf)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC &&
|
||||
sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) {
|
||||
/* This card lacks the VERS_1 tuple. */
|
||||
static const char cis1_info[] =
|
||||
"Spectec\0SDIO WLAN Card\0SDW-820\0\0";
|
||||
|
||||
sf->cis.cis1_major = 0x01;
|
||||
sf->cis.cis1_minor = 0x00;
|
||||
|
||||
p = sf->cis.cis1_info_buf;
|
||||
strlcpy(p, cis1_info, sizeof(sf->cis.cis1_info_buf));
|
||||
for (i = 0; i < 4; i++) {
|
||||
sf->cis.cis1_info[i] = p;
|
||||
p += strlen(p) + 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,670 @@
|
|||
/* $NetBSD: sdmmc_io.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sdmmc_io.c,v 1.10 2007/09/17 01:33:33 krw Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Routines for SD I/O cards. */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: sdmmc_io.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmc_ioreg.h>
|
||||
#include <dev/sdmmc/sdmmcchip.h>
|
||||
#include <dev/sdmmc/sdmmcreg.h>
|
||||
#include <dev/sdmmc/sdmmcvar.h>
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
#define DPRINTF(s) do { printf s; } while (0)
|
||||
#else
|
||||
#define DPRINTF(s) do {} while (0)
|
||||
#endif
|
||||
|
||||
struct sdmmc_intr_handler {
|
||||
struct sdmmc_softc *ih_softc;
|
||||
char *ih_name;
|
||||
int (*ih_fun)(void *);
|
||||
void *ih_arg;
|
||||
TAILQ_ENTRY(sdmmc_intr_handler) entry;
|
||||
};
|
||||
|
||||
static int sdmmc_io_rw_direct(struct sdmmc_softc *,
|
||||
struct sdmmc_function *, int, u_char *, int);
|
||||
static int sdmmc_io_rw_extended(struct sdmmc_softc *,
|
||||
struct sdmmc_function *, int, u_char *, int, int);
|
||||
#if 0
|
||||
static int sdmmc_io_xchg(struct sdmmc_softc *, struct sdmmc_function *,
|
||||
int, u_char *);
|
||||
#endif
|
||||
static void sdmmc_io_reset(struct sdmmc_softc *);
|
||||
static int sdmmc_io_send_op_cond(struct sdmmc_softc *, uint32_t,
|
||||
uint32_t *);
|
||||
|
||||
/*
|
||||
* Initialize SD I/O card functions (before memory cards). The host
|
||||
* system and controller must support card interrupts in order to use
|
||||
* I/O functions.
|
||||
*/
|
||||
int
|
||||
sdmmc_io_enable(struct sdmmc_softc *sc)
|
||||
{
|
||||
uint32_t host_ocr;
|
||||
uint32_t card_ocr;
|
||||
int error;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
|
||||
/* Set host mode to SD "combo" card. */
|
||||
SET(sc->sc_flags, SMF_SD_MODE|SMF_IO_MODE|SMF_MEM_MODE);
|
||||
|
||||
/* Reset I/O functions. */
|
||||
sdmmc_io_reset(sc);
|
||||
|
||||
/*
|
||||
* Read the I/O OCR value, determine the number of I/O
|
||||
* functions and whether memory is also present (a "combo
|
||||
* card") by issuing CMD5. SD memory-only and MMC cards
|
||||
* do not respond to CMD5.
|
||||
*/
|
||||
error = sdmmc_io_send_op_cond(sc, 0, &card_ocr);
|
||||
if (error) {
|
||||
/* No SDIO card; switch to SD memory-only mode. */
|
||||
CLR(sc->sc_flags, SMF_IO_MODE);
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Parse the additional bits in the I/O OCR value. */
|
||||
if (!ISSET(card_ocr, SD_IO_OCR_MEM_PRESENT)) {
|
||||
/* SDIO card without memory (not a "combo card"). */
|
||||
DPRINTF(("%s: no memory present\n", SDMMCDEVNAME(sc)));
|
||||
CLR(sc->sc_flags, SMF_MEM_MODE);
|
||||
}
|
||||
sc->sc_function_count = SD_IO_OCR_NUM_FUNCTIONS(card_ocr);
|
||||
if (sc->sc_function_count == 0) {
|
||||
/* Useless SDIO card without any I/O functions. */
|
||||
DPRINTF(("%s: no I/O functions\n", SDMMCDEVNAME(sc)));
|
||||
CLR(sc->sc_flags, SMF_IO_MODE);
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
card_ocr &= SD_IO_OCR_MASK;
|
||||
|
||||
/* Set the lowest voltage supported by the card and host. */
|
||||
host_ocr = sdmmc_chip_host_ocr(sc->sc_sct, sc->sc_sch);
|
||||
error = sdmmc_set_bus_power(sc, host_ocr, card_ocr);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"couldn't supply voltage requested by card\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Reset I/O functions (again). */
|
||||
sdmmc_io_reset(sc);
|
||||
|
||||
/* Send the new OCR value until all cards are ready. */
|
||||
error = sdmmc_io_send_op_cond(sc, host_ocr, NULL);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't send I/O OCR\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
SDMMC_UNLOCK(sc);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate sdmmc_function structures for SD card I/O function
|
||||
* (including function 0).
|
||||
*/
|
||||
void
|
||||
sdmmc_io_scan(struct sdmmc_softc *sc)
|
||||
{
|
||||
struct sdmmc_function *sf0, *sf;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
|
||||
sf0 = sdmmc_function_alloc(sc);
|
||||
sf0->number = 0;
|
||||
error = sdmmc_set_relative_addr(sc, sf0);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't set I/O RCA\n");
|
||||
SET(sf0->flags, SFF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
sc->sc_fn0 = sf0;
|
||||
SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf0, sf_list);
|
||||
|
||||
/* Verify that the RCA has been set by selecting the card. */
|
||||
error = sdmmc_select_card(sc, sf0);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't select I/O RCA %d\n",
|
||||
sf0->rca);
|
||||
SET(sf0->flags, SFF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 1; i <= sc->sc_function_count; i++) {
|
||||
sf = sdmmc_function_alloc(sc);
|
||||
sf->number = i;
|
||||
sf->rca = sf0->rca;
|
||||
SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf, sf_list);
|
||||
}
|
||||
|
||||
out:
|
||||
SDMMC_UNLOCK(sc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize SDIO card functions.
|
||||
*/
|
||||
int
|
||||
sdmmc_io_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
|
||||
if (sf->number == 0) {
|
||||
sdmmc_io_write_1(sf, SD_IO_CCCR_BUS_WIDTH, CCCR_BUS_WIDTH_1);
|
||||
|
||||
error = sdmmc_read_cis(sf, &sf->cis);
|
||||
if (error) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't read CIS\n");
|
||||
SET(sf->flags, SFF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdmmc_check_cis_quirks(sf);
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
if (sdmmcdebug)
|
||||
sdmmc_print_cis(sf);
|
||||
#endif
|
||||
}
|
||||
|
||||
out:
|
||||
SDMMC_UNLOCK(sc);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate whether the function is ready to operate.
|
||||
*/
|
||||
static int
|
||||
sdmmc_io_function_ready(struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
struct sdmmc_function *sf0 = sc->sc_fn0;
|
||||
uint8_t reg;
|
||||
|
||||
if (sf->number == 0)
|
||||
return 1; /* FN0 is always ready */
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
reg = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_IOREADY);
|
||||
SDMMC_UNLOCK(sc);
|
||||
return (reg & (1 << sf->number)) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_io_function_enable(struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
struct sdmmc_function *sf0 = sc->sc_fn0;
|
||||
uint8_t reg;
|
||||
int retry;
|
||||
|
||||
if (sf->number == 0)
|
||||
return 0; /* FN0 is always enabled */
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
reg = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_ENABLE);
|
||||
SET(reg, (1U << sf->number));
|
||||
sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_ENABLE, reg);
|
||||
SDMMC_UNLOCK(sc);
|
||||
|
||||
retry = 5;
|
||||
while (!sdmmc_io_function_ready(sf) && retry-- > 0)
|
||||
tsleep(&lbolt, PPAUSE, "pause", 0);
|
||||
return (retry >= 0) ? 0 : ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the I/O function. Return zero if the function was
|
||||
* disabled successfully.
|
||||
*/
|
||||
void
|
||||
sdmmc_io_function_disable(struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
struct sdmmc_function *sf0 = sc->sc_fn0;
|
||||
uint8_t reg;
|
||||
|
||||
if (sf->number == 0)
|
||||
return; /* FN0 is always enabled */
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
reg = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_ENABLE);
|
||||
CLR(reg, (1U << sf->number));
|
||||
sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_ENABLE, reg);
|
||||
SDMMC_UNLOCK(sc);
|
||||
}
|
||||
|
||||
static int
|
||||
sdmmc_io_rw_direct(struct sdmmc_softc *sc, struct sdmmc_function *sf,
|
||||
int reg, u_char *datap, int arg)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
/* Make sure the card is selected. */
|
||||
error = sdmmc_select_card(sc, sf);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
arg |= ((sf == NULL ? 0 : sf->number) & SD_ARG_CMD52_FUNC_MASK) <<
|
||||
SD_ARG_CMD52_FUNC_SHIFT;
|
||||
arg |= (reg & SD_ARG_CMD52_REG_MASK) <<
|
||||
SD_ARG_CMD52_REG_SHIFT;
|
||||
arg |= (*datap & SD_ARG_CMD52_DATA_MASK) <<
|
||||
SD_ARG_CMD52_DATA_SHIFT;
|
||||
|
||||
memset(&cmd, 0, sizeof cmd);
|
||||
cmd.c_opcode = SD_IO_RW_DIRECT;
|
||||
cmd.c_arg = arg;
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R5;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
*datap = SD_R5_DATA(cmd.c_resp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Useful values of `arg' to pass in are either SD_ARG_CMD53_READ or
|
||||
* SD_ARG_CMD53_WRITE. SD_ARG_CMD53_INCREMENT may be ORed into `arg'
|
||||
* to access successive register locations instead of accessing the
|
||||
* same register many times.
|
||||
*/
|
||||
static int
|
||||
sdmmc_io_rw_extended(struct sdmmc_softc *sc, struct sdmmc_function *sf,
|
||||
int reg, u_char *datap, int datalen, int arg)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
#if 0
|
||||
/* Make sure the card is selected. */
|
||||
error = sdmmc_select_card(sc, sf);
|
||||
if (error)
|
||||
return error;
|
||||
#endif
|
||||
|
||||
arg |= (((sf == NULL) ? 0 : sf->number) & SD_ARG_CMD53_FUNC_MASK) <<
|
||||
SD_ARG_CMD53_FUNC_SHIFT;
|
||||
arg |= (reg & SD_ARG_CMD53_REG_MASK) <<
|
||||
SD_ARG_CMD53_REG_SHIFT;
|
||||
arg |= (datalen & SD_ARG_CMD53_LENGTH_MASK) <<
|
||||
SD_ARG_CMD53_LENGTH_SHIFT;
|
||||
|
||||
memset(&cmd, 0, sizeof cmd);
|
||||
cmd.c_opcode = SD_IO_RW_EXTENDED;
|
||||
cmd.c_arg = arg;
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R5;
|
||||
cmd.c_data = datap;
|
||||
cmd.c_datalen = datalen;
|
||||
cmd.c_blklen = MIN(datalen,
|
||||
sdmmc_chip_host_maxblklen(sc->sc_sct,sc->sc_sch));
|
||||
if (!ISSET(arg, SD_ARG_CMD53_WRITE))
|
||||
cmd.c_flags |= SCF_CMD_READ;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
sdmmc_io_read_1(struct sdmmc_function *sf, int reg)
|
||||
{
|
||||
uint8_t data = 0;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
(void)sdmmc_io_rw_direct(sf->sc, sf, reg, (u_char *)&data,
|
||||
SD_ARG_CMD52_READ);
|
||||
return data;
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_io_write_1(struct sdmmc_function *sf, int reg, uint8_t data)
|
||||
{
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
(void)sdmmc_io_rw_direct(sf->sc, sf, reg, (u_char *)&data,
|
||||
SD_ARG_CMD52_WRITE);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
sdmmc_io_read_2(struct sdmmc_function *sf, int reg)
|
||||
{
|
||||
uint16_t data = 0;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
(void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 2,
|
||||
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT);
|
||||
return data;
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_io_write_2(struct sdmmc_function *sf, int reg, uint16_t data)
|
||||
{
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
(void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 2,
|
||||
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
sdmmc_io_read_4(struct sdmmc_function *sf, int reg)
|
||||
{
|
||||
uint32_t data = 0;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
(void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 4,
|
||||
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT);
|
||||
return data;
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_io_write_4(struct sdmmc_function *sf, int reg, uint32_t data)
|
||||
{
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
(void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 4,
|
||||
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sdmmc_io_read_multi_1(struct sdmmc_function *sf, int reg, u_char *data,
|
||||
int datalen)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
while (datalen > SD_ARG_CMD53_LENGTH_MAX) {
|
||||
error = sdmmc_io_rw_extended(sf->sc, sf, reg, data,
|
||||
SD_ARG_CMD53_LENGTH_MAX, SD_ARG_CMD53_READ);
|
||||
if (error)
|
||||
goto error;
|
||||
data += SD_ARG_CMD53_LENGTH_MAX;
|
||||
datalen -= SD_ARG_CMD53_LENGTH_MAX;
|
||||
}
|
||||
|
||||
error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen,
|
||||
SD_ARG_CMD53_READ);
|
||||
error:
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_io_write_multi_1(struct sdmmc_function *sf, int reg, u_char *data,
|
||||
int datalen)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
while (datalen > SD_ARG_CMD53_LENGTH_MAX) {
|
||||
error = sdmmc_io_rw_extended(sf->sc, sf, reg, data,
|
||||
SD_ARG_CMD53_LENGTH_MAX, SD_ARG_CMD53_WRITE);
|
||||
if (error)
|
||||
goto error;
|
||||
data += SD_ARG_CMD53_LENGTH_MAX;
|
||||
datalen -= SD_ARG_CMD53_LENGTH_MAX;
|
||||
}
|
||||
|
||||
error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen,
|
||||
SD_ARG_CMD53_WRITE);
|
||||
error:
|
||||
return error;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int
|
||||
sdmmc_io_xchg(struct sdmmc_softc *sc, struct sdmmc_function *sf,
|
||||
int reg, u_char *datap)
|
||||
{
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
return sdmmc_io_rw_direct(sc, sf, reg, datap,
|
||||
SD_ARG_CMD52_WRITE|SD_ARG_CMD52_EXCHANGE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Reset the I/O functions of the card.
|
||||
*/
|
||||
static void
|
||||
sdmmc_io_reset(struct sdmmc_softc *sc)
|
||||
{
|
||||
|
||||
/* Don't lock */
|
||||
#if 0 /* XXX command fails */
|
||||
(void)sdmmc_io_write(sc, NULL, SD_IO_REG_CCCR_CTL, CCCR_CTL_RES);
|
||||
sdmmc_delay(100000);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Get or set the card's I/O OCR value (SDIO).
|
||||
*/
|
||||
static int
|
||||
sdmmc_io_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, u_int32_t *ocrp)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
int retry;
|
||||
|
||||
DPRINTF(("sdmmc_io_send_op_cond: ocr = %#x\n", ocr));
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
/*
|
||||
* If we change the OCR value, retry the command until the OCR
|
||||
* we receive in response has the "CARD BUSY" bit set, meaning
|
||||
* that all cards are ready for identification.
|
||||
*/
|
||||
for (retry = 0; retry < 100; retry++) {
|
||||
memset(&cmd, 0, sizeof cmd);
|
||||
cmd.c_opcode = SD_IO_SEND_OP_COND;
|
||||
cmd.c_arg = ocr;
|
||||
cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R4;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error)
|
||||
break;
|
||||
if (ISSET(MMC_R4(cmd.c_resp), SD_IO_OCR_MEM_READY) || ocr == 0)
|
||||
break;
|
||||
|
||||
error = ETIMEDOUT;
|
||||
sdmmc_delay(10000);
|
||||
}
|
||||
if (error == 0 && ocrp != NULL)
|
||||
*ocrp = MMC_R4(cmd.c_resp);
|
||||
|
||||
DPRINTF(("sdmmc_io_send_op_cond: error = %d\n", error));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Card interrupt handling
|
||||
*/
|
||||
|
||||
void
|
||||
sdmmc_intr_enable(struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
struct sdmmc_function *sf0 = sc->sc_fn0;
|
||||
uint8_t reg;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
reg = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_INTEN);
|
||||
reg |= 1 << sf->number;
|
||||
sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_INTEN, reg);
|
||||
SDMMC_UNLOCK(sc);
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_intr_disable(struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
struct sdmmc_function *sf0 = sc->sc_fn0;
|
||||
uint8_t reg;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
reg = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_INTEN);
|
||||
reg &= ~(1 << sf->number);
|
||||
sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_INTEN, reg);
|
||||
SDMMC_UNLOCK(sc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Establish a handler for the SDIO card interrupt. Because the
|
||||
* interrupt may be shared with different SDIO functions, multiple
|
||||
* handlers can be established.
|
||||
*/
|
||||
void *
|
||||
sdmmc_intr_establish(device_t dev, int (*fun)(void *), void *arg,
|
||||
const char *name)
|
||||
{
|
||||
struct sdmmc_softc *sc = device_private(dev);
|
||||
struct sdmmc_intr_handler *ih;
|
||||
int s;
|
||||
|
||||
if (sc->sc_sct->card_enable_intr == NULL)
|
||||
return NULL;
|
||||
|
||||
ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
|
||||
if (ih == NULL)
|
||||
return NULL;
|
||||
|
||||
ih->ih_name = malloc(strlen(name) + 1, M_DEVBUF,
|
||||
M_WAITOK|M_CANFAIL|M_ZERO);
|
||||
if (ih->ih_name == NULL) {
|
||||
free(ih, M_DEVBUF);
|
||||
return NULL;
|
||||
}
|
||||
strlcpy(ih->ih_name, name, strlen(name));
|
||||
ih->ih_softc = sc;
|
||||
ih->ih_fun = fun;
|
||||
ih->ih_arg = arg;
|
||||
|
||||
s = splhigh();
|
||||
if (TAILQ_EMPTY(&sc->sc_intrq)) {
|
||||
sdmmc_intr_enable(sc->sc_fn0);
|
||||
sdmmc_chip_card_enable_intr(sc->sc_sct, sc->sc_sch, 1);
|
||||
}
|
||||
TAILQ_INSERT_TAIL(&sc->sc_intrq, ih, entry);
|
||||
splx(s);
|
||||
|
||||
return ih;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disestablish the given handler.
|
||||
*/
|
||||
void
|
||||
sdmmc_intr_disestablish(void *cookie)
|
||||
{
|
||||
struct sdmmc_intr_handler *ih = cookie;
|
||||
struct sdmmc_softc *sc = ih->ih_softc;
|
||||
int s;
|
||||
|
||||
if (sc->sc_sct->card_enable_intr == NULL)
|
||||
return;
|
||||
|
||||
s = splhigh();
|
||||
TAILQ_REMOVE(&sc->sc_intrq, ih, entry);
|
||||
if (TAILQ_EMPTY(&sc->sc_intrq)) {
|
||||
sdmmc_chip_card_enable_intr(sc->sc_sct, sc->sc_sch, 0);
|
||||
sdmmc_intr_disable(sc->sc_fn0);
|
||||
}
|
||||
splx(s);
|
||||
|
||||
free(ih->ih_name, M_DEVBUF);
|
||||
free(ih, M_DEVBUF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call established SDIO card interrupt handlers. The host controller
|
||||
* must call this function from its own interrupt handler to handle an
|
||||
* SDIO interrupt from the card.
|
||||
*/
|
||||
void
|
||||
sdmmc_card_intr(device_t dev)
|
||||
{
|
||||
struct sdmmc_softc *sc = device_private(dev);
|
||||
|
||||
if (sc->sc_sct->card_enable_intr) {
|
||||
mutex_enter(&sc->sc_intr_task_mtx);
|
||||
if (!sdmmc_task_pending(&sc->sc_intr_task))
|
||||
sdmmc_add_task(sc, &sc->sc_intr_task);
|
||||
mutex_exit(&sc->sc_intr_task_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_intr_task(void *arg)
|
||||
{
|
||||
struct sdmmc_softc *sc = (struct sdmmc_softc *)arg;
|
||||
struct sdmmc_intr_handler *ih;
|
||||
int s;
|
||||
|
||||
s = splsdmmc();
|
||||
TAILQ_FOREACH(ih, &sc->sc_intrq, entry) {
|
||||
splx(s);
|
||||
/* XXX examine return value and do evcount stuff*/
|
||||
(void)(*ih->ih_fun)(ih->ih_arg);
|
||||
s = splsdmmc();
|
||||
}
|
||||
sdmmc_chip_card_intr_ack(sc->sc_sct, sc->sc_sch);
|
||||
splx(s);
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/* $NetBSD: sdmmc_ioreg.h,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sdmmc_ioreg.h,v 1.4 2007/06/02 01:48:37 uwe Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDMMC_IOREG_H_
|
||||
#define _SDMMC_IOREG_H_
|
||||
|
||||
/* SDIO commands */ /* response type */
|
||||
#define SD_IO_SEND_OP_COND 5 /* R4 */
|
||||
#define SD_IO_RW_DIRECT 52 /* R5 */
|
||||
#define SD_IO_RW_EXTENDED 53 /* R5? */
|
||||
|
||||
/* CMD52 arguments */
|
||||
#define SD_ARG_CMD52_READ (0<<31)
|
||||
#define SD_ARG_CMD52_WRITE (1<<31)
|
||||
#define SD_ARG_CMD52_FUNC_SHIFT 28
|
||||
#define SD_ARG_CMD52_FUNC_MASK 0x7
|
||||
#define SD_ARG_CMD52_EXCHANGE (1<<27)
|
||||
#define SD_ARG_CMD52_REG_SHIFT 9
|
||||
#define SD_ARG_CMD52_REG_MASK 0x1ffff
|
||||
#define SD_ARG_CMD52_DATA_SHIFT 0
|
||||
#define SD_ARG_CMD52_DATA_MASK 0xff
|
||||
#define SD_R5_DATA(resp) ((resp)[0] & 0xff)
|
||||
|
||||
/* CMD53 arguments */
|
||||
#define SD_ARG_CMD53_READ (0<<31)
|
||||
#define SD_ARG_CMD53_WRITE (1<<31)
|
||||
#define SD_ARG_CMD53_FUNC_SHIFT 28
|
||||
#define SD_ARG_CMD53_FUNC_MASK 0x7
|
||||
#define SD_ARG_CMD53_BLOCK_MODE (1<<27)
|
||||
#define SD_ARG_CMD53_INCREMENT (1<<26)
|
||||
#define SD_ARG_CMD53_REG_SHIFT 9
|
||||
#define SD_ARG_CMD53_REG_MASK 0x1ffff
|
||||
#define SD_ARG_CMD53_LENGTH_SHIFT 0
|
||||
#define SD_ARG_CMD53_LENGTH_MASK 0x1ff
|
||||
#define SD_ARG_CMD53_LENGTH_MAX 64 /* XXX should be 511? */
|
||||
|
||||
/* 48-bit response decoding (32 bits w/o CRC) */
|
||||
#define MMC_R4(resp) ((resp)[0])
|
||||
#define MMC_R5(resp) ((resp)[0])
|
||||
|
||||
/* SD R4 response (IO OCR) */
|
||||
#define SD_IO_OCR_MEM_READY (1<<31)
|
||||
#define SD_IO_OCR_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x3)
|
||||
/* XXX big fat memory present "flag" because we don't know better */
|
||||
#define SD_IO_OCR_MEM_PRESENT (0xf<<24)
|
||||
#define SD_IO_OCR_MASK 0x00fffff0
|
||||
|
||||
/* Card Common Control Registers (CCCR) */
|
||||
#define SD_IO_CCCR_START 0x00000
|
||||
#define SD_IO_CCCR_SIZE 0x100
|
||||
#define SD_IO_CCCR_CCCR_SDIO_REV 0x00
|
||||
#define SD_IO_CCCR_CCCR_REV(r) ((r) & 0xf)
|
||||
#define CCCR_CCCR_REV_1_00 0
|
||||
#define CCCR_CCCR_REV_1_10 1
|
||||
#define SD_IO_CCCR_SDIO_REV(r) (((r) >> 4) & 0xf)
|
||||
#define CCCR_SDIO_REV_1_00 0
|
||||
#define CCCR_SDIO_REV_1_10 1
|
||||
#define SD_IO_CCCR_SPEC_REV 0x01
|
||||
#define SD_IO_CCCR_SD_PHYS_SPEC_VER(r) ((r) & 0xf)
|
||||
#define CCCR_SD_PHYS_SPEC_VER_1_01 0
|
||||
#define CCCR_SD_PHYS_SPEC_VER_1_10 1
|
||||
#define SD_IO_CCCR_FN_ENABLE 0x02
|
||||
#define SD_IO_CCCR_FN_IOREADY 0x03
|
||||
#define SD_IO_CCCR_FN_INTEN 0x04
|
||||
#define CCCR_INTEN_INTM (1<<0)
|
||||
#define SD_IO_CCCR_FN_INTPENDING 0x05
|
||||
#define SD_IO_CCCR_CTL 0x06
|
||||
#define CCCR_CTL_RES (1<<3)
|
||||
#define SD_IO_CCCR_BUS_WIDTH 0x07
|
||||
#define CCCR_BUS_WIDTH_4 (1<<1)
|
||||
#define CCCR_BUS_WIDTH_1 (1<<0)
|
||||
#define SD_IO_CCCR_CAPABILITY 0x08
|
||||
#define CCCR_CAPS_SDC (1<<0)
|
||||
#define CCCR_CAPS_SMB (1<<1) /* Multi-Block support */
|
||||
#define CCCR_CAPS_SRB (1<<2) /* Read Wait support */
|
||||
#define CCCR_CAPS_SBS (1<<3) /* Suspend/Resume support */
|
||||
#define CCCR_CAPS_S4MI (1<<4) /* intr support in 4-bit mode */
|
||||
#define CCCR_CAPS_E4MI (1<<5) /* enable intr in 4-bit mode */
|
||||
#define CCCR_CAPS_LSC (1<<6) /* Low speed card */
|
||||
#define CCCR_CAPS_4BLS (1<<7) /* 4-bit support for low speed */
|
||||
#define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */
|
||||
#define SD_IO_CCCR_BUS_SUSPEND 0x0c
|
||||
#define SD_IO_CCCR_FUNC_SELECT 0x0d
|
||||
#define CCCR_FUNC_FS(r) ((r) & 0xf)
|
||||
#define CCCR_FUNC_FS_FN(fn) ((fn) & 0x7)
|
||||
#define CCCR_FUNC_FS_MEM 8
|
||||
#define SD_IO_CCCR_FN_EXEC_FLG 0x0e
|
||||
#define SD_IO_CCCR_FN_READY_FLG 0x0f
|
||||
#define SD_IO_CCCR_FN0_BLKSIZ 0x10 /* 0x10-0x11 */
|
||||
#define SD_IO_CCCR_POWER_CTL 0x12
|
||||
|
||||
/* Function Basic Registers (FBR) */
|
||||
#define SD_IO_FBR_START 0x00100
|
||||
#define SD_IO_FBR_SIZE 0x00700
|
||||
|
||||
/* Card Information Structure (CIS) */
|
||||
#define SD_IO_CIS_START 0x01000
|
||||
#define SD_IO_CIS_SIZE 0x17000
|
||||
|
||||
/* CIS tuple codes (based on PC Card 16) */
|
||||
#define SD_IO_CISTPL_VERS_1 0x15
|
||||
#define SD_IO_CISTPL_MANFID 0x20
|
||||
#define SD_IO_CISTPL_FUNCID 0x21
|
||||
#define SD_IO_CISTPL_FUNCE 0x22
|
||||
|
||||
/* CISTPL_FUNCID codes */
|
||||
#define SDMMC_FUNCTION_WLAN 0x0c
|
||||
|
||||
#endif /* _SDMMC_IOREG_H_ */
|
|
@ -0,0 +1,730 @@
|
|||
/* $NetBSD: sdmmc_mem.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sdmmc_mem.c,v 1.10 2009/01/09 10:55:22 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2007-2009 NONAKA Kimihiro <nonaka@netbsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Routines for SD/MMC memory cards. */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
|
||||
#include <uvm/uvm_extern.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmcchip.h>
|
||||
#include <dev/sdmmc/sdmmcreg.h>
|
||||
#include <dev/sdmmc/sdmmcvar.h>
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
#define DPRINTF(s) do { printf s; } while (/*CONSTCOND*/0)
|
||||
#else
|
||||
#define DPRINTF(s) do {} while (/*CONSTCOND*/0)
|
||||
#endif
|
||||
|
||||
static int sdmmc_mem_send_op_cond(struct sdmmc_softc *, uint32_t, uint32_t *);
|
||||
static int sdmmc_mem_send_if_cond(struct sdmmc_softc *, uint32_t, uint32_t *);
|
||||
static int sdmmc_mem_set_blocklen(struct sdmmc_softc *,
|
||||
struct sdmmc_function *);
|
||||
#ifdef SDMMC_DUMP_CSD
|
||||
static void sdmmc_print_csd(sdmmc_response, struct sdmmc_csd *);
|
||||
#endif
|
||||
static int sdmmc_mem_read_block_subr(struct sdmmc_function *, uint32_t,
|
||||
u_char *, size_t);
|
||||
static int sdmmc_mem_write_block_subr(struct sdmmc_function *, uint32_t,
|
||||
u_char *, size_t);
|
||||
|
||||
/*
|
||||
* Initialize SD/MMC memory cards and memory in SDIO "combo" cards.
|
||||
*/
|
||||
int
|
||||
sdmmc_mem_enable(struct sdmmc_softc *sc)
|
||||
{
|
||||
uint32_t host_ocr;
|
||||
uint32_t card_ocr;
|
||||
int error;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
|
||||
/* Set host mode to SD "combo" card or SD memory-only. */
|
||||
SET(sc->sc_flags, SMF_SD_MODE|SMF_MEM_MODE);
|
||||
|
||||
/* Reset memory (*must* do that before CMD55 or CMD1). */
|
||||
sdmmc_go_idle_state(sc);
|
||||
|
||||
/*
|
||||
* Read the SD/MMC memory OCR value by issuing CMD55 followed
|
||||
* by ACMD41 to read the OCR value from memory-only SD cards.
|
||||
* MMC cards will not respond to CMD55 or ACMD41 and this is
|
||||
* how we distinguish them from SD cards.
|
||||
*/
|
||||
mmc_mode:
|
||||
error = sdmmc_mem_send_op_cond(sc, 0, &card_ocr);
|
||||
if (error) {
|
||||
if (ISSET(sc->sc_flags, SMF_SD_MODE) &&
|
||||
!ISSET(sc->sc_flags, SMF_IO_MODE)) {
|
||||
/* Not a SD card, switch to MMC mode. */
|
||||
DPRINTF(("%s: switch to MMC mode\n", SDMMCDEVNAME(sc)));
|
||||
CLR(sc->sc_flags, SMF_SD_MODE);
|
||||
goto mmc_mode;
|
||||
}
|
||||
if (!ISSET(sc->sc_flags, SMF_SD_MODE)) {
|
||||
DPRINTF(("%s: couldn't read memory OCR\n",
|
||||
SDMMCDEVNAME(sc)));
|
||||
goto out;
|
||||
} else {
|
||||
/* Not a "combo" card. */
|
||||
CLR(sc->sc_flags, SMF_MEM_MODE);
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the lowest voltage supported by the card and host. */
|
||||
host_ocr = sdmmc_chip_host_ocr(sc->sc_sct, sc->sc_sch);
|
||||
error = sdmmc_set_bus_power(sc, host_ocr, card_ocr);
|
||||
if (error) {
|
||||
DPRINTF(("%s: couldn't supply voltage requested by card\n",
|
||||
SDMMCDEVNAME(sc)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Tell the card(s) to enter the idle state (again). */
|
||||
sdmmc_go_idle_state(sc);
|
||||
|
||||
error = sdmmc_mem_send_if_cond(sc, 0x1aa, &card_ocr);
|
||||
if (error == 0 && card_ocr == 0x1aa)
|
||||
SET(host_ocr, MMC_OCR_HCS);
|
||||
|
||||
/* Send the new OCR value until all cards are ready. */
|
||||
error = sdmmc_mem_send_op_cond(sc, host_ocr, NULL);
|
||||
if (error) {
|
||||
DPRINTF(("%s: couldn't send memory OCR\n", SDMMCDEVNAME(sc)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
SDMMC_UNLOCK(sc);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the CSD and CID from all cards and assign each card a unique
|
||||
* relative card address (RCA). CMD2 is ignored by SDIO-only cards.
|
||||
*/
|
||||
void
|
||||
sdmmc_mem_scan(struct sdmmc_softc *sc)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
struct sdmmc_function *sf;
|
||||
uint16_t next_rca;
|
||||
int error;
|
||||
int retry;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
|
||||
/*
|
||||
* CMD2 is a broadcast command understood by SD cards and MMC
|
||||
* cards. All cards begin to respond to the command, but back
|
||||
* off if another card drives the CMD line to a different level.
|
||||
* Only one card will get its entire response through. That
|
||||
* card remains silent once it has been assigned a RCA.
|
||||
*/
|
||||
for (retry = 0; retry < 100; retry++) {
|
||||
memset(&cmd, 0, sizeof cmd);
|
||||
cmd.c_opcode = MMC_ALL_SEND_CID;
|
||||
cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R2;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error == ETIMEDOUT) {
|
||||
/* No more cards there. */
|
||||
break;
|
||||
} else if (error) {
|
||||
DPRINTF(("%s: couldn't read CID\n", SDMMCDEVNAME(sc)));
|
||||
break;
|
||||
}
|
||||
|
||||
/* In MMC mode, find the next available RCA. */
|
||||
next_rca = 1;
|
||||
if (!ISSET(sc->sc_flags, SMF_SD_MODE)) {
|
||||
SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list)
|
||||
next_rca++;
|
||||
}
|
||||
|
||||
/* Allocate a sdmmc_function structure. */
|
||||
sf = sdmmc_function_alloc(sc);
|
||||
sf->rca = next_rca;
|
||||
|
||||
/*
|
||||
* Remember the CID returned in the CMD2 response for
|
||||
* later decoding.
|
||||
*/
|
||||
memcpy(sf->raw_cid, cmd.c_resp, sizeof(sf->raw_cid));
|
||||
|
||||
/*
|
||||
* Silence the card by assigning it a unique RCA, or
|
||||
* querying it for its RCA in the case of SD.
|
||||
*/
|
||||
if (sdmmc_set_relative_addr(sc, sf) != 0) {
|
||||
aprint_error_dev(sc->sc_dev, "couldn't set mem RCA\n");
|
||||
sdmmc_function_free(sf);
|
||||
break;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Verify that the RCA has been set by selecting the card. */
|
||||
if (sdmmc_select_card(sc, sf) != 0) {
|
||||
printf("%s: can't select mem RCA %d (verify)\n",
|
||||
SDMMCDEVNAME(sc), sf->rca);
|
||||
sdmmc_function_free(sf);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Deselect. */
|
||||
(void)sdmmc_select_card(sc, NULL);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If this is a memory-only card, the card responding
|
||||
* first becomes an alias for SDIO function 0.
|
||||
*/
|
||||
if (sc->sc_fn0 == NULL)
|
||||
sc->sc_fn0 = sf;
|
||||
|
||||
SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf, sf_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* All cards are either inactive or awaiting further commands.
|
||||
* Read the CSDs and decode the raw CID for each card.
|
||||
*/
|
||||
SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
|
||||
memset(&cmd, 0, sizeof cmd);
|
||||
cmd.c_opcode = MMC_SEND_CSD;
|
||||
cmd.c_arg = MMC_ARG_RCA(sf->rca);
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R2;
|
||||
|
||||
if (sdmmc_mmc_command(sc, &cmd) != 0) {
|
||||
SET(sf->flags, SFF_ERROR);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sdmmc_decode_csd(sc, cmd.c_resp, sf) != 0 ||
|
||||
sdmmc_decode_cid(sc, sf->raw_cid, sf) != 0) {
|
||||
SET(sf->flags, SFF_ERROR);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
printf("%s: CID: ", SDMMCDEVNAME(sc));
|
||||
sdmmc_print_cid(&sf->cid);
|
||||
#endif
|
||||
}
|
||||
|
||||
SDMMC_UNLOCK(sc);
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp,
|
||||
struct sdmmc_function *sf)
|
||||
{
|
||||
/* TRAN_SPEED(2:0): transfer rate exponent */
|
||||
static const int speed_exponent[8] = {
|
||||
100 * 1, /* 100 Kbits/s */
|
||||
1 * 1000, /* 1 Mbits/s */
|
||||
10 * 1000, /* 10 Mbits/s */
|
||||
100 * 1000, /* 100 Mbits/s */
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
/* TRAN_SPEED(6:3): time mantissa */
|
||||
static const int speed_mantissa[16] = {
|
||||
0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80,
|
||||
};
|
||||
struct sdmmc_csd *csd = &sf->csd;
|
||||
int e, m;
|
||||
|
||||
if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
|
||||
/*
|
||||
* CSD version 1.0 corresponds to SD system
|
||||
* specification version 1.0 - 1.10. (SanDisk, 3.5.3)
|
||||
*/
|
||||
csd->csdver = SD_CSD_CSDVER(resp);
|
||||
switch (csd->csdver) {
|
||||
case SD_CSD_CSDVER_2_0:
|
||||
DPRINTF(("%s: SD Ver.2.0\n", SDMMCDEVNAME(sc)));
|
||||
csd->capacity = SD_CSD_V2_CAPACITY(resp);
|
||||
csd->read_bl_len = SD_CSD_V2_BL_LEN;
|
||||
break;
|
||||
|
||||
case SD_CSD_CSDVER_1_0:
|
||||
DPRINTF(("%s: SD Ver.1.0\n", SDMMCDEVNAME(sc)));
|
||||
csd->capacity = SD_CSD_CAPACITY(resp);
|
||||
csd->read_bl_len = SD_CSD_READ_BL_LEN(resp);
|
||||
break;
|
||||
|
||||
default:
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"unknown SD CSD structure version 0x%x\n",
|
||||
csd->csdver);
|
||||
return 1;
|
||||
}
|
||||
|
||||
csd->mmcver = SD_CSD_MMCVER(resp);
|
||||
csd->write_bl_len = SD_CSD_WRITE_BL_LEN(resp);
|
||||
csd->r2w_factor = SD_CSD_R2W_FACTOR(resp);
|
||||
e = SD_CSD_SPEED_EXP(resp);
|
||||
m = SD_CSD_SPEED_MANT(resp);
|
||||
csd->tran_speed = speed_exponent[e] * speed_mantissa[m] / 10;
|
||||
} else {
|
||||
csd->csdver = MMC_CSD_CSDVER(resp);
|
||||
if (csd->csdver != MMC_CSD_CSDVER_1_0 &&
|
||||
csd->csdver != MMC_CSD_CSDVER_2_0) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"unknown MMC CSD structure version 0x%x\n",
|
||||
csd->csdver);
|
||||
return 1;
|
||||
}
|
||||
|
||||
csd->mmcver = MMC_CSD_MMCVER(resp);
|
||||
csd->capacity = MMC_CSD_CAPACITY(resp);
|
||||
csd->read_bl_len = MMC_CSD_READ_BL_LEN(resp);
|
||||
csd->write_bl_len = MMC_CSD_WRITE_BL_LEN(resp);
|
||||
csd->r2w_factor = MMC_CSD_R2W_FACTOR(resp);
|
||||
e = MMC_CSD_TRAN_SPEED_EXP(resp);
|
||||
m = MMC_CSD_TRAN_SPEED_MANT(resp);
|
||||
csd->tran_speed = speed_exponent[e] * speed_mantissa[m] / 10;
|
||||
}
|
||||
csd->sector_size = MIN((1 << csd->read_bl_len),
|
||||
sdmmc_chip_host_maxblklen(sc->sc_sct, sc->sc_sch));
|
||||
if (csd->sector_size < (1 << csd->read_bl_len))
|
||||
csd->capacity *= (1 << csd->read_bl_len) / csd->sector_size;
|
||||
csd->sector_size_sb = ffs(csd->sector_size) - 1;
|
||||
|
||||
if (sc->sc_busclk > csd->tran_speed)
|
||||
sc->sc_busclk = csd->tran_speed;
|
||||
|
||||
#ifdef SDMMC_DUMP_CSD
|
||||
sdmmc_print_csd(resp, csd);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_decode_cid(struct sdmmc_softc *sc, sdmmc_response resp,
|
||||
struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_cid *cid = &sf->cid;
|
||||
|
||||
if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
|
||||
cid->mid = SD_CID_MID(resp);
|
||||
cid->oid = SD_CID_OID(resp);
|
||||
SD_CID_PNM_CPY(resp, cid->pnm);
|
||||
cid->rev = SD_CID_REV(resp);
|
||||
cid->psn = SD_CID_PSN(resp);
|
||||
cid->mdt = SD_CID_MDT(resp);
|
||||
} else {
|
||||
switch(sf->csd.mmcver) {
|
||||
case MMC_CSD_MMCVER_1_0:
|
||||
case MMC_CSD_MMCVER_1_4:
|
||||
cid->mid = MMC_CID_MID_V1(resp);
|
||||
MMC_CID_PNM_V1_CPY(resp, cid->pnm);
|
||||
cid->rev = MMC_CID_REV_V1(resp);
|
||||
cid->psn = MMC_CID_PSN_V1(resp);
|
||||
cid->mdt = MMC_CID_MDT_V1(resp);
|
||||
break;
|
||||
case MMC_CSD_MMCVER_2_0:
|
||||
case MMC_CSD_MMCVER_3_1:
|
||||
case MMC_CSD_MMCVER_4_0:
|
||||
cid->mid = MMC_CID_MID_V2(resp);
|
||||
cid->oid = MMC_CID_OID_V2(resp);
|
||||
MMC_CID_PNM_V2_CPY(resp, cid->pnm);
|
||||
cid->psn = MMC_CID_PSN_V2(resp);
|
||||
break;
|
||||
default:
|
||||
aprint_error_dev(sc->sc_dev, "unknown MMC version %d\n",
|
||||
sf->csd.mmcver);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sdmmc_print_cid(struct sdmmc_cid *cid)
|
||||
{
|
||||
|
||||
printf("mid=0x%02x oid=0x%04x pnm=\"%s\" rev=0x%02x psn=0x%08x"
|
||||
" mdt=%03x\n", cid->mid, cid->oid, cid->pnm, cid->rev, cid->psn,
|
||||
cid->mdt);
|
||||
}
|
||||
|
||||
#ifdef SDMMC_DUMP_CSD
|
||||
static void
|
||||
sdmmc_print_csd(sdmmc_response resp, struct sdmmc_csd *csd)
|
||||
{
|
||||
|
||||
printf("csdver = %d\n", csd->csdver);
|
||||
printf("mmcver = %d\n", csd->mmcver);
|
||||
printf("capacity = %08x\n", csd->capacity);
|
||||
printf("read_bl_len = %d\n", csd->read_bl_len);
|
||||
printf("write_cl_len = %d\n", csd->write_bl_len);
|
||||
printf("r2w_factor = %d\n", csd->r2w_factor);
|
||||
printf("tran_speed = %d\n", csd->tran_speed);
|
||||
printf("sector_size = %d\n", csd->sector_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize a SD/MMC memory card.
|
||||
*/
|
||||
int
|
||||
sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
|
||||
{
|
||||
int error;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
|
||||
error = sdmmc_select_card(sc, sf);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (!ISSET(sf->flags, SFF_SDHC)) {
|
||||
error = sdmmc_mem_set_blocklen(sc, sf);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
SDMMC_UNLOCK(sc);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get or set the card's memory OCR value (SD or MMC).
|
||||
*/
|
||||
static int
|
||||
sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, uint32_t ocr, uint32_t *ocrp)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
int retry;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
/*
|
||||
* If we change the OCR value, retry the command until the OCR
|
||||
* we receive in response has the "CARD BUSY" bit set, meaning
|
||||
* that all cards are ready for identification.
|
||||
*/
|
||||
for (retry = 0; retry < 100; retry++) {
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_arg = ocr;
|
||||
cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R3;
|
||||
|
||||
if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
|
||||
cmd.c_opcode = SD_APP_OP_COND;
|
||||
error = sdmmc_app_command(sc, &cmd);
|
||||
} else {
|
||||
cmd.c_opcode = MMC_SEND_OP_COND;
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
}
|
||||
if (error)
|
||||
break;
|
||||
if (ISSET(MMC_R3(cmd.c_resp), MMC_OCR_MEM_READY) || ocr == 0)
|
||||
break;
|
||||
|
||||
error = ETIMEDOUT;
|
||||
sdmmc_delay(10000);
|
||||
}
|
||||
if (error == 0 && ocrp != NULL)
|
||||
*ocrp = MMC_R3(cmd.c_resp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
sdmmc_mem_send_if_cond(struct sdmmc_softc *sc, uint32_t ocr, uint32_t *ocrp)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_arg = ocr;
|
||||
cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R7;
|
||||
cmd.c_opcode = SD_SEND_IF_COND;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error == 0 && ocrp != NULL)
|
||||
*ocrp = MMC_R7(cmd.c_resp);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the read block length appropriately for this card, according to
|
||||
* the card CSD register value.
|
||||
*/
|
||||
static int
|
||||
sdmmc_mem_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_function *sf)
|
||||
{
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
|
||||
/* Don't lock */
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_opcode = MMC_SET_BLOCKLEN;
|
||||
cmd.c_arg = sf->csd.sector_size;
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
|
||||
DPRINTF(("%s: sdmmc_mem_set_blocklen: read_bl_len=%d sector_size=%d\n",
|
||||
SDMMCDEVNAME(sc), 1 << sf->csd.read_bl_len, sf->csd.sector_size));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
sdmmc_mem_read_block_subr(struct sdmmc_function *sf, uint32_t blkno,
|
||||
u_char *data, size_t datalen)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
|
||||
error = sdmmc_select_card(sc, sf);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_data = data;
|
||||
cmd.c_datalen = datalen;
|
||||
cmd.c_blklen = sf->csd.sector_size;
|
||||
cmd.c_opcode = (cmd.c_datalen / cmd.c_blklen) > 1 ?
|
||||
MMC_READ_BLOCK_MULTIPLE : MMC_READ_BLOCK_SINGLE;
|
||||
cmd.c_arg = blkno;
|
||||
if (!ISSET(sf->flags, SFF_SDHC))
|
||||
cmd.c_arg <<= sf->csd.sector_size_sb;
|
||||
cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1;
|
||||
if (ISSET(sc->sc_caps, SMC_CAPS_DMA))
|
||||
cmd.c_dmamap = sc->sc_dmap;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (!ISSET(sc->sc_caps, SMC_CAPS_AUTO_STOP)) {
|
||||
if (cmd.c_opcode == MMC_READ_BLOCK_MULTIPLE) {
|
||||
memset(&cmd, 0, sizeof cmd);
|
||||
cmd.c_opcode = MMC_STOP_TRANSMISSION;
|
||||
cmd.c_arg = MMC_ARG_RCA(sf->rca);
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1B;
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_opcode = MMC_SEND_STATUS;
|
||||
cmd.c_arg = MMC_ARG_RCA(sf->rca);
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error)
|
||||
break;
|
||||
/* XXX time out */
|
||||
} while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA));
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_mem_read_block(struct sdmmc_function *sf, uint32_t blkno, u_char *data,
|
||||
size_t datalen)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
int error;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
|
||||
if (!ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
|
||||
error = sdmmc_mem_read_block_subr(sf, blkno, data, datalen);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* DMA transfer */
|
||||
error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, data, datalen, NULL,
|
||||
BUS_DMA_NOWAIT|BUS_DMA_STREAMING|BUS_DMA_READ);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
|
||||
BUS_DMASYNC_PREREAD);
|
||||
|
||||
error = sdmmc_mem_read_block_subr(sf, blkno, data, datalen);
|
||||
if (error)
|
||||
goto unload;
|
||||
|
||||
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
|
||||
BUS_DMASYNC_POSTREAD);
|
||||
unload:
|
||||
bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
|
||||
|
||||
out:
|
||||
SDMMC_UNLOCK(sc);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
sdmmc_mem_write_block_subr(struct sdmmc_function *sf, uint32_t blkno,
|
||||
u_char *data, size_t datalen)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
struct sdmmc_command cmd;
|
||||
int error;
|
||||
|
||||
error = sdmmc_select_card(sc, sf);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_data = data;
|
||||
cmd.c_datalen = datalen;
|
||||
cmd.c_blklen = sf->csd.sector_size;
|
||||
cmd.c_opcode = (cmd.c_datalen / cmd.c_blklen) > 1 ?
|
||||
MMC_WRITE_BLOCK_MULTIPLE : MMC_WRITE_BLOCK_SINGLE;
|
||||
cmd.c_arg = blkno;
|
||||
if (!ISSET(sf->flags, SFF_SDHC))
|
||||
cmd.c_arg <<= sf->csd.sector_size_sb;
|
||||
cmd.c_flags = SCF_CMD_ADTC | SCF_RSP_R1;
|
||||
if (ISSET(sc->sc_caps, SMC_CAPS_DMA))
|
||||
cmd.c_dmamap = sc->sc_dmap;
|
||||
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (!ISSET(sc->sc_caps, SMC_CAPS_AUTO_STOP)) {
|
||||
if (cmd.c_opcode == MMC_WRITE_BLOCK_MULTIPLE) {
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_opcode = MMC_STOP_TRANSMISSION;
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1B;
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.c_opcode = MMC_SEND_STATUS;
|
||||
cmd.c_arg = MMC_ARG_RCA(sf->rca);
|
||||
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
|
||||
error = sdmmc_mmc_command(sc, &cmd);
|
||||
if (error)
|
||||
break;
|
||||
/* XXX time out */
|
||||
} while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA));
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
sdmmc_mem_write_block(struct sdmmc_function *sf, uint32_t blkno, u_char *data,
|
||||
size_t datalen)
|
||||
{
|
||||
struct sdmmc_softc *sc = sf->sc;
|
||||
int error;
|
||||
|
||||
SDMMC_LOCK(sc);
|
||||
|
||||
if (sdmmc_chip_write_protect(sc->sc_sct, sc->sc_sch)) {
|
||||
aprint_normal_dev(sc->sc_dev, "write-protected\n");
|
||||
error = EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
|
||||
error = sdmmc_mem_write_block_subr(sf, blkno, data, datalen);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* DMA transfer */
|
||||
error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, data, datalen, NULL,
|
||||
BUS_DMA_NOWAIT|BUS_DMA_STREAMING|BUS_DMA_WRITE);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
|
||||
BUS_DMASYNC_PREWRITE);
|
||||
|
||||
error = sdmmc_mem_write_block_subr(sf, blkno, data, datalen);
|
||||
if (error)
|
||||
goto unload;
|
||||
|
||||
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
|
||||
BUS_DMASYNC_POSTWRITE);
|
||||
unload:
|
||||
bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
|
||||
|
||||
out:
|
||||
SDMMC_UNLOCK(sc);
|
||||
|
||||
return error;
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/* $NetBSD: sdmmcchip.h,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */
|
||||
/* $OpenBSD: sdmmcchip.h,v 1.3 2007/05/31 10:09:01 uwe Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDMMC_CHIP_H_
|
||||
#define _SDMMC_CHIP_H_
|
||||
|
||||
#include <sys/device.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
struct sdmmc_command;
|
||||
|
||||
typedef struct sdmmc_chip_functions *sdmmc_chipset_tag_t;
|
||||
typedef void *sdmmc_chipset_handle_t;
|
||||
|
||||
struct sdmmc_chip_functions {
|
||||
/* host controller reset */
|
||||
int (*host_reset)(sdmmc_chipset_handle_t);
|
||||
|
||||
/* host capabilities */
|
||||
uint32_t (*host_ocr)(sdmmc_chipset_handle_t);
|
||||
int (*host_maxblklen)(sdmmc_chipset_handle_t);
|
||||
|
||||
/* card detection */
|
||||
int (*card_detect)(sdmmc_chipset_handle_t);
|
||||
|
||||
/* write protect */
|
||||
int (*write_protect)(sdmmc_chipset_handle_t);
|
||||
|
||||
/* bus power, clock frequency and width */
|
||||
int (*bus_power)(sdmmc_chipset_handle_t, uint32_t);
|
||||
int (*bus_clock)(sdmmc_chipset_handle_t, int);
|
||||
int (*bus_width)(sdmmc_chipset_handle_t, int);
|
||||
|
||||
/* command execution */
|
||||
void (*exec_command)(sdmmc_chipset_handle_t,
|
||||
struct sdmmc_command *);
|
||||
|
||||
/* card interrupt */
|
||||
void (*card_enable_intr)(sdmmc_chipset_handle_t, int);
|
||||
void (*card_intr_ack)(sdmmc_chipset_handle_t);
|
||||
};
|
||||
|
||||
/* host controller reset */
|
||||
#define sdmmc_chip_host_reset(tag, handle) \
|
||||
((tag)->host_reset((handle)))
|
||||
/* host capabilities */
|
||||
#define sdmmc_chip_host_ocr(tag, handle) \
|
||||
((tag)->host_ocr((handle)))
|
||||
#define sdmmc_chip_host_maxblklen(tag, handle) \
|
||||
((tag)->host_maxblklen((handle)))
|
||||
/* card detection */
|
||||
#define sdmmc_chip_card_detect(tag, handle) \
|
||||
((tag)->card_detect((handle)))
|
||||
/* write protect */
|
||||
#define sdmmc_chip_write_protect(tag, handle) \
|
||||
((tag)->write_protect((handle)))
|
||||
/* bus power, clock frequency and width */
|
||||
#define sdmmc_chip_bus_power(tag, handle, ocr) \
|
||||
((tag)->bus_power((handle), (ocr)))
|
||||
#define sdmmc_chip_bus_clock(tag, handle, freq) \
|
||||
((tag)->bus_clock((handle), (freq)))
|
||||
#define sdmmc_chip_bus_width(tag, handle, width) \
|
||||
((tag)->bus_width((handle), (width)))
|
||||
/* command execution */
|
||||
#define sdmmc_chip_exec_command(tag, handle, cmdp) \
|
||||
((tag)->exec_command((handle), (cmdp)))
|
||||
/* card interrupt */
|
||||
#define sdmmc_chip_card_enable_intr(tag, handle, enable) \
|
||||
((tag)->card_enable_intr((handle), (enable)))
|
||||
#define sdmmc_chip_card_intr_ack(tag, handle) \
|
||||
((tag)->card_intr_ack((handle)))
|
||||
|
||||
/* clock frequencies for sdmmc_chip_bus_clock() */
|
||||
#define SDMMC_SDCLK_OFF 0
|
||||
#define SDMMC_SDCLK_400K 400
|
||||
|
||||
struct sdmmcbus_attach_args {
|
||||
const char *saa_busname;
|
||||
sdmmc_chipset_tag_t saa_sct;
|
||||
sdmmc_chipset_handle_t saa_sch;
|
||||
bus_dma_tag_t saa_dmat;
|
||||
u_int saa_clkmin;
|
||||
u_int saa_clkmax;
|
||||
uint32_t saa_caps; /* see sdmmc_softc.sc_caps */
|
||||
};
|
||||
|
||||
void sdmmc_needs_discover(device_t);
|
||||
void sdmmc_card_intr(device_t);
|
||||
void sdmmc_delay(u_int);
|
||||
|
||||
#endif /* _SDMMC_CHIP_H_ */
|
|
@ -0,0 +1,70 @@
|
|||
$NetBSD: sdmmcdevs,v 1.1 2009/04/21 03:00:31 nonaka Exp $
|
||||
/* $OpenBSD: sdmmcdevs,v 1.8 2007/05/11 17:16:16 mglocker Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* List of known SD card vendors
|
||||
*/
|
||||
vendor CGUYS 0x0092 C-guys, Inc.
|
||||
vendor TOSHIBA 0x0098 Toshiba
|
||||
vendor SOCKETCOM 0x0104 Socket Communications, Inc.
|
||||
vendor ATHEROS 0x0271 Atheros
|
||||
vendor SYCHIP 0x02db SyChip Inc.
|
||||
vendor SPECTEC 0x02fe Spectec Computer Co., Ltd
|
||||
vendor MEDIATEK 0x037a MediaTek Inc.
|
||||
vendor GLOBALSAT 0x0501 Globalsat Technology Co.
|
||||
vendor ABOCOM 0x13d1 AboCom Systems, Inc.
|
||||
|
||||
/*
|
||||
* List of known products, grouped by vendor
|
||||
*/
|
||||
|
||||
/* AboCom Systems, Inc. */
|
||||
product ABOCOM SDW11G 0xac02 SDW11G
|
||||
|
||||
/* Atheros */
|
||||
product ATHEROS AR6001_8 0x0108 AR6001
|
||||
product ATHEROS AR6001_9 0x0109 AR6001
|
||||
product ATHEROS AR6001_a 0x010a AR6001
|
||||
product ATHEROS AR6001_b 0x010b AR6001
|
||||
|
||||
/* C-guys, Inc. */
|
||||
product CGUYS TIACX100 0x0001 TI ACX100 SD-Link11b WiFi Card
|
||||
product CGUYS SDFMRADIO2 0x0005 C-guys SD FM Radio 2
|
||||
product CGUYS SDFMRADIO 0x5544 C-guys SD FM Radio
|
||||
|
||||
/* Globalsat Technology Co. */
|
||||
product GLOBALSAT SD501 0xf501 Globalsat SD-501 GPS Card
|
||||
|
||||
/* MediaTek Inc. */
|
||||
product MEDIATEK S2YWLAN 0x5911 MediaTek/Spectec WLAN-11b/g
|
||||
|
||||
/* Spectec Computer Co., Ltd */
|
||||
product SPECTEC SDW820 0x2128 Spectec SDIO WLAN Card
|
||||
|
||||
/* SyChip Inc. */
|
||||
product SYCHIP WLAN6060SD 0x0002 SyChip Pegasus WLAN SDIO Card
|
||||
|
||||
/* Toshiba */
|
||||
product TOSHIBA SDBTCARD1 0x0001 Toshiba SD BT Card 1
|
||||
product TOSHIBA SDBTCARD2 0x0002 Toshiba SD BT Card 2
|
||||
product TOSHIBA SDBTCARD3 0x0003 Toshiba SD BT Card 3
|
||||
|
||||
/* Socket Communications, Inc. */
|
||||
product SOCKETCOM SDSCANNER 0x005e Socket SD Scanner
|
||||
product SOCKETCOM BTCARD 0x00c5 Socket BT Card
|
|
@ -0,0 +1,94 @@
|
|||
/* $NetBSD: sdmmcdevs.h,v 1.1 2009/04/21 03:00:31 nonaka Exp $ */
|
||||
|
||||
/*
|
||||
* THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
*
|
||||
* generated from:
|
||||
* NetBSD
|
||||
*/
|
||||
/* $OpenBSD: sdmmcdevs,v 1.8 2007/05/11 17:16:16 mglocker Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* List of known SD card vendors
|
||||
*/
|
||||
#define SDMMC_VENDOR_CGUYS 0x0092 /* C-guys, Inc. */
|
||||
#define SDMMC_VENDOR_TOSHIBA 0x0098 /* Toshiba */
|
||||
#define SDMMC_VENDOR_SOCKETCOM 0x0104 /* Socket Communications, Inc. */
|
||||
#define SDMMC_VENDOR_ATHEROS 0x0271 /* Atheros */
|
||||
#define SDMMC_VENDOR_SYCHIP 0x02db /* SyChip Inc. */
|
||||
#define SDMMC_VENDOR_SPECTEC 0x02fe /* Spectec Computer Co., Ltd */
|
||||
#define SDMMC_VENDOR_MEDIATEK 0x037a /* MediaTek Inc. */
|
||||
#define SDMMC_VENDOR_GLOBALSAT 0x0501 /* Globalsat Technology Co. */
|
||||
#define SDMMC_VENDOR_ABOCOM 0x13d1 /* AboCom Systems, Inc. */
|
||||
|
||||
/*
|
||||
* List of known products, grouped by vendor
|
||||
*/
|
||||
|
||||
/* AboCom Systems, Inc. */
|
||||
#define SDMMC_CIS_ABOCOM_SDW11G { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_ABOCOM_SDW11G 0xac02
|
||||
|
||||
/* Atheros */
|
||||
#define SDMMC_CIS_ATHEROS_AR6001_8 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_ATHEROS_AR6001_8 0x0108
|
||||
#define SDMMC_CIS_ATHEROS_AR6001_9 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_ATHEROS_AR6001_9 0x0109
|
||||
#define SDMMC_CIS_ATHEROS_AR6001_a { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_ATHEROS_AR6001_a 0x010a
|
||||
#define SDMMC_CIS_ATHEROS_AR6001_b { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_ATHEROS_AR6001_b 0x010b
|
||||
|
||||
/* C-guys, Inc. */
|
||||
#define SDMMC_CIS_CGUYS_TIACX100 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_CGUYS_TIACX100 0x0001
|
||||
#define SDMMC_CIS_CGUYS_SDFMRADIO2 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_CGUYS_SDFMRADIO2 0x0005
|
||||
#define SDMMC_CIS_CGUYS_SDFMRADIO { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_CGUYS_SDFMRADIO 0x5544
|
||||
|
||||
/* Globalsat Technology Co. */
|
||||
#define SDMMC_CIS_GLOBALSAT_SD501 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_GLOBALSAT_SD501 0xf501
|
||||
|
||||
/* MediaTek Inc. */
|
||||
#define SDMMC_CIS_MEDIATEK_S2YWLAN { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_MEDIATEK_S2YWLAN 0x5911
|
||||
|
||||
/* Spectec Computer Co., Ltd */
|
||||
#define SDMMC_CIS_SPECTEC_SDW820 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_SPECTEC_SDW820 0x2128
|
||||
|
||||
/* SyChip Inc. */
|
||||
#define SDMMC_CIS_SYCHIP_WLAN6060SD { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_SYCHIP_WLAN6060SD 0x0002
|
||||
|
||||
/* Toshiba */
|
||||
#define SDMMC_CIS_TOSHIBA_SDBTCARD1 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_TOSHIBA_SDBTCARD1 0x0001
|
||||
#define SDMMC_CIS_TOSHIBA_SDBTCARD2 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_TOSHIBA_SDBTCARD2 0x0002
|
||||
#define SDMMC_CIS_TOSHIBA_SDBTCARD3 { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_TOSHIBA_SDBTCARD3 0x0003
|
||||
|
||||
/* Socket Communications, Inc. */
|
||||
#define SDMMC_CIS_SOCKETCOM_SDSCANNER { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_SOCKETCOM_SDSCANNER 0x005e
|
||||
#define SDMMC_CIS_SOCKETCOM_BTCARD { NULL, NULL, NULL, NULL }
|
||||
#define SDMMC_PRODUCT_SOCKETCOM_BTCARD 0x00c5
|
|
@ -0,0 +1,275 @@
|
|||
/* $NetBSD: sdmmcreg.h,v 1.1 2009/04/21 03:00:31 nonaka Exp $ */
|
||||
/* $OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDMMCREG_H_
|
||||
#define _SDMMCREG_H_
|
||||
|
||||
/* MMC commands */ /* response type */
|
||||
#define MMC_GO_IDLE_STATE 0 /* R0 */
|
||||
#define MMC_SEND_OP_COND 1 /* R3 */
|
||||
#define MMC_ALL_SEND_CID 2 /* R2 */
|
||||
#define MMC_SET_RELATIVE_ADDR 3 /* R1 */
|
||||
#define MMC_SELECT_CARD 7 /* R1 */
|
||||
#define MMC_SEND_CSD 9 /* R2 */
|
||||
#define MMC_STOP_TRANSMISSION 12 /* R1b */
|
||||
#define MMC_SEND_STATUS 13 /* R1 */
|
||||
#define MMC_INACTIVE_STATE 15 /* R0 */
|
||||
#define MMC_SET_BLOCKLEN 16 /* R1 */
|
||||
#define MMC_READ_BLOCK_SINGLE 17 /* R1 */
|
||||
#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */
|
||||
#define MMC_SET_BLOCK_COUNT 23 /* R1 */
|
||||
#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */
|
||||
#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */
|
||||
#define MMC_PROGRAM_CSD 27 /* R1 */
|
||||
#define MMC_SET_WRITE_PROT 28 /* R1b */
|
||||
#define MMC_SET_CLR_WRITE_PROT 29 /* R1b */
|
||||
#define MMC_SET_SEND_WRITE_PROT 30 /* R1 */
|
||||
#define MMC_TAG_SECTOR_START 32 /* R1 */
|
||||
#define MMC_TAG_SECTOR_END 33 /* R1 */
|
||||
#define MMC_UNTAG_SECTOR 34 /* R1 */
|
||||
#define MMC_TAG_ERASE_GROUP_START 35 /* R1 */
|
||||
#define MMC_TAG_ERASE_GROUP_END 36 /* R1 */
|
||||
#define MMC_UNTAG_ERASE_GROUP 37 /* R1 */
|
||||
#define MMC_ERASE 38 /* R1b */
|
||||
#define MMC_LOCK_UNLOCK 42 /* R1b */
|
||||
#define MMC_APP_CMD 55 /* R1 */
|
||||
|
||||
/* SD commands */ /* response type */
|
||||
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
|
||||
#define SD_SEND_IF_COND 8 /* R7 */
|
||||
|
||||
/* SD application commands */ /* response type */
|
||||
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
|
||||
#define SD_APP_OP_COND 41 /* R3 */
|
||||
#define SD_APP_SEND_SCR 51 /* R1 */
|
||||
|
||||
/* OCR bits */
|
||||
#define MMC_OCR_MEM_READY (1U<<31)/* memory power-up status bit */
|
||||
#define MMC_OCR_HCS (1<<30)
|
||||
#define MMC_OCR_3_5V_3_6V (1<<23)
|
||||
#define MMC_OCR_3_4V_3_5V (1<<22)
|
||||
#define MMC_OCR_3_3V_3_4V (1<<21)
|
||||
#define MMC_OCR_3_2V_3_3V (1<<20)
|
||||
#define MMC_OCR_3_1V_3_2V (1<<19)
|
||||
#define MMC_OCR_3_0V_3_1V (1<<18)
|
||||
#define MMC_OCR_2_9V_3_0V (1<<17)
|
||||
#define MMC_OCR_2_8V_2_9V (1<<16)
|
||||
#define MMC_OCR_2_7V_2_8V (1<<15)
|
||||
#define MMC_OCR_2_6V_2_7V (1<<14)
|
||||
#define MMC_OCR_2_5V_2_6V (1<<13)
|
||||
#define MMC_OCR_2_4V_2_5V (1<<12)
|
||||
#define MMC_OCR_2_3V_2_4V (1<<11)
|
||||
#define MMC_OCR_2_2V_2_3V (1<<10)
|
||||
#define MMC_OCR_2_1V_2_2V (1<<9)
|
||||
#define MMC_OCR_2_0V_2_1V (1<<8)
|
||||
#define MMC_OCR_1_9V_2_0V (1<<7)
|
||||
#define MMC_OCR_1_8V_1_9V (1<<6)
|
||||
#define MMC_OCR_1_7V_1_8V (1<<5)
|
||||
#define MMC_OCR_1_6V_1_7V (1<<4)
|
||||
|
||||
/* R1 response type bits */
|
||||
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
|
||||
#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
|
||||
|
||||
/* 48-bit response decoding (32 bits w/o CRC) */
|
||||
#define MMC_R1(resp) ((resp)[0])
|
||||
#define MMC_R3(resp) ((resp)[0])
|
||||
#define SD_R6(resp) ((resp)[0])
|
||||
#define MMC_R7(resp) ((resp)[0])
|
||||
|
||||
/* RCA argument and response */
|
||||
#define MMC_ARG_RCA(rca) ((rca) << 16)
|
||||
#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16)
|
||||
|
||||
/* bus width argument */
|
||||
#define SD_ARG_BUS_WIDTH_1 0
|
||||
#define SD_ARG_BUS_WIDTH_4 2
|
||||
|
||||
/* MMC R2 response (CSD) */
|
||||
#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
|
||||
#define MMC_CSD_CSDVER_1_0 1
|
||||
#define MMC_CSD_CSDVER_2_0 2
|
||||
#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4)
|
||||
#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */
|
||||
#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */
|
||||
#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */
|
||||
#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */
|
||||
#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */
|
||||
#define MMC_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8)
|
||||
#define MMC_CSD_TAAC_MANT(resp) MMC_RSP_BITS((resp), 115, 4)
|
||||
#define MMC_CSD_TAAC_EXP(resp) MMC_RSP_BITS((resp), 112, 3)
|
||||
#define MMC_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8)
|
||||
#define MMC_CSD_TRAN_SPEED(resp) MMC_RSP_BITS((resp), 96, 8)
|
||||
#define MMC_CSD_TRAN_SPEED_MANT(resp) MMC_RSP_BITS((resp), 99, 4)
|
||||
#define MMC_CSD_TRAN_SPEED_EXP(resp) MMC_RSP_BITS((resp), 96, 3)
|
||||
#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
|
||||
#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
|
||||
#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \
|
||||
(MMC_CSD_C_SIZE_MULT((resp))+2))
|
||||
#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
|
||||
#define MMC_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3)
|
||||
#define MMC_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4)
|
||||
|
||||
/* MMC v1 R2 response (CID) */
|
||||
#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24)
|
||||
#define MMC_CID_PNM_V1_CPY(resp, pnm) \
|
||||
do { \
|
||||
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||
(pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
|
||||
(pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \
|
||||
(pnm)[7] = '\0'; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8)
|
||||
#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24)
|
||||
#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8)
|
||||
|
||||
/* MMC v2 R2 response (CID) */
|
||||
#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8)
|
||||
#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16)
|
||||
#define MMC_CID_PNM_V2_CPY(resp, pnm) \
|
||||
do { \
|
||||
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||
(pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
|
||||
(pnm)[6] = '\0'; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32)
|
||||
|
||||
/* SD R2 response (CSD) */
|
||||
#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
|
||||
#define SD_CSD_CSDVER_1_0 0
|
||||
#define SD_CSD_CSDVER_2_0 1
|
||||
#define SD_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4)
|
||||
#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8)
|
||||
#define SD_CSD_TAAC_EXP(resp) MMC_RSP_BITS((resp), 115, 4)
|
||||
#define SD_CSD_TAAC_MANT(resp) MMC_RSP_BITS((resp), 112, 3)
|
||||
#define SD_CSD_TAAC_1_5_MSEC 0x26
|
||||
#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8)
|
||||
#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8)
|
||||
#define SD_CSD_SPEED_MANT(resp) MMC_RSP_BITS((resp), 99, 4)
|
||||
#define SD_CSD_SPEED_EXP(resp) MMC_RSP_BITS((resp), 96, 3)
|
||||
#define SD_CSD_SPEED_25_MHZ 0x32
|
||||
#define SD_CSD_SPEED_50_MHZ 0x5a
|
||||
#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12)
|
||||
#define SD_CSD_CCC_ALL 0x5f5
|
||||
#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
|
||||
#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1)
|
||||
#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1)
|
||||
#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1)
|
||||
#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1)
|
||||
#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
|
||||
#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \
|
||||
(SD_CSD_C_SIZE_MULT((resp))+2))
|
||||
#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3)
|
||||
#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3)
|
||||
#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3)
|
||||
#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3)
|
||||
#define SD_CSD_VDD_RW_CURR_100mA 0x7
|
||||
#define SD_CSD_VDD_RW_CURR_80mA 0x6
|
||||
#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22)
|
||||
#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10)
|
||||
#define SD_CSD_V2_BL_LEN 0x9 /* 512 */
|
||||
#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
|
||||
#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1)
|
||||
#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */
|
||||
#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */
|
||||
#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1)
|
||||
#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3)
|
||||
#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4)
|
||||
#define SD_CSD_RW_BL_LEN_2G 0xa
|
||||
#define SD_CSD_RW_BL_LEN_1G 0x9
|
||||
#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1)
|
||||
#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1)
|
||||
#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1)
|
||||
#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1)
|
||||
#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1)
|
||||
#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2)
|
||||
|
||||
/* SD R2 response (CID) */
|
||||
#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8)
|
||||
#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16)
|
||||
#define SD_CID_PNM_CPY(resp, pnm) \
|
||||
do { \
|
||||
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||
(pnm)[5] = '\0'; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8)
|
||||
#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32)
|
||||
#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12)
|
||||
|
||||
/* SCR (SD Configuration Register) */
|
||||
#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4)
|
||||
#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */
|
||||
#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4)
|
||||
#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 */
|
||||
#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1)
|
||||
#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3)
|
||||
#define SCR_SD_SECURITY_NONE 0 /* no security */
|
||||
#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */
|
||||
#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */
|
||||
#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4)
|
||||
#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */
|
||||
#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */
|
||||
#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 32, 16)
|
||||
#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32)
|
||||
|
||||
/* Might be slow, but it should work on big and little endian systems. */
|
||||
#define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len))
|
||||
static inline int
|
||||
__bitfield(uint32_t *src, int start, int len)
|
||||
{
|
||||
uint8_t *sp;
|
||||
uint32_t dst, mask;
|
||||
int shift, bs, bc;
|
||||
|
||||
if (start < 0 || len < 0 || len > 32)
|
||||
return 0;
|
||||
|
||||
dst = 0;
|
||||
mask = len % 32 ? UINT_MAX >> (32 - (len % 32)) : UINT_MAX;
|
||||
shift = 0;
|
||||
|
||||
while (len > 0) {
|
||||
sp = (uint8_t *)src + start / 8;
|
||||
bs = start % 8;
|
||||
bc = 8 - bs;
|
||||
if (bc > len)
|
||||
bc = len;
|
||||
dst |= (*sp++ >> bs) << shift;
|
||||
shift += bc;
|
||||
start += bc;
|
||||
len -= bc;
|
||||
}
|
||||
|
||||
dst &= mask;
|
||||
return (int)dst;
|
||||
}
|
||||
|
||||
#endif /* _SDMMCREG_H_ */
|
|
@ -0,0 +1,293 @@
|
|||
/* $NetBSD: sdmmcvar.h,v 1.1 2009/04/21 03:00:31 nonaka Exp $ */
|
||||
/* $OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDMMCVAR_H_
|
||||
#define _SDMMCVAR_H_
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/sdmmc/sdmmcchip.h>
|
||||
#include <dev/sdmmc/sdmmcreg.h>
|
||||
|
||||
struct sdmmc_csd {
|
||||
int csdver; /* CSD structure format */
|
||||
u_int mmcver; /* MMC version (for CID format) */
|
||||
int capacity; /* total number of sectors */
|
||||
int sector_size; /* sector size in bytes */
|
||||
int sector_size_sb; /* sector size in shift bit */
|
||||
int read_bl_len; /* block length for reads */
|
||||
int write_bl_len; /* block length for writes */
|
||||
int r2w_factor;
|
||||
int tran_speed; /* transfer speed (kbit/s) */
|
||||
/* ... */
|
||||
};
|
||||
|
||||
struct sdmmc_cid {
|
||||
int mid; /* manufacturer identification number */
|
||||
int oid; /* OEM/product identification number */
|
||||
char pnm[8]; /* product name (MMC v1 has the longest) */
|
||||
int rev; /* product revision */
|
||||
int psn; /* product serial number */
|
||||
int mdt; /* manufacturing date */
|
||||
};
|
||||
|
||||
typedef uint32_t sdmmc_response[4];
|
||||
|
||||
struct sdmmc_softc;
|
||||
|
||||
struct sdmmc_task {
|
||||
void (*func)(void *arg);
|
||||
void *arg;
|
||||
int onqueue;
|
||||
struct sdmmc_softc *sc;
|
||||
TAILQ_ENTRY(sdmmc_task) next;
|
||||
};
|
||||
|
||||
#define sdmmc_init_task(xtask, xfunc, xarg) \
|
||||
do { \
|
||||
(xtask)->func = (xfunc); \
|
||||
(xtask)->arg = (xarg); \
|
||||
(xtask)->onqueue = 0; \
|
||||
(xtask)->sc = NULL; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define sdmmc_task_pending(xtask) ((xtask)->onqueue)
|
||||
|
||||
struct sdmmc_command {
|
||||
struct sdmmc_task c_task; /* task queue entry */
|
||||
uint16_t c_opcode; /* SD or MMC command index */
|
||||
uint32_t c_arg; /* SD/MMC command argument */
|
||||
sdmmc_response c_resp; /* response buffer */
|
||||
bus_dmamap_t c_dmamap;
|
||||
void *c_data; /* buffer to send or read into */
|
||||
int c_datalen; /* length of data buffer */
|
||||
int c_blklen; /* block length */
|
||||
int c_flags; /* see below */
|
||||
#define SCF_ITSDONE 0x0001 /* command is complete */
|
||||
#define SCF_CMD_AC 0x0000
|
||||
#define SCF_CMD_ADTC 0x0010
|
||||
#define SCF_CMD_BC 0x0020
|
||||
#define SCF_CMD_BCR 0x0030
|
||||
#define SCF_CMD_READ 0x0040 /* read command (data expected) */
|
||||
#define SCF_RSP_BSY 0x0100
|
||||
#define SCF_RSP_136 0x0200
|
||||
#define SCF_RSP_CRC 0x0400
|
||||
#define SCF_RSP_IDX 0x0800
|
||||
#define SCF_RSP_PRESENT 0x1000
|
||||
/* response types */
|
||||
#define SCF_RSP_R0 0 /* none */
|
||||
#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||
#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
|
||||
#define SCF_RSP_R3 (SCF_RSP_PRESENT)
|
||||
#define SCF_RSP_R4 (SCF_RSP_PRESENT)
|
||||
#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||
#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||
int c_error; /* errno value on completion */
|
||||
|
||||
/* Host controller owned fields for data xfer in progress */
|
||||
int c_resid; /* remaining I/O */
|
||||
u_char *c_buf; /* remaining data */
|
||||
};
|
||||
|
||||
/*
|
||||
* Decoded PC Card 16 based Card Information Structure (CIS),
|
||||
* per card (function 0) and per function (1 and greater).
|
||||
*/
|
||||
struct sdmmc_cis {
|
||||
uint16_t manufacturer;
|
||||
#define SDMMC_VENDOR_INVALID 0xffff
|
||||
uint16_t product;
|
||||
#define SDMMC_PRODUCT_INVALID 0xffff
|
||||
uint8_t function;
|
||||
#define SDMMC_FUNCTION_INVALID 0xff
|
||||
u_char cis1_major;
|
||||
u_char cis1_minor;
|
||||
char cis1_info_buf[256];
|
||||
char *cis1_info[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure describing either an SD card I/O function or a SD/MMC
|
||||
* memory card from a "stack of cards" that responded to CMD2. For a
|
||||
* combo card with one I/O function and one memory card, there will be
|
||||
* two of these structures allocated. Each card slot has such a list
|
||||
* of sdmmc_function structures.
|
||||
*/
|
||||
struct sdmmc_function {
|
||||
/* common members */
|
||||
struct sdmmc_softc *sc; /* card slot softc */
|
||||
uint16_t rca; /* relative card address */
|
||||
int flags;
|
||||
#define SFF_ERROR 0x0001 /* function is poo; ignore it */
|
||||
#define SFF_SDHC 0x0002 /* SD High Capacity card */
|
||||
SIMPLEQ_ENTRY(sdmmc_function) sf_list;
|
||||
/* SD card I/O function members */
|
||||
int number; /* I/O function number or -1 */
|
||||
device_t child; /* function driver */
|
||||
struct sdmmc_cis cis; /* decoded CIS */
|
||||
/* SD/MMC memory card members */
|
||||
struct sdmmc_csd csd; /* decoded CSD value */
|
||||
struct sdmmc_cid cid; /* decoded CID value */
|
||||
sdmmc_response raw_cid; /* temp. storage for decoding */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure describing a single SD/MMC/SDIO card slot.
|
||||
*/
|
||||
struct sdmmc_softc {
|
||||
device_t sc_dev; /* base device */
|
||||
#define SDMMCDEVNAME(sc) (device_xname(sc->sc_dev))
|
||||
|
||||
sdmmc_chipset_tag_t sc_sct; /* host controller chipset tag */
|
||||
sdmmc_chipset_handle_t sc_sch; /* host controller chipset handle */
|
||||
bus_dma_tag_t sc_dmat;
|
||||
bus_dmamap_t sc_dmap;
|
||||
#define SDMMC_MAXNSEGS 16 /* (MAXPHYS / PAGE_SIZE) */
|
||||
|
||||
struct kmutex sc_mtx; /* lock around host controller */
|
||||
int sc_dying; /* bus driver is shutting down */
|
||||
|
||||
uint32_t sc_flags;
|
||||
#define SMF_INITED 0x0001
|
||||
#define SMF_SD_MODE 0x0002 /* host in SD mode (MMC otherwise) */
|
||||
#define SMF_IO_MODE 0x0004 /* host in I/O mode (SD mode only) */
|
||||
#define SMF_MEM_MODE 0x0008 /* host in memory mode (SD or MMC) */
|
||||
#define SMF_CARD_PRESENT 0x4000 /* card presence noticed */
|
||||
#define SMF_CARD_ATTACHED 0x8000 /* card driver(s) attached */
|
||||
|
||||
uint32_t sc_caps; /* host capability */
|
||||
#define SMC_CAPS_AUTO_STOP 0x0001 /* send CMD12 automagically by host */
|
||||
#define SMC_CAPS_4BIT_MODE 0x0002 /* 4-bits data bus width */
|
||||
#define SMC_CAPS_DMA 0x0004 /* DMA transfer */
|
||||
|
||||
/* function */
|
||||
int sc_function_count; /* number of I/O functions (SDIO) */
|
||||
struct sdmmc_function *sc_card; /* selected card */
|
||||
struct sdmmc_function *sc_fn0; /* function 0, the card itself */
|
||||
SIMPLEQ_HEAD(, sdmmc_function) sf_head; /* list of card functions */
|
||||
|
||||
/* task queue */
|
||||
struct lwp *sc_tskq_lwp; /* asynchronous tasks */
|
||||
TAILQ_HEAD(, sdmmc_task) sc_tskq; /* task thread work queue */
|
||||
struct kmutex sc_tskq_mtx;
|
||||
struct kcondvar sc_tskq_cv;
|
||||
|
||||
/* discover task */
|
||||
struct sdmmc_task sc_discover_task; /* card attach/detach task */
|
||||
struct kmutex sc_discover_task_mtx;
|
||||
|
||||
/* interrupt task */
|
||||
struct sdmmc_task sc_intr_task; /* card interrupt task */
|
||||
struct kmutex sc_intr_task_mtx;
|
||||
TAILQ_HEAD(, sdmmc_intr_handler) sc_intrq; /* interrupt handlers */
|
||||
|
||||
u_int sc_clkmin; /* host min bus clock */
|
||||
u_int sc_clkmax; /* host max bus clock */
|
||||
u_int sc_busclk; /* host bus clock */
|
||||
int sc_buswidth; /* host bus width */
|
||||
};
|
||||
|
||||
/*
|
||||
* Attach devices at the sdmmc bus.
|
||||
*/
|
||||
struct sdmmc_attach_args {
|
||||
uint16_t manufacturer;
|
||||
uint16_t product;
|
||||
struct sdmmc_function *sf;
|
||||
};
|
||||
|
||||
struct sdmmc_product {
|
||||
uint16_t pp_vendor;
|
||||
uint16_t pp_product;
|
||||
const char *pp_cisinfo[4];
|
||||
};
|
||||
|
||||
#ifndef IPL_SDMMC
|
||||
#define IPL_SDMMC IPL_BIO
|
||||
#endif
|
||||
|
||||
#ifndef splsdmmc
|
||||
#define splsdmmc() splbio()
|
||||
#endif
|
||||
|
||||
#define SDMMC_LOCK(sc)
|
||||
#define SDMMC_UNLOCK(sc)
|
||||
|
||||
#ifdef SDMMC_DEBUG
|
||||
extern int sdmmcdebug;
|
||||
#endif
|
||||
|
||||
void sdmmc_add_task(struct sdmmc_softc *, struct sdmmc_task *);
|
||||
void sdmmc_del_task(struct sdmmc_task *);
|
||||
|
||||
struct sdmmc_function *sdmmc_function_alloc(struct sdmmc_softc *);
|
||||
void sdmmc_function_free(struct sdmmc_function *);
|
||||
int sdmmc_set_bus_power(struct sdmmc_softc *, uint32_t, uint32_t);
|
||||
int sdmmc_mmc_command(struct sdmmc_softc *, struct sdmmc_command *);
|
||||
int sdmmc_app_command(struct sdmmc_softc *, struct sdmmc_command *);
|
||||
void sdmmc_go_idle_state(struct sdmmc_softc *);
|
||||
int sdmmc_select_card(struct sdmmc_softc *, struct sdmmc_function *);
|
||||
int sdmmc_set_relative_addr(struct sdmmc_softc *,
|
||||
struct sdmmc_function *);
|
||||
|
||||
void sdmmc_intr_enable(struct sdmmc_function *);
|
||||
void sdmmc_intr_disable(struct sdmmc_function *);
|
||||
void *sdmmc_intr_establish(device_t, int (*)(void *), void *, const char *);
|
||||
void sdmmc_intr_disestablish(void *);
|
||||
void sdmmc_intr_task(void *);
|
||||
|
||||
int sdmmc_decode_csd(struct sdmmc_softc *, sdmmc_response,
|
||||
struct sdmmc_function *);
|
||||
int sdmmc_decode_cid(struct sdmmc_softc *, sdmmc_response,
|
||||
struct sdmmc_function *);
|
||||
void sdmmc_print_cid(struct sdmmc_cid *);
|
||||
|
||||
int sdmmc_io_enable(struct sdmmc_softc *);
|
||||
void sdmmc_io_scan(struct sdmmc_softc *);
|
||||
int sdmmc_io_init(struct sdmmc_softc *, struct sdmmc_function *);
|
||||
uint8_t sdmmc_io_read_1(struct sdmmc_function *, int);
|
||||
uint16_t sdmmc_io_read_2(struct sdmmc_function *, int);
|
||||
uint32_t sdmmc_io_read_4(struct sdmmc_function *, int);
|
||||
int sdmmc_io_read_multi_1(struct sdmmc_function *, int, u_char *, int);
|
||||
void sdmmc_io_write_1(struct sdmmc_function *, int, uint8_t);
|
||||
void sdmmc_io_write_2(struct sdmmc_function *, int, uint16_t);
|
||||
void sdmmc_io_write_4(struct sdmmc_function *, int, uint32_t);
|
||||
int sdmmc_io_write_multi_1(struct sdmmc_function *, int, u_char *, int);
|
||||
int sdmmc_io_function_enable(struct sdmmc_function *);
|
||||
void sdmmc_io_function_disable(struct sdmmc_function *);
|
||||
|
||||
int sdmmc_read_cis(struct sdmmc_function *, struct sdmmc_cis *);
|
||||
void sdmmc_print_cis(struct sdmmc_function *);
|
||||
void sdmmc_check_cis_quirks(struct sdmmc_function *);
|
||||
|
||||
int sdmmc_mem_enable(struct sdmmc_softc *);
|
||||
void sdmmc_mem_scan(struct sdmmc_softc *);
|
||||
int sdmmc_mem_init(struct sdmmc_softc *, struct sdmmc_function *);
|
||||
int sdmmc_mem_read_block(struct sdmmc_function *, uint32_t, u_char *,
|
||||
size_t);
|
||||
int sdmmc_mem_write_block(struct sdmmc_function *, uint32_t, u_char *,
|
||||
size_t);
|
||||
|
||||
#endif /* _SDMMCVAR_H_ */
|
Loading…
Reference in New Issue