An MMC/SD/SDIO stack using CAM
Implement the MMC/SD/SDIO protocol within a CAM framework. CAM's flexible queueing will make it easier to write non-storage drivers than the legacy stack. SDIO drivers from both the kernel and as userland daemons are possible, though much of that functionality will come later. Some of the CAM integration isn't complete (there are sleeps in the device probe state machine, for example), but those minor issues can be improved in-tree more easily than out of tree and shouldn't gate progress on other fronts. Appologies to reviews if specific items have been overlooked. Submitted by: Ilya Bakulin Reviewed by: emaste, imp, mav, adrian, ian Differential Review: https://reviews.freebsd.org/D4761 merge with first commit, various compile hacks.
This commit is contained in:
parent
7683ad70d3
commit
a94a63f0a6
@ -90,6 +90,8 @@
|
||||
cam
|
||||
ata
|
||||
..
|
||||
mmc
|
||||
..
|
||||
nvme
|
||||
..
|
||||
scsi
|
||||
|
@ -42,7 +42,7 @@ LHDRS= aio.h errno.h fcntl.h linker_set.h poll.h stdatomic.h stdint.h \
|
||||
LDIRS= bsm cam geom net net80211 netgraph netinet netinet6 \
|
||||
netipsec netsmb nfs nfsclient nfsserver sys vm
|
||||
|
||||
LSUBDIRS= cam/ata cam/nvme cam/scsi \
|
||||
LSUBDIRS= cam/ata cam/mmc cam/nvme cam/scsi \
|
||||
dev/acpica dev/agp dev/an dev/bktr dev/ciss dev/filemon dev/firewire \
|
||||
dev/hwpmc dev/hyperv \
|
||||
dev/ic dev/iicbus dev/io dev/lmc dev/mfi dev/mmc dev/nvme \
|
||||
|
@ -38,6 +38,7 @@ MLINKS+= cam.3 cam_open_device.3 \
|
||||
|
||||
.PATH: ${SRCTOP}/sys/cam \
|
||||
${SRCTOP}/sys/cam/ata \
|
||||
${SRCTOP}/sys/cam/mmc \
|
||||
${SRCTOP}/sys/cam/scsi
|
||||
|
||||
CFLAGS+= -I${.CURDIR} -I${SRCTOP}/sys
|
||||
|
36
sys/amd64/conf/MMCCAM
Normal file
36
sys/amd64/conf/MMCCAM
Normal file
@ -0,0 +1,36 @@
|
||||
# MMCCAM is the kernel config for doing MMC on CAM development
|
||||
# and testing on bhyve
|
||||
# $FreeBSD$
|
||||
|
||||
include MINIMAL
|
||||
|
||||
ident MMCCAM
|
||||
|
||||
# Access GPT-formatted and labeled root volume
|
||||
options GEOM_PART_GPT
|
||||
options GEOM_LABEL
|
||||
|
||||
# UART -- for bhyve console
|
||||
device uart
|
||||
|
||||
# kgdb stub
|
||||
device bvmdebug
|
||||
|
||||
# VirtIO support, needed for bhyve
|
||||
device virtio # Generic VirtIO bus (required)
|
||||
device virtio_pci # VirtIO PCI device
|
||||
device vtnet # VirtIO Ethernet device
|
||||
device virtio_blk # VirtIO Block device
|
||||
device virtio_scsi # VirtIO SCSI device
|
||||
device virtio_balloon # VirtIO Memory Balloon device
|
||||
|
||||
# CAM-specific stuff
|
||||
device pass
|
||||
device scbus
|
||||
device da
|
||||
device mmccam
|
||||
|
||||
options MMCCAM
|
||||
# Add CAMDEBUG stuff
|
||||
options CAMDEBUG
|
||||
options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH|CAM_DEBUG_TRACE)
|
@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include "mmcbr_if.h"
|
||||
#include "sdhci_if.h"
|
||||
|
||||
#include "opt_mmccam.h"
|
||||
|
||||
#include "bcm2835_dma.h"
|
||||
#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
|
||||
#include "bcm2835_vcbus.h"
|
||||
@ -253,7 +255,11 @@ bcm_sdhci_attach(device_t dev)
|
||||
bus_generic_probe(dev);
|
||||
bus_generic_attach(dev);
|
||||
|
||||
#ifdef MMCCAM
|
||||
sdhci_cam_start_slot(&sc->sc_slot);
|
||||
#else
|
||||
sdhci_start_slot(&sc->sc_slot);
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
|
||||
|
21
sys/arm/conf/BEAGLEBONE-MMCCAM
Normal file
21
sys/arm/conf/BEAGLEBONE-MMCCAM
Normal file
@ -0,0 +1,21 @@
|
||||
#
|
||||
# BEAGLEBONE-MMCCAM
|
||||
#
|
||||
# Custom kernel for Beaglebone plus MMCCAM as opposed to the prior MMC stack. It is
|
||||
# present to keep it building in tree since it wouldn't work in LINT.
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
include BEAGLEBONE
|
||||
|
||||
# Add CAMDEBUG stuff
|
||||
options CAMDEBUG
|
||||
options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH|CAM_DEBUG_TRACE)
|
||||
|
||||
# pass(4) device
|
||||
device pass
|
||||
device mmccam
|
||||
options MMCCAM
|
||||
|
||||
nodevice mmc
|
||||
nodevice mmcsd
|
@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/rman.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/taskqueue.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
@ -60,6 +62,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <arm/ti/ti_hwmods.h>
|
||||
#include "gpio_if.h"
|
||||
|
||||
#include "opt_mmccam.h"
|
||||
|
||||
struct ti_sdhci_softc {
|
||||
device_t dev;
|
||||
struct sdhci_fdt_gpio * gpio;
|
||||
@ -122,6 +126,11 @@ static struct ofw_compat_data compat_data[] = {
|
||||
#define MMCHS_SD_CAPA_VS30 (1 << 25)
|
||||
#define MMCHS_SD_CAPA_VS33 (1 << 24)
|
||||
|
||||
/* Forward declarations, CAM-relataed */
|
||||
// static void ti_sdhci_cam_poll(struct cam_sim *);
|
||||
// static void ti_sdhci_cam_action(struct cam_sim *, union ccb *);
|
||||
// static int ti_sdhci_cam_settran_settings(struct ti_sdhci_softc *sc, union ccb *);
|
||||
|
||||
static inline uint32_t
|
||||
ti_mmchs_read_4(struct ti_sdhci_softc *sc, bus_size_t off)
|
||||
{
|
||||
@ -241,6 +250,22 @@ ti_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
|
||||
struct ti_sdhci_softc *sc = device_get_softc(dev);
|
||||
uint32_t val32;
|
||||
|
||||
#ifdef MMCCAM
|
||||
uint32_t newval32;
|
||||
if (off == SDHCI_HOST_CONTROL) {
|
||||
val32 = ti_mmchs_read_4(sc, MMCHS_CON);
|
||||
newval32 = val32;
|
||||
if (val & SDHCI_CTRL_8BITBUS) {
|
||||
device_printf(dev, "Custom-enabling 8-bit bus\n");
|
||||
newval32 |= MMCHS_CON_DW8;
|
||||
} else {
|
||||
device_printf(dev, "Custom-disabling 8-bit bus\n");
|
||||
newval32 &= ~MMCHS_CON_DW8;
|
||||
}
|
||||
if (newval32 != val32)
|
||||
ti_mmchs_write_4(sc, MMCHS_CON, newval32);
|
||||
}
|
||||
#endif
|
||||
val32 = RD4(sc, off & ~3);
|
||||
val32 &= ~(0xff << (off & 3) * 8);
|
||||
val32 |= (val << (off & 3) * 8);
|
||||
@ -658,8 +683,11 @@ ti_sdhci_attach(device_t dev)
|
||||
bus_generic_probe(dev);
|
||||
bus_generic_attach(dev);
|
||||
|
||||
#ifdef MMCCAM
|
||||
sdhci_cam_start_slot(&sc->slot);
|
||||
#else
|
||||
sdhci_start_slot(&sc->slot);
|
||||
|
||||
#endif
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
@ -730,4 +758,7 @@ static driver_t ti_sdhci_driver = {
|
||||
DRIVER_MODULE(sdhci_ti, simplebus, ti_sdhci_driver, ti_sdhci_devclass, NULL,
|
||||
NULL);
|
||||
MODULE_DEPEND(sdhci_ti, sdhci, 1, 1, 1);
|
||||
|
||||
#ifndef MMCCAM
|
||||
MMC_DECLARE_BRIDGE(sdhci_ti);
|
||||
#endif
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <cam/scsi/scsi_all.h>
|
||||
#include <cam/ata/ata_all.h>
|
||||
#include <cam/nvme/nvme_all.h>
|
||||
#include <cam/mmc/mmc_all.h>
|
||||
|
||||
/* General allocation length definitions for CCB structures */
|
||||
#define IOCDBLEN CAM_MAX_CDBLEN /* Space for CDB bytes/pointer */
|
||||
@ -208,10 +209,10 @@ typedef enum {
|
||||
XPT_NVME_IO = 0x1c | XPT_FC_DEV_QUEUED,
|
||||
/* Execiute the requestred NVMe I/O operation */
|
||||
|
||||
XPT_MMCSD_IO = 0x1d | XPT_FC_DEV_QUEUED,
|
||||
XPT_MMC_IO = 0x1d | XPT_FC_DEV_QUEUED,
|
||||
/* Placeholder for MMC / SD / SDIO I/O stuff */
|
||||
|
||||
XPT_SCAN_TGT = 0x1E | XPT_FC_QUEUED | XPT_FC_USER_CCB
|
||||
XPT_SCAN_TGT = 0x1e | XPT_FC_QUEUED | XPT_FC_USER_CCB
|
||||
| XPT_FC_XPT_ONLY,
|
||||
/* Scan Target */
|
||||
|
||||
@ -267,6 +268,7 @@ typedef enum {
|
||||
PROTO_SATAPM, /* SATA Port Multiplier */
|
||||
PROTO_SEMB, /* SATA Enclosure Management Bridge */
|
||||
PROTO_NVME, /* NVME */
|
||||
PROTO_MMCSD, /* MMC, SD, SDIO */
|
||||
} cam_proto;
|
||||
|
||||
typedef enum {
|
||||
@ -283,6 +285,7 @@ typedef enum {
|
||||
XPORT_ISCSI, /* iSCSI */
|
||||
XPORT_SRP, /* SCSI RDMA Protocol */
|
||||
XPORT_NVME, /* NVMe over PCIe */
|
||||
XPORT_MMCSD, /* MMC, SD, SDIO card */
|
||||
} cam_xport;
|
||||
|
||||
#define XPORT_IS_NVME(t) ((t) == XPORT_NVME)
|
||||
@ -498,6 +501,7 @@ struct device_match_result {
|
||||
cam_proto protocol;
|
||||
struct scsi_inquiry_data inq_data;
|
||||
struct ata_params ident_data;
|
||||
struct mmc_params mmc_ident_data;
|
||||
dev_result_flags flags;
|
||||
};
|
||||
|
||||
@ -773,6 +777,16 @@ struct ccb_ataio {
|
||||
uint32_t unused;
|
||||
};
|
||||
|
||||
/*
|
||||
* MMC I/O Request CCB used for the XPT_MMC_IO function code.
|
||||
*/
|
||||
struct ccb_mmcio {
|
||||
struct ccb_hdr ccb_h;
|
||||
union ccb *next_ccb; /* Ptr for next CCB for action */
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command stop;
|
||||
};
|
||||
|
||||
struct ccb_accept_tio {
|
||||
struct ccb_hdr ccb_h;
|
||||
cdb_t cdb_io; /* Union for CDB bytes/pointer */
|
||||
@ -1005,7 +1019,28 @@ struct ccb_trans_settings_nvme
|
||||
u_int max_xfer; /* Max transfer size (0 -> unlimited */
|
||||
u_int caps;
|
||||
};
|
||||
|
||||
|
||||
#include <cam/mmc/mmc_bus.h>
|
||||
struct ccb_trans_settings_mmc {
|
||||
struct mmc_ios ios;
|
||||
#define MMC_CLK (1 << 1)
|
||||
#define MMC_VDD (1 << 2)
|
||||
#define MMC_CS (1 << 3)
|
||||
#define MMC_BW (1 << 4)
|
||||
#define MMC_PM (1 << 5)
|
||||
#define MMC_BT (1 << 6)
|
||||
#define MMC_BM (1 << 7)
|
||||
uint32_t ios_valid;
|
||||
/* The folowing is used only for GET_TRAN_SETTINGS */
|
||||
uint32_t host_ocr;
|
||||
int host_f_min;
|
||||
int host_f_max;
|
||||
#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */
|
||||
#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */
|
||||
#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */
|
||||
uint32_t host_caps;
|
||||
};
|
||||
|
||||
/* Get/Set transfer rate/width/disconnection/tag queueing settings */
|
||||
struct ccb_trans_settings {
|
||||
struct ccb_hdr ccb_h;
|
||||
@ -1019,6 +1054,7 @@ struct ccb_trans_settings {
|
||||
struct ccb_trans_settings_ata ata;
|
||||
struct ccb_trans_settings_scsi scsi;
|
||||
struct ccb_trans_settings_nvme nvme;
|
||||
struct ccb_trans_settings_mmc mmc;
|
||||
} proto_specific;
|
||||
union {
|
||||
u_int valid; /* Which fields to honor */
|
||||
@ -1284,6 +1320,7 @@ union ccb {
|
||||
struct ccb_dev_advinfo cdai;
|
||||
struct ccb_async casync;
|
||||
struct ccb_nvmeio nvmeio;
|
||||
struct ccb_mmcio mmcio;
|
||||
};
|
||||
|
||||
#define CCB_CLEAR_ALL_EXCEPT_HDR(ccbp) \
|
||||
@ -1326,6 +1363,13 @@ cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
|
||||
uint8_t *smp_response, int smp_response_len,
|
||||
uint32_t timeout);
|
||||
|
||||
static __inline void
|
||||
cam_fill_mmcio(struct ccb_mmcio *mmcio, uint32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags,
|
||||
uint32_t mmc_opcode, uint32_t mmc_arg, uint32_t mmc_flags,
|
||||
struct mmc_data *mmc_d,
|
||||
uint32_t timeout);
|
||||
|
||||
static __inline void
|
||||
cam_fill_csio(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *, union ccb *),
|
||||
@ -1414,6 +1458,34 @@ cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
|
||||
smpio->smp_response_len = smp_response_len;
|
||||
}
|
||||
|
||||
static __inline void
|
||||
cam_fill_mmcio(struct ccb_mmcio *mmcio, uint32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *, union ccb *), uint32_t flags,
|
||||
uint32_t mmc_opcode, uint32_t mmc_arg, uint32_t mmc_flags,
|
||||
struct mmc_data *mmc_d,
|
||||
uint32_t timeout)
|
||||
{
|
||||
mmcio->ccb_h.func_code = XPT_MMC_IO;
|
||||
mmcio->ccb_h.flags = flags;
|
||||
mmcio->ccb_h.retry_count = retries;
|
||||
mmcio->ccb_h.cbfcnp = cbfcnp;
|
||||
mmcio->ccb_h.timeout = timeout;
|
||||
mmcio->cmd.opcode = mmc_opcode;
|
||||
mmcio->cmd.arg = mmc_arg;
|
||||
mmcio->cmd.flags = mmc_flags;
|
||||
mmcio->stop.opcode = 0;
|
||||
mmcio->stop.arg = 0;
|
||||
mmcio->stop.flags = 0;
|
||||
if (mmc_d != NULL) {
|
||||
mmcio->cmd.data = mmc_d;
|
||||
} else
|
||||
mmcio->cmd.data = NULL;
|
||||
mmcio->cmd.resp[0] = 0;
|
||||
mmcio->cmd.resp[1] = 0;
|
||||
mmcio->cmd.resp[2] = 0;
|
||||
mmcio->cmd.resp[3] = 0;
|
||||
}
|
||||
|
||||
static __inline void
|
||||
cam_set_ccbstatus(union ccb *ccb, cam_status status)
|
||||
{
|
||||
|
@ -827,6 +827,18 @@ cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo,
|
||||
dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK;
|
||||
numbufs = 1;
|
||||
break;
|
||||
case XPT_MMC_IO:
|
||||
if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE)
|
||||
return(0);
|
||||
/* Two mappings: one for cmd->data and one for cmd->data->data */
|
||||
data_ptrs[0] = (unsigned char **)&ccb->mmcio.cmd.data;
|
||||
lengths[0] = sizeof(struct mmc_data *);
|
||||
dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK;
|
||||
data_ptrs[1] = (unsigned char **)&ccb->mmcio.cmd.data->data;
|
||||
lengths[1] = ccb->mmcio.cmd.data->len;
|
||||
dirs[1] = ccb->ccb_h.flags & CAM_DIR_MASK;
|
||||
numbufs = 2;
|
||||
break;
|
||||
case XPT_SMP_IO:
|
||||
data_ptrs[0] = &ccb->smpio.smp_request;
|
||||
lengths[0] = ccb->smpio.smp_request_len;
|
||||
|
@ -329,7 +329,6 @@ static xpt_devicefunc_t xptsetasyncfunc;
|
||||
static xpt_busfunc_t xptsetasyncbusfunc;
|
||||
static cam_status xptregister(struct cam_periph *periph,
|
||||
void *arg);
|
||||
static const char * xpt_action_name(uint32_t action);
|
||||
static __inline int device_is_queued(struct cam_ed *device);
|
||||
|
||||
static __inline int
|
||||
@ -412,7 +411,7 @@ xptioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
xptdoioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
|
||||
{
|
||||
@ -820,6 +819,8 @@ xpt_scanner_thread(void *dummy)
|
||||
TAILQ_REMOVE(&xsoftc.ccb_scanq, &ccb->ccb_h, sim_links.tqe);
|
||||
xpt_unlock_buses();
|
||||
|
||||
printf("xpt_scanner_thread is firing on path ");
|
||||
xpt_print_path(ccb->ccb_h.path);printf("\n");
|
||||
/*
|
||||
* Since lock can be dropped inside and path freed
|
||||
* by completion callback even before return here,
|
||||
@ -1503,7 +1504,7 @@ xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns,
|
||||
|
||||
cur_pattern = &patterns[i].pattern.device_pattern;
|
||||
|
||||
/* Error out if mutually exclusive options are specified. */
|
||||
/* Error out if mutually exclusive options are specified. */
|
||||
if ((cur_pattern->flags & (DEV_MATCH_INQUIRY|DEV_MATCH_DEVID))
|
||||
== (DEV_MATCH_INQUIRY|DEV_MATCH_DEVID))
|
||||
return(DM_RET_ERROR);
|
||||
@ -1905,6 +1906,9 @@ xptedtdevicefunc(struct cam_ed *device, void *arg)
|
||||
bcopy(&device->ident_data,
|
||||
&cdm->matches[j].result.device_result.ident_data,
|
||||
sizeof(struct ata_params));
|
||||
bcopy(&device->mmc_ident_data,
|
||||
&cdm->matches[j].result.device_result.mmc_ident_data,
|
||||
sizeof(struct mmc_params));
|
||||
|
||||
/* Let the user know whether this device is unconfigured */
|
||||
if (device->flags & CAM_DEV_UNCONFIGURED)
|
||||
@ -2690,6 +2694,8 @@ xpt_action_default(union ccb *start_ccb)
|
||||
if (start_ccb->ccb_h.func_code == XPT_NVME_IO)
|
||||
start_ccb->nvmeio.resid = 0;
|
||||
/* FALLTHROUGH */
|
||||
case XPT_MMC_IO:
|
||||
/* XXX just like nmve_io? */
|
||||
case XPT_RESET_DEV:
|
||||
case XPT_ENG_EXEC:
|
||||
case XPT_SMP_IO:
|
||||
@ -2801,11 +2807,12 @@ xpt_action_default(union ccb *start_ccb)
|
||||
mtx_lock(mtx);
|
||||
else
|
||||
mtx = NULL;
|
||||
|
||||
CAM_DEBUG(path, CAM_DEBUG_TRACE,
|
||||
("sim->sim_action: func=%#x\n", start_ccb->ccb_h.func_code));
|
||||
("Calling sim->sim_action(): func=%#x\n", start_ccb->ccb_h.func_code));
|
||||
(*(sim->sim_action))(sim, start_ccb);
|
||||
CAM_DEBUG(path, CAM_DEBUG_TRACE,
|
||||
("sim->sim_action: status=%#x\n", start_ccb->ccb_h.status));
|
||||
("sim->sim_action returned: status=%#x\n", start_ccb->ccb_h.status));
|
||||
if (mtx)
|
||||
mtx_unlock(mtx);
|
||||
break;
|
||||
@ -5540,7 +5547,7 @@ static struct kv map[] = {
|
||||
{ XPT_GET_SIM_KNOB, "XPT_GET_SIM_KNOB" },
|
||||
{ XPT_SET_SIM_KNOB, "XPT_SET_SIM_KNOB" },
|
||||
{ XPT_NVME_IO, "XPT_NVME_IO" },
|
||||
{ XPT_MMCSD_IO, "XPT_MMCSD_IO" },
|
||||
{ XPT_MMC_IO, "XPT_MMC_IO" },
|
||||
{ XPT_SMP_IO, "XPT_SMP_IO" },
|
||||
{ XPT_SCAN_TGT, "XPT_SCAN_TGT" },
|
||||
{ XPT_ENG_INQ, "XPT_ENG_INQ" },
|
||||
@ -5556,7 +5563,7 @@ static struct kv map[] = {
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static const char *
|
||||
const char *
|
||||
xpt_action_name(uint32_t action)
|
||||
{
|
||||
static char buffer[32]; /* Only for unknown messages -- racy */
|
||||
|
@ -141,6 +141,8 @@ void xpt_copy_path(struct cam_path *new_path,
|
||||
|
||||
void xpt_release_path(struct cam_path *path);
|
||||
|
||||
const char * xpt_action_name(uint32_t action);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* _CAM_CAM_XPT_H */
|
||||
|
@ -125,6 +125,7 @@ struct cam_ed {
|
||||
uint32_t rcap_len;
|
||||
uint8_t *rcap_buf;
|
||||
struct ata_params ident_data;
|
||||
struct mmc_params mmc_ident_data;
|
||||
u_int8_t inq_flags; /*
|
||||
* Current settings for inquiry flags.
|
||||
* This allows us to override settings
|
||||
|
94
sys/cam/mmc/mmc.h
Normal file
94
sys/cam/mmc/mmc.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*-
|
||||
* Copyright (c) 2014-2016 Ilya Bakulin. 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.
|
||||
*
|
||||
* Portions of this software may have been developed with reference to
|
||||
* the SD Simplified Specification. The following disclaimer may apply:
|
||||
*
|
||||
* The following conditions apply to the release of the simplified
|
||||
* specification ("Simplified Specification") by the SD Card Association and
|
||||
* the SD Group. The Simplified Specification is a subset of the complete SD
|
||||
* Specification which is owned by the SD Card Association and the SD
|
||||
* Group. This Simplified Specification is provided on a non-confidential
|
||||
* basis subject to the disclaimers below. Any implementation of the
|
||||
* Simplified Specification may require a license from the SD Card
|
||||
* Association, SD Group, SD-3C LLC or other third parties.
|
||||
*
|
||||
* Disclaimers:
|
||||
*
|
||||
* The information contained in the Simplified Specification is presented only
|
||||
* as a standard specification for SD Cards and SD Host/Ancillary products and
|
||||
* is provided "AS-IS" without any representations or warranties of any
|
||||
* kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
|
||||
* Card Association for any damages, any infringements of patents or other
|
||||
* right of the SD Group, SD-3C LLC, the SD Card Association or any third
|
||||
* parties, which may result from its use. No license is granted by
|
||||
* implication, estoppel or otherwise under any patent or other rights of the
|
||||
* SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
|
||||
* herein shall be construed as an obligation by the SD Group, the SD-3C LLC
|
||||
* or the SD Card Association to disclose or distribute any technical
|
||||
* information, know-how or other confidential information to any third party.
|
||||
*
|
||||
* Inspired coded in sys/dev/mmc. Thanks to Warner Losh <imp@FreeBSD.org>,
|
||||
* Bernd Walter <tisco@FreeBSD.org>, and other authors.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef CAM_MMC_H
|
||||
#define CAM_MMC_H
|
||||
|
||||
#include <dev/mmc/mmcreg.h>
|
||||
/*
|
||||
* This structure describes an MMC/SD card
|
||||
*/
|
||||
struct mmc_params {
|
||||
u_int8_t model[40]; /* Card model */
|
||||
|
||||
/* Card OCR */
|
||||
uint32_t card_ocr;
|
||||
|
||||
/* OCR of the IO portion of the card */
|
||||
uint32_t io_ocr;
|
||||
|
||||
/* Card CID -- raw and parsed */
|
||||
uint32_t card_cid[4];
|
||||
struct mmc_cid cid;
|
||||
|
||||
/* Card CSD -- raw */
|
||||
uint32_t card_csd[4];
|
||||
|
||||
/* Card RCA */
|
||||
uint16_t card_rca;
|
||||
|
||||
/* What kind of card is it */
|
||||
uint32_t card_features;
|
||||
#define CARD_FEATURE_MEMORY 0x1
|
||||
#define CARD_FEATURE_SDHC 0x1 << 1
|
||||
#define CARD_FEATURE_SDIO 0x1 << 2
|
||||
#define CARD_FEATURE_SD20 0x1 << 3
|
||||
#define CARD_FEATURE_MMC 0x1 << 4
|
||||
|
||||
uint8_t sdio_func_count;
|
||||
} __packed;
|
||||
|
||||
#endif
|
70
sys/cam/mmc/mmc_all.h
Normal file
70
sys/cam/mmc/mmc_all.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*-
|
||||
* Copyright (c) 2014-2016 Ilya Bakulin. 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.
|
||||
*
|
||||
* Portions of this software may have been developed with reference to
|
||||
* the SD Simplified Specification. The following disclaimer may apply:
|
||||
*
|
||||
* The following conditions apply to the release of the simplified
|
||||
* specification ("Simplified Specification") by the SD Card Association and
|
||||
* the SD Group. The Simplified Specification is a subset of the complete SD
|
||||
* Specification which is owned by the SD Card Association and the SD
|
||||
* Group. This Simplified Specification is provided on a non-confidential
|
||||
* basis subject to the disclaimers below. Any implementation of the
|
||||
* Simplified Specification may require a license from the SD Card
|
||||
* Association, SD Group, SD-3C LLC or other third parties.
|
||||
*
|
||||
* Disclaimers:
|
||||
*
|
||||
* The information contained in the Simplified Specification is presented only
|
||||
* as a standard specification for SD Cards and SD Host/Ancillary products and
|
||||
* is provided "AS-IS" without any representations or warranties of any
|
||||
* kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
|
||||
* Card Association for any damages, any infringements of patents or other
|
||||
* right of the SD Group, SD-3C LLC, the SD Card Association or any third
|
||||
* parties, which may result from its use. No license is granted by
|
||||
* implication, estoppel or otherwise under any patent or other rights of the
|
||||
* SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
|
||||
* herein shall be construed as an obligation by the SD Group, the SD-3C LLC
|
||||
* or the SD Card Association to disclose or distribute any technical
|
||||
* information, know-how or other confidential information to any third party.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* MMC function that should be visible to the CAM subsystem
|
||||
* and are somehow useful should be declared here
|
||||
*
|
||||
* Like in other *_all.h, it's also a nice place to include
|
||||
* some other transport-specific headers.
|
||||
*/
|
||||
|
||||
#ifndef CAM_MMC_ALL_H
|
||||
#define CAM_MMC_ALL_H
|
||||
|
||||
#include <cam/mmc/mmc.h>
|
||||
#include <dev/mmc/mmcreg.h>
|
||||
|
||||
void mmc_print_ident(struct mmc_params *ident_data);
|
||||
|
||||
#endif
|
5
sys/cam/mmc/mmc_bus.h
Normal file
5
sys/cam/mmc/mmc_bus.h
Normal file
@ -0,0 +1,5 @@
|
||||
/*
|
||||
* This file is in the public domain.
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <dev/mmc/bridge.h>
|
1432
sys/cam/mmc/mmc_da.c
Normal file
1432
sys/cam/mmc/mmc_da.c
Normal file
File diff suppressed because it is too large
Load Diff
126
sys/cam/mmc/mmc_sdio.c
Normal file
126
sys/cam/mmc/mmc_sdio.c
Normal file
@ -0,0 +1,126 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 Ilya Bakulin <ilya@bakulin.de>
|
||||
* 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,
|
||||
* without modification, immediately at the beginning of the file.
|
||||
* 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>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/interrupt.h>
|
||||
#include <sys/sbuf.h>
|
||||
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/condvar.h>
|
||||
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <cam/cam_queue.h>
|
||||
#include <cam/cam_periph.h>
|
||||
#include <cam/cam_sim.h>
|
||||
#include <cam/cam_xpt.h>
|
||||
#include <cam/cam_xpt_sim.h>
|
||||
#include <cam/cam_xpt_periph.h>
|
||||
#include <cam/cam_xpt_internal.h>
|
||||
#include <cam/cam_debug.h>
|
||||
|
||||
#include <cam/mmc/mmc.h>
|
||||
#include <cam/mmc/mmc_bus.h>
|
||||
#include <cam/mmc/mmc_sdio.h>
|
||||
|
||||
#include <machine/stdarg.h> /* for xpt_print below */
|
||||
#include <machine/_inttypes.h> /* for PRIu64 */
|
||||
#include "opt_cam.h"
|
||||
|
||||
|
||||
void
|
||||
sdio_print_stupid_message(struct cam_periph *periph) {
|
||||
|
||||
CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
|
||||
("%s\n", __func__));
|
||||
}
|
||||
|
||||
/*
|
||||
* f - function to read from / write to
|
||||
* wr - is write
|
||||
* adr - address to r/w
|
||||
* data - actual data to write
|
||||
*/
|
||||
void sdio_fill_mmcio_rw_direct(union ccb *ccb, uint8_t f, uint8_t wr, uint32_t adr, uint8_t *data) {
|
||||
struct ccb_mmcio *mmcio;
|
||||
|
||||
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
|
||||
("sdio_fill_mmcio(f=%d, wr=%d, adr=%02x, data=%02x)\n", f, wr, adr, (data == NULL ? 0 : *data)));
|
||||
mmcio = &ccb->mmcio;
|
||||
|
||||
mmcio->cmd.opcode = SD_IO_RW_DIRECT;
|
||||
mmcio->cmd.arg = SD_IO_RW_FUNC(f) | SD_IO_RW_ADR(adr);
|
||||
if (wr)
|
||||
mmcio->cmd.arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
|
||||
mmcio->cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
|
||||
mmcio->cmd.data->len = 0;
|
||||
}
|
||||
|
||||
uint8_t sdio_parse_mmcio_rw_direct(union ccb *ccb, uint8_t *data) {
|
||||
struct ccb_mmcio *mmcio;
|
||||
|
||||
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
|
||||
("sdio_parse_mmcio(datap=%p)\n", data));
|
||||
mmcio = &ccb->mmcio;
|
||||
|
||||
if (mmcio->cmd.error)
|
||||
return (mmcio->cmd.error);
|
||||
if (mmcio->cmd.resp[0] & R5_COM_CRC_ERROR)
|
||||
return (MMC_ERR_BADCRC);
|
||||
if (mmcio->cmd.resp[0] & (R5_ILLEGAL_COMMAND | R5_FUNCTION_NUMBER))
|
||||
return (MMC_ERR_INVALID);
|
||||
if (mmcio->cmd.resp[0] & R5_OUT_OF_RANGE)
|
||||
return (MMC_ERR_FAILED);
|
||||
|
||||
/* Just for information... */
|
||||
if (R5_IO_CURRENT_STATE(mmcio->cmd.resp[0]) != 1)
|
||||
printf("!!! SDIO state %d\n", R5_IO_CURRENT_STATE(mmcio->cmd.resp[0]));
|
||||
|
||||
if (mmcio->cmd.resp[0] & R5_ERROR)
|
||||
printf("An error was detected!\n");
|
||||
|
||||
if (mmcio->cmd.resp[0] & R5_COM_CRC_ERROR)
|
||||
printf("A CRC error was detected!\n");
|
||||
|
||||
if (data != NULL)
|
||||
*data = (uint8_t) (mmcio->cmd.resp[0] & 0xff);
|
||||
return (MMC_ERR_NONE);
|
||||
|
||||
}
|
64
sys/cam/mmc/mmc_sdio.h
Normal file
64
sys/cam/mmc/mmc_sdio.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Ilya Bakulin. 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.
|
||||
*
|
||||
* Portions of this software may have been developed with reference to
|
||||
* the SD Simplified Specification. The following disclaimer may apply:
|
||||
*
|
||||
* The following conditions apply to the release of the simplified
|
||||
* specification ("Simplified Specification") by the SD Card Association and
|
||||
* the SD Group. The Simplified Specification is a subset of the complete SD
|
||||
* Specification which is owned by the SD Card Association and the SD
|
||||
* Group. This Simplified Specification is provided on a non-confidential
|
||||
* basis subject to the disclaimers below. Any implementation of the
|
||||
* Simplified Specification may require a license from the SD Card
|
||||
* Association, SD Group, SD-3C LLC or other third parties.
|
||||
*
|
||||
* Disclaimers:
|
||||
*
|
||||
* The information contained in the Simplified Specification is presented only
|
||||
* as a standard specification for SD Cards and SD Host/Ancillary products and
|
||||
* is provided "AS-IS" without any representations or warranties of any
|
||||
* kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
|
||||
* Card Association for any damages, any infringements of patents or other
|
||||
* right of the SD Group, SD-3C LLC, the SD Card Association or any third
|
||||
* parties, which may result from its use. No license is granted by
|
||||
* implication, estoppel or otherwise under any patent or other rights of the
|
||||
* SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
|
||||
* herein shall be construed as an obligation by the SD Group, the SD-3C LLC
|
||||
* or the SD Card Association to disclose or distribute any technical
|
||||
* information, know-how or other confidential information to any third party.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Various SDIO-related stuff
|
||||
*/
|
||||
|
||||
#ifndef CAM_MMC_SDIO_H
|
||||
#define CAM_MMC_SDIO_H
|
||||
|
||||
void sdio_print_stupid_message(struct cam_periph *periph);
|
||||
void sdio_fill_mmcio_rw_direct(union ccb *ccb, uint8_t f, uint8_t wr, uint32_t adr, uint8_t *data);
|
||||
uint8_t sdio_parse_mmcio_rw_direct(union ccb *ccb, uint8_t *data);
|
||||
#endif
|
1077
sys/cam/mmc/mmc_xpt.c
Normal file
1077
sys/cam/mmc/mmc_xpt.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -2202,7 +2202,7 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
|
||||
*/
|
||||
fc = ccb->ccb_h.func_code;
|
||||
if ((fc == XPT_SCSI_IO) || (fc == XPT_ATA_IO) || (fc == XPT_SMP_IO)
|
||||
|| (fc == XPT_DEV_MATCH) || (fc == XPT_DEV_ADVINFO)) {
|
||||
|| (fc == XPT_DEV_MATCH) || (fc == XPT_DEV_ADVINFO) || (fc == XPT_MMC_IO)) {
|
||||
bzero(&mapinfo, sizeof(mapinfo));
|
||||
|
||||
/*
|
||||
|
@ -110,6 +110,9 @@ cam/ctl/ctl_tpc_local.c optional ctl
|
||||
cam/ctl/ctl_error.c optional ctl
|
||||
cam/ctl/ctl_util.c optional ctl
|
||||
cam/ctl/scsi_ctl.c optional ctl
|
||||
cam/mmc/mmc_xpt.c optional scbus mmccam
|
||||
cam/mmc/mmc_da.c optional scbus mmccam da
|
||||
cam/mmc/mmc_sdio.c optional scbus mmccam
|
||||
cam/scsi/scsi_da.c optional da
|
||||
cam/scsi/scsi_low.c optional ncv | nsp | stg
|
||||
cam/scsi/scsi_pass.c optional pass
|
||||
@ -2240,11 +2243,12 @@ dev/mlx/mlx.c optional mlx
|
||||
dev/mlx/mlx_disk.c optional mlx
|
||||
dev/mlx/mlx_pci.c optional mlx pci
|
||||
dev/mly/mly.c optional mly
|
||||
dev/mmc/mmc_subr.c optional mmc | mmcsd
|
||||
dev/mmc/mmc.c optional mmc
|
||||
dev/mmc/mmc_subr.c optional mmc | mmcsd !mmccam
|
||||
dev/mmc/mmc.c optional mmc !mmccam
|
||||
dev/mmc/mmcbr_if.m standard
|
||||
dev/mmc/mmcbus_if.m standard
|
||||
dev/mmc/mmcsd.c optional mmcsd
|
||||
dev/mmc/mmcsd.c optional mmcsd !mmccam
|
||||
dev/mmcnull/mmcnull.c optional mmcnull
|
||||
dev/mn/if_mn.c optional mn pci
|
||||
dev/mpr/mpr.c optional mpr
|
||||
dev/mpr/mpr_config.c optional mpr
|
||||
|
@ -996,5 +996,7 @@ UINPUT_DEBUG opt_evdev.h
|
||||
# Hyper-V network driver
|
||||
HN_DEBUG opt_hn.h
|
||||
|
||||
# CAM-based MMC stack
|
||||
MMCCAM
|
||||
# Encrypted kernel crash dumps
|
||||
EKCD opt_ekcd.h
|
||||
|
@ -174,6 +174,7 @@ struct mmc_host {
|
||||
struct mmc_ios ios; /* Current state of the host */
|
||||
};
|
||||
|
||||
#ifdef _KERNEL
|
||||
extern driver_t mmc_driver;
|
||||
extern devclass_t mmc_devclass;
|
||||
|
||||
@ -184,5 +185,6 @@ extern devclass_t mmc_devclass;
|
||||
MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION);
|
||||
#define MMC_DEPEND(name) \
|
||||
MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION);
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* DEV_MMC_BRIDGE_H */
|
||||
|
@ -56,7 +56,6 @@
|
||||
#define DEV_MMC_MMCBRVAR_H
|
||||
|
||||
#include <dev/mmc/mmcreg.h>
|
||||
|
||||
#include "mmcbr_if.h"
|
||||
|
||||
enum mmcbr_device_ivars {
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
|
||||
* Copyright (c) 2017 Marius Strobl <marius@FreeBSD.org>
|
||||
* Copyright (c) 2015-2016 Ilya Bakulin <kibab@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@ -156,6 +157,34 @@ struct mmc_command {
|
||||
#define R1_STATE_PRG 7
|
||||
#define R1_STATE_DIS 8
|
||||
|
||||
/* R4 response (SDIO) */
|
||||
#define R4_IO_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x3)
|
||||
#define R4_IO_MEM_PRESENT (0x1<<27)
|
||||
#define R4_IO_OCR_MASK 0x00fffff0
|
||||
|
||||
/*
|
||||
* R5 responses
|
||||
*
|
||||
* Types (per SD 2.0 standard)
|
||||
*e : error bit
|
||||
*s : status bit
|
||||
*r : detected and set for the actual command response
|
||||
*x : Detected and set during command execution. The host can get
|
||||
* the status by issuing a command with R1 response.
|
||||
*
|
||||
* Clear Condition (per SD 2.0 standard)
|
||||
*a : according to the card current state.
|
||||
*b : always related to the previous command. reception of a valid
|
||||
* command will clear it (with a delay of one command).
|
||||
*c : clear by read
|
||||
*/
|
||||
#define R5_COM_CRC_ERROR (1u << 15)/* er, b */
|
||||
#define R5_ILLEGAL_COMMAND (1u << 14)/* er, b */
|
||||
#define R5_IO_CURRENT_STATE_MASK (3u << 12)/* s, b */
|
||||
#define R5_IO_CURRENT_STATE(x) (((x) & R5_IO_CURRENT_STATE_MASK) >> 12)
|
||||
#define R5_ERROR (1u << 11)/* erx, c */
|
||||
#define R5_FUNCTION_NUMBER (1u << 9)/* er, c */
|
||||
#define R5_OUT_OF_RANGE (1u << 8)/* er, c */
|
||||
struct mmc_data {
|
||||
size_t len; /* size of the data */
|
||||
size_t xfer_len;
|
||||
@ -187,6 +216,7 @@ struct mmc_request {
|
||||
#define SD_SEND_RELATIVE_ADDR 3
|
||||
#define MMC_SET_DSR 4
|
||||
#define MMC_SLEEP_AWAKE 5
|
||||
#define IO_SEND_OP_COND 5
|
||||
#define MMC_SWITCH_FUNC 6
|
||||
#define MMC_SWITCH_FUNC_CMDS 0
|
||||
#define MMC_SWITCH_FUNC_SET 1
|
||||
@ -269,7 +299,31 @@ struct mmc_request {
|
||||
|
||||
/* Class 9: I/O cards (sd) */
|
||||
#define SD_IO_RW_DIRECT 52
|
||||
/* 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)
|
||||
|
||||
#define SD_IO_RW_EXTENDED 53
|
||||
/* 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? */
|
||||
|
||||
/* Class 10: Switch function commands */
|
||||
#define SD_SWITCH_FUNC 6
|
||||
@ -440,6 +494,54 @@ struct mmc_request {
|
||||
/* Specifications require 400 kHz max. during ID phase. */
|
||||
#define SD_MMC_CARD_ID_FREQUENCY 400000
|
||||
|
||||
/*
|
||||
* SDIO Direct & Extended I/O
|
||||
*/
|
||||
#define SD_IO_RW_WR (1u << 31)
|
||||
#define SD_IO_RW_FUNC(x) (((x) & 0x7) << 28)
|
||||
#define SD_IO_RW_RAW (1u << 27)
|
||||
#define SD_IO_RW_INCR (1u << 26)
|
||||
#define SD_IO_RW_ADR(x) (((x) & 0x1FFFF) << 9)
|
||||
#define SD_IO_RW_DAT(x) (((x) & 0xFF) << 0)
|
||||
#define SD_IO_RW_LEN(x) (((x) & 0xFF) << 0)
|
||||
|
||||
#define SD_IOE_RW_LEN(x) (((x) & 0x1FF) << 0)
|
||||
#define SD_IOE_RW_BLK (1u << 27)
|
||||
|
||||
/* Card Common Control Registers (CCCR) */
|
||||
#define SD_IO_CCCR_START 0x00000
|
||||
#define SD_IO_CCCR_SIZE 0x100
|
||||
#define SD_IO_CCCR_FN_ENABLE 0x02
|
||||
#define SD_IO_CCCR_FN_READY 0x03
|
||||
#define SD_IO_CCCR_INT_ENABLE 0x04
|
||||
#define SD_IO_CCCR_INT_PENDING 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_CARDCAP 0x08
|
||||
#define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */
|
||||
|
||||
/* 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
|
||||
#define SD_IO_CISTPL_END 0xff
|
||||
|
||||
/* CISTPL_FUNCID codes */
|
||||
/* OpenBSD incorrectly defines 0x0c as FUNCTION_WLAN */
|
||||
/* #define SDMMC_FUNCTION_WLAN 0x0c */
|
||||
|
||||
/* OCR bits */
|
||||
|
||||
/*
|
||||
|
@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
|
||||
* This supports both eSDHC (earlier SoCs) and uSDHC (more recent SoCs).
|
||||
*/
|
||||
|
||||
#include "opt_mmccam.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/types.h>
|
||||
@ -911,7 +913,11 @@ fsl_sdhci_attach(device_t dev)
|
||||
bus_generic_probe(dev);
|
||||
bus_generic_attach(dev);
|
||||
|
||||
#ifdef MMCCAM
|
||||
sdhci_cam_start_slot(&sc->slot);
|
||||
#else
|
||||
sdhci_start_slot(&sc->slot);
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
|
||||
@ -988,4 +994,7 @@ static driver_t fsl_sdhci_driver = {
|
||||
DRIVER_MODULE(sdhci_fsl, simplebus, fsl_sdhci_driver, fsl_sdhci_devclass,
|
||||
NULL, NULL);
|
||||
MODULE_DEPEND(sdhci_fsl, sdhci, 1, 1, 1);
|
||||
|
||||
#ifndef MMCCAM
|
||||
MMC_DECLARE_BRIDGE(sdhci_fsl);
|
||||
#endif
|
||||
|
@ -48,13 +48,21 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/mmc/mmcreg.h>
|
||||
#include <dev/mmc/mmcbrvar.h>
|
||||
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <cam/cam_debug.h>
|
||||
#include <cam/cam_sim.h>
|
||||
#include <cam/cam_xpt_sim.h>
|
||||
|
||||
#include "mmcbr_if.h"
|
||||
#include "sdhci.h"
|
||||
#include "sdhci_if.h"
|
||||
|
||||
#include "opt_mmccam.h"
|
||||
|
||||
SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver");
|
||||
|
||||
static int sdhci_debug;
|
||||
static int sdhci_debug = 0;
|
||||
SYSCTL_INT(_hw_sdhci, OID_AUTO, debug, CTLFLAG_RWTUN, &sdhci_debug, 0,
|
||||
"Debug level");
|
||||
u_int sdhci_quirk_clear = 0;
|
||||
@ -83,6 +91,14 @@ static void sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data);
|
||||
static void sdhci_card_poll(void *);
|
||||
static void sdhci_card_task(void *, int);
|
||||
|
||||
/* CAM-related */
|
||||
int sdhci_cam_get_possible_host_clock(struct sdhci_slot *slot, int proposed_clock);
|
||||
static int sdhci_cam_update_ios(struct sdhci_slot *slot);
|
||||
static int sdhci_cam_request(struct sdhci_slot *slot, union ccb *ccb);
|
||||
static void sdhci_cam_action(struct cam_sim *sim, union ccb *ccb);
|
||||
static void sdhci_cam_poll(struct cam_sim *sim);
|
||||
static int sdhci_cam_settran_settings(struct sdhci_slot *slot, union ccb *ccb);
|
||||
|
||||
/* helper routines */
|
||||
static void sdhci_dumpregs(struct sdhci_slot *slot);
|
||||
static int slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
|
||||
@ -252,7 +268,7 @@ sdhci_init(struct sdhci_slot *slot)
|
||||
SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
|
||||
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
|
||||
SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
|
||||
SDHCI_INT_ACMD12ERR;
|
||||
SDHCI_INT_ACMD12ERR | SDHCI_INT_CARD_INT;
|
||||
|
||||
if (!(slot->quirks & SDHCI_QUIRK_POLL_CARD_PRESENT) &&
|
||||
!(slot->opt & SDHCI_NON_REMOVABLE)) {
|
||||
@ -531,25 +547,87 @@ sdhci_card_task(void *arg, int pending __unused)
|
||||
|
||||
SDHCI_LOCK(slot);
|
||||
if (SDHCI_GET_CARD_PRESENT(slot->bus, slot)) {
|
||||
#ifdef MMCCAM
|
||||
if (slot->card_present == 0) {
|
||||
#else
|
||||
if (slot->dev == NULL) {
|
||||
#endif
|
||||
/* If card is present - attach mmc bus. */
|
||||
if (bootverbose || sdhci_debug)
|
||||
slot_printf(slot, "Card inserted\n");
|
||||
#ifdef MMCCAM
|
||||
slot->card_present = 1;
|
||||
union ccb *ccb;
|
||||
uint32_t pathid;
|
||||
pathid = cam_sim_path(slot->sim);
|
||||
ccb = xpt_alloc_ccb_nowait();
|
||||
if (ccb == NULL) {
|
||||
slot_printf(slot, "Unable to alloc CCB for rescan\n");
|
||||
SDHCI_UNLOCK(slot);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We create a rescan request for BUS:0:0, since the card
|
||||
* will be at lun 0.
|
||||
*/
|
||||
if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid,
|
||||
/* target */ 0, /* lun */ 0) != CAM_REQ_CMP) {
|
||||
slot_printf(slot, "Unable to create path for rescan\n");
|
||||
SDHCI_UNLOCK(slot);
|
||||
xpt_free_ccb(ccb);
|
||||
return;
|
||||
}
|
||||
SDHCI_UNLOCK(slot);
|
||||
xpt_rescan(ccb);
|
||||
#else
|
||||
slot->dev = device_add_child(slot->bus, "mmc", -1);
|
||||
device_set_ivars(slot->dev, slot);
|
||||
SDHCI_UNLOCK(slot);
|
||||
device_probe_and_attach(slot->dev);
|
||||
#endif
|
||||
} else
|
||||
SDHCI_UNLOCK(slot);
|
||||
} else {
|
||||
#ifdef MMCCAM
|
||||
if (slot->card_present == 1) {
|
||||
#else
|
||||
if (slot->dev != NULL) {
|
||||
#endif
|
||||
/* If no card present - detach mmc bus. */
|
||||
if (bootverbose || sdhci_debug)
|
||||
slot_printf(slot, "Card removed\n");
|
||||
d = slot->dev;
|
||||
slot->dev = NULL;
|
||||
#ifdef MMCCAM
|
||||
slot->card_present = 0;
|
||||
union ccb *ccb;
|
||||
uint32_t pathid;
|
||||
pathid = cam_sim_path(slot->sim);
|
||||
ccb = xpt_alloc_ccb_nowait();
|
||||
if (ccb == NULL) {
|
||||
slot_printf(slot, "Unable to alloc CCB for rescan\n");
|
||||
SDHCI_UNLOCK(slot);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We create a rescan request for BUS:0:0, since the card
|
||||
* will be at lun 0.
|
||||
*/
|
||||
if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid,
|
||||
/* target */ 0, /* lun */ 0) != CAM_REQ_CMP) {
|
||||
slot_printf(slot, "Unable to create path for rescan\n");
|
||||
SDHCI_UNLOCK(slot);
|
||||
xpt_free_ccb(ccb);
|
||||
return;
|
||||
}
|
||||
SDHCI_UNLOCK(slot);
|
||||
xpt_rescan(ccb);
|
||||
#else
|
||||
SDHCI_UNLOCK(slot);
|
||||
device_delete_child(slot->bus, d);
|
||||
#endif
|
||||
} else
|
||||
SDHCI_UNLOCK(slot);
|
||||
}
|
||||
@ -571,7 +649,11 @@ sdhci_handle_card_present_locked(struct sdhci_slot *slot, bool is_present)
|
||||
* because once power is removed, a full card re-init is needed, and
|
||||
* that happens by deleting and recreating the child device.
|
||||
*/
|
||||
#ifdef MMCCAM
|
||||
was_present = slot->card_present;
|
||||
#else
|
||||
was_present = slot->dev != NULL;
|
||||
#endif
|
||||
if (!was_present && is_present) {
|
||||
taskqueue_enqueue_timeout(taskqueue_swi_giant,
|
||||
&slot->card_delayed_task, -SDHCI_INSERT_DELAY_TICKS);
|
||||
@ -607,6 +689,7 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
|
||||
int err;
|
||||
|
||||
SDHCI_LOCK_INIT(slot);
|
||||
|
||||
slot->num = num;
|
||||
slot->bus = dev;
|
||||
|
||||
@ -938,6 +1021,10 @@ sdhci_generic_update_ios(device_t brdev, device_t reqdev)
|
||||
struct sdhci_slot *slot = device_get_ivars(reqdev);
|
||||
struct mmc_ios *ios = &slot->host.ios;
|
||||
|
||||
device_printf(brdev, "This is a bridge device\n");
|
||||
device_printf(reqdev, "This is a request device\n");
|
||||
|
||||
slot_printf(slot, " <--- The locking slot is this\n");
|
||||
SDHCI_LOCK(slot);
|
||||
/* Do full reset on bus power down to clear from any state. */
|
||||
if (ios->power_mode == power_off) {
|
||||
@ -1029,8 +1116,31 @@ sdhci_generic_switch_vccq(device_t brdev __unused, device_t reqdev)
|
||||
return (err);
|
||||
}
|
||||
|
||||
#ifdef MMCCAM
|
||||
static void
|
||||
sdhci_req_done(struct sdhci_slot *slot)
|
||||
{
|
||||
union ccb *ccb;
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "sdhci_req_done()\n");
|
||||
if (slot->ccb != NULL && slot->curcmd != NULL) {
|
||||
callout_stop(&slot->timeout_callout);
|
||||
ccb = slot->ccb;
|
||||
slot->ccb = NULL;
|
||||
slot->curcmd = NULL;
|
||||
|
||||
/* Tell CAM the request is finished */
|
||||
struct ccb_mmcio *mmcio;
|
||||
mmcio = &ccb->mmcio;
|
||||
|
||||
ccb->ccb_h.status =
|
||||
(mmcio->cmd.error == 0 ? CAM_REQ_CMP : CAM_REQ_CMP_ERR);
|
||||
xpt_done(ccb);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void
|
||||
sdhci_req_done(struct sdhci_slot *slot)
|
||||
{
|
||||
struct mmc_request *req;
|
||||
|
||||
@ -1042,6 +1152,7 @@ sdhci_req_done(struct sdhci_slot *slot)
|
||||
req->done(req);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
sdhci_timeout(void *arg)
|
||||
@ -1072,8 +1183,16 @@ sdhci_set_transfer_mode(struct sdhci_slot *slot, struct mmc_data *data)
|
||||
mode |= SDHCI_TRNS_MULTI;
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
mode |= SDHCI_TRNS_READ;
|
||||
#ifdef MMCCAM
|
||||
struct ccb_mmcio *mmcio;
|
||||
mmcio = &slot->ccb->mmcio;
|
||||
if (mmcio->stop.opcode == MMC_STOP_TRANSMISSION
|
||||
&& !(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP))
|
||||
mode |= SDHCI_TRNS_ACMD12;
|
||||
#else
|
||||
if (slot->req->stop && !(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP))
|
||||
mode |= SDHCI_TRNS_ACMD12;
|
||||
#endif
|
||||
if (slot->flags & SDHCI_USE_DMA)
|
||||
mode |= SDHCI_TRNS_DMA;
|
||||
|
||||
@ -1106,6 +1225,9 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
|
||||
if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot) ||
|
||||
slot->power == 0 ||
|
||||
slot->clock == 0) {
|
||||
slot_printf(slot,
|
||||
"Cannot issue a command (power=%d clock=%d)",
|
||||
slot->power, slot->clock);
|
||||
cmd->error = MMC_ERR_FAILED;
|
||||
sdhci_req_done(slot);
|
||||
return;
|
||||
@ -1113,11 +1235,17 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
|
||||
/* Always wait for free CMD bus. */
|
||||
mask = SDHCI_CMD_INHIBIT;
|
||||
/* Wait for free DAT if we have data or busy signal. */
|
||||
if (cmd->data || (cmd->flags & MMC_RSP_BUSY))
|
||||
if (cmd->data != NULL || (cmd->flags & MMC_RSP_BUSY))
|
||||
mask |= SDHCI_DAT_INHIBIT;
|
||||
/* We shouldn't wait for DAT for stop commands. */
|
||||
#ifdef MMCCAM
|
||||
struct ccb_mmcio *mmcio = &slot->ccb->mmcio;
|
||||
if (cmd == &mmcio->stop)
|
||||
mask &= ~SDHCI_DAT_INHIBIT;
|
||||
#else
|
||||
if (cmd == slot->req->stop)
|
||||
mask &= ~SDHCI_DAT_INHIBIT;
|
||||
#endif
|
||||
/*
|
||||
* Wait for bus no more then 250 ms. Typically there will be no wait
|
||||
* here at all, but when writing a crash dump we may be bypassing the
|
||||
@ -1155,7 +1283,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
|
||||
flags |= SDHCI_CMD_CRC;
|
||||
if (cmd->flags & MMC_RSP_OPCODE)
|
||||
flags |= SDHCI_CMD_INDEX;
|
||||
if (cmd->data)
|
||||
if (cmd->data != NULL)
|
||||
flags |= SDHCI_CMD_DATA;
|
||||
if (cmd->opcode == MMC_STOP_TRANSMISSION)
|
||||
flags |= SDHCI_CMD_TYPE_ABORT;
|
||||
@ -1174,6 +1302,8 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
|
||||
WR4(slot, SDHCI_ARGUMENT, cmd->arg);
|
||||
/* Set data transfer mode. */
|
||||
sdhci_set_transfer_mode(slot, cmd->data);
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "Starting command!\n");
|
||||
/* Start command. */
|
||||
WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff));
|
||||
/* Start timeout callout. */
|
||||
@ -1188,6 +1318,9 @@ sdhci_finish_command(struct sdhci_slot *slot)
|
||||
uint32_t val;
|
||||
uint8_t extra;
|
||||
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "%s: called, err %d flags %d\n",
|
||||
__func__, slot->curcmd->error, slot->curcmd->flags);
|
||||
slot->cmd_done = 1;
|
||||
/*
|
||||
* Interrupt aggregation: Restore command interrupt.
|
||||
@ -1221,6 +1354,11 @@ sdhci_finish_command(struct sdhci_slot *slot)
|
||||
} else
|
||||
slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE);
|
||||
}
|
||||
if (sdhci_debug > 1)
|
||||
printf("Resp: %02x %02x %02x %02x\n",
|
||||
slot->curcmd->resp[0], slot->curcmd->resp[1],
|
||||
slot->curcmd->resp[2], slot->curcmd->resp[3]);
|
||||
|
||||
/* If data ready - finish. */
|
||||
if (slot->data_done)
|
||||
sdhci_start(slot);
|
||||
@ -1301,6 +1439,11 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data)
|
||||
(data->len < 512) ? data->len : 512));
|
||||
/* Set block count. */
|
||||
WR2(slot, SDHCI_BLOCK_COUNT, (data->len + 511) / 512);
|
||||
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "Block size: %02x, count %lu\n", (unsigned int)
|
||||
SDHCI_MAKE_BLKSZ(DMA_BOUNDARY, (data->len < 512)?data->len:512),
|
||||
(unsigned long)(data->len + 511) / 512);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1342,6 +1485,47 @@ sdhci_finish_data(struct sdhci_slot *slot)
|
||||
sdhci_start(slot);
|
||||
}
|
||||
|
||||
#ifdef MMCCAM
|
||||
static void
|
||||
sdhci_start(struct sdhci_slot *slot)
|
||||
{
|
||||
union ccb *ccb;
|
||||
|
||||
ccb = slot->ccb;
|
||||
if (ccb == NULL)
|
||||
return;
|
||||
|
||||
struct ccb_mmcio *mmcio;
|
||||
mmcio = &ccb->mmcio;
|
||||
|
||||
if (!(slot->flags & CMD_STARTED)) {
|
||||
slot->flags |= CMD_STARTED;
|
||||
sdhci_start_command(slot, &mmcio->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Old stack doesn't use this!
|
||||
* Enabling this code causes significant performance degradation
|
||||
* and IRQ storms on BBB, Wandboard behaves fine.
|
||||
* Not using this code does no harm...
|
||||
if (!(slot->flags & STOP_STARTED) && mmcio->stop.opcode != 0) {
|
||||
slot->flags |= STOP_STARTED;
|
||||
sdhci_start_command(slot, &mmcio->stop);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "result: %d\n", mmcio->cmd.error);
|
||||
if (mmcio->cmd.error == 0 &&
|
||||
(slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
|
||||
sdhci_reset(slot, SDHCI_RESET_CMD);
|
||||
sdhci_reset(slot, SDHCI_RESET_DATA);
|
||||
}
|
||||
|
||||
sdhci_req_done(slot);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
sdhci_start(struct sdhci_slot *slot)
|
||||
{
|
||||
@ -1374,6 +1558,7 @@ sdhci_start(struct sdhci_slot *slot)
|
||||
|
||||
sdhci_req_done(slot);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
sdhci_generic_request(device_t brdev __unused, device_t reqdev,
|
||||
@ -1472,7 +1657,6 @@ static void
|
||||
sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
|
||||
{
|
||||
struct mmc_data *data;
|
||||
size_t left;
|
||||
|
||||
if (!slot->curcmd) {
|
||||
slot_printf(slot, "Got data interrupt 0x%08x, but "
|
||||
@ -1518,6 +1702,7 @@ sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
|
||||
/* Handle DMA border. */
|
||||
if (intmask & SDHCI_INT_DMA_END) {
|
||||
data = slot->curcmd->data;
|
||||
size_t left;
|
||||
|
||||
/* Unload DMA buffer ... */
|
||||
left = data->len - slot->offset;
|
||||
@ -1640,6 +1825,10 @@ sdhci_generic_intr(struct sdhci_slot *slot)
|
||||
slot_printf(slot,
|
||||
"Card is consuming too much power!\n");
|
||||
intmask &= ~SDHCI_INT_BUS_POWER;
|
||||
}
|
||||
/* Handle card interrupt. */
|
||||
if (intmask & SDHCI_INT_CARD_INT) {
|
||||
|
||||
}
|
||||
/* The rest is unknown. */
|
||||
if (intmask) {
|
||||
@ -1724,6 +1913,7 @@ sdhci_generic_write_ivar(device_t bus, device_t child, int which,
|
||||
uint32_t clock, max_clock;
|
||||
int i;
|
||||
|
||||
slot_printf(slot, "sdhci_generic_write_ivar, var=%d\n", which);
|
||||
switch (which) {
|
||||
default:
|
||||
return (EINVAL);
|
||||
@ -1789,4 +1979,323 @@ sdhci_generic_write_ivar(device_t bus, device_t child, int which,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* CAM-related functions */
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <cam/cam_debug.h>
|
||||
#include <cam/cam_sim.h>
|
||||
#include <cam/cam_xpt_sim.h>
|
||||
|
||||
void
|
||||
sdhci_cam_start_slot(struct sdhci_slot *slot)
|
||||
{
|
||||
if ((slot->devq = cam_simq_alloc(1)) == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mtx_init(&slot->sim_mtx, "sdhcisim", NULL, MTX_DEF);
|
||||
slot->sim = cam_sim_alloc(sdhci_cam_action, sdhci_cam_poll,
|
||||
"sdhci_slot", slot, device_get_unit(slot->bus),
|
||||
&slot->sim_mtx, 1, 1, slot->devq);
|
||||
|
||||
if (slot->sim == NULL) {
|
||||
cam_simq_free(slot->devq);
|
||||
slot_printf(slot, "cannot allocate CAM SIM\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mtx_lock(&slot->sim_mtx);
|
||||
if (xpt_bus_register(slot->sim, slot->bus, 0) != 0) {
|
||||
slot_printf(slot,
|
||||
"cannot register SCSI pass-through bus\n");
|
||||
cam_sim_free(slot->sim, FALSE);
|
||||
cam_simq_free(slot->devq);
|
||||
mtx_unlock(&slot->sim_mtx);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mtx_unlock(&slot->sim_mtx);
|
||||
/* End CAM-specific init */
|
||||
slot->card_present = 0;
|
||||
sdhci_card_task(slot, 0);
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (slot->sim != NULL) {
|
||||
mtx_lock(&slot->sim_mtx);
|
||||
xpt_bus_deregister(cam_sim_path(slot->sim));
|
||||
cam_sim_free(slot->sim, FALSE);
|
||||
mtx_unlock(&slot->sim_mtx);
|
||||
}
|
||||
|
||||
if (slot->devq != NULL)
|
||||
cam_simq_free(slot->devq);
|
||||
}
|
||||
|
||||
static void
|
||||
sdhci_cam_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
|
||||
{
|
||||
struct sdhci_slot *slot;
|
||||
|
||||
slot = cam_sim_softc(sim);
|
||||
|
||||
sdhci_cam_request(slot, ccb);
|
||||
}
|
||||
|
||||
void
|
||||
sdhci_cam_action(struct cam_sim *sim, union ccb *ccb)
|
||||
{
|
||||
struct sdhci_slot *slot;
|
||||
|
||||
slot = cam_sim_softc(sim);
|
||||
if (slot == NULL) {
|
||||
ccb->ccb_h.status = CAM_SEL_TIMEOUT;
|
||||
xpt_done(ccb);
|
||||
return;
|
||||
}
|
||||
|
||||
mtx_assert(&slot->sim_mtx, MA_OWNED);
|
||||
|
||||
switch (ccb->ccb_h.func_code) {
|
||||
case XPT_PATH_INQ:
|
||||
{
|
||||
struct ccb_pathinq *cpi;
|
||||
|
||||
cpi = &ccb->cpi;
|
||||
cpi->version_num = 1;
|
||||
cpi->hba_inquiry = 0;
|
||||
cpi->target_sprt = 0;
|
||||
cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN;
|
||||
cpi->hba_eng_cnt = 0;
|
||||
cpi->max_target = 0;
|
||||
cpi->max_lun = 0;
|
||||
cpi->initiator_id = 1;
|
||||
cpi->maxio = MAXPHYS;
|
||||
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
|
||||
strncpy(cpi->hba_vid, "Deglitch Networks", HBA_IDLEN);
|
||||
strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
|
||||
cpi->unit_number = cam_sim_unit(sim);
|
||||
cpi->bus_id = cam_sim_bus(sim);
|
||||
cpi->base_transfer_speed = 100; /* XXX WTF? */
|
||||
cpi->protocol = PROTO_MMCSD;
|
||||
cpi->protocol_version = SCSI_REV_0;
|
||||
cpi->transport = XPORT_MMCSD;
|
||||
cpi->transport_version = 0;
|
||||
|
||||
cpi->ccb_h.status = CAM_REQ_CMP;
|
||||
break;
|
||||
}
|
||||
case XPT_GET_TRAN_SETTINGS:
|
||||
{
|
||||
struct ccb_trans_settings *cts = &ccb->cts;
|
||||
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "Got XPT_GET_TRAN_SETTINGS\n");
|
||||
|
||||
cts->protocol = PROTO_MMCSD;
|
||||
cts->protocol_version = 1;
|
||||
cts->transport = XPORT_MMCSD;
|
||||
cts->transport_version = 1;
|
||||
cts->xport_specific.valid = 0;
|
||||
cts->proto_specific.mmc.host_ocr = slot->host.host_ocr;
|
||||
cts->proto_specific.mmc.host_f_min = slot->host.f_min;
|
||||
cts->proto_specific.mmc.host_f_max = slot->host.f_max;
|
||||
cts->proto_specific.mmc.host_caps = slot->host.caps;
|
||||
memcpy(&cts->proto_specific.mmc.ios, &slot->host.ios, sizeof(struct mmc_ios));
|
||||
ccb->ccb_h.status = CAM_REQ_CMP;
|
||||
break;
|
||||
}
|
||||
case XPT_SET_TRAN_SETTINGS:
|
||||
{
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "Got XPT_SET_TRAN_SETTINGS\n");
|
||||
sdhci_cam_settran_settings(slot, ccb);
|
||||
ccb->ccb_h.status = CAM_REQ_CMP;
|
||||
break;
|
||||
}
|
||||
case XPT_RESET_BUS:
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "Got XPT_RESET_BUS, ACK it...\n");
|
||||
ccb->ccb_h.status = CAM_REQ_CMP;
|
||||
break;
|
||||
case XPT_MMC_IO:
|
||||
/*
|
||||
* Here is the HW-dependent part of
|
||||
* sending the command to the underlying h/w
|
||||
* At some point in the future an interrupt comes.
|
||||
* Then the request will be marked as completed.
|
||||
*/
|
||||
if (sdhci_debug > 1)
|
||||
slot_printf(slot, "Got XPT_MMC_IO\n");
|
||||
ccb->ccb_h.status = CAM_REQ_INPROG;
|
||||
|
||||
sdhci_cam_handle_mmcio(sim, ccb);
|
||||
return;
|
||||
/* NOTREACHED */
|
||||
break;
|
||||
default:
|
||||
ccb->ccb_h.status = CAM_REQ_INVALID;
|
||||
break;
|
||||
}
|
||||
xpt_done(ccb);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
sdhci_cam_poll(struct cam_sim *sim)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int sdhci_cam_get_possible_host_clock(struct sdhci_slot *slot, int proposed_clock) {
|
||||
int max_clock, clock, i;
|
||||
|
||||
if (proposed_clock == 0)
|
||||
return 0;
|
||||
max_clock = slot->max_clk;
|
||||
clock = max_clock;
|
||||
|
||||
if (slot->version < SDHCI_SPEC_300) {
|
||||
for (i = 0; i < SDHCI_200_MAX_DIVIDER;
|
||||
i <<= 1) {
|
||||
if (clock <= proposed_clock)
|
||||
break;
|
||||
clock >>= 1;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < SDHCI_300_MAX_DIVIDER;
|
||||
i += 2) {
|
||||
if (clock <= proposed_clock)
|
||||
break;
|
||||
clock = max_clock / (i + 2);
|
||||
}
|
||||
}
|
||||
return clock;
|
||||
}
|
||||
|
||||
int
|
||||
sdhci_cam_settran_settings(struct sdhci_slot *slot, union ccb *ccb)
|
||||
{
|
||||
struct mmc_ios *ios;
|
||||
struct mmc_ios *new_ios;
|
||||
struct ccb_trans_settings_mmc *cts;
|
||||
|
||||
ios = &slot->host.ios;
|
||||
|
||||
cts = &ccb->cts.proto_specific.mmc;
|
||||
new_ios = &cts->ios;
|
||||
|
||||
/* Update only requested fields */
|
||||
if (cts->ios_valid & MMC_CLK) {
|
||||
ios->clock = sdhci_cam_get_possible_host_clock(slot, new_ios->clock);
|
||||
slot_printf(slot, "Clock => %d\n", ios->clock);
|
||||
}
|
||||
if (cts->ios_valid & MMC_VDD) {
|
||||
ios->vdd = new_ios->vdd;
|
||||
slot_printf(slot, "VDD => %d\n", ios->vdd);
|
||||
}
|
||||
if (cts->ios_valid & MMC_CS) {
|
||||
ios->chip_select = new_ios->chip_select;
|
||||
slot_printf(slot, "CS => %d\n", ios->chip_select);
|
||||
}
|
||||
if (cts->ios_valid & MMC_BW) {
|
||||
ios->bus_width = new_ios->bus_width;
|
||||
slot_printf(slot, "Bus width => %d\n", ios->bus_width);
|
||||
}
|
||||
if (cts->ios_valid & MMC_PM) {
|
||||
ios->power_mode = new_ios->power_mode;
|
||||
slot_printf(slot, "Power mode => %d\n", ios->power_mode);
|
||||
}
|
||||
if (cts->ios_valid & MMC_BT) {
|
||||
ios->timing = new_ios->timing;
|
||||
slot_printf(slot, "Timing => %d\n", ios->timing);
|
||||
}
|
||||
if (cts->ios_valid & MMC_BM) {
|
||||
ios->bus_mode = new_ios->bus_mode;
|
||||
slot_printf(slot, "Bus mode => %d\n", ios->bus_mode);
|
||||
}
|
||||
|
||||
/* XXX Provide a way to call a chip-specific IOS update, required for TI */
|
||||
return (sdhci_cam_update_ios(slot));
|
||||
}
|
||||
|
||||
int
|
||||
sdhci_cam_update_ios(struct sdhci_slot *slot)
|
||||
{
|
||||
struct mmc_ios *ios = &slot->host.ios;
|
||||
|
||||
slot_printf(slot, "%s: power_mode=%d, clk=%d, bus_width=%d, timing=%d\n",
|
||||
__func__, ios->power_mode, ios->clock, ios->bus_width, ios->timing);
|
||||
SDHCI_LOCK(slot);
|
||||
/* Do full reset on bus power down to clear from any state. */
|
||||
if (ios->power_mode == power_off) {
|
||||
WR4(slot, SDHCI_SIGNAL_ENABLE, 0);
|
||||
sdhci_init(slot);
|
||||
}
|
||||
/* Configure the bus. */
|
||||
sdhci_set_clock(slot, ios->clock);
|
||||
sdhci_set_power(slot, (ios->power_mode == power_off) ? 0 : ios->vdd);
|
||||
if (ios->bus_width == bus_width_8) {
|
||||
slot->hostctrl |= SDHCI_CTRL_8BITBUS;
|
||||
slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
|
||||
} else if (ios->bus_width == bus_width_4) {
|
||||
slot->hostctrl &= ~SDHCI_CTRL_8BITBUS;
|
||||
slot->hostctrl |= SDHCI_CTRL_4BITBUS;
|
||||
} else if (ios->bus_width == bus_width_1) {
|
||||
slot->hostctrl &= ~SDHCI_CTRL_8BITBUS;
|
||||
slot->hostctrl &= ~SDHCI_CTRL_4BITBUS;
|
||||
} else {
|
||||
panic("Invalid bus width: %d", ios->bus_width);
|
||||
}
|
||||
if (ios->timing == bus_timing_hs &&
|
||||
!(slot->quirks & SDHCI_QUIRK_DONT_SET_HISPD_BIT))
|
||||
slot->hostctrl |= SDHCI_CTRL_HISPD;
|
||||
else
|
||||
slot->hostctrl &= ~SDHCI_CTRL_HISPD;
|
||||
WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl);
|
||||
/* Some controllers like reset after bus changes. */
|
||||
if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS)
|
||||
sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
|
||||
SDHCI_UNLOCK(slot);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
sdhci_cam_request(struct sdhci_slot *slot, union ccb *ccb)
|
||||
{
|
||||
struct ccb_mmcio *mmcio;
|
||||
|
||||
mmcio = &ccb->mmcio;
|
||||
|
||||
SDHCI_LOCK(slot);
|
||||
/* if (slot->req != NULL) {
|
||||
SDHCI_UNLOCK(slot);
|
||||
return (EBUSY);
|
||||
}
|
||||
*/
|
||||
if (sdhci_debug > 1) {
|
||||
slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
|
||||
mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags,
|
||||
mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0,
|
||||
mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0);
|
||||
}
|
||||
if (mmcio->cmd.data != NULL) {
|
||||
if (mmcio->cmd.data->len == 0 || mmcio->cmd.data->flags == 0)
|
||||
panic("data->len = %d, data->flags = %d -- something is b0rked",
|
||||
(int)mmcio->cmd.data->len, mmcio->cmd.data->flags);
|
||||
}
|
||||
slot->ccb = ccb;
|
||||
slot->flags = 0;
|
||||
sdhci_start(slot);
|
||||
SDHCI_UNLOCK(slot);
|
||||
if (dumping) {
|
||||
while (slot->ccb != NULL) {
|
||||
sdhci_generic_intr(slot);
|
||||
DELAY(10);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
MODULE_VERSION(sdhci, 1);
|
||||
|
@ -367,6 +367,13 @@ struct sdhci_slot {
|
||||
#define SDHCI_USE_DMA 4 /* Use DMA for this req. */
|
||||
#define PLATFORM_DATA_STARTED 8 /* Data xfer is handled by platform */
|
||||
struct mtx mtx; /* Slot mutex */
|
||||
|
||||
/* CAM stuff */
|
||||
union ccb *ccb;
|
||||
struct cam_devq *devq;
|
||||
struct cam_sim *sim;
|
||||
struct mtx sim_mtx;
|
||||
u_char card_present; /* XXX Maybe derive this from elsewhere? */
|
||||
};
|
||||
|
||||
int sdhci_generic_read_ivar(device_t bus, device_t child, int which,
|
||||
@ -393,4 +400,6 @@ bool sdhci_generic_get_card_present(device_t brdev, struct sdhci_slot *slot);
|
||||
void sdhci_generic_set_uhs_timing(device_t brdev, struct sdhci_slot *slot);
|
||||
void sdhci_handle_card_present(struct sdhci_slot *slot, bool is_present);
|
||||
|
||||
/* CAM-related */
|
||||
void sdhci_cam_start_slot(struct sdhci_slot *slot);
|
||||
#endif /* __SDHCI_H__ */
|
||||
|
@ -400,4 +400,7 @@ static devclass_t sdhci_acpi_devclass;
|
||||
DRIVER_MODULE(sdhci_acpi, acpi, sdhci_acpi_driver, sdhci_acpi_devclass, NULL,
|
||||
NULL);
|
||||
MODULE_DEPEND(sdhci_acpi, sdhci, 1, 1, 1);
|
||||
|
||||
#ifndef MMCCAM
|
||||
MMC_DECLARE_BRIDGE(sdhci_acpi);
|
||||
#endif
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_mmccam.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
@ -396,8 +398,13 @@ sdhci_pci_attach(device_t dev)
|
||||
device_printf(dev, "Can't setup IRQ\n");
|
||||
pci_enable_busmaster(dev);
|
||||
/* Process cards detection. */
|
||||
for (i = 0; i < sc->num_slots; i++)
|
||||
for (i = 0; i < sc->num_slots; i++) {
|
||||
#ifdef MMCCAM
|
||||
sdhci_cam_start_slot(&sc->slots[i]);
|
||||
#else
|
||||
sdhci_start_slot(&sc->slots[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -518,4 +525,7 @@ static devclass_t sdhci_pci_devclass;
|
||||
DRIVER_MODULE(sdhci_pci, pci, sdhci_pci_driver, sdhci_pci_devclass, NULL,
|
||||
NULL);
|
||||
MODULE_DEPEND(sdhci_pci, sdhci, 1, 1, 1);
|
||||
|
||||
#ifndef MMCCAM
|
||||
MMC_DECLARE_BRIDGE(sdhci_pci);
|
||||
#endif
|
||||
|
@ -251,8 +251,6 @@ SUBDIR= \
|
||||
${_mlx5} \
|
||||
${_mlx5en} \
|
||||
${_mly} \
|
||||
mmc \
|
||||
mmcsd \
|
||||
mpr \
|
||||
mps \
|
||||
mpt \
|
||||
|
Loading…
Reference in New Issue
Block a user