/* * cb710/mmc.c * * Copyright by Michał Mirosław, 2008-2009 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/delay.h> #include "cb710-mmc.h" static const u8 cb710_clock_divider_log2[8] = { /* 1, 2, 4, 8, 16, 32, 128, 512 */ 0, 1, 2, 3, 4, 5, 7, 9 }; #define CB710_MAX_DIVIDER_IDX \ (ARRAY_SIZE(cb710_clock_divider_log2) - 1) static const u8 cb710_src_freq_mhz[16] = { 33, 10, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85 }; static void cb710_mmc_select_clock_divider(struct mmc_host *mmc, int hz) { struct cb710_slot *slot = cb710_mmc_to_slot(mmc); struct pci_dev *pdev = cb710_slot_to_chip(slot)->pdev; u32 src_freq_idx; u32 divider_idx; int src_hz; /* on CB710 in HP nx9500: * src_freq_idx == 0 * indexes 1-7 work as written in the table * indexes 0,8-15 give no clock output */ pci_read_config_dword(pdev, 0x48, &src_freq_idx); src_freq_idx = (src_freq_idx >> 16) & 0xF; src_hz = cb710_src_freq_mhz[src_freq_idx] * 1000000; for (divider_idx = 0; divider_idx < CB710_MAX_DIVIDER_IDX; ++divider_idx) { if (hz >= src_hz >> cb710_clock_divider_log2[divider_idx]) break; } if (src_freq_idx) divider_idx |= 0x8; else if (divider_idx == 0) divider_idx = 1; cb710_pci_update_config_reg(pdev, 0x40, ~0xF0000000, divider_idx << 28); dev_dbg(cb710_slot_dev(slot), "clock set to %d Hz, wanted %d Hz; src_freq_idx = %d, divider_idx = %d|%d\n", src_hz >> cb710_clock_divider_log2[divider_idx & 7], hz, src_freq_idx, divider_idx & 7, divider_idx & 8); } static void __cb710_mmc_enable_irq(struct cb710_slot *slot, unsigned short enable, unsigned short mask) { /* clear global IE * - it gets set later if any interrupt sources are enabled */ mask |= CB710_MMC_IE_IRQ_ENABLE; /* look like interrupt is fired whenever * WORD[0x0C] & WORD[0x10] != 0; * -> bit 15 port 0x0C seems to be global interrupt enable */ enable = (cb710_read_port_16(slot, CB710_MMC_IRQ_ENABLE_PORT) & ~mask) | enable; if (enable) enable |= CB710_MMC_IE_IRQ_ENABLE; cb710_write_port_16(slot, CB710_MMC_IRQ_ENABLE_PORT, enable); } static void cb710_mmc_enable_irq(struct cb710_slot *slot, unsigned short enable, unsigned short mask) { struct cb710_mmc_reader *reader = mmc_priv(cb710_slot_to_mmc(slot)); unsigned long flags; spin_lock_irqsave(&reader->irq_lock, flags); /* this is the only thing irq_lock protects */ __cb710_mmc_enable_irq(slot, enable, mask); spin_unlock_irqrestore(&reader->irq_lock, flags); } static void cb710_mmc_reset_events(struct cb710_slot *slot) { cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, 0xFF); cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, 0xFF); cb710_write_port_8(slot, CB710_MMC_STATUS2_PORT, 0xFF); } static void cb710_mmc_enable_4bit_data(struct cb710_slot *slot, int enable) { if (enable) cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, CB710_MMC_C1_4BIT_DATA_BUS, 0); else cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0, CB710_MMC_C1_4BIT_DATA_BUS); } static int cb710_check_event(struct cb710_slot *slot, u8 what) { u16 status; status = cb710_read_port_16(slot, CB710_MMC_STATUS_PORT); if (status & CB710_MMC_S0_FIFO_UNDERFLOW) { /* it is just a guess, so log it */ dev_dbg(cb710_slot_dev(slot), "CHECK : ignoring bit 6 in status %04X\n", status); cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, CB710_MMC_S0_FIFO_UNDERFLOW); status &= ~CB710_MMC_S0_FIFO_UNDERFLOW; } if (status & CB710_MMC_STATUS_ERROR_EVENTS) { dev_dbg(cb710_slot_dev(slot), "CHECK : returning EIO on status %04X\n", status); cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, status & 0xFF); cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, CB710_MMC_S1_RESET); return -EIO; } /* 'what' is a bit in MMC_STATUS1 */ if ((status >> 8) & what) { cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, what); return 1; } return 0; } static int cb710_wait_for_event(struct cb710_slot *slot, u8 what) { int err = 0; unsigned limit = 2000000; /* FIXME: real timeout */ #ifdef CONFIG_CB710_DEBUG u32 e, x; e = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); #endif while (!(err = cb710_check_event(slot, what))) { if (!--limit) { cb710_dump_regs(cb710_slot_to_chip(slot), CB710_DUMP_REGS_MMC); err = -ETIMEDOUT; break; } udelay(1); } #ifdef CONFIG_CB710_DEBUG x = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); limit = 2000000 - limit; if (limit > 100) dev_dbg(cb710_slot_dev(slot), "WAIT10: waited %d loops, what %d, entry val %08X, exit val %08X\n", limit, what, e, x); #endif return err < 0 ? err : 0; } static int cb710_wait_while_busy(struct cb710_slot *slot, uint8_t mask) { unsigned limit = 500000; /* FIXME: real timeout */ int err = 0; #ifdef CONFIG_CB710_DEBUG u32 e, x; e = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); #endif while (cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT) & mask) { if (!--limit) { cb710_dump_regs(cb710_slot_to_chip(slot), CB710_DUMP_REGS_MMC); err = -ETIMEDOUT; break; } udelay(1); } #ifdef CONFIG_CB710_DEBUG x = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); limit = 500000 - limit; if (limit > 100) dev_dbg(cb710_slot_dev(slot), "WAIT12: waited %d loops, mask %02X, entry val %08X, exit val %08X\n", limit, mask, e, x); #endif return err; } static void cb710_mmc_set_transfer_size(struct cb710_slot *slot, size_t count, size_t blocksize) { cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); cb710_write_port_32(slot, CB710_MMC_TRANSFER_SIZE_PORT, ((count - 1) << 16)|(blocksize - 1)); dev_vdbg(cb710_slot_dev(slot), "set up for %zu block%s of %zu bytes\n", count, count == 1 ? "" : "s", blocksize); } static void cb710_mmc_fifo_hack(struct cb710_slot *slot) { /* without this, received data is prepended with 8-bytes of zeroes */ u32 r1, r2; int ok = 0; r1 = cb710_read_port_32(slot, CB710_MMC_DATA_PORT); r2 = cb710_read_port_32(slot, CB710_MMC_DATA_PORT); if (cb710_read_port_8(slot, CB710_MMC_STATUS0_PORT) & CB710_MMC_S0_FIFO_UNDERFLOW) { cb710_write_port_8(slot, CB710_MMC_STATUS0_PORT, CB710_MMC_S0_FIFO_UNDERFLOW); ok = 1; } dev_dbg(cb710_slot_dev(slot), "FIFO-read-hack: expected STATUS0 bit was %s\n", ok ? "set." : "NOT SET!"); dev_dbg(cb710_slot_dev(slot), "FIFO-read-hack: dwords ignored: %08X %08X - %s\n", r1, r2, (r1|r2) ? "BAD (NOT ZERO)!" : "ok"); } static int cb710_mmc_receive_pio(struct cb710_slot *slot, struct sg_mapping_iter *miter, size_t dw_count) { if (!(cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT) & CB710_MMC_S2_FIFO_READY)) { int err = cb710_wait_for_event(slot, CB710_MMC_S1_PIO_TRANSFER_DONE); if (err) return err; } cb710_sg_dwiter_write_from_io(miter, slot->iobase + CB710_MMC_DATA_PORT, dw_count); return 0; } static bool cb710_is_transfer_size_supported(struct mmc_data *data) { return !(data->blksz & 15 && (data->blocks != 1 || data->blksz != 8)); } static int cb710_mmc_receive(struct cb710_slot *slot, struct mmc_data *data) { struct sg_mapping_iter miter; size_t len, blocks = data->blocks; int err = 0; /* TODO: I don't know how/if the hardware handles non-16B-boundary blocks * except single 8B block */ if (unlikely(data->blksz & 15 && (data->blocks != 1 || data->blksz != 8))) return -EINVAL; sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_TO_SG); cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, 15, CB710_MMC_C2_READ_PIO_SIZE_MASK); cb710_mmc_fifo_hack(slot); while (blocks-- > 0) { len = data->blksz; while (len >= 16) { err = cb710_mmc_receive_pio(slot, &miter, 4); if (err) goto out; len -= 16; } if (!len) continue; cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, len - 1, CB710_MMC_C2_READ_PIO_SIZE_MASK); len = (len >= 8) ? 4 : 2; err = cb710_mmc_receive_pio(slot, &miter, len); if (err) goto out; } out: sg_miter_stop(&miter); return err; } static int cb710_mmc_send(struct cb710_slot *slot, struct mmc_data *data) { struct sg_mapping_iter miter; size_t len, blocks = data->blocks; int err = 0; /* TODO: I don't know how/if the hardware handles multiple * non-16B-boundary blocks */ if (unlikely(data->blocks > 1 && data->blksz & 15)) return -EINVAL; sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_FROM_SG); cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, 0, CB710_MMC_C2_READ_PIO_SIZE_MASK); while (blocks-- > 0) { len = (data->blksz + 15) >> 4; do { if (!(cb710_read_port_8(slot, CB710_MMC_STATUS2_PORT) & CB710_MMC_S2_FIFO_EMPTY)) { err = cb710_wait_for_event(slot, CB710_MMC_S1_PIO_TRANSFER_DONE); if (err) goto out; } cb710_sg_dwiter_read_to_io(&miter, slot->iobase + CB710_MMC_DATA_PORT, 4); } while (--len); } out: sg_miter_stop(&miter); return err; } static u16 cb710_encode_cmd_flags(struct cb710_mmc_reader *reader, struct mmc_command *cmd) { unsigned int flags = cmd->flags; u16 cb_flags = 0; /* Windows driver returned 0 for commands for which no response * is expected. It happened that there were only two such commands * used: MMC_GO_IDLE_STATE and MMC_GO_INACTIVE_STATE so it might * as well be a bug in that driver. * * Original driver set bit 14 for MMC/SD application * commands. There's no difference 'on the wire' and * it apparently works without it anyway. */ switch (flags & MMC_CMD_MASK) { case MMC_CMD_AC: cb_flags = CB710_MMC_CMD_AC; break; case MMC_CMD_ADTC: cb_flags = CB710_MMC_CMD_ADTC; break; case MMC_CMD_BC: cb_flags = CB710_MMC_CMD_BC; break; case MMC_CMD_BCR: cb_flags = CB710_MMC_CMD_BCR; break; } if (flags & MMC_RSP_BUSY) cb_flags |= CB710_MMC_RSP_BUSY; cb_flags |= cmd->opcode << CB710_MMC_CMD_CODE_SHIFT; if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) cb_flags |= CB710_MMC_DATA_READ; if (flags & MMC_RSP_PRESENT) { /* Windows driver set 01 at bits 4,3 except for * MMC_SET_BLOCKLEN where it set 10. Maybe the * hardware can do something special about this * command? The original driver looks buggy/incomplete * anyway so we ignore this for now. * * I assume that 00 here means no response is expected. */ cb_flags |= CB710_MMC_RSP_PRESENT; if (flags & MMC_RSP_136) cb_flags |= CB710_MMC_RSP_136; if (!(flags & MMC_RSP_CRC)) cb_flags |= CB710_MMC_RSP_NO_CRC; } return cb_flags; } static void cb710_receive_response(struct cb710_slot *slot, struct mmc_command *cmd) { unsigned rsp_opcode, wanted_opcode; /* Looks like final byte with CRC is always stripped (same as SDHCI) */ if (cmd->flags & MMC_RSP_136) { u32 resp[4]; resp[0] = cb710_read_port_32(slot, CB710_MMC_RESPONSE3_PORT); resp[1] = cb710_read_port_32(slot, CB710_MMC_RESPONSE2_PORT); resp[2] = cb710_read_port_32(slot, CB710_MMC_RESPONSE1_PORT); resp[3] = cb710_read_port_32(slot, CB710_MMC_RESPONSE0_PORT); rsp_opcode = resp[0] >> 24; cmd->resp[0] = (resp[0] << 8)|(resp[1] >> 24); cmd->resp[1] = (resp[1] << 8)|(resp[2] >> 24); cmd->resp[2] = (resp[2] << 8)|(resp[3] >> 24); cmd->resp[3] = (resp[3] << 8); } else { rsp_opcode = cb710_read_port_32(slot, CB710_MMC_RESPONSE1_PORT) & 0x3F; cmd->resp[0] = cb710_read_port_32(slot, CB710_MMC_RESPONSE0_PORT); } wanted_opcode = (cmd->flags & MMC_RSP_OPCODE) ? cmd->opcode : 0x3F; if (rsp_opcode != wanted_opcode) cmd->error = -EILSEQ; } static int cb710_mmc_transfer_data(struct cb710_slot *slot, struct mmc_data *data) { int error, to; if (data->flags & MMC_DATA_READ) error = cb710_mmc_receive(slot, data); else error = cb710_mmc_send(slot, data); to = cb710_wait_for_event(slot, CB710_MMC_S1_DATA_TRANSFER_DONE); if (!error) error = to; if (!error) data->bytes_xfered = data->blksz * data->blocks; return error; } static int cb710_mmc_command(struct mmc_host *mmc, struct mmc_command *cmd) { struct cb710_slot *slot = cb710_mmc_to_slot(mmc); struct cb710_mmc_reader *reader = mmc_priv(mmc); struct mmc_data *data = cmd->data; u16 cb_cmd = cb710_encode_cmd_flags(reader, cmd); dev_dbg(cb710_slot_dev(slot), "cmd request: 0x%04X\n", cb_cmd); if (data) { if (!cb710_is_transfer_size_supported(data)) { data->error = -EINVAL; return -1; } cb710_mmc_set_transfer_size(slot, data->blocks, data->blksz); } cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20|CB710_MMC_S2_BUSY_10); cb710_write_port_16(slot, CB710_MMC_CMD_TYPE_PORT, cb_cmd); cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); cb710_write_port_32(slot, CB710_MMC_CMD_PARAM_PORT, cmd->arg); cb710_mmc_reset_events(slot); cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x01, 0); cmd->error = cb710_wait_for_event(slot, CB710_MMC_S1_COMMAND_SENT); if (cmd->error) return -1; if (cmd->flags & MMC_RSP_PRESENT) { cb710_receive_response(slot, cmd); if (cmd->error) return -1; } if (data) data->error = cb710_mmc_transfer_data(slot, data); return 0; } static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct cb710_slot *slot = cb710_mmc_to_slot(mmc); struct cb710_mmc_reader *reader = mmc_priv(mmc); WARN_ON(reader->mrq != NULL); reader->mrq = mrq; cb710_mmc_enable_irq(slot, CB710_MMC_IE_TEST_MASK, 0); if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop) cb710_mmc_command(mmc, mrq->stop); tasklet_schedule(&reader->finish_req_tasklet); } static int cb710_mmc_powerup(struct cb710_slot *slot) { #ifdef CONFIG_CB710_DEBUG struct cb710_chip *chip = cb710_slot_to_chip(slot); #endif int err; /* a lot of magic for now */ dev_dbg(cb710_slot_dev(slot), "bus powerup\n"); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); if (unlikely(err)) return err; cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x80, 0); cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0x80, 0); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); mdelay(1); dev_dbg(cb710_slot_dev(slot), "after delay 1\n"); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); if (unlikely(err)) return err; cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x09, 0); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); mdelay(1); dev_dbg(cb710_slot_dev(slot), "after delay 2\n"); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); if (unlikely(err)) return err; cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0, 0x08); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); mdelay(2); dev_dbg(cb710_slot_dev(slot), "after delay 3\n"); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x06, 0); cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0x70, 0); cb710_modify_port_8(slot, CB710_MMC_CONFIG2_PORT, 0x80, 0); cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0x03, 0); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); err = cb710_wait_while_busy(slot, CB710_MMC_S2_BUSY_20); if (unlikely(err)) return err; /* This port behaves weird: quick byte reads of 0x08,0x09 return * 0xFF,0x00 after writing 0xFFFF to 0x08; it works correctly when * read/written from userspace... What am I missing here? * (it doesn't depend on write-to-read delay) */ cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0xFFFF); cb710_modify_port_8(slot, CB710_MMC_CONFIG0_PORT, 0x06, 0); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); dev_dbg(cb710_slot_dev(slot), "bus powerup finished\n"); return cb710_check_event(slot, 0); } static void cb710_mmc_powerdown(struct cb710_slot *slot) { cb710_modify_port_8(slot, CB710_MMC_CONFIG1_PORT, 0, 0x81); cb710_modify_port_8(slot, CB710_MMC_CONFIG3_PORT, 0, 0x80); } static void cb710_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct cb710_slot *slot = cb710_mmc_to_slot(mmc); struct cb710_mmc_reader *reader = mmc_priv(mmc); int err; cb710_mmc_select_clock_divider(mmc, ios->clock); if (ios->power_mode != reader->last_power_mode) switch (ios->power_mode) { case MMC_POWER_ON: err = cb710_mmc_powerup(slot); if (err) { dev_warn(cb710_slot_dev(slot), "powerup failed (%d)- retrying\n", err); cb710_mmc_powerdown(slot); udelay(1); err = cb710_mmc_powerup(slot); if (err) dev_warn(cb710_slot_dev(slot), "powerup retry failed (%d) - expect errors\n", err); } reader->last_power_mode = MMC_POWER_ON; break; case MMC_POWER_OFF: cb710_mmc_powerdown(slot); reader->last_power_mode = MMC_POWER_OFF; break; case MMC_POWER_UP: default: /* ignore */; } cb710_mmc_enable_4bit_data(slot, ios->bus_width != MMC_BUS_WIDTH_1); cb710_mmc_enable_irq(slot, CB710_MMC_IE_TEST_MASK, 0); } static int cb710_mmc_get_ro(struct mmc_host *mmc) { struct cb710_slot *slot = cb710_mmc_to_slot(mmc); return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT) & CB710_MMC_S3_WRITE_PROTECTED; } static int cb710_mmc_get_cd(struct mmc_host *mmc) { struct cb710_slot *slot = cb710_mmc_to_slot(mmc); return cb710_read_port_8(slot, CB710_MMC_STATUS3_PORT) & CB710_MMC_S3_CARD_DETECTED; } static int cb710_mmc_irq_handler(struct cb710_slot *slot) { struct mmc_host *mmc = cb710_slot_to_mmc(slot); struct cb710_mmc_reader *reader = mmc_priv(mmc); u32 status, config1, config2, irqen; status = cb710_read_port_32(slot, CB710_MMC_STATUS_PORT); irqen = cb710_read_port_32(slot, CB710_MMC_IRQ_ENABLE_PORT); config2 = cb710_read_port_32(slot, CB710_MMC_CONFIGB_PORT); config1 = cb710_read_port_32(slot, CB710_MMC_CONFIG_PORT); dev_dbg(cb710_slot_dev(slot), "interrupt; status: %08X, " "ie: %08X, c2: %08X, c1: %08X\n", status, irqen, config2, config1); if (status & (CB710_MMC_S1_CARD_CHANGED << 8)) { /* ack the event */ cb710_write_port_8(slot, CB710_MMC_STATUS1_PORT, CB710_MMC_S1_CARD_CHANGED); if ((irqen & CB710_MMC_IE_CISTATUS_MASK) == CB710_MMC_IE_CISTATUS_MASK) mmc_detect_change(mmc, HZ/5); } else { dev_dbg(cb710_slot_dev(slot), "unknown interrupt (test)\n"); spin_lock(&reader->irq_lock); __cb710_mmc_enable_irq(slot, 0, CB710_MMC_IE_TEST_MASK); spin_unlock(&reader->irq_lock); } return 1; } static void cb710_mmc_finish_request_tasklet(unsigned long data) { struct mmc_host *mmc = (void *)data; struct cb710_mmc_reader *reader = mmc_priv(mmc); struct mmc_request *mrq = reader->mrq; reader->mrq = NULL; mmc_request_done(mmc, mrq); } static const struct mmc_host_ops cb710_mmc_host = { .request = cb710_mmc_request, .set_ios = cb710_mmc_set_ios, .get_ro = cb710_mmc_get_ro, .get_cd = cb710_mmc_get_cd, }; #ifdef CONFIG_PM static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); cb710_mmc_enable_irq(slot, 0, ~0); return 0; } static int cb710_mmc_resume(struct platform_device *pdev) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); cb710_mmc_enable_irq(slot, 0, ~0); return 0; } #endif /* CONFIG_PM */ static int cb710_mmc_init(struct platform_device *pdev) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); struct cb710_chip *chip = cb710_slot_to_chip(slot); struct mmc_host *mmc; struct cb710_mmc_reader *reader; int err; u32 val; mmc = mmc_alloc_host(sizeof(*reader), cb710_slot_dev(slot)); if (!mmc) return -ENOMEM; platform_set_drvdata(pdev, mmc); /* harmless (maybe) magic */ pci_read_config_dword(chip->pdev, 0x48, &val); val = cb710_src_freq_mhz[(val >> 16) & 0xF]; dev_dbg(cb710_slot_dev(slot), "source frequency: %dMHz\n", val); val *= 1000000; mmc->ops = &cb710_mmc_host; mmc->f_max = val; mmc->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX]; mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA; reader = mmc_priv(mmc); tasklet_init(&reader->finish_req_tasklet, cb710_mmc_finish_request_tasklet, (unsigned long)mmc); spin_lock_init(&reader->irq_lock); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); cb710_mmc_enable_irq(slot, 0, ~0); cb710_set_irq_handler(slot, cb710_mmc_irq_handler); err = mmc_add_host(mmc); if (unlikely(err)) goto err_free_mmc; dev_dbg(cb710_slot_dev(slot), "mmc_hostname is %s\n", mmc_hostname(mmc)); cb710_mmc_enable_irq(slot, CB710_MMC_IE_CARD_INSERTION_STATUS, 0); return 0; err_free_mmc: dev_dbg(cb710_slot_dev(slot), "mmc_add_host() failed: %d\n", err); cb710_set_irq_handler(slot, NULL); mmc_free_host(mmc); return err; } static int cb710_mmc_exit(struct platform_device *pdev) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); struct mmc_host *mmc = cb710_slot_to_mmc(slot); struct cb710_mmc_reader *reader = mmc_priv(mmc); cb710_mmc_enable_irq(slot, 0, CB710_MMC_IE_CARD_INSERTION_STATUS); mmc_remove_host(mmc); /* IRQs should be disabled now, but let's stay on the safe side */ cb710_mmc_enable_irq(slot, 0, ~0); cb710_set_irq_handler(slot, NULL); /* clear config ports - just in case */ cb710_write_port_32(slot, CB710_MMC_CONFIG_PORT, 0); cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0); tasklet_kill(&reader->finish_req_tasklet); mmc_free_host(mmc); return 0; } static struct platform_driver cb710_mmc_driver = { .driver.name = "cb710-mmc", .probe = cb710_mmc_init, .remove = cb710_mmc_exit, #ifdef CONFIG_PM .suspend = cb710_mmc_suspend, .resume = cb710_mmc_resume, #endif }; module_platform_driver(cb710_mmc_driver); MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>"); MODULE_DESCRIPTION("ENE CB710 memory card reader driver - MMC/SD part"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:cb710-mmc");