misc: cardreader: add new Alcor Micro Cardreader PCI driver
authorOleksij Rempel <linux@rempel-privat.de>
Sun, 2 Dec 2018 10:30:45 +0000 (11:30 +0100)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 17 Dec 2018 07:26:24 +0000 (08:26 +0100)
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@rempel-privat.de>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/misc/Makefile
drivers/misc/cardreader/Kconfig
drivers/misc/cardreader/Makefile
drivers/misc/cardreader/alcor_pci.c [new file with mode: 0644]
include/linux/alcor_pci.h [new file with mode: 0644]

index af22bbc..fe3134c 100644 (file)
@@ -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/
index 69e815e..ed8993b 100644 (file)
@@ -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
index 9fabfcc..9882d2a 100644 (file)
@@ -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 (file)
index 0000000..6872b8e
--- /dev/null
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * 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>
+
+#define DRV_NAME_ALCOR_PCI                     "alcor_pci"
+
+static DEFINE_IDA(alcor_pci_idr);
+
+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;
+
+       ret = ida_simple_get(&alcor_pci_idr, 0, 0, GFP_KERNEL);
+       if (ret < 0)
+               return ret;
+       priv->id = 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);
+
+       ida_simple_remove(&alcor_pci_idr, priv->id);
+
+       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_pci_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_pci_pm_ops
+       },
+};
+
+module_pci_driver(alcor_driver);
+
+MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
+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 (file)
index 0000000..da973e8
--- /dev/null
@@ -0,0 +1,286 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * 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_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                      31000000
+#define AU6601_MIN_CLOCK                       150000
+#define AU6601_MAX_CLOCK                       208000000
+#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