Am 17.10.2014 um 14:37 schrieb Venkatraman S: > On Sat, Sep 27, 2014 at 12:41 PM, Oleksij Rempel <linux@xxxxxxxxxxxxxxxx> wrote: >> >> This driver is based on documentation which was based on my RE-work and >> comparision with other MMC drivers. >> It works in legacy mode and can provide 20MB/s R/W spead for most modern >> SD cards, even if at least 40MB/s should be possible on this hardware. >> It was not possible provide description for all register. But some of them >> are important to make this hardware work in some unknown way. >> >> Biggest part of RE-work was done by emulating AU6601 in QEMU. >> >> Signed-off-by: Oleksij Rempel <linux@xxxxxxxxxxxxxxxx> >> --- >> drivers/mmc/host/Kconfig | 8 + >> drivers/mmc/host/Makefile | 1 + >> drivers/mmc/host/au6601.c | 1215 +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 1224 insertions(+) >> create mode 100644 drivers/mmc/host/au6601.c >> >> --- /dev/null >> +++ b/drivers/mmc/host/au6601.c >> @@ -0,0 +1,1215 @@ >> +/* >> + * Copyright (C) 2014 Oleksij Rempel. >> + * >> + * Authors: Oleksij Rempel <linux@xxxxxxxxxxxxxxxx> >> + * >> + * This software is licensed under the terms of the GNU General Public >> + * License version 2, as published by the Free Software Foundation, and >> + * may be copied, distributed, and modified under those terms. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + */ >> + >> + >> +#include <linux/delay.h> >> +#include <linux/pci.h> >> +#include <linux/module.h> >> +#include <linux/io.h> >> +#include <linux/pm.h> >> +#include <linux/irq.h> >> +#include <linux/interrupt.h> >> + >> +#include <linux/mmc/host.h> >> +#include <linux/mmc/mmc.h> >> + >> +#define DRVNAME "au6601-pci" >> +#define PCI_ID_ALCOR_MICRO 0x1aea >> +#define PCI_ID_AU6601 0x6601 >> + >> +#define MHZ_TO_HZ(freq) ((freq) * 1000 * 1000) >> + >> +#define AU6601_MIN_CLOCK (150 * 1000) >> +#define AU6601_MAX_CLOCK MHZ_TO_HZ(208) >> +#define AU6601_MAX_SEGMENTS 512 >> +#define AU6601_MAX_BLOCK_LENGTH 512 >> +#define AU6601_MAX_DMA_BLOCKS 8 >> +#define AU6601_DMA_LOCAL_SEGMENTS 3 >> +#define AU6601_MAX_BLOCK_COUNT 65536 >> + >> +/* SDMA phy address. Higer then 0x0800.0000? */ >> +#define AU6601_REG_SDMA_ADDR 0x00 >> + #define AU6601_SDMA_MASK 0xfffff000 >> +/* ADMA block count? AU6621 only. */ >> +#define REG_05 0x05 >> +/* PIO */ >> +#define AU6601_REG_BUFFER 0x08 >> +/* ADMA ctrl? AU6621 only. */ >> +#define REG_0C 0x0c >> +/* ADMA phy address. AU6621 only. */ >> +#define REG_10 0x10 >> +/* CMD index */ >> +#define AU6601_REG_CMD_OPCODE 0x23 >> +/* CMD parametr */ >> +#define AU6601_REG_CMD_ARG 0x24 >> +/* CMD response 4x4 Bytes */ >> +#define AU6601_REG_CMD_RSP0 0x30 >> +#define AU6601_REG_CMD_RSP1 0x34 >> +#define AU6601_REG_CMD_RSP2 0x38 >> +#define AU6601_REG_CMD_RSP3 0x3C >> +/* LED ctrl? */ >> +#define REG_51 0x51 >> +/* ??? */ >> +#define REG_52 0x52 >> +/* LED related? Always toggled BIT0 */ >> +#define REG_61 0x61 >> +/* Same as REG_61? */ >> +#define REG_63 0x63 >> +/* ??? */ >> +#define REG_69 0x69 >> +/* Block size for SDMA or PIO */ >> +#define AU6601_REG_BLOCK_SIZE 0x6c >> +/* Some power related reg, used together with REG_7A */ >> +#define REG_70 0x70 >> +/* PLL ctrl */ >> +#define AU6601_REG_PLL_CTRL 0x72 >> +/* ??? */ >> +#define REG_74 0x74 >> +/* ??? */ >> +#define REG_75 0x75 >> +/* card slot state? */ >> +#define REG_76 0x76 >> +/* ??? */ >> +#define REG_77 0x77 >> +/* looks like soft reset? */ >> +#define AU6601_REG_SW_RESET 0x79 >> + #define AU6601_RESET_UNK BIT(7) /* unknown bit */ >> + #define AU6601_RESET_DATA BIT(3) >> + #define AU6601_RESET_CMD BIT(0) >> +/* see REG_70 */ >> +#define REG_7A 0x7a >> +/* ??? Padding? Timeing? */ >> +#define REG_7B 0x7b >> +/* ??? Padding? Timeing? */ >> +#define REG_7C 0x7c >> +/* ??? Padding? Timeing? */ >> +#define REG_7D 0x7d >> +/* read EEPROM? */ >> +#define REG_7F 0x7f >> + >> +#define AU6601_REG_CMD_CTRL 0x81 >> +#define AU6601_REG_BUS_CTRL 0x82 >> + #define AU6601_BUS_WIDTH_4BIT BIT(5) >> +#define REG_83 0x83 >> + >> +#define AU6601_REG_BUS_STATUS 0x84 >> + #define AU6601_BUS_STAT_CMD BIT(15) >> +/* BIT(4) - BIT(7) are permanently 1. >> + * May be reseved or not attached DAT4-DAT7 */ >> + #define AU6601_BUS_STAT_DAT3 BIT(3) >> + #define AU6601_BUS_STAT_DAT2 BIT(2) >> + #define AU6601_BUS_STAT_DAT1 BIT(1) >> + #define AU6601_BUS_STAT_DAT0 BIT(0) >> + #define AU6601_BUS_STAT_DAT_MASK 0xf >> +#define REG_85 0x85 >> +/* ??? */ >> +#define REG_86 0x86 >> +#define AU6601_REG_INT_STATUS 0x90 /* IRQ intmask */ >> +#define AU6601_REG_INT_ENABLE 0x94 >> +/* ??? */ >> +#define REG_A1 0xa1 >> +/* ??? */ >> +#define REG_A2 0xa2 >> +/* ??? */ >> +#define REG_A3 0xa3 >> +/* ??? */ >> +#define REG_B0 0xb0 >> +/* ??? */ >> +#define REG_B4 0xb4 >> + >> + /* AU6601_REG_INT_STATUS is identical or almost identical with sdhci.h */ >> + /* OK - are tested and confirmed bits */ >> + #define AU6601_INT_RESPONSE 0x00000001 /* ok */ >> + #define AU6601_INT_DATA_END 0x00000002 /* fifo, ok */ >> + #define AU6601_INT_BLK_GAP 0x00000004 >> + #define AU6601_INT_DMA_END 0x00000008 >> + #define AU6601_INT_SPACE_AVAIL 0x00000010 /* fifo, ok */ >> + #define AU6601_INT_DATA_AVAIL 0x00000020 /* fifo, ok */ >> + #define AU6601_INT_CARD_REMOVE 0x00000040 >> + #define AU6601_INT_CARD_INSERT 0x00000080 /* 0x40 and 0x80 flip */ >> + #define AU6601_INT_CARD_INT 0x00000100 >> + #define AU6601_INT_ERROR 0x00008000 /* ok */ >> + #define AU6601_INT_TIMEOUT 0x00010000 /* seems to be ok */ >> + #define AU6601_INT_CRC 0x00020000 /* seems to be ok */ >> + #define AU6601_INT_END_BIT 0x00040000 >> + #define AU6601_INT_INDEX 0x00080000 >> + #define AU6601_INT_DATA_TIMEOUT 0x00100000 >> + #define AU6601_INT_DATA_CRC 0x00200000 >> + #define AU6601_INT_DATA_END_BIT 0x00400000 >> + #define AU6601_INT_BUS_POWER 0x00800000 >> + #define AU6601_INT_ACMD12ERR 0x01000000 >> + #define AU6601_INT_ADMA_ERROR 0x02000000 >> + >> + #define AU6601_INT_NORMAL_MASK 0x00007FFF >> + #define AU6601_INT_ERROR_MASK 0xFFFF8000 >> + >> +/* magic 0xF0001 */ >> + #define AU6601_INT_CMD_MASK (AU6601_INT_RESPONSE | AU6601_INT_TIMEOUT | \ >> + AU6601_INT_CRC | AU6601_INT_END_BIT | AU6601_INT_INDEX) >> +/* magic 0x70003A */ >> + #define AU6601_INT_DATA_MASK (AU6601_INT_DATA_END | AU6601_INT_DMA_END | \ >> + AU6601_INT_DATA_AVAIL | AU6601_INT_SPACE_AVAIL | \ >> + AU6601_INT_DATA_TIMEOUT | AU6601_INT_DATA_CRC | \ >> + AU6601_INT_DATA_END_BIT) >> + #define AU6601_INT_ALL_MASK ((uint32_t)-1) >> + >> +bool disable_dma = 0; >> + >> +struct au6601_host { >> + struct pci_dev *pdev; >> + struct device *dev; >> + void __iomem *iobase; >> + void __iomem *virt_base; >> + dma_addr_t phys_base; >> + >> + struct mmc_host *mmc; >> + struct mmc_request *mrq; >> + struct mmc_command *cmd; >> + struct mmc_data *data; >> + unsigned int data_early:1; /* Data finished before cmd */ >> + unsigned int dma_on:1; >> + unsigned int trigger_dma_dac:1; /* Trigger Data after Command. >> + * In some cases data ragister >> + * should be triggered after >> + * command was done */ >> + >> + struct mutex cmd_mutex; >> + >> + struct timer_list timer; >> + >> + struct sg_mapping_iter sg_miter; /* SG state for PIO */ >> + unsigned int blocks; /* remaining PIO blocks */ >> + unsigned int requested_blocks; /* count of requested */ >> + int sg_count; /* Mapped sg entries */ >> +}; >> + >> +static void au6601_send_cmd(struct au6601_host *host, >> + struct mmc_command *cmd); >> + >> +static void au6601_prepare_data(struct au6601_host *host, >> + struct mmc_command *cmd); >> +static void au6601_finish_data(struct au6601_host *host); >> +static void au6601_request_complete(struct au6601_host *host); >> + >> +static const struct pci_device_id pci_ids[] = { >> + { >> + .vendor = PCI_ID_ALCOR_MICRO, >> + .device = PCI_ID_AU6601, >> + .subvendor = PCI_ANY_ID, >> + .subdevice = PCI_ANY_ID, >> + }, >> + { /* end: all zeroes */ }, >> +}; >> +MODULE_DEVICE_TABLE(pci, pci_ids); >> + >> +static inline void au6601_rmw(void __iomem *reg, u32 clear, u32 set) >> +{ >> + u32 var; >> + >> + var = ioread32(reg); >> + var &= ~clear; >> + var |= set; >> + iowrite32(var, reg); >> +} >> + >> +static inline void au6601_mask_irqs(struct au6601_host *host) >> +{ >> + iowrite32(0, host->iobase + AU6601_REG_INT_ENABLE); >> +} >> + >> +static inline void au6601_unmask_irqs(struct au6601_host *host) >> +{ >> + iowrite32(AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK | >> + AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE | >> + AU6601_INT_CARD_INT | AU6601_INT_BUS_POWER, >> + host->iobase + AU6601_REG_INT_ENABLE); >> +} >> + >> +static void au6601_clear_set_reg86(struct au6601_host *host, u32 clear, u32 set) >> +{ >> + au6601_rmw(host->iobase + REG_86, clear, set); >> +} >> + >> +/* >> + * check if one of data line is pulled down >> + */ >> +static inline int au6601_card_busy(struct au6601_host *host) >> +{ >> + u8 status; >> + >> + status = (ioread8(host->iobase + AU6601_REG_BUS_STATUS) & >> + AU6601_BUS_STAT_DAT_MASK); >> + /* If all data lines are up, then card is not busy */ >> + if (status == (AU6601_BUS_STAT_DAT0 | AU6601_BUS_STAT_DAT1 | >> + AU6601_BUS_STAT_DAT2 | AU6601_BUS_STAT_DAT3)) > > If all lines have to be up, shouldn't it be "&" instead of "|" ? No :) 01 & 10 = 00 01 | 1O = 11 in case of & status will be compared against 0. > >> + return 0; >> + >> + return 1; >> +} -- Regards, Oleksij
Attachment:
signature.asc
Description: OpenPGP digital signature