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:
nonaka 2009-04-21 03:00:29 +00:00
parent 91d9da05bb
commit e0297d1ead
34 changed files with 7584 additions and 26 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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?

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"

204
sys/arch/zaurus/dev/zmci.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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
#

299
sys/dev/pci/sdhc_pci.c Normal file
View File

@ -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);
}

View File

@ -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

144
sys/dev/sdmmc/devlist2h.awk Normal file
View File

@ -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
}

20
sys/dev/sdmmc/files.sdmmc Normal file
View File

@ -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

240
sys/dev/sdmmc/ld_sdmmc.c Normal file
View File

@ -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);
}

583
sys/dev/sdmmc/sbt.c Normal file
View File

@ -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);
}

1167
sys/dev/sdmmc/sdhc.c Normal file

File diff suppressed because it is too large Load Diff

181
sys/dev/sdmmc/sdhcreg.h Normal file
View File

@ -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_ */

51
sys/dev/sdmmc/sdhcvar.h Normal file
View File

@ -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_ */

785
sys/dev/sdmmc/sdmmc.c Normal file
View File

@ -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

213
sys/dev/sdmmc/sdmmc_cis.c Normal file
View File

@ -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;
}
}
}

670
sys/dev/sdmmc/sdmmc_io.c Normal file
View File

@ -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);
}

125
sys/dev/sdmmc/sdmmc_ioreg.h Normal file
View File

@ -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_ */

730
sys/dev/sdmmc/sdmmc_mem.c Normal file
View File

@ -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;
}

108
sys/dev/sdmmc/sdmmcchip.h Normal file
View File

@ -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_ */

70
sys/dev/sdmmc/sdmmcdevs Normal file
View File

@ -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

94
sys/dev/sdmmc/sdmmcdevs.h Normal file
View File

@ -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

275
sys/dev/sdmmc/sdmmcreg.h Normal file
View File

@ -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_ */

293
sys/dev/sdmmc/sdmmcvar.h Normal file
View File

@ -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_ */