Re: [PATCH v5] mmc: add new Alcor Micro Cardreader driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 14 November 2018 at 11:15, Oleksij Rempel <linux@xxxxxxxxxxxxxxxx> wrote:
> Hi Ulf,
>
> this patch was send for one month. Just to make sure it was not forgotten.

Thanks for pinging me, I have just been too busy lately. I am getting
to it next week, as then I am back from LPC.

Kind regards
Uffe

>
> Am 14.10.18 um 12:33 schrieb Oleksij Rempel:
>> This driver provides support for Alcor Micro AU6601 and AU6621
>> card readers.
>>
>> This is single LUN HW and it is expected to work with following standards:
>> - Support SDR104 / SDR50
>> - MultiMedia Card (MMC)
>> - Memory Stick (MS)
>> - Memory Stick PRO (MS_Pro)
>>
>> Since it is a PCIe controller, it should work on any architecture
>> supporting PCIe. For now, it was developed and tested only on x86_64.
>>
>> This driver is a result of RE work and was created without any
>> documentation or real knowledge of HW internals.
>>
>> Signed-off-by: Oleksij Rempel <linux@xxxxxxxxxxxxxxxx>
>> ---
>> changes
>> 2018.10.09 v5:
>>  - fix "depends *on*" from v4 and retest results on au6601 and au6621.
>>
>> 2018.10.09 v4:
>>  - use depends MISC_ALCOR_PCI instead of select.
>>
>> 2018.10.08 v3:
>>  - rework driver and split it to PCI MFD + SDMMC parts
>>  - remove module parameters
>>  - remove most of debug outputs
>>  - remove some comments
>>
>>  drivers/misc/Makefile               |    2 +-
>>  drivers/misc/cardreader/Kconfig     |   11 +
>>  drivers/misc/cardreader/Makefile    |    4 +-
>>  drivers/misc/cardreader/alcor_pci.c |  377 +++++++++
>>  drivers/mmc/host/Kconfig            |    7 +
>>  drivers/mmc/host/Makefile           |    1 +
>>  drivers/mmc/host/alcor.c            | 1167 +++++++++++++++++++++++++++
>>  include/linux/alcor_pci.h           |  287 +++++++
>>  8 files changed, 1853 insertions(+), 3 deletions(-)
>>  create mode 100644 drivers/misc/cardreader/alcor_pci.c
>>  create mode 100644 drivers/mmc/host/alcor.c
>>  create mode 100644 include/linux/alcor_pci.h
>>
>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>> index af22bbc3d00c..fe3134cf3008 100644
>> --- a/drivers/misc/Makefile
>> +++ b/drivers/misc/Makefile
>> @@ -57,4 +57,4 @@ obj-$(CONFIG_ASPEED_LPC_CTRL)       += aspeed-lpc-ctrl.o
>>  obj-$(CONFIG_ASPEED_LPC_SNOOP)       += aspeed-lpc-snoop.o
>>  obj-$(CONFIG_PCI_ENDPOINT_TEST)      += pci_endpoint_test.o
>>  obj-$(CONFIG_OCXL)           += ocxl/
>> -obj-$(CONFIG_MISC_RTSX)              += cardreader/
>> +obj-y                += cardreader/
>> diff --git a/drivers/misc/cardreader/Kconfig b/drivers/misc/cardreader/Kconfig
>> index 69e815e32a8c..ed8993b5d058 100644
>> --- a/drivers/misc/cardreader/Kconfig
>> +++ b/drivers/misc/cardreader/Kconfig
>> @@ -1,3 +1,14 @@
>> +config MISC_ALCOR_PCI
>> +     tristate "Alcor Micro/Alcor Link PCI-E card reader"
>> +     depends on PCI
>> +     select MFD_CORE
>> +     help
>> +       This supports for Alcor Micro PCI-Express card reader including au6601,
>> +       au6621.
>> +       Alcor Micro card readers support access to many types of memory cards,
>> +       such as Memory Stick, Memory Stick Pro, Secure Digital and
>> +       MultiMediaCard.
>> +
>>  config MISC_RTSX_PCI
>>       tristate "Realtek PCI-E card reader"
>>       depends on PCI
>> diff --git a/drivers/misc/cardreader/Makefile b/drivers/misc/cardreader/Makefile
>> index 9fabfcc6fa7a..9882d2a1025c 100644
>> --- a/drivers/misc/cardreader/Makefile
>> +++ b/drivers/misc/cardreader/Makefile
>> @@ -1,4 +1,4 @@
>> -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o
>> -
>> +obj-$(CONFIG_MISC_ALCOR_PCI) += alcor_pci.o
>>  obj-$(CONFIG_MISC_RTSX_PCI)  += rtsx_pci.o
>> +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o
>>  obj-$(CONFIG_MISC_RTSX_USB)  += rtsx_usb.o
>> diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c
>> new file mode 100644
>> index 000000000000..899904a92b01
>> --- /dev/null
>> +++ b/drivers/misc/cardreader/alcor_pci.c
>> @@ -0,0 +1,377 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (C) 2018 Oleksij Rempel <linux@xxxxxxxxxxxxxxxx>
>> + *
>> + * Driver for Alcor Micro AU6601 and AU6621 controllers
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/irq.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm.h>
>> +
>> +#include <linux/alcor_pci.h>
>> +
>> +static DEFINE_IDR(alcor_pci_idr);
>> +static DEFINE_SPINLOCK(alcor_pci_lock);
>> +
>> +static struct mfd_cell alcor_pci_cells[] = {
>> +     [ALCOR_SD_CARD] = {
>> +             .name = DRV_NAME_ALCOR_PCI_SDMMC,
>> +     },
>> +     [ALCOR_MS_CARD] = {
>> +             .name = DRV_NAME_ALCOR_PCI_MS,
>> +     },
>> +};
>> +
>> +static const struct alcor_dev_cfg alcor_cfg = {
>> +     .dma = 0,
>> +};
>> +
>> +static const struct alcor_dev_cfg au6621_cfg = {
>> +     .dma = 1,
>> +};
>> +
>> +static const struct pci_device_id pci_ids[] = {
>> +     { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6601),
>> +             .driver_data = (kernel_ulong_t)&alcor_cfg },
>> +     { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6621),
>> +             .driver_data = (kernel_ulong_t)&au6621_cfg },
>> +     { },
>> +};
>> +MODULE_DEVICE_TABLE(pci, pci_ids);
>> +
>> +void alcor_write8(struct alcor_pci_priv *priv, u8 val, unsigned int addr)
>> +{
>> +     writeb(val, priv->iobase + addr);
>> +}
>> +EXPORT_SYMBOL_GPL(alcor_write8);
>> +
>> +void alcor_write16(struct alcor_pci_priv *priv, u16 val, unsigned int addr)
>> +{
>> +     writew(val, priv->iobase + addr);
>> +}
>> +EXPORT_SYMBOL_GPL(alcor_write16);
>> +
>> +void alcor_write32(struct alcor_pci_priv *priv, u32 val, unsigned int addr)
>> +{
>> +     writel(val, priv->iobase + addr);
>> +}
>> +EXPORT_SYMBOL_GPL(alcor_write32);
>> +
>> +void alcor_write32be(struct alcor_pci_priv *priv, u32 val, unsigned int addr)
>> +{
>> +     iowrite32be(val, priv->iobase + addr);
>> +}
>> +EXPORT_SYMBOL_GPL(alcor_write32be);
>> +
>> +u8 alcor_read8(struct alcor_pci_priv *priv, unsigned int addr)
>> +{
>> +     return readb(priv->iobase + addr);
>> +}
>> +EXPORT_SYMBOL_GPL(alcor_read8);
>> +
>> +u32 alcor_read32(struct alcor_pci_priv *priv, unsigned int addr)
>> +{
>> +     return readl(priv->iobase + addr);
>> +}
>> +EXPORT_SYMBOL_GPL(alcor_read32);
>> +
>> +u32 alcor_read32be(struct alcor_pci_priv *priv, unsigned int addr)
>> +{
>> +     return ioread32be(priv->iobase + addr);
>> +}
>> +EXPORT_SYMBOL_GPL(alcor_read32be);
>> +
>> +static int alcor_pci_find_cap_offset(struct alcor_pci_priv *priv,
>> +                                  struct pci_dev *pci)
>> +{
>> +     int where;
>> +     u8 val8;
>> +     u32 val32;
>> +
>> +     where = ALCOR_CAP_START_OFFSET;
>> +     pci_read_config_byte(pci, where, &val8);
>> +     if (!val8)
>> +             return 0;
>> +
>> +     where = (int)val8;
>> +     while (1) {
>> +             pci_read_config_dword(pci, where, &val32);
>> +             if (val32 == 0xffffffff) {
>> +                     dev_dbg(priv->dev, "find_cap_offset invailid value %x.\n",
>> +                             val32);
>> +                     return 0;
>> +             }
>> +
>> +             if ((val32 & 0xff) == 0x10) {
>> +                     dev_dbg(priv->dev, "pcie cap offset: %x\n", where);
>> +                     return where;
>> +             }
>> +
>> +             if ((val32 & 0xff00) == 0x00) {
>> +                     dev_dbg(priv->dev, "pci_find_cap_offset invailid value %x.\n",
>> +                             val32);
>> +                     break;
>> +             }
>> +             where = (int)((val32 >> 8) & 0xff);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static void alcor_pci_init_check_aspm(struct alcor_pci_priv *priv)
>> +{
>> +     struct pci_dev *pci;
>> +     int where;
>> +     u32 val32;
>> +
>> +     priv->pdev_cap_off    = alcor_pci_find_cap_offset(priv, priv->pdev);
>> +     priv->parent_cap_off = alcor_pci_find_cap_offset(priv,
>> +                                                      priv->parent_pdev);
>> +
>> +     if ((priv->pdev_cap_off == 0) || (priv->parent_cap_off == 0)) {
>> +             dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n",
>> +                     priv->pdev_cap_off, priv->parent_cap_off);
>> +             return;
>> +     }
>> +
>> +     /* link capability */
>> +     pci   = priv->pdev;
>> +     where = priv->pdev_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET;
>> +     pci_read_config_dword(pci, where, &val32);
>> +     priv->pdev_aspm_cap = (u8)(val32 >> 10) & 0x03;
>> +
>> +     pci   = priv->parent_pdev;
>> +     where = priv->parent_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET;
>> +     pci_read_config_dword(pci, where, &val32);
>> +     priv->parent_aspm_cap = (u8)(val32 >> 10) & 0x03;
>> +
>> +     if (priv->pdev_aspm_cap != priv->parent_aspm_cap) {
>> +             u8 aspm_cap;
>> +
>> +             dev_dbg(priv->dev, "pdev_aspm_cap: %x, parent_aspm_cap: %x\n",
>> +                     priv->pdev_aspm_cap, priv->parent_aspm_cap);
>> +             aspm_cap = priv->pdev_aspm_cap & priv->parent_aspm_cap;
>> +             priv->pdev_aspm_cap    = aspm_cap;
>> +             priv->parent_aspm_cap = aspm_cap;
>> +     }
>> +
>> +     dev_dbg(priv->dev, "ext_config_dev_aspm: %x, pdev_aspm_cap: %x\n",
>> +             priv->ext_config_dev_aspm, priv->pdev_aspm_cap);
>> +     priv->ext_config_dev_aspm &= priv->pdev_aspm_cap;
>> +}
>> +
>> +static void alcor_pci_aspm_ctrl(struct alcor_pci_priv *priv, u8 aspm_enable)
>> +{
>> +     struct pci_dev *pci;
>> +     u8 aspm_ctrl, i;
>> +     int where;
>> +     u32 val32;
>> +
>> +     if ((!priv->pdev_cap_off) || (!priv->parent_cap_off)) {
>> +             dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n",
>> +                     priv->pdev_cap_off, priv->parent_cap_off);
>> +             return;
>> +     }
>> +
>> +     if (!priv->pdev_aspm_cap)
>> +             return;
>> +
>> +     aspm_ctrl = 0;
>> +     if (aspm_enable) {
>> +             aspm_ctrl = priv->ext_config_dev_aspm;
>> +
>> +             if (!aspm_ctrl) {
>> +                     dev_dbg(priv->dev, "aspm_ctrl == 0\n");
>> +                     return;
>> +             }
>> +     }
>> +
>> +     for (i = 0; i < 2; i++) {
>> +
>> +             if (i) {
>> +                     pci   = priv->parent_pdev;
>> +                     where = priv->parent_cap_off
>> +                             + ALCOR_PCIE_LINK_CTRL_OFFSET;
>> +             } else {
>> +                     pci   = priv->pdev;
>> +                     where = priv->pdev_cap_off
>> +                             + ALCOR_PCIE_LINK_CTRL_OFFSET;
>> +             }
>> +
>> +             pci_read_config_dword(pci, where, &val32);
>> +             val32 &= (~0x03);
>> +             val32 |= (aspm_ctrl & priv->pdev_aspm_cap);
>> +             pci_write_config_byte(pci, where, (u8)val32);
>> +     }
>> +
>> +}
>> +
>> +static inline void alcor_mask_sd_irqs(struct alcor_pci_priv *priv)
>> +{
>> +     alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
>> +}
>> +
>> +static inline void alcor_unmask_sd_irqs(struct alcor_pci_priv *priv)
>> +{
>> +     alcor_write32(priv, AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK |
>> +               AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE |
>> +               AU6601_INT_OVER_CURRENT_ERR,
>> +               AU6601_REG_INT_ENABLE);
>> +}
>> +
>> +static inline void alcor_mask_ms_irqs(struct alcor_pci_priv *priv)
>> +{
>> +     alcor_write32(priv, 0, AU6601_MS_INT_ENABLE);
>> +}
>> +
>> +static inline void alcor_unmask_ms_irqs(struct alcor_pci_priv *priv)
>> +{
>> +     alcor_write32(priv, 0x3d00fa, AU6601_MS_INT_ENABLE);
>> +}
>> +
>> +static int alcor_pci_probe(struct pci_dev *pdev,
>> +                        const struct pci_device_id *ent)
>> +{
>> +     struct alcor_dev_cfg *cfg;
>> +     struct alcor_pci_priv *priv;
>> +     int ret, i, bar = 0;
>> +
>> +     cfg = (void *)ent->driver_data;
>> +
>> +     ret = pcim_enable_device(pdev);
>> +     if (ret)
>> +             return ret;
>> +
>> +     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> +     if (!priv)
>> +             return -ENOMEM;
>> +
>> +     idr_preload(GFP_KERNEL);
>> +     spin_lock(&alcor_pci_lock);
>> +     ret = idr_alloc(&alcor_pci_idr, priv, 0, 0, GFP_NOWAIT);
>> +     if (ret >= 0)
>> +             priv->id = ret;
>> +     spin_unlock(&alcor_pci_lock);
>> +     idr_preload_end();
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     priv->pdev = pdev;
>> +     priv->parent_pdev = pdev->bus->self;
>> +     priv->dev = &pdev->dev;
>> +     priv->cfg = cfg;
>> +     priv->irq = pdev->irq;
>> +
>> +     ret = pci_request_regions(pdev, DRV_NAME_ALCOR_PCI);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Cannot request region\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
>> +             dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
>> +             ret = -ENODEV;
>> +             goto error_release_regions;
>> +     }
>> +
>> +     priv->iobase = pcim_iomap(pdev, bar, 0);
>> +     if (!priv->iobase) {
>> +             ret = -ENOMEM;
>> +             goto error_release_regions;
>> +     }
>> +
>> +     /* make sure irqs are disabled */
>> +     alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
>> +     alcor_write32(priv, 0, AU6601_MS_INT_ENABLE);
>> +
>> +     ret = dma_set_mask_and_coherent(priv->dev, AU6601_SDMA_MASK);
>> +     if (ret) {
>> +             dev_err(priv->dev, "Failed to set DMA mask\n");
>> +             goto error_release_regions;
>> +     }
>> +
>> +     pci_set_master(pdev);
>> +     pci_set_drvdata(pdev, priv);
>> +     alcor_pci_init_check_aspm(priv);
>> +
>> +     for (i = 0; i < ARRAY_SIZE(alcor_pci_cells); i++) {
>> +             alcor_pci_cells[i].platform_data = priv;
>> +             alcor_pci_cells[i].pdata_size = sizeof(*priv);
>> +     }
>> +     ret = mfd_add_devices(&pdev->dev, priv->id, alcor_pci_cells,
>> +                     ARRAY_SIZE(alcor_pci_cells), NULL, 0, NULL);
>> +     if (ret < 0)
>> +             goto error_release_regions;
>> +
>> +     alcor_pci_aspm_ctrl(priv, 0);
>> +
>> +     return 0;
>> +
>> +error_release_regions:
>> +     pci_release_regions(pdev);
>> +     return ret;
>> +}
>> +
>> +static void alcor_pci_remove(struct pci_dev *pdev)
>> +{
>> +     struct alcor_pci_priv *priv;
>> +
>> +     priv = pci_get_drvdata(pdev);
>> +
>> +     alcor_pci_aspm_ctrl(priv, 1);
>> +
>> +     mfd_remove_devices(&pdev->dev);
>> +
>> +     spin_lock(&alcor_pci_lock);
>> +     idr_remove(&alcor_pci_idr, priv->id);
>> +     spin_unlock(&alcor_pci_lock);
>> +
>> +     pci_release_regions(pdev);
>> +     pci_set_drvdata(pdev, NULL);
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int alcor_suspend(struct device *dev)
>> +{
>> +     struct pci_dev *pdev = to_pci_dev(dev);
>> +     struct alcor_pci_priv *priv = pci_get_drvdata(pdev);
>> +
>> +     alcor_pci_aspm_ctrl(priv, 1);
>> +     return 0;
>> +}
>> +
>> +static int alcor_resume(struct device *dev)
>> +{
>> +
>> +     struct pci_dev *pdev = to_pci_dev(dev);
>> +     struct alcor_pci_priv *priv = pci_get_drvdata(pdev);
>> +
>> +     alcor_pci_aspm_ctrl(priv, 0);
>> +     return 0;
>> +}
>> +#endif /* CONFIG_PM_SLEEP */
>> +
>> +static SIMPLE_DEV_PM_OPS(alcor_pm_ops, alcor_suspend, alcor_resume);
>> +
>> +static struct pci_driver alcor_driver = {
>> +     .name   =       DRV_NAME_ALCOR_PCI,
>> +     .id_table =     pci_ids,
>> +     .probe  =       alcor_pci_probe,
>> +     .remove =       alcor_pci_remove,
>> +     .driver =       {
>> +             .pm     = &alcor_pm_ops
>> +     },
>> +};
>> +
>> +module_pci_driver(alcor_driver);
>> +
>> +MODULE_AUTHOR("Oleksij Rempel <linux@xxxxxxxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
>> index 694d0828215d..f0627fc96040 100644
>> --- a/drivers/mmc/host/Kconfig
>> +++ b/drivers/mmc/host/Kconfig
>> @@ -430,6 +430,13 @@ config MMC_WBSD
>>
>>         If unsure, say N.
>>
>> +config MMC_ALCOR
>> +     tristate "Alcor Micro/Alcor Link SD/MMC controller"
>> +     depends on MISC_ALCOR_PCI
>> +     help
>> +       Say Y here to include driver code to support SD/MMC card interface
>> +       of Alcor Micro PCI-E card reader
>> +
>>  config MMC_AU1X
>>       tristate "Alchemy AU1XX0 MMC Card Interface support"
>>       depends on MIPS_ALCHEMY
>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>> index ce8398e6f2c0..271e488a7173 100644
>> --- a/drivers/mmc/host/Makefile
>> +++ b/drivers/mmc/host/Makefile
>> @@ -23,6 +23,7 @@ obj-$(CONFIG_MMC_SDHCI_F_SDH30)     += sdhci_f_sdh30.o
>>  obj-$(CONFIG_MMC_SDHCI_SPEAR)        += sdhci-spear.o
>>  obj-$(CONFIG_MMC_WBSD)               += wbsd.o
>>  obj-$(CONFIG_MMC_AU1X)               += au1xmmc.o
>> +obj-$(CONFIG_MMC_ALCOR)      += alcor.o
>>  obj-$(CONFIG_MMC_MTK)                += mtk-sd.o
>>  obj-$(CONFIG_MMC_OMAP)               += omap.o
>>  obj-$(CONFIG_MMC_OMAP_HS)    += omap_hsmmc.o
>> diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c
>> new file mode 100644
>> index 000000000000..461733405c0d
>> --- /dev/null
>> +++ b/drivers/mmc/host/alcor.c
>> @@ -0,0 +1,1167 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (C) 2018 Oleksij Rempel <linux@xxxxxxxxxxxxxxxx>
>> + *
>> + * Driver for Alcor Micro AU6601 and AU6621 controllers
>> + */
>> +
>> +/* Note: this driver was created without any documentation. Based
>> + * on sniffing, testing and in some cases mimic of original driver.
>> + * As soon as some one with documentation or more experience in SD/MMC, or
>> + * reverse engineering then me, please review this driver and question every
>> + * thing what I did. 2018 Oleksij Rempel <linux@xxxxxxxxxxxxxxxx>
>> + */
>> +
>> +#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/platform_device.h>
>> +
>> +#include <linux/mmc/host.h>
>> +#include <linux/mmc/mmc.h>
>> +
>> +#include <linux/alcor_pci.h>
>> +
>> +enum alcor_cookie {
>> +     COOKIE_UNMAPPED,
>> +     COOKIE_PRE_MAPPED,
>> +     COOKIE_MAPPED,
>> +};
>> +
>> +struct alcor_pll_conf {
>> +     unsigned int clk_src_freq;
>> +     unsigned int clk_src_reg;
>> +     unsigned int min_div;
>> +     unsigned int max_div;
>> +};
>> +
>> +struct alcor_sdmmc_host {
>> +     struct  device *dev;
>> +     struct alcor_pci_priv *alcor_pci;
>> +
>> +     struct mmc_host *mmc;
>> +     struct mmc_request *mrq;
>> +     struct mmc_command *cmd;
>> +     struct mmc_data *data;
>> +     unsigned int dma_on:1;
>> +     unsigned int early_data:1;
>> +
>> +     struct mutex cmd_mutex;
>> +
>> +     struct delayed_work timeout_work;
>> +
>> +     struct sg_mapping_iter sg_miter;        /* SG state for PIO */
>> +     struct scatterlist *sg;
>> +     unsigned int blocks;            /* remaining PIO blocks */
>> +     int sg_count;
>> +
>> +     u32                     irq_status_sd;
>> +     unsigned char           cur_power_mode;
>> +};
>> +
>> +static const struct alcor_pll_conf alcor_pll_cfg[] = {
>> +     /* MHZ,         CLK src,                max div, min div */
>> +     { 31250000,     AU6601_CLK_31_25_MHZ,   1,      511},
>> +     { 48000000,     AU6601_CLK_48_MHZ,      1,      511},
>> +     {125000000,     AU6601_CLK_125_MHZ,     1,      511},
>> +     {384000000,     AU6601_CLK_384_MHZ,     1,      511},
>> +};
>> +
>> +static inline void alcor_rmw8(struct alcor_sdmmc_host *host, unsigned int addr,
>> +                            u8 clear, u8 set)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     u32 var;
>> +
>> +     var = alcor_read8(priv, addr);
>> +     var &= ~clear;
>> +     var |= set;
>> +     alcor_write8(priv, var, addr);
>> +}
>> +
>> +/* As soon as irqs are masked, some status updates may be missed.
>> + * Use this with care.
>> + */
>> +static inline void alcor_mask_sd_irqs(struct alcor_sdmmc_host *host)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +
>> +     alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
>> +}
>> +
>> +static inline void alcor_unmask_sd_irqs(struct alcor_sdmmc_host *host)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +
>> +     alcor_write32(priv, AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK |
>> +               AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE |
>> +               AU6601_INT_OVER_CURRENT_ERR,
>> +               AU6601_REG_INT_ENABLE);
>> +}
>> +
>> +static void alcor_reset(struct alcor_sdmmc_host *host, u8 val)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     int i;
>> +
>> +     alcor_write8(priv, val | AU6601_BUF_CTRL_RESET,
>> +                   AU6601_REG_SW_RESET);
>> +     for (i = 0; i < 100; i++) {
>> +             if (!(alcor_read8(priv, AU6601_REG_SW_RESET) & val))
>> +                     return;
>> +             udelay(50);
>> +     }
>> +     dev_err(host->dev, "%s: timeout\n", __func__);
>> +}
>> +
>> +static void alcor_data_set_dma(struct alcor_sdmmc_host *host)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     u32 addr, len;
>> +
>> +     if (!host->sg_count)
>> +             return;
>> +
>> +     if (!host->sg) {
>> +             dev_err(host->dev, "have blocks, but no SG\n");
>> +             return;
>> +     }
>> +
>> +     if (!sg_dma_len(host->sg)) {
>> +             dev_err(host->dev, "DMA SG len == 0\n");
>> +             return;
>> +     }
>> +
>> +
>> +     addr = (u32)sg_dma_address(host->sg);
>> +     len = sg_dma_len(host->sg);
>> +
>> +     alcor_write32(priv, addr, AU6601_REG_SDMA_ADDR);
>> +     host->sg = sg_next(host->sg);
>> +     host->sg_count--;
>> +}
>> +
>> +static void alcor_trigger_data_transfer(struct alcor_sdmmc_host *host,
>> +                                     bool early)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     struct mmc_data *data = host->data;
>> +     u8 ctrl = 0;
>> +
>> +     if (data->flags & MMC_DATA_WRITE)
>> +             ctrl |= AU6601_DATA_WRITE;
>> +
>> +     if (data->host_cookie == COOKIE_MAPPED) {
>> +             if (host->early_data) {
>> +                     host->early_data = false;
>> +                     return;
>> +             }
>> +
>> +             host->early_data = early;
>> +
>> +             alcor_data_set_dma(host);
>> +             ctrl |= AU6601_DATA_DMA_MODE;
>> +             host->dma_on = 1;
>> +             alcor_write32(priv, data->sg_count * 0x1000,
>> +                            AU6601_REG_BLOCK_SIZE);
>> +     } else {
>> +             alcor_write32(priv, data->blksz, AU6601_REG_BLOCK_SIZE);
>> +     }
>> +
>> +     alcor_write8(priv, ctrl | AU6601_DATA_START_XFER,
>> +                   AU6601_DATA_XFER_CTRL);
>> +}
>> +
>> +static void alcor_trf_block_pio(struct alcor_sdmmc_host *host, bool read)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     size_t blksize, len;
>> +     u8 *buf;
>> +
>> +     if (!host->blocks)
>> +             return;
>> +
>> +     if (host->dma_on) {
>> +             dev_err(host->dev, "configured DMA but got PIO request.\n");
>> +             return;
>> +     }
>> +
>> +     if (!!(host->data->flags & MMC_DATA_READ) != read) {
>> +             dev_err(host->dev, "got unexpected direction %i != %i\n",
>> +                     !!(host->data->flags & MMC_DATA_READ), read);
>> +     }
>> +
>> +     if (!sg_miter_next(&host->sg_miter))
>> +             return;
>> +
>> +     blksize = host->data->blksz;
>> +     len = min(host->sg_miter.length, blksize);
>> +
>> +     dev_dbg(host->dev, "PIO, %s block size: 0x%zx\n",
>> +             read ? "read" : "write", blksize);
>> +
>> +     host->sg_miter.consumed = len;
>> +     host->blocks--;
>> +
>> +     buf = host->sg_miter.addr;
>> +
>> +     if (read)
>> +             ioread32_rep(priv->iobase + AU6601_REG_BUFFER, buf, len >> 2);
>> +     else
>> +             iowrite32_rep(priv->iobase + AU6601_REG_BUFFER, buf, len >> 2);
>> +
>> +     sg_miter_stop(&host->sg_miter);
>> +}
>> +
>> +static void alcor_prepare_sg_miter(struct alcor_sdmmc_host *host)
>> +{
>> +     unsigned int flags = SG_MITER_ATOMIC;
>> +     struct mmc_data *data = host->data;
>> +
>> +     if (data->flags & MMC_DATA_READ)
>> +             flags |= SG_MITER_TO_SG;
>> +     else
>> +             flags |= SG_MITER_FROM_SG;
>> +     sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
>> +}
>> +
>> +static void alcor_prepare_data(struct alcor_sdmmc_host *host,
>> +                            struct mmc_command *cmd)
>> +{
>> +     struct mmc_data *data = cmd->data;
>> +
>> +     if (!data)
>> +             return;
>> +
>> +
>> +     host->data = data;
>> +     host->data->bytes_xfered = 0;
>> +     host->blocks = data->blocks;
>> +     host->sg = data->sg;
>> +     host->sg_count = data->sg_count;
>> +     dev_dbg(host->dev, "prepare DATA: sg %i, blocks: %i\n",
>> +                     host->sg_count, host->blocks);
>> +
>> +     if (data->host_cookie != COOKIE_MAPPED)
>> +             alcor_prepare_sg_miter(host);
>> +
>> +     alcor_trigger_data_transfer(host, true);
>> +}
>> +
>> +static void alcor_send_cmd(struct alcor_sdmmc_host *host,
>> +                        struct mmc_command *cmd, bool set_timeout)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     unsigned long timeout = 0;
>> +     u8 ctrl = 0;
>> +
>> +     host->cmd = cmd;
>> +     alcor_prepare_data(host, cmd);
>> +
>> +     dev_dbg(host->dev, "send CMD. opcode: 0x%02x, arg; 0x%08x\n",
>> +             cmd->opcode, cmd->arg);
>> +     alcor_write8(priv, cmd->opcode | 0x40, AU6601_REG_CMD_OPCODE);
>> +     alcor_write32be(priv, cmd->arg, AU6601_REG_CMD_ARG);
>> +
>> +     switch (mmc_resp_type(cmd)) {
>> +     case MMC_RSP_NONE:
>> +             ctrl = AU6601_CMD_NO_RESP;
>> +             break;
>> +     case MMC_RSP_R1:
>> +             ctrl = AU6601_CMD_6_BYTE_CRC;
>> +             break;
>> +     case MMC_RSP_R1B:
>> +             ctrl = AU6601_CMD_6_BYTE_CRC | AU6601_CMD_STOP_WAIT_RDY;
>> +             break;
>> +     case MMC_RSP_R2:
>> +             ctrl = AU6601_CMD_17_BYTE_CRC;
>> +             break;
>> +     case MMC_RSP_R3:
>> +             ctrl = AU6601_CMD_6_BYTE_WO_CRC;
>> +             break;
>> +     default:
>> +             dev_err(host->dev, "%s: cmd->flag (0x%02x) is not valid\n",
>> +                     mmc_hostname(host->mmc), mmc_resp_type(cmd));
>> +             break;
>> +     }
>> +
>> +     if (set_timeout) {
>> +             if (!cmd->data && cmd->busy_timeout)
>> +                     timeout = cmd->busy_timeout;
>> +             else
>> +                     timeout = 10000;
>> +
>> +             schedule_delayed_work(&host->timeout_work,
>> +                                   msecs_to_jiffies(timeout));
>> +     }
>> +
>> +     dev_dbg(host->dev, "xfer ctrl: 0x%02x; timeout: %lu\n", ctrl, timeout);
>> +     alcor_write8(priv, ctrl | AU6601_CMD_START_XFER,
>> +                              AU6601_CMD_XFER_CTRL);
>> +}
>> +
>> +static void alcor_request_complete(struct alcor_sdmmc_host *host,
>> +                                bool cancel_timeout)
>> +{
>> +     struct mmc_request *mrq;
>> +
>> +     /*
>> +      * If this tasklet gets rescheduled while running, it will
>> +      * be run again afterwards but without any active request.
>> +      */
>> +     if (!host->mrq)
>> +             return;
>> +
>> +     if (cancel_timeout)
>> +             cancel_delayed_work(&host->timeout_work);
>> +
>> +     mrq = host->mrq;
>> +
>> +     host->mrq = NULL;
>> +     host->cmd = NULL;
>> +     host->data = NULL;
>> +     host->dma_on = 0;
>> +
>> +     mmc_request_done(host->mmc, mrq);
>> +}
>> +
>> +static void alcor_finish_data(struct alcor_sdmmc_host *host)
>> +{
>> +     struct mmc_data *data;
>> +
>> +     data = host->data;
>> +     host->data = NULL;
>> +     host->dma_on = 0;
>> +
>> +     /*
>> +      * The specification states that the block count register must
>> +      * be updated, but it does not specify at what point in the
>> +      * data flow. That makes the register entirely useless to read
>> +      * back so we have to assume that nothing made it to the card
>> +      * in the event of an error.
>> +      */
>> +     if (data->error)
>> +             data->bytes_xfered = 0;
>> +     else
>> +             data->bytes_xfered = data->blksz * data->blocks;
>> +
>> +     /*
>> +      * Need to send CMD12 if -
>> +      * a) open-ended multiblock transfer (no CMD23)
>> +      * b) error in multiblock transfer
>> +      */
>> +     if (data->stop &&
>> +         (data->error ||
>> +          !host->mrq->sbc)) {
>> +
>> +             /*
>> +              * The controller needs a reset of internal state machines
>> +              * upon error conditions.
>> +              */
>> +             if (data->error)
>> +                     alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA);
>> +
>> +             alcor_unmask_sd_irqs(host);
>> +             alcor_send_cmd(host, data->stop, false);
>> +             return;
>> +     }
>> +
>> +     alcor_request_complete(host, 1);
>> +}
>> +
>> +static void alcor_err_irq(struct alcor_sdmmc_host *host, u32 intmask)
>> +{
>> +     dev_dbg(host->dev, "ERR IRQ %x\n", intmask);
>> +
>> +     if (host->cmd) {
>> +             if (intmask & AU6601_INT_CMD_TIMEOUT_ERR)
>> +                     host->cmd->error = -ETIMEDOUT;
>> +             else
>> +                     host->cmd->error = -EILSEQ;
>> +     }
>> +
>> +     if (host->data) {
>> +             if (intmask & AU6601_INT_DATA_TIMEOUT_ERR)
>> +                     host->data->error = -ETIMEDOUT;
>> +             else
>> +                     host->data->error = -EILSEQ;
>> +
>> +             host->data->bytes_xfered = 0;
>> +     }
>> +
>> +     alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA);
>> +     alcor_request_complete(host, 1);
>> +}
>> +
>> +static int alcor_cmd_irq_done(struct alcor_sdmmc_host *host, u32 intmask)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +
>> +     intmask &= AU6601_INT_CMD_END;
>> +
>> +     if (!intmask)
>> +             return true;
>> +
>> +     /* got CMD_END but no CMD is in progress, wake thread an process the
>> +      * error
>> +      */
>> +     if (!host->cmd)
>> +             return false;
>> +
>> +     if (host->cmd->flags & MMC_RSP_PRESENT) {
>> +             struct mmc_command *cmd = host->cmd;
>> +
>> +             cmd->resp[0] = alcor_read32be(priv, AU6601_REG_CMD_RSP0);
>> +             dev_dbg(host->dev, "RSP0: 0x%04x\n", cmd->resp[0]);
>> +             if (host->cmd->flags & MMC_RSP_136) {
>> +                     cmd->resp[1] =
>> +                             alcor_read32be(priv, AU6601_REG_CMD_RSP1);
>> +                     cmd->resp[2] =
>> +                             alcor_read32be(priv, AU6601_REG_CMD_RSP2);
>> +                     cmd->resp[3] =
>> +                             alcor_read32be(priv, AU6601_REG_CMD_RSP3);
>> +                     dev_dbg(host->dev, "RSP1,2,3: 0x%04x 0x%04x 0x%04x\n",
>> +                             cmd->resp[1], cmd->resp[2], cmd->resp[3]);
>> +             }
>> +
>> +     }
>> +
>> +     host->cmd->error = 0;
>> +
>> +     /* Processed actual command. */
>> +     if (!host->data)
>> +             return false;
>> +
>> +     alcor_trigger_data_transfer(host, false);
>> +     host->cmd = NULL;
>> +     return true;
>> +}
>> +
>> +static void alcor_cmd_irq_thread(struct alcor_sdmmc_host *host, u32 intmask)
>> +{
>> +     intmask &= AU6601_INT_CMD_END;
>> +
>> +     if (!intmask)
>> +             return;
>> +
>> +     if (!host->cmd && intmask & AU6601_INT_CMD_END) {
>> +             dev_dbg(host->dev, "Got command interrupt 0x%08x even though no command operation was in progress.\n",
>> +                     intmask);
>> +     }
>> +
>> +     /* Processed actual command. */
>> +     if (!host->data)
>> +             alcor_request_complete(host, 1);
>> +     else
>> +             alcor_trigger_data_transfer(host, false);
>> +     host->cmd = NULL;
>> +}
>> +
>> +static int alcor_data_irq_done(struct alcor_sdmmc_host *host, u32 intmask)
>> +{
>> +     u32 tmp;
>> +
>> +     intmask &= AU6601_INT_DATA_MASK;
>> +
>> +     /* nothing here to do */
>> +     if (!intmask)
>> +             return 1;
>> +
>> +     /* we was too fast and got DATA_END after it was processed?
>> +      * lets ignore it for now.
>> +      */
>> +     if (!host->data && intmask == AU6601_INT_DATA_END)
>> +             return 1;
>> +
>> +     /* looks like an error, so lets handle it. */
>> +     if (!host->data)
>> +             return 0;
>> +
>> +     tmp = intmask & (AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY
>> +                      | AU6601_INT_DMA_END);
>> +     switch (tmp) {
>> +     case 0:
>> +             break;
>> +     case AU6601_INT_READ_BUF_RDY:
>> +             alcor_trf_block_pio(host, true);
>> +             if (!host->blocks)
>> +                     break;
>> +             alcor_trigger_data_transfer(host, false);
>> +             return 1;
>> +     case AU6601_INT_WRITE_BUF_RDY:
>> +             alcor_trf_block_pio(host, false);
>> +             if (!host->blocks)
>> +                     break;
>> +             alcor_trigger_data_transfer(host, false);
>> +             return 1;
>> +     case AU6601_INT_DMA_END:
>> +             if (!host->sg_count)
>> +                     break;
>> +
>> +             alcor_data_set_dma(host);
>> +             break;
>> +     default:
>> +             dev_err(host->dev, "Got READ_BUF_RDY and WRITE_BUF_RDY at same time\n");
>> +             break;
>> +     }
>> +
>> +     if (intmask & AU6601_INT_DATA_END)
>> +             return 0;
>> +
>> +     return 1;
>> +}
>> +
>> +static void alcor_data_irq_thread(struct alcor_sdmmc_host *host, u32 intmask)
>> +{
>> +     intmask &= AU6601_INT_DATA_MASK;
>> +
>> +     if (!intmask)
>> +             return;
>> +
>> +     if (!host->data) {
>> +             dev_dbg(host->dev, "Got data interrupt 0x%08x even though no data operation was in progress.\n",
>> +                     intmask);
>> +             alcor_reset(host, AU6601_RESET_DATA);
>> +             return;
>> +     }
>> +
>> +     if (alcor_data_irq_done(host, intmask))
>> +             return;
>> +
>> +     if ((intmask & AU6601_INT_DATA_END) || !host->blocks ||
>> +         (host->dma_on && !host->sg_count))
>> +             alcor_finish_data(host);
>> +}
>> +
>> +static void alcor_cd_irq(struct alcor_sdmmc_host *host, u32 intmask)
>> +{
>> +     dev_dbg(host->dev, "card %s\n",
>> +             intmask & AU6601_INT_CARD_REMOVE ? "removed" : "inserted");
>> +
>> +     if (host->mrq) {
>> +             dev_dbg(host->dev, "cancel all pending tasks.\n");
>> +
>> +             if (host->data)
>> +                     host->data->error = -ENOMEDIUM;
>> +
>> +             if (host->cmd)
>> +                     host->cmd->error = -ENOMEDIUM;
>> +             else
>> +                     host->mrq->cmd->error = -ENOMEDIUM;
>> +
>> +             alcor_request_complete(host, 1);
>> +     }
>> +
>> +     mmc_detect_change(host->mmc, msecs_to_jiffies(1));
>> +}
>> +
>> +static irqreturn_t alcor_irq_thread(int irq, void *d)
>> +{
>> +     struct alcor_sdmmc_host *host = d;
>> +     irqreturn_t ret = IRQ_HANDLED;
>> +     u32 intmask, tmp;
>> +
>> +     mutex_lock(&host->cmd_mutex);
>> +
>> +     intmask = host->irq_status_sd;
>> +
>> +     /* some thing bad */
>> +     if (unlikely(!intmask || AU6601_INT_ALL_MASK == intmask)) {
>> +             dev_dbg(host->dev, "unexpected IRQ: 0x%04x\n", intmask);
>> +             ret = IRQ_NONE;
>> +             goto exit;
>> +     }
>> +
>> +     tmp = intmask & (AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK);
>> +     if (tmp) {
>> +             if (tmp & AU6601_INT_ERROR_MASK)
>> +                     alcor_err_irq(host, tmp);
>> +             else {
>> +                     alcor_cmd_irq_thread(host, tmp);
>> +                     alcor_data_irq_thread(host, tmp);
>> +             }
>> +             intmask &= ~(AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK);
>> +     }
>> +
>> +     if (intmask & (AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE)) {
>> +             alcor_cd_irq(host, intmask);
>> +             intmask &= ~(AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE);
>> +     }
>> +
>> +     if (intmask & AU6601_INT_OVER_CURRENT_ERR) {
>> +             dev_warn(host->dev,
>> +                      "warning: over current detected!\n");
>> +             intmask &= ~AU6601_INT_OVER_CURRENT_ERR;
>> +     }
>> +
>> +     if (intmask)
>> +             dev_dbg(host->dev, "got not handled IRQ: 0x%04x\n", intmask);
>> +
>> +exit:
>> +     mutex_unlock(&host->cmd_mutex);
>> +     alcor_unmask_sd_irqs(host);
>> +     return ret;
>> +}
>> +
>> +
>> +static irqreturn_t alcor_irq(int irq, void *d)
>> +{
>> +     struct alcor_sdmmc_host *host = d;
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     u32 status, tmp;
>> +     irqreturn_t ret;
>> +     int cmd_done, data_done;
>> +
>> +     status = alcor_read32(priv, AU6601_REG_INT_STATUS);
>> +     if (!status)
>> +             return IRQ_NONE;
>> +
>> +     alcor_write32(priv, status, AU6601_REG_INT_STATUS);
>> +
>> +     tmp = status & (AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY
>> +                     | AU6601_INT_DATA_END | AU6601_INT_DMA_END
>> +                     | AU6601_INT_CMD_END);
>> +     if (tmp == status) {
>> +             cmd_done = alcor_cmd_irq_done(host, tmp);
>> +             data_done = alcor_data_irq_done(host, tmp);
>> +             /* use fast path for simple tasks */
>> +             if (cmd_done && data_done) {
>> +                     ret = IRQ_HANDLED;
>> +                     goto alcor_irq_done;
>> +             }
>> +     }
>> +
>> +     host->irq_status_sd = status;
>> +     ret = IRQ_WAKE_THREAD;
>> +     alcor_mask_sd_irqs(host);
>> +alcor_irq_done:
>> +     return ret;
>> +}
>> +
>> +static void alcor_set_clock(struct alcor_sdmmc_host *host, unsigned int clock)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     unsigned int clock_out = 0;
>> +     int i, diff = 0x7fffffff, tmp_clock = 0;
>> +     u16 clk_src = 0;
>> +     u8 clk_div = 0;
>> +
>> +     if (clock == 0) {
>> +             alcor_write16(priv, 0, AU6601_CLK_SELECT);
>> +             return;
>> +     }
>> +
>> +     for (i = 0; i < ARRAY_SIZE(alcor_pll_cfg); i++) {
>> +             unsigned int tmp_div, tmp_diff;
>> +             const struct alcor_pll_conf *cfg = &alcor_pll_cfg[i];
>> +
>> +             tmp_div = DIV_ROUND_UP(cfg->clk_src_freq, clock);
>> +             if (cfg->min_div > tmp_div || tmp_div > cfg->max_div)
>> +                     continue;
>> +
>> +             tmp_clock = DIV_ROUND_UP(cfg->clk_src_freq, tmp_div);
>> +             tmp_diff = abs(clock - tmp_clock);
>> +
>> +             if (tmp_diff >= 0 && tmp_diff < diff) {
>> +                     diff = tmp_diff;
>> +                     clk_src = cfg->clk_src_reg;
>> +                     clk_div = tmp_div;
>> +                     clock_out = tmp_clock;
>> +             }
>> +     }
>> +
>> +     clk_src |= ((clk_div - 1) << 8);
>> +     clk_src |= AU6601_CLK_ENABLE;
>> +
>> +     dev_dbg(host->dev, "set freq %d cal freq %d, use div %d, mod %x\n",
>> +                     clock, tmp_clock, clk_div, clk_src);
>> +
>> +     alcor_write16(priv, clk_src, AU6601_CLK_SELECT);
>> +
>> +}
>> +
>> +static void alcor_set_timing(struct mmc_host *mmc, struct mmc_ios *ios)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +
>> +     if (ios->timing == MMC_TIMING_LEGACY) {
>> +             alcor_rmw8(host, AU6601_CLK_DELAY,
>> +                         AU6601_CLK_POSITIVE_EDGE_ALL, 0);
>> +     } else {
>> +             alcor_rmw8(host, AU6601_CLK_DELAY,
>> +                         0, AU6601_CLK_POSITIVE_EDGE_ALL);
>> +     }
>> +}
>> +
>> +static void alcor_set_bus_width(struct mmc_host *mmc, struct mmc_ios *ios)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +
>> +     if (ios->bus_width == MMC_BUS_WIDTH_1) {
>> +             alcor_write8(priv, 0, AU6601_REG_BUS_CTRL);
>> +     } else if (ios->bus_width == MMC_BUS_WIDTH_4) {
>> +             alcor_write8(priv, AU6601_BUS_WIDTH_4BIT,
>> +                           AU6601_REG_BUS_CTRL);
>> +     } else
>> +             dev_err(host->dev, "Unknown BUS mode\n");
>> +
>> +}
>> +
>> +static int alcor_card_busy(struct mmc_host *mmc)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     u8 status;
>> +
>> +     /* Check whether dat[0:3] low */
>> +     status = alcor_read8(priv, AU6601_DATA_PIN_STATE);
>> +
>> +     return !(status & AU6601_BUS_STAT_DAT_MASK);
>> +}
>> +
>> +static int alcor_get_cd(struct mmc_host *mmc)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     u8 detect;
>> +
>> +     detect = alcor_read8(priv, AU6601_DETECT_STATUS)
>> +             & AU6601_DETECT_STATUS_M;
>> +     /* check if card is present then send command and data */
>> +     return (detect == AU6601_SD_DETECTED);
>> +}
>> +
>> +static int alcor_get_ro(struct mmc_host *mmc)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     u8 status;
>> +
>> +     /* get write protect pin status */
>> +     status = alcor_read8(priv, AU6601_INTERFACE_MODE_CTRL);
>> +
>> +     return !!(status & AU6601_SD_CARD_WP);
>> +}
>> +
>> +static void alcor_request(struct mmc_host *mmc, struct mmc_request *mrq)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +
>> +     mutex_lock(&host->cmd_mutex);
>> +
>> +     host->mrq = mrq;
>> +
>> +     /* check if card is present then send command and data */
>> +     if (alcor_get_cd(mmc))
>> +             alcor_send_cmd(host, mrq->cmd, true);
>> +     else {
>> +             mrq->cmd->error = -ENOMEDIUM;
>> +             alcor_request_complete(host, 1);
>> +     }
>> +
>> +     mutex_unlock(&host->cmd_mutex);
>> +}
>> +
>> +static void alcor_pre_req(struct mmc_host *mmc,
>> +                        struct mmc_request *mrq)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +     struct mmc_data *data = mrq->data;
>> +     struct mmc_command *cmd = mrq->cmd;
>> +     struct scatterlist *sg;
>> +     unsigned int i, sg_len;
>> +
>> +     if (!data || !cmd)
>> +             return;
>> +
>> +     data->host_cookie = COOKIE_UNMAPPED;
>> +
>> +     /* FIXME: looks like the DMA engine works only with CMD18 */
>> +     if (cmd->opcode != 18)
>> +             return;
>> +     /*
>> +      * We don't do DMA on "complex" transfers, i.e. with
>> +      * non-word-aligned buffers or lengths. Also, we don't bother
>> +      * with all the DMA setup overhead for short transfers.
>> +      */
>> +     if (data->blocks * data->blksz < AU6601_MAX_DMA_BLOCK_SIZE)
>> +             return;
>> +
>> +     if (data->blksz & 3)
>> +             return;
>> +
>> +     for_each_sg(data->sg, sg, data->sg_len, i) {
>> +             if (sg->length != AU6601_MAX_DMA_BLOCK_SIZE)
>> +                     return;
>> +     }
>> +
>> +     /* This data might be unmapped at this time */
>> +
>> +     sg_len = dma_map_sg(host->dev, data->sg, data->sg_len,
>> +                         mmc_get_dma_dir(data));
>> +     if (sg_len)
>> +             data->host_cookie = COOKIE_MAPPED;
>> +
>> +     data->sg_count = sg_len;
>> +}
>> +
>> +static void alcor_post_req(struct mmc_host *mmc,
>> +                         struct mmc_request *mrq,
>> +                         int err)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +     struct mmc_data *data = mrq->data;
>> +
>> +     if (!data)
>> +             return;
>> +
>> +     if (data->host_cookie == COOKIE_MAPPED) {
>> +             dma_unmap_sg(host->dev,
>> +                          data->sg,
>> +                          data->sg_len,
>> +                          mmc_get_dma_dir(data));
>> +     }
>> +
>> +     data->host_cookie = COOKIE_UNMAPPED;
>> +}
>> +
>> +static void alcor_set_power_mode(struct mmc_host *mmc, struct mmc_ios *ios)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +
>> +     switch (ios->power_mode) {
>> +     case MMC_POWER_OFF:
>> +             alcor_set_clock(host, ios->clock);
>> +             /* set all pins to input */
>> +             alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE);
>> +             /* turn of VDD */
>> +             alcor_write8(priv, 0, AU6601_POWER_CONTROL);
>> +             break;
>> +     case MMC_POWER_UP:
>> +             break;
>> +     case MMC_POWER_ON:
>> +             /* This is most trickiest part. The order and timings of
>> +              * instructions seems to play important role. Any changes may
>> +              * confuse internal state engine if this HW.
>> +              * FIXME: If we will ever get access to documentation, then this
>> +              * part should be reviewed again.
>> +              */
>> +
>> +             /* enable SD card mode */
>> +             alcor_write8(priv, AU6601_SD_CARD,
>> +                           AU6601_ACTIVE_CTRL);
>> +             /* set signal voltage to 3.3V */
>> +             alcor_write8(priv, 0, AU6601_OPT);
>> +             /* no documentation about clk delay, for now just try to mimic
>> +              * original driver.
>> +              */
>> +             alcor_write8(priv, 0x20, AU6601_CLK_DELAY);
>> +             /* set BUS width to 1 bit */
>> +             alcor_write8(priv, 0, AU6601_REG_BUS_CTRL);
>> +             /* set CLK first time */
>> +             alcor_set_clock(host, ios->clock);
>> +             /* power on VDD */
>> +             alcor_write8(priv, AU6601_SD_CARD,
>> +                           AU6601_POWER_CONTROL);
>> +             /* wait until the CLK will get stable */
>> +             mdelay(20);
>> +             /* set CLK again, mimic original driver. */
>> +             alcor_set_clock(host, ios->clock);
>> +
>> +             /* enable output */
>> +             alcor_write8(priv, AU6601_SD_CARD,
>> +                           AU6601_OUTPUT_ENABLE);
>> +             /* The clk will not work on au6621. We need to trigger data
>> +              * transfer.
>> +              */
>> +             alcor_write8(priv, AU6601_DATA_WRITE,
>> +                           AU6601_DATA_XFER_CTRL);
>> +             /* configure timeout. Not clear what exactly it means. */
>> +             alcor_write8(priv, 0x7d, AU6601_TIME_OUT_CTRL);
>> +             mdelay(100);
>> +             break;
>> +     default:
>> +             dev_err(host->dev, "Unknown power parameter\n");
>> +     }
>> +}
>> +
>> +static void alcor_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +
>> +     mutex_lock(&host->cmd_mutex);
>> +
>> +     dev_dbg(host->dev, "set ios. bus width: %x, power mode: %x\n",
>> +             ios->bus_width, ios->power_mode);
>> +
>> +     if (ios->power_mode != host->cur_power_mode) {
>> +             alcor_set_power_mode(mmc, ios);
>> +             host->cur_power_mode = ios->power_mode;
>> +     } else {
>> +             alcor_set_timing(mmc, ios);
>> +             alcor_set_bus_width(mmc, ios);
>> +             alcor_set_clock(host, ios->clock);
>> +     }
>> +
>> +     mutex_unlock(&host->cmd_mutex);
>> +}
>> +
>> +static int alcor_signal_voltage_switch(struct mmc_host *mmc,
>> +                                    struct mmc_ios *ios)
>> +{
>> +     struct alcor_sdmmc_host *host = mmc_priv(mmc);
>> +
>> +     mutex_lock(&host->cmd_mutex);
>> +
>> +     switch (ios->signal_voltage) {
>> +     case MMC_SIGNAL_VOLTAGE_330:
>> +             alcor_rmw8(host, AU6601_OPT, AU6601_OPT_SD_18V, 0);
>> +             break;
>> +     case MMC_SIGNAL_VOLTAGE_180:
>> +             alcor_rmw8(host, AU6601_OPT, 0, AU6601_OPT_SD_18V);
>> +             break;
>> +     default:
>> +             /* No signal voltage switch required */
>> +             break;
>> +     }
>> +
>> +     mutex_unlock(&host->cmd_mutex);
>> +     return 0;
>> +}
>> +
>> +static const struct mmc_host_ops alcor_sdc_ops = {
>> +     .card_busy      = alcor_card_busy,
>> +     .get_cd         = alcor_get_cd,
>> +     .get_ro         = alcor_get_ro,
>> +     .post_req       = alcor_post_req,
>> +     .pre_req        = alcor_pre_req,
>> +     .request        = alcor_request,
>> +     .set_ios        = alcor_set_ios,
>> +     .start_signal_voltage_switch = alcor_signal_voltage_switch,
>> +};
>> +
>> +static void alcor_timeout_timer(struct work_struct *work)
>> +{
>> +     struct delayed_work *d = to_delayed_work(work);
>> +     struct alcor_sdmmc_host *host = container_of(d, struct alcor_sdmmc_host,
>> +                                             timeout_work);
>> +     mutex_lock(&host->cmd_mutex);
>> +
>> +     dev_dbg(host->dev, "triggered timeout\n");
>> +     if (host->mrq) {
>> +             dev_err(host->dev, "Timeout waiting for hardware interrupt.\n");
>> +
>> +             if (host->data) {
>> +                     host->data->error = -ETIMEDOUT;
>> +             } else {
>> +                     if (host->cmd)
>> +                             host->cmd->error = -ETIMEDOUT;
>> +                     else
>> +                             host->mrq->cmd->error = -ETIMEDOUT;
>> +             }
>> +
>> +             alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA);
>> +             alcor_request_complete(host, 0);
>> +     }
>> +
>> +     mmiowb();
>> +     mutex_unlock(&host->cmd_mutex);
>> +}
>> +
>> +static void alcor_hw_init(struct alcor_sdmmc_host *host)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +     struct alcor_dev_cfg *cfg = priv->cfg;
>> +
>> +     /* FIXME: This part is a mimics HW init of original driver.
>> +      * If we will ever get access to documentation, then this part
>> +      * should be reviewed again.
>> +      */
>> +
>> +     /* reset command state engine */
>> +     alcor_reset(host, AU6601_RESET_CMD);
>> +
>> +     alcor_write8(priv, 0, AU6601_DMA_BOUNDARY);
>> +     /* enable sd card mode */
>> +     alcor_write8(priv, AU6601_SD_CARD, AU6601_ACTIVE_CTRL);
>> +
>> +     /* set BUS width to 1 bit */
>> +     alcor_write8(priv, 0, AU6601_REG_BUS_CTRL);
>> +
>> +     /* reset data state engine */
>> +     alcor_reset(host, AU6601_RESET_DATA);
>> +     /* Not sure if a voodoo with AU6601_DMA_BOUNDARY is really needed */
>> +     alcor_write8(priv, 0, AU6601_DMA_BOUNDARY);
>> +
>> +     alcor_write8(priv, 0, AU6601_INTERFACE_MODE_CTRL);
>> +     /* not clear what we are doing here. */
>> +     alcor_write8(priv, 0x44, AU6601_PAD_DRIVE0);
>> +     alcor_write8(priv, 0x44, AU6601_PAD_DRIVE1);
>> +     alcor_write8(priv, 0x00, AU6601_PAD_DRIVE2);
>> +
>> +     /* for 6601 - dma_boundary; for 6621 - dma_page_cnt
>> +      * exact meaning of this register is not clear.
>> +      */
>> +     alcor_write8(priv, cfg->dma, AU6601_DMA_BOUNDARY);
>> +
>> +     /* make sure all pins are set to input and VDD is off */
>> +     alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE);
>> +     alcor_write8(priv, 0, AU6601_POWER_CONTROL);
>> +
>> +     alcor_write8(priv, AU6601_DETECT_EN, AU6601_DETECT_STATUS);
>> +     /* now we should be safe to enable IRQs */
>> +     alcor_unmask_sd_irqs(host);
>> +}
>> +
>> +static void alcor_hw_uninit(struct alcor_sdmmc_host *host)
>> +{
>> +     struct alcor_pci_priv *priv = host->alcor_pci;
>> +
>> +     alcor_mask_sd_irqs(host);
>> +     alcor_reset(host, AU6601_RESET_CMD | AU6601_RESET_DATA);
>> +
>> +     alcor_write8(priv, 0, AU6601_DETECT_STATUS);
>> +
>> +     alcor_write8(priv, 0, AU6601_OUTPUT_ENABLE);
>> +     alcor_write8(priv, 0, AU6601_POWER_CONTROL);
>> +
>> +     alcor_write8(priv, 0, AU6601_OPT);
>> +}
>> +
>> +static void alcor_init_mmc(struct alcor_sdmmc_host *host)
>> +{
>> +     struct mmc_host *mmc = host->mmc;
>> +
>> +     mmc->f_min = AU6601_MIN_CLOCK;
>> +     mmc->f_max = AU6601_MAX_CLOCK;
>> +     mmc->ocr_avail = MMC_VDD_33_34;
>> +     mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED
>> +             | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50
>> +             | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50;
>> +     mmc->caps2 = MMC_CAP2_NO_SDIO;
>> +     mmc->ops = &alcor_sdc_ops;
>> +
>> +     /* Hardware cannot do scatter lists */
>> +     mmc->max_segs = AU6601_MAX_DMA_SEGMENTS;
>> +     mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE;
>> +
>> +     mmc->max_blk_size = mmc->max_seg_size;
>> +     mmc->max_blk_count = mmc->max_segs;
>> +
>> +     mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
>> +}
>> +
>> +static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
>> +{
>> +     struct alcor_pci_priv *priv = pdev->dev.platform_data;
>> +     struct mmc_host *mmc;
>> +     struct alcor_sdmmc_host *host;
>> +     int ret;
>> +
>> +     mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
>> +     if (!mmc) {
>> +             dev_err(&pdev->dev, "Can't allocate MMC\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     host = mmc_priv(mmc);
>> +     host->mmc = mmc;
>> +     host->dev = &pdev->dev;
>> +     host->cur_power_mode = MMC_POWER_UNDEFINED;
>> +     host->alcor_pci = priv;
>> +
>> +     /* make sure irqs are disabled */
>> +     alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
>> +     alcor_write32(priv, 0, AU6601_MS_INT_ENABLE);
>> +
>> +     ret = devm_request_threaded_irq(&pdev->dev, priv->irq,
>> +                     alcor_irq, alcor_irq_thread, IRQF_SHARED,
>> +                     DRV_NAME_ALCOR_PCI_SDMMC, host);
>> +
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Failed to get irq for data line\n");
>> +             return ret;
>> +     }
>> +
>> +     mutex_init(&host->cmd_mutex);
>> +     INIT_DELAYED_WORK(&host->timeout_work, alcor_timeout_timer);
>> +
>> +     alcor_init_mmc(host);
>> +     alcor_hw_init(host);
>> +
>> +     platform_set_drvdata(pdev, host);
>> +     mmc_add_host(mmc);
>> +     return 0;
>> +}
>> +
>> +static int alcor_pci_sdmmc_drv_remove(struct platform_device *pdev)
>> +{
>> +     struct alcor_sdmmc_host *host = platform_get_drvdata(pdev);
>> +
>> +     if (cancel_delayed_work_sync(&host->timeout_work))
>> +             alcor_request_complete(host, 0);
>> +
>> +     alcor_hw_uninit(host);
>> +     mmc_remove_host(host->mmc);
>> +     mmc_free_host(host->mmc);
>> +
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static int alcor_pci_sdmmc_suspend(struct platform_device *pdev,
>> +                                pm_message_t state)
>> +{
>> +     struct alcor_sdmmc_host *host = platform_get_drvdata(pdev);
>> +
>> +     if (cancel_delayed_work_sync(&host->timeout_work))
>> +             alcor_request_complete(host, 0);
>> +
>> +     alcor_hw_uninit(host);
>> +
>> +     return 0;
>> +}
>> +
>> +static int alcor_pci_sdmmc_resume(struct platform_device *pdev)
>> +{
>> +     struct alcor_sdmmc_host *host = platform_get_drvdata(pdev);
>> +
>> +     alcor_hw_init(host);
>> +
>> +     return 0;
>> +}
>> +#else
>> +#define alcor_pci_sdmmc_suspend NULL
>> +#define alcor_pci_sdmmc_resume NULL
>> +#endif
>> +
>> +static const struct platform_device_id alcor_pci_sdmmc_ids[] = {
>> +     {
>> +             .name = DRV_NAME_ALCOR_PCI_SDMMC,
>> +     }, {
>> +             /* sentinel */
>> +     }
>> +};
>> +MODULE_DEVICE_TABLE(platform, alcor_pci_sdmmc_ids);
>> +
>> +static struct platform_driver alcor_pci_sdmmc_driver = {
>> +     .probe          = alcor_pci_sdmmc_drv_probe,
>> +     .remove         = alcor_pci_sdmmc_drv_remove,
>> +     .suspend        = alcor_pci_sdmmc_suspend,
>> +     .resume         = alcor_pci_sdmmc_resume,
>> +     .id_table       = alcor_pci_sdmmc_ids,
>> +     .driver         = {
>> +             .name   = DRV_NAME_ALCOR_PCI_SDMMC,
>> +     },
>> +};
>> +module_platform_driver(alcor_pci_sdmmc_driver);
>> +
>> +MODULE_AUTHOR("Oleksij Rempel <linux@xxxxxxxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/alcor_pci.h b/include/linux/alcor_pci.h
>> new file mode 100644
>> index 000000000000..eac8d0403a41
>> --- /dev/null
>> +++ b/include/linux/alcor_pci.h
>> @@ -0,0 +1,287 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Copyright (C) 2018 Oleksij Rempel <linux@xxxxxxxxxxxxxxxx>
>> + *
>> + * Driver for Alcor Micro AU6601 and AU6621 controllers
>> + */
>> +
>> +#ifndef __ALCOR_PCI_H
>> +#define __ALCOR_PCI_H
>> +
>> +#define ALCOR_SD_CARD 0
>> +#define ALCOR_MS_CARD 1
>> +
>> +#define DRV_NAME_ALCOR_PCI                   "alcor_pci"
>> +#define DRV_NAME_ALCOR_PCI_SDMMC             "alcor_sdmmc"
>> +#define DRV_NAME_ALCOR_PCI_MS                        "alcor_ms"
>> +
>> +#define PCI_ID_ALCOR_MICRO                   0x1AEA
>> +#define PCI_ID_AU6601                                0x6601
>> +#define PCI_ID_AU6621                                0x6621
>> +
>> +#define MHZ_TO_HZ(freq)                              ((freq) * 1000 * 1000)
>> +
>> +#define AU6601_BASE_CLOCK                    MHZ_TO_HZ(31)
>> +#define AU6601_MIN_CLOCK                     (150 * 1000)
>> +#define AU6601_MAX_CLOCK                     MHZ_TO_HZ(208)
>> +#define AU6601_MAX_DMA_SEGMENTS                      1
>> +#define AU6601_MAX_PIO_SEGMENTS                      1
>> +#define AU6601_MAX_DMA_BLOCK_SIZE            0x1000
>> +#define AU6601_MAX_PIO_BLOCK_SIZE            0x200
>> +#define AU6601_MAX_DMA_BLOCKS                        1
>> +#define AU6601_DMA_LOCAL_SEGMENTS            1
>> +
>> +/* registers spotter by reverse engineering but still
>> + * with unknown functionality:
>> + * 0x10 - ADMA phy address. AU6621 only?
>> + * 0x51 - LED ctrl?
>> + * 0x52 - unknown
>> + * 0x61 - LED related? Always toggled BIT0
>> + * 0x63 - Same as 0x61?
>> + * 0x77 - unknown
>> + */
>> +
>> +/* SDMA phy address. Higher then 0x0800.0000?
>> + * The au6601 and au6621 have different DMA engines with different issues. One
>> + * For example au6621 engine is triggered by addr change. No other interaction
>> + * is needed. This means, if we get two buffers with same address, then engine
>> + * will stall.
>> + */
>> +#define AU6601_REG_SDMA_ADDR                 0x00
>> +#define AU6601_SDMA_MASK                     0xffffffff
>> +
>> +#define AU6601_DMA_BOUNDARY                  0x05
>> +#define AU6621_DMA_PAGE_CNT                  0x05
>> +/* PIO */
>> +#define AU6601_REG_BUFFER                    0x08
>> +/* ADMA ctrl? AU6621 only. */
>> +#define AU6621_DMA_CTRL                              0x0c
>> +#define AU6621_DMA_ENABLE                    BIT(0)
>> +/* 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
>> +/* default timeout set to 125: 125 * 40ms = 5 sec
>> + * how exactly it is calculated?
>> + */
>> +#define AU6601_TIME_OUT_CTRL                 0x69
>> +/* Block size for SDMA or PIO */
>> +#define AU6601_REG_BLOCK_SIZE                        0x6c
>> +/* Some power related reg, used together with AU6601_OUTPUT_ENABLE */
>> +#define AU6601_POWER_CONTROL                 0x70
>> +
>> +/* PLL ctrl */
>> +#define AU6601_CLK_SELECT                    0x72
>> +#define      AU6601_CLK_OVER_CLK                     0x80
>> +#define      AU6601_CLK_384_MHZ                      0x30
>> +#define      AU6601_CLK_125_MHZ                      0x20
>> +#define      AU6601_CLK_48_MHZ                       0x10
>> +#define      AU6601_CLK_EXT_PLL                      0x04
>> +#define AU6601_CLK_X2_MODE                   0x02
>> +#define AU6601_CLK_ENABLE                    0x01
>> +#define AU6601_CLK_31_25_MHZ                 0x00
>> +
>> +#define AU6601_CLK_DIVIDER                   0x73
>> +
>> +#define AU6601_INTERFACE_MODE_CTRL           0x74
>> +#define AU6601_DLINK_MODE                    0x80
>> +#define      AU6601_INTERRUPT_DELAY_TIME             0x40
>> +#define      AU6601_SIGNAL_REQ_CTRL                  0x30
>> +#define AU6601_MS_CARD_WP                    BIT(3)
>> +#define AU6601_SD_CARD_WP                    BIT(0)
>> +
>> +/* same register values are used for:
>> + *  - AU6601_OUTPUT_ENABLE
>> + *  - AU6601_POWER_CONTROL
>> + */
>> +#define AU6601_ACTIVE_CTRL                   0x75
>> +#define AU6601_XD_CARD                               BIT(4)
>> +/* AU6601_MS_CARD_ACTIVE - will cativate MS card section? */
>> +#define AU6601_MS_CARD                               BIT(3)
>> +#define AU6601_SD_CARD                               BIT(0)
>> +
>> +/* card slot state. It should automatically detect type of
>> + * the card
>> + */
>> +#define AU6601_DETECT_STATUS                 0x76
>> +#define AU6601_DETECT_EN                     BIT(7)
>> +#define AU6601_MS_DETECTED                   BIT(3)
>> +#define AU6601_SD_DETECTED                   BIT(0)
>> +#define AU6601_DETECT_STATUS_M                       0xf
>> +
>> +#define AU6601_REG_SW_RESET                  0x79
>> +#define AU6601_BUF_CTRL_RESET                        BIT(7)
>> +#define AU6601_RESET_DATA                    BIT(3)
>> +#define AU6601_RESET_CMD                     BIT(0)
>> +
>> +#define AU6601_OUTPUT_ENABLE                 0x7a
>> +
>> +#define AU6601_PAD_DRIVE0                    0x7b
>> +#define AU6601_PAD_DRIVE1                    0x7c
>> +#define AU6601_PAD_DRIVE2                    0x7d
>> +/* read EEPROM? */
>> +#define AU6601_FUNCTION                              0x7f
>> +
>> +#define AU6601_CMD_XFER_CTRL                 0x81
>> +#define      AU6601_CMD_17_BYTE_CRC                  0xc0
>> +#define      AU6601_CMD_6_BYTE_WO_CRC                0x80
>> +#define      AU6601_CMD_6_BYTE_CRC                   0x40
>> +#define      AU6601_CMD_START_XFER                   0x20
>> +#define      AU6601_CMD_STOP_WAIT_RDY                0x10
>> +#define      AU6601_CMD_NO_RESP                      0x00
>> +
>> +#define AU6601_REG_BUS_CTRL                  0x82
>> +#define AU6601_BUS_WIDTH_4BIT                        0x20
>> +#define AU6601_BUS_WIDTH_8BIT                        0x10
>> +#define AU6601_BUS_WIDTH_1BIT                        0x00
>> +
>> +#define AU6601_DATA_XFER_CTRL                        0x83
>> +#define AU6601_DATA_WRITE                    BIT(7)
>> +#define AU6601_DATA_DMA_MODE                 BIT(6)
>> +#define AU6601_DATA_START_XFER                       BIT(0)
>> +
>> +#define AU6601_DATA_PIN_STATE                        0x84
>> +#define AU6601_BUS_STAT_CMD                  BIT(15)
>> +/* BIT(4) - BIT(7) are permanently 1.
>> + * May be reserved 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 AU6601_OPT                           0x85
>> +#define      AU6601_OPT_CMD_LINE_LEVEL               0x80
>> +#define      AU6601_OPT_NCRC_16_CLK                  BIT(4)
>> +#define      AU6601_OPT_CMD_NWT                      BIT(3)
>> +#define      AU6601_OPT_STOP_CLK                     BIT(2)
>> +#define      AU6601_OPT_DDR_MODE                     BIT(1)
>> +#define      AU6601_OPT_SD_18V                       BIT(0)
>> +
>> +#define AU6601_CLK_DELAY                     0x86
>> +#define      AU6601_CLK_DATA_POSITIVE_EDGE           0x80
>> +#define      AU6601_CLK_CMD_POSITIVE_EDGE            0x40
>> +#define      AU6601_CLK_POSITIVE_EDGE_ALL            (AU6601_CLK_CMD_POSITIVE_EDGE \
>> +                                             | AU6601_CLK_DATA_POSITIVE_EDGE)
>> +
>> +
>> +#define AU6601_REG_INT_STATUS                        0x90
>> +#define AU6601_REG_INT_ENABLE                        0x94
>> +#define AU6601_INT_DATA_END_BIT_ERR          BIT(22)
>> +#define AU6601_INT_DATA_CRC_ERR                      BIT(21)
>> +#define AU6601_INT_DATA_TIMEOUT_ERR          BIT(20)
>> +#define AU6601_INT_CMD_INDEX_ERR             BIT(19)
>> +#define AU6601_INT_CMD_END_BIT_ERR           BIT(18)
>> +#define AU6601_INT_CMD_CRC_ERR                       BIT(17)
>> +#define AU6601_INT_CMD_TIMEOUT_ERR           BIT(16)
>> +#define AU6601_INT_ERROR                     BIT(15)
>> +#define AU6601_INT_OVER_CURRENT_ERR          BIT(8)
>> +#define AU6601_INT_CARD_INSERT                       BIT(7)
>> +#define AU6601_INT_CARD_REMOVE                       BIT(6)
>> +#define AU6601_INT_READ_BUF_RDY                      BIT(5)
>> +#define AU6601_INT_WRITE_BUF_RDY             BIT(4)
>> +#define AU6601_INT_DMA_END                   BIT(3)
>> +#define AU6601_INT_DATA_END                  BIT(1)
>> +#define AU6601_INT_CMD_END                   BIT(0)
>> +
>> +#define AU6601_INT_NORMAL_MASK                       0x00007FFF
>> +#define AU6601_INT_ERROR_MASK                        0xFFFF8000
>> +
>> +#define AU6601_INT_CMD_MASK  (AU6601_INT_CMD_END | \
>> +             AU6601_INT_CMD_TIMEOUT_ERR | AU6601_INT_CMD_CRC_ERR | \
>> +             AU6601_INT_CMD_END_BIT_ERR | AU6601_INT_CMD_INDEX_ERR)
>> +#define AU6601_INT_DATA_MASK (AU6601_INT_DATA_END | AU6601_INT_DMA_END | \
>> +             AU6601_INT_READ_BUF_RDY | AU6601_INT_WRITE_BUF_RDY | \
>> +             AU6601_INT_DATA_TIMEOUT_ERR | AU6601_INT_DATA_CRC_ERR | \
>> +             AU6601_INT_DATA_END_BIT_ERR)
>> +#define AU6601_INT_ALL_MASK                  ((u32)-1)
>> +
>> +/* MS_CARD mode registers */
>> +
>> +#define AU6601_MS_STATUS                     0xa0
>> +
>> +#define AU6601_MS_BUS_MODE_CTRL                      0xa1
>> +#define AU6601_MS_BUS_8BIT_MODE                      0x03
>> +#define AU6601_MS_BUS_4BIT_MODE                      0x01
>> +#define AU6601_MS_BUS_1BIT_MODE                      0x00
>> +
>> +#define AU6601_MS_TPC_CMD                    0xa2
>> +#define AU6601_MS_TPC_READ_PAGE_DATA         0x02
>> +#define AU6601_MS_TPC_READ_REG                       0x04
>> +#define AU6601_MS_TPC_GET_INT                        0x07
>> +#define AU6601_MS_TPC_WRITE_PAGE_DATA                0x0D
>> +#define AU6601_MS_TPC_WRITE_REG                      0x0B
>> +#define AU6601_MS_TPC_SET_RW_REG_ADRS                0x08
>> +#define AU6601_MS_TPC_SET_CMD                        0x0E
>> +#define AU6601_MS_TPC_EX_SET_CMD             0x09
>> +#define AU6601_MS_TPC_READ_SHORT_DATA                0x03
>> +#define AU6601_MS_TPC_WRITE_SHORT_DATA               0x0C
>> +
>> +#define AU6601_MS_TRANSFER_MODE                      0xa3
>> +#define      AU6601_MS_XFER_INT_TIMEOUT_CHK          BIT(2)
>> +#define      AU6601_MS_XFER_DMA_ENABLE               BIT(1)
>> +#define      AU6601_MS_XFER_START                    BIT(0)
>> +
>> +#define AU6601_MS_DATA_PIN_STATE             0xa4
>> +
>> +#define AU6601_MS_INT_STATUS                 0xb0
>> +#define AU6601_MS_INT_ENABLE                 0xb4
>> +#define AU6601_MS_INT_OVER_CURRENT_ERROR     BIT(23)
>> +#define AU6601_MS_INT_DATA_CRC_ERROR         BIT(21)
>> +#define AU6601_MS_INT_INT_TIMEOUT            BIT(20)
>> +#define AU6601_MS_INT_INT_RESP_ERROR         BIT(19)
>> +#define AU6601_MS_INT_CED_ERROR                      BIT(18)
>> +#define AU6601_MS_INT_TPC_TIMEOUT            BIT(16)
>> +#define AU6601_MS_INT_ERROR                  BIT(15)
>> +#define AU6601_MS_INT_CARD_INSERT            BIT(7)
>> +#define AU6601_MS_INT_CARD_REMOVE            BIT(6)
>> +#define AU6601_MS_INT_BUF_READ_RDY           BIT(5)
>> +#define AU6601_MS_INT_BUF_WRITE_RDY          BIT(4)
>> +#define AU6601_MS_INT_DMA_END                        BIT(3)
>> +#define AU6601_MS_INT_TPC_END                        BIT(1)
>> +
>> +#define AU6601_MS_INT_DATA_MASK                      0x00000038
>> +#define AU6601_MS_INT_TPC_MASK                       0x003d8002
>> +#define AU6601_MS_INT_TPC_ERROR                      0x003d0000
>> +
>> +#define ALCOR_PCIE_LINK_CTRL_OFFSET          0x10
>> +#define ALCOR_PCIE_LINK_CAP_OFFSET           0x0c
>> +#define ALCOR_CAP_START_OFFSET                       0x34
>> +
>> +struct alcor_dev_cfg {
>> +     u8      dma;
>> +};
>> +
>> +struct alcor_pci_priv {
>> +     struct pci_dev *pdev;
>> +     struct pci_dev *parent_pdev;
>> +     struct  device *dev;
>> +     void __iomem *iobase;
>> +     unsigned int irq;
>> +
>> +     unsigned long id; /* idr id */
>> +
>> +     struct alcor_dev_cfg    *cfg;
>> +
>> +     /* PCI ASPM related vars */
>> +     int pdev_cap_off;
>> +     u8  pdev_aspm_cap;
>> +     int parent_cap_off;
>> +     u8  parent_aspm_cap;
>> +     u8 ext_config_dev_aspm;
>> +};
>> +
>> +void alcor_write8(struct alcor_pci_priv *priv, u8 val, unsigned int addr);
>> +void alcor_write16(struct alcor_pci_priv *priv, u16 val, unsigned int addr);
>> +void alcor_write32(struct alcor_pci_priv *priv, u32 val, unsigned int addr);
>> +void alcor_write32be(struct alcor_pci_priv *priv, u32 val, unsigned int addr);
>> +u8 alcor_read8(struct alcor_pci_priv *priv, unsigned int addr);
>> +u32 alcor_read32(struct alcor_pci_priv *priv, unsigned int addr);
>> +u32 alcor_read32be(struct alcor_pci_priv *priv, unsigned int addr);
>> +#endif
>>
>
>
> --
> Regards,
> Oleksij
>




[Index of Archives]     [Linux Memonry Technology]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux