// SPDX-License-Identifier: GPL-2.0-or-later /* * linux/drivers/mmc/host/wbsd.c - Winbond W83L51xD SD/MMC driver * * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. * * Warning! * * Changes to the FIFO system should be done with extreme care since * the hardware is full of bugs related to the FIFO. Known issues are: * * - FIFO size field in FSR is always zero. * * - FIFO interrupts tend not to work as they should. Interrupts are * triggered only for full/empty events, not for threshold values. * * - On APIC systems the FIFO empty interrupt is sometimes lost.
*/
/* * MMC layer might call back into the driver so first unlock.
*/
spin_unlock(&host->lock);
mmc_request_done(host->mmc, mrq);
spin_lock(&host->lock);
}
/* * Scatter/gather functions
*/
staticinlinevoid wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data)
{ /* * Get info. about SG list from data structure.
*/
host->cur_sg = data->sg;
host->num_sg = data->sg_len;
/* * Drain the fifo. This has a tendency to loop longer * than the FIFO length (usually one block).
*/ while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) { /* * The size field in the FSR is broken so we have to * do some guessing.
*/ if (fsr & WBSD_FIFO_FULL)
fifo = 16; elseif (fsr & WBSD_FIFO_FUTHRE)
fifo = 8; else
fifo = 1;
for (i = 0; i < fifo; i++) {
buffer[idx++] = inb(host->base + WBSD_DFR);
host->offset++;
host->remain--;
data->bytes_xfered++;
/* * End of scatter list entry?
*/ if (host->remain == 0) {
kunmap_local(buffer); /* * Get next entry. Check if last.
*/ if (!wbsd_next_sg(host)) return;
/* * This is a very dirty hack to solve a * hardware problem. The chip doesn't trigger * FIFO threshold interrupts properly.
*/ if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
queue_work(system_bh_wq, &host->fifo_bh_work);
}
staticvoid wbsd_fill_fifo(struct wbsd_host *host)
{ struct mmc_data *data = host->mrq->cmd->data; char *buffer; int i, idx, fsr, fifo;
/* * Check that we aren't being called after the * entire buffer has been transferred.
*/ if (host->num_sg == 0) return;
/* * Fill the fifo. This has a tendency to loop longer * than the FIFO length (usually one block).
*/ while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) { /* * The size field in the FSR is broken so we have to * do some guessing.
*/ if (fsr & WBSD_FIFO_EMPTY)
fifo = 0; elseif (fsr & WBSD_FIFO_EMTHRE)
fifo = 8; else
fifo = 15;
for (i = 16; i > fifo; i--) {
outb(buffer[idx], host->base + WBSD_DFR);
host->offset++;
host->remain--;
data->bytes_xfered++;
/* * End of scatter list entry?
*/ if (host->remain == 0) {
kunmap_local(buffer); /* * Get next entry. Check if last.
*/ if (!wbsd_next_sg(host)) return;
/* * The controller stops sending interrupts for * 'FIFO empty' under certain conditions. So we * need to be a bit more pro-active.
*/
queue_work(system_bh_wq, &host->fifo_bh_work);
}
/* * Check timeout values for overflow. * (Yes, some cards cause this value to overflow).
*/ if (data->timeout_ns > 127000000)
wbsd_write_index(host, WBSD_IDX_TAAC, 127); else {
wbsd_write_index(host, WBSD_IDX_TAAC,
data->timeout_ns / 1000000);
}
/* * Inform the chip of how large blocks will be * sent. It needs this to determine when to * calculate CRC. * * Space for CRC must be included in the size. * Two bytes are needed for each data line.
*/ if (host->bus_width == MMC_BUS_WIDTH_1) {
blksize = data->blksz + 2;
/* * Clear the FIFO. This is needed even for DMA * transfers since the chip still uses the FIFO * internally.
*/
setup = wbsd_read_index(host, WBSD_IDX_SETUP);
setup |= WBSD_FIFO_RESET;
wbsd_write_index(host, WBSD_IDX_SETUP, setup);
/* * DMA transfer?
*/ if (host->dma >= 0) { /* * The buffer for DMA is only 64 kB.
*/
BUG_ON(size > 0x10000); if (size > 0x10000) {
data->error = -EINVAL; return;
}
/* * Transfer data from the SG list to * the DMA buffer.
*/ if (data->flags & MMC_DATA_WRITE)
wbsd_sg_to_dma(host, data);
/* * Initialise the ISA DMA controller.
*/
dmaflags = claim_dma_lock();
disable_dma(host->dma);
clear_dma_ff(host->dma); if (data->flags & MMC_DATA_READ)
set_dma_mode(host->dma, DMA_MODE_READ & ~0x40); else
set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
set_dma_addr(host->dma, host->dma_addr);
set_dma_count(host->dma, size);
/* * Enable DMA on the host.
*/
wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);
} else { /* * This flag is used to keep printk * output to a minimum.
*/
host->firsterr = 1;
/* * Initialise the SG list.
*/
wbsd_init_sg(host, data);
/* * Turn off DMA.
*/
wbsd_write_index(host, WBSD_IDX_DMA, 0);
/* * Set up FIFO threshold levels (and fill * buffer if doing a write).
*/ if (data->flags & MMC_DATA_READ) {
wbsd_write_index(host, WBSD_IDX_FIFOEN,
WBSD_FIFOEN_FULL | 8);
} else {
wbsd_write_index(host, WBSD_IDX_FIFOEN,
WBSD_FIFOEN_EMPTY | 8);
wbsd_fill_fifo(host);
}
}
/* * Send a stop command if needed.
*/ if (data->stop)
wbsd_send_command(host, data->stop);
/* * Wait for the controller to leave data * transfer state.
*/ do {
status = wbsd_read_index(host, WBSD_IDX_STATUS);
} while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));
/* * DMA transfer?
*/ if (host->dma >= 0) { /* * Disable DMA on the host.
*/
wbsd_write_index(host, WBSD_IDX_DMA, 0);
/* * Turn of ISA DMA controller.
*/
dmaflags = claim_dma_lock();
disable_dma(host->dma);
clear_dma_ff(host->dma);
count = get_dma_residue(host->dma);
release_dma_lock(dmaflags);
/* * Any leftover data?
*/ if (count) {
pr_err("%s: Incomplete DMA transfer. " "%d bytes left.\n",
mmc_hostname(host->mmc), count);
if (!data->error)
data->error = -EIO;
} else { /* * Transfer data from DMA buffer to * SG list.
*/ if (data->flags & MMC_DATA_READ)
wbsd_dma_to_sg(host, data);
}
if (data->error) { if (data->bytes_xfered)
data->bytes_xfered -= data->blksz;
}
}
/* * Disable bh works to avoid a deadlock.
*/
spin_lock_bh(&host->lock);
BUG_ON(host->mrq != NULL);
cmd = mrq->cmd;
host->mrq = mrq;
/* * Check that there is actually a card in the slot.
*/ if (!(host->flags & WBSD_FCARD_PRESENT)) {
cmd->error = -ENOMEDIUM; goto done;
}
if (cmd->data) { /* * The hardware is so delightfully stupid that it has a list * of "data" commands. If a command isn't on this list, it'll * just go back to the idle state and won't send any data * interrupts.
*/ switch (cmd->opcode) { case SD_SWITCH_VOLTAGE: case MMC_READ_SINGLE_BLOCK: case MMC_READ_MULTIPLE_BLOCK: case MMC_WRITE_DAT_UNTIL_STOP: case MMC_WRITE_BLOCK: case MMC_WRITE_MULTIPLE_BLOCK: case MMC_PROGRAM_CID: case MMC_PROGRAM_CSD: case MMC_SEND_WRITE_PROT: case MMC_LOCK_UNLOCK: case MMC_GEN_CMD: break;
/* ACMDs. We don't keep track of state, so we just treat them
* like any other command. */ case SD_APP_SEND_SCR: break;
default:
pr_warn("%s: Data command %d is not supported by this controller\n",
mmc_hostname(host->mmc), cmd->opcode);
cmd->error = -EINVAL;
goto done;
}
}
/* * Does the request include data?
*/ if (cmd->data) {
wbsd_prepare_data(host, cmd->data);
if (cmd->data->error) goto done;
}
wbsd_send_command(host, cmd);
/* * If this is a data transfer the request * will be finished after the data has * transferred.
*/ if (cmd->data && !cmd->error) { /* * Dirty fix for hardware bug.
*/ if (host->dma == -1)
queue_work(system_bh_wq, &host->fifo_bh_work);
/* * Only write to the clock register when * there is an actual change.
*/ if (clk != host->clk) {
wbsd_write_index(host, WBSD_IDX_CLK, clk);
host->clk = clk;
}
/* * Power up card.
*/ if (ios->power_mode != MMC_POWER_OFF) {
pwr = inb(host->base + WBSD_CSR);
pwr &= ~WBSD_POWER_N;
outb(pwr, host->base + WBSD_CSR);
}
/* * MMC cards need to have pin 1 high during init. * It wreaks havoc with the card detection though so * that needs to be disabled.
*/
setup = wbsd_read_index(host, WBSD_IDX_SETUP); if (ios->chip_select == MMC_CS_HIGH) {
BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
setup |= WBSD_DAT3_H;
host->flags |= WBSD_FIGNORE_DETECT;
} else { if (setup & WBSD_DAT3_H) {
setup &= ~WBSD_DAT3_H;
/* * We cannot resume card detection immediately * because of capacitance and delays in the chip.
*/
mod_timer(&host->ignore_timer, jiffies + HZ / 100);
}
}
wbsd_write_index(host, WBSD_IDX_SETUP, setup);
/* * Store bus width for later. Will be used when * setting up the data transfer.
*/
host->bus_width = ios->bus_width;
/* * Set up timers
*/
timer_setup(&host->ignore_timer, wbsd_reset_ignore, 0);
/* * Maximum number of segments. Worst case is one sector per segment * so this will be 64kB/512.
*/
mmc->max_segs = 128;
/* * Maximum request size. Also limited by 64KiB buffer.
*/
mmc->max_req_size = 65536;
/* * Maximum segment size. Could be one segment with the maximum number * of bytes.
*/
mmc->max_seg_size = mmc->max_req_size;
/* * Maximum block size. We have 12 bits (= 4095) but have to subtract * space for CRC. So the maximum is 4095 - 4*2 = 4087.
*/
mmc->max_blk_size = 4087;
/* * Maximum block count. There is no real limit so the maximum * request size will be the only restriction.
*/
mmc->max_blk_count = mmc->max_req_size;
staticint wbsd_scan(struct wbsd_host *host)
{ int i, j, k; int id;
/* * Iterate through all ports, all codes to * find hardware that is in our known list.
*/ for (i = 0; i < ARRAY_SIZE(config_ports); i++) { if (!request_region(config_ports[i], 2, DRIVER_NAME)) continue;
outb(WBSD_CONF_ID_HI, config_ports[i]);
id = inb(config_ports[i] + 1) << 8;
outb(WBSD_CONF_ID_LO, config_ports[i]);
id |= inb(config_ports[i] + 1);
wbsd_lock_config(host);
for (k = 0; k < ARRAY_SIZE(valid_ids); k++) { if (id == valid_ids[k]) {
host->chip_id = id;
return 0;
}
}
if (id != 0xFFFF) {
DBG("Unknown hardware (id %x) found at %x\n",
id, config_ports[i]);
}
}
release_region(config_ports[i], 2);
}
host->config = 0;
host->unlock_code = 0;
return -ENODEV;
}
/* * Allocate/free io port ranges
*/
staticint wbsd_request_region(struct wbsd_host *host, int base)
{ if (base & 0x7) return -EINVAL;
if (!request_region(base, 8, DRIVER_NAME)) return -EIO;
host->base = base;
return 0;
}
staticvoid wbsd_release_regions(struct wbsd_host *host)
{ if (host->base)
release_region(host->base, 8);
host->base = 0;
if (host->config)
release_region(host->config, 2);
host->config = 0;
}
/* * Allocate/free DMA port and buffer
*/
staticvoid wbsd_request_dma(struct wbsd_host *host, int dma)
{ if (dma < 0) return;
if (request_dma(dma, DRIVER_NAME)) goto err;
/* * We need to allocate a special buffer in * order for ISA to be able to DMA to it.
*/
host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
GFP_NOIO | GFP_DMA | __GFP_RETRY_MAYFAIL | __GFP_NOWARN); if (!host->dma_buffer) goto free;
/* * Translate the address to a physical address.
*/
host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer,
WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); if (dma_mapping_error(mmc_dev(host->mmc), host->dma_addr)) goto kfree;
/* * ISA DMA must be aligned on a 64k basis.
*/ if ((host->dma_addr & 0xffff) != 0) goto unmap; /* * ISA cannot access memory above 16 MB.
*/ elseif (host->dma_addr >= 0x1000000) goto unmap;
host->dma = dma;
return;
unmap: /* * If we've gotten here then there is some kind of alignment bug
*/
BUG_ON(1);
staticint wbsd_request_irq(struct wbsd_host *host, int irq)
{ int ret;
/* * Set up bh works. Must be done before requesting interrupt.
*/
INIT_WORK(&host->card_bh_work, wbsd_card_bh_work);
INIT_WORK(&host->fifo_bh_work, wbsd_fifo_bh_work);
INIT_WORK(&host->crc_bh_work, wbsd_crc_bh_work);
INIT_WORK(&host->timeout_bh_work, wbsd_timeout_bh_work);
INIT_WORK(&host->finish_bh_work, wbsd_finish_bh_work);
/* * Allocate interrupt.
*/
ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host); if (ret) return ret;
host->irq = irq;
return 0;
}
staticvoid wbsd_release_irq(struct wbsd_host *host)
{ if (!host->irq) return;
staticint wbsd_init(struct device *dev, int base, int irq, int dma, int pnp)
{ struct wbsd_host *host = NULL; struct mmc_host *mmc = NULL; int ret;
ret = wbsd_alloc_mmc(dev); if (ret) return ret;
mmc = dev_get_drvdata(dev);
host = mmc_priv(mmc);
/* * Scan for hardware.
*/
ret = wbsd_scan(host); if (ret) { if (pnp && (ret == -ENODEV)) {
pr_warn(DRIVER_NAME ": Unable to confirm device presence - you may experience lock-ups\n");
} else {
wbsd_free_mmc(dev); return ret;
}
}
/* * Request resources.
*/
ret = wbsd_request_resources(host, base, irq, dma); if (ret) {
wbsd_release_resources(host);
wbsd_free_mmc(dev); return ret;
}
/* * See if chip needs to be configured.
*/ if (pnp) { if ((host->config != 0) && !wbsd_chip_validate(host)) {
pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n");
wbsd_chip_config(host);
}
} else
wbsd_chip_config(host);
/* * Power Management stuff. No idea how this works. * Not tested.
*/ #ifdef CONFIG_PM if (host->config) {
wbsd_unlock_config(host);
wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
wbsd_lock_config(host);
} #endif /* * Allow device to initialise itself properly.
*/
mdelay(5);
/* * Reset the chip into a known state.
*/
wbsd_init_device(host);
ret = mmc_add_host(mmc); if (ret) { if (!pnp)
wbsd_chip_poweroff(host);
/* * See if chip needs to be configured.
*/ if (host->config != 0) { if (!wbsd_chip_validate(host)) {
pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n");
wbsd_chip_config(host);
}
}
/* * Allow device to initialise itself properly.
*/
mdelay(5);
#ifdef CONFIG_PNP
MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)"); #endif
MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.27 Sekunden
(vorverarbeitet am 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.