linux/drivers/misc/cardreader/alcor_pci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2018 Oleksij Rempel <linux@rempel-privat.de>
   4 *
   5 * Driver for Alcor Micro AU6601 and AU6621 controllers
   6 */
   7
   8#include <linux/delay.h>
   9#include <linux/interrupt.h>
  10#include <linux/io.h>
  11#include <linux/irq.h>
  12#include <linux/mfd/core.h>
  13#include <linux/module.h>
  14#include <linux/pci.h>
  15#include <linux/platform_device.h>
  16#include <linux/pm.h>
  17
  18#include <linux/alcor_pci.h>
  19
  20#define DRV_NAME_ALCOR_PCI                      "alcor_pci"
  21
  22static DEFINE_IDA(alcor_pci_idr);
  23
  24static struct mfd_cell alcor_pci_cells[] = {
  25        [ALCOR_SD_CARD] = {
  26                .name = DRV_NAME_ALCOR_PCI_SDMMC,
  27        },
  28        [ALCOR_MS_CARD] = {
  29                .name = DRV_NAME_ALCOR_PCI_MS,
  30        },
  31};
  32
  33static const struct alcor_dev_cfg alcor_cfg = {
  34        .dma = 0,
  35};
  36
  37static const struct alcor_dev_cfg au6621_cfg = {
  38        .dma = 1,
  39};
  40
  41static const struct alcor_dev_cfg au6625_cfg = {
  42        .dma = 0,
  43};
  44
  45static const struct pci_device_id pci_ids[] = {
  46        { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6601),
  47                .driver_data = (kernel_ulong_t)&alcor_cfg },
  48        { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6621),
  49                .driver_data = (kernel_ulong_t)&au6621_cfg },
  50        { PCI_DEVICE(PCI_ID_ALCOR_MICRO, PCI_ID_AU6625),
  51                .driver_data = (kernel_ulong_t)&au6625_cfg },
  52        {},
  53};
  54MODULE_DEVICE_TABLE(pci, pci_ids);
  55
  56void alcor_write8(struct alcor_pci_priv *priv, u8 val, unsigned int addr)
  57{
  58        writeb(val, priv->iobase + addr);
  59}
  60EXPORT_SYMBOL_GPL(alcor_write8);
  61
  62void alcor_write16(struct alcor_pci_priv *priv, u16 val, unsigned int addr)
  63{
  64        writew(val, priv->iobase + addr);
  65}
  66EXPORT_SYMBOL_GPL(alcor_write16);
  67
  68void alcor_write32(struct alcor_pci_priv *priv, u32 val, unsigned int addr)
  69{
  70        writel(val, priv->iobase + addr);
  71}
  72EXPORT_SYMBOL_GPL(alcor_write32);
  73
  74void alcor_write32be(struct alcor_pci_priv *priv, u32 val, unsigned int addr)
  75{
  76        iowrite32be(val, priv->iobase + addr);
  77}
  78EXPORT_SYMBOL_GPL(alcor_write32be);
  79
  80u8 alcor_read8(struct alcor_pci_priv *priv, unsigned int addr)
  81{
  82        return readb(priv->iobase + addr);
  83}
  84EXPORT_SYMBOL_GPL(alcor_read8);
  85
  86u32 alcor_read32(struct alcor_pci_priv *priv, unsigned int addr)
  87{
  88        return readl(priv->iobase + addr);
  89}
  90EXPORT_SYMBOL_GPL(alcor_read32);
  91
  92u32 alcor_read32be(struct alcor_pci_priv *priv, unsigned int addr)
  93{
  94        return ioread32be(priv->iobase + addr);
  95}
  96EXPORT_SYMBOL_GPL(alcor_read32be);
  97
  98static int alcor_pci_find_cap_offset(struct alcor_pci_priv *priv,
  99                                     struct pci_dev *pci)
 100{
 101        int where;
 102        u8 val8;
 103        u32 val32;
 104
 105        where = ALCOR_CAP_START_OFFSET;
 106        pci_read_config_byte(pci, where, &val8);
 107        if (!val8)
 108                return 0;
 109
 110        where = (int)val8;
 111        while (1) {
 112                pci_read_config_dword(pci, where, &val32);
 113                if (val32 == 0xffffffff) {
 114                        dev_dbg(priv->dev, "find_cap_offset invalid value %x.\n",
 115                                val32);
 116                        return 0;
 117                }
 118
 119                if ((val32 & 0xff) == 0x10) {
 120                        dev_dbg(priv->dev, "pcie cap offset: %x\n", where);
 121                        return where;
 122                }
 123
 124                if ((val32 & 0xff00) == 0x00) {
 125                        dev_dbg(priv->dev, "pci_find_cap_offset invalid value %x.\n",
 126                                val32);
 127                        break;
 128                }
 129                where = (int)((val32 >> 8) & 0xff);
 130        }
 131
 132        return 0;
 133}
 134
 135static void alcor_pci_init_check_aspm(struct alcor_pci_priv *priv)
 136{
 137        struct pci_dev *pci;
 138        int where;
 139        u32 val32;
 140
 141        priv->pdev_cap_off    = alcor_pci_find_cap_offset(priv, priv->pdev);
 142        /*
 143         * A device might be attached to root complex directly and
 144         * priv->parent_pdev will be NULL. In this case we don't check its
 145         * capability and disable ASPM completely.
 146         */
 147        if (priv->parent_pdev)
 148                priv->parent_cap_off = alcor_pci_find_cap_offset(priv,
 149                                                         priv->parent_pdev);
 150
 151        if ((priv->pdev_cap_off == 0) || (priv->parent_cap_off == 0)) {
 152                dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n",
 153                        priv->pdev_cap_off, priv->parent_cap_off);
 154                return;
 155        }
 156
 157        /* link capability */
 158        pci   = priv->pdev;
 159        where = priv->pdev_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET;
 160        pci_read_config_dword(pci, where, &val32);
 161        priv->pdev_aspm_cap = (u8)(val32 >> 10) & 0x03;
 162
 163        pci   = priv->parent_pdev;
 164        where = priv->parent_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET;
 165        pci_read_config_dword(pci, where, &val32);
 166        priv->parent_aspm_cap = (u8)(val32 >> 10) & 0x03;
 167
 168        if (priv->pdev_aspm_cap != priv->parent_aspm_cap) {
 169                u8 aspm_cap;
 170
 171                dev_dbg(priv->dev, "pdev_aspm_cap: %x, parent_aspm_cap: %x\n",
 172                        priv->pdev_aspm_cap, priv->parent_aspm_cap);
 173                aspm_cap = priv->pdev_aspm_cap & priv->parent_aspm_cap;
 174                priv->pdev_aspm_cap    = aspm_cap;
 175                priv->parent_aspm_cap = aspm_cap;
 176        }
 177
 178        dev_dbg(priv->dev, "ext_config_dev_aspm: %x, pdev_aspm_cap: %x\n",
 179                priv->ext_config_dev_aspm, priv->pdev_aspm_cap);
 180        priv->ext_config_dev_aspm &= priv->pdev_aspm_cap;
 181}
 182
 183static void alcor_pci_aspm_ctrl(struct alcor_pci_priv *priv, u8 aspm_enable)
 184{
 185        struct pci_dev *pci;
 186        u8 aspm_ctrl, i;
 187        int where;
 188        u32 val32;
 189
 190        if ((!priv->pdev_cap_off) || (!priv->parent_cap_off)) {
 191                dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n",
 192                        priv->pdev_cap_off, priv->parent_cap_off);
 193                return;
 194        }
 195
 196        if (!priv->pdev_aspm_cap)
 197                return;
 198
 199        aspm_ctrl = 0;
 200        if (aspm_enable) {
 201                aspm_ctrl = priv->ext_config_dev_aspm;
 202
 203                if (!aspm_ctrl) {
 204                        dev_dbg(priv->dev, "aspm_ctrl == 0\n");
 205                        return;
 206                }
 207        }
 208
 209        for (i = 0; i < 2; i++) {
 210
 211                if (i) {
 212                        pci   = priv->parent_pdev;
 213                        where = priv->parent_cap_off
 214                                + ALCOR_PCIE_LINK_CTRL_OFFSET;
 215                } else {
 216                        pci   = priv->pdev;
 217                        where = priv->pdev_cap_off
 218                                + ALCOR_PCIE_LINK_CTRL_OFFSET;
 219                }
 220
 221                pci_read_config_dword(pci, where, &val32);
 222                val32 &= (~0x03);
 223                val32 |= (aspm_ctrl & priv->pdev_aspm_cap);
 224                pci_write_config_byte(pci, where, (u8)val32);
 225        }
 226
 227}
 228
 229static inline void alcor_mask_sd_irqs(struct alcor_pci_priv *priv)
 230{
 231        alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
 232}
 233
 234static inline void alcor_unmask_sd_irqs(struct alcor_pci_priv *priv)
 235{
 236        alcor_write32(priv, AU6601_INT_CMD_MASK | AU6601_INT_DATA_MASK |
 237                  AU6601_INT_CARD_INSERT | AU6601_INT_CARD_REMOVE |
 238                  AU6601_INT_OVER_CURRENT_ERR,
 239                  AU6601_REG_INT_ENABLE);
 240}
 241
 242static inline void alcor_mask_ms_irqs(struct alcor_pci_priv *priv)
 243{
 244        alcor_write32(priv, 0, AU6601_MS_INT_ENABLE);
 245}
 246
 247static inline void alcor_unmask_ms_irqs(struct alcor_pci_priv *priv)
 248{
 249        alcor_write32(priv, 0x3d00fa, AU6601_MS_INT_ENABLE);
 250}
 251
 252static int alcor_pci_probe(struct pci_dev *pdev,
 253                           const struct pci_device_id *ent)
 254{
 255        struct alcor_dev_cfg *cfg;
 256        struct alcor_pci_priv *priv;
 257        int ret, i, bar = 0;
 258
 259        cfg = (void *)ent->driver_data;
 260
 261        ret = pcim_enable_device(pdev);
 262        if (ret)
 263                return ret;
 264
 265        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 266        if (!priv)
 267                return -ENOMEM;
 268
 269        ret = ida_simple_get(&alcor_pci_idr, 0, 0, GFP_KERNEL);
 270        if (ret < 0)
 271                return ret;
 272        priv->id = ret;
 273
 274        priv->pdev = pdev;
 275        priv->parent_pdev = pdev->bus->self;
 276        priv->dev = &pdev->dev;
 277        priv->cfg = cfg;
 278        priv->irq = pdev->irq;
 279
 280        ret = pci_request_regions(pdev, DRV_NAME_ALCOR_PCI);
 281        if (ret) {
 282                dev_err(&pdev->dev, "Cannot request region\n");
 283                return -ENOMEM;
 284        }
 285
 286        if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
 287                dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
 288                ret = -ENODEV;
 289                goto error_release_regions;
 290        }
 291
 292        priv->iobase = pcim_iomap(pdev, bar, 0);
 293        if (!priv->iobase) {
 294                ret = -ENOMEM;
 295                goto error_release_regions;
 296        }
 297
 298        /* make sure irqs are disabled */
 299        alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
 300        alcor_write32(priv, 0, AU6601_MS_INT_ENABLE);
 301
 302        ret = dma_set_mask_and_coherent(priv->dev, AU6601_SDMA_MASK);
 303        if (ret) {
 304                dev_err(priv->dev, "Failed to set DMA mask\n");
 305                goto error_release_regions;
 306        }
 307
 308        pci_set_master(pdev);
 309        pci_set_drvdata(pdev, priv);
 310        alcor_pci_init_check_aspm(priv);
 311
 312        for (i = 0; i < ARRAY_SIZE(alcor_pci_cells); i++) {
 313                alcor_pci_cells[i].platform_data = priv;
 314                alcor_pci_cells[i].pdata_size = sizeof(*priv);
 315        }
 316        ret = mfd_add_devices(&pdev->dev, priv->id, alcor_pci_cells,
 317                        ARRAY_SIZE(alcor_pci_cells), NULL, 0, NULL);
 318        if (ret < 0)
 319                goto error_release_regions;
 320
 321        alcor_pci_aspm_ctrl(priv, 0);
 322
 323        return 0;
 324
 325error_release_regions:
 326        pci_release_regions(pdev);
 327        return ret;
 328}
 329
 330static void alcor_pci_remove(struct pci_dev *pdev)
 331{
 332        struct alcor_pci_priv *priv;
 333
 334        priv = pci_get_drvdata(pdev);
 335
 336        alcor_pci_aspm_ctrl(priv, 1);
 337
 338        mfd_remove_devices(&pdev->dev);
 339
 340        ida_simple_remove(&alcor_pci_idr, priv->id);
 341
 342        pci_release_regions(pdev);
 343        pci_set_drvdata(pdev, NULL);
 344}
 345
 346#ifdef CONFIG_PM_SLEEP
 347static int alcor_suspend(struct device *dev)
 348{
 349        struct alcor_pci_priv *priv = dev_get_drvdata(dev);
 350
 351        alcor_pci_aspm_ctrl(priv, 1);
 352        return 0;
 353}
 354
 355static int alcor_resume(struct device *dev)
 356{
 357
 358        struct alcor_pci_priv *priv = dev_get_drvdata(dev);
 359
 360        alcor_pci_aspm_ctrl(priv, 0);
 361        return 0;
 362}
 363#endif /* CONFIG_PM_SLEEP */
 364
 365static SIMPLE_DEV_PM_OPS(alcor_pci_pm_ops, alcor_suspend, alcor_resume);
 366
 367static struct pci_driver alcor_driver = {
 368        .name   =       DRV_NAME_ALCOR_PCI,
 369        .id_table =     pci_ids,
 370        .probe  =       alcor_pci_probe,
 371        .remove =       alcor_pci_remove,
 372        .driver =       {
 373                .pm     = &alcor_pci_pm_ops
 374        },
 375};
 376
 377module_pci_driver(alcor_driver);
 378
 379MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
 380MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface");
 381MODULE_LICENSE("GPL");
 382