Re: [PATCH v6 1/2] misc: cardreader: add new Alcor Micro Cardreader PCI driver
From: Ulf Hansson
Date: Wed Dec 05 2018 - 09:24:54 EST
On Sun, 2 Dec 2018 at 11:31, Oleksij Rempel <linux@xxxxxxxxxxxxxxxx> wrote:
>
> 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>
Applied for next, thanks!
Kind regards
Uffe
> ---
> drivers/misc/Makefile | 2 +-
> drivers/misc/cardreader/Kconfig | 11 +
> drivers/misc/cardreader/Makefile | 4 +-
> drivers/misc/cardreader/alcor_pci.c | 371 ++++++++++++++++++++++++++++
> include/linux/alcor_pci.h | 286 +++++++++++++++++++++
> 5 files changed, 671 insertions(+), 3 deletions(-)
> create mode 100644 drivers/misc/cardreader/alcor_pci.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..6872b8e29b4d
> --- /dev/null
> +++ b/drivers/misc/cardreader/alcor_pci.c
> @@ -0,0 +1,371 @@
> +// 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>
> +
> +#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@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..da973e8a2da8
> --- /dev/null
> +++ b/include/linux/alcor_pci.h
> @@ -0,0 +1,286 @@
> +/* 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_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
> --
> 2.17.1
>