linux/drivers/spi/spi-dw-pci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * PCI interface driver for DW SPI Core
   4 *
   5 * Copyright (c) 2009, 2014 Intel Corporation.
   6 */
   7
   8#include <linux/pci.h>
   9#include <linux/pm_runtime.h>
  10#include <linux/slab.h>
  11#include <linux/spi/spi.h>
  12#include <linux/module.h>
  13
  14#include "spi-dw.h"
  15
  16#define DRIVER_NAME "dw_spi_pci"
  17
  18/* HW info for MRST Clk Control Unit, 32b reg per controller */
  19#define MRST_SPI_CLK_BASE       100000000       /* 100m */
  20#define MRST_CLK_SPI_REG        0xff11d86c
  21#define CLK_SPI_BDIV_OFFSET     0
  22#define CLK_SPI_BDIV_MASK       0x00000007
  23#define CLK_SPI_CDIV_OFFSET     9
  24#define CLK_SPI_CDIV_MASK       0x00000e00
  25#define CLK_SPI_DISABLE_OFFSET  8
  26
  27struct spi_pci_desc {
  28        int     (*setup)(struct dw_spi *);
  29        u16     num_cs;
  30        u16     bus_num;
  31        u32     max_freq;
  32};
  33
  34static int spi_mid_init(struct dw_spi *dws)
  35{
  36        void __iomem *clk_reg;
  37        u32 clk_cdiv;
  38
  39        clk_reg = ioremap(MRST_CLK_SPI_REG, 16);
  40        if (!clk_reg)
  41                return -ENOMEM;
  42
  43        /* Get SPI controller operating freq info */
  44        clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32));
  45        clk_cdiv &= CLK_SPI_CDIV_MASK;
  46        clk_cdiv >>= CLK_SPI_CDIV_OFFSET;
  47        dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1);
  48
  49        iounmap(clk_reg);
  50
  51        dw_spi_dma_setup_mfld(dws);
  52
  53        return 0;
  54}
  55
  56static int spi_generic_init(struct dw_spi *dws)
  57{
  58        dw_spi_dma_setup_generic(dws);
  59
  60        return 0;
  61}
  62
  63static struct spi_pci_desc spi_pci_mid_desc_1 = {
  64        .setup = spi_mid_init,
  65        .num_cs = 5,
  66        .bus_num = 0,
  67};
  68
  69static struct spi_pci_desc spi_pci_mid_desc_2 = {
  70        .setup = spi_mid_init,
  71        .num_cs = 2,
  72        .bus_num = 1,
  73};
  74
  75static struct spi_pci_desc spi_pci_ehl_desc = {
  76        .setup = spi_generic_init,
  77        .num_cs = 2,
  78        .bus_num = -1,
  79        .max_freq = 100000000,
  80};
  81
  82static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  83{
  84        struct dw_spi *dws;
  85        struct spi_pci_desc *desc = (struct spi_pci_desc *)ent->driver_data;
  86        int pci_bar = 0;
  87        int ret;
  88
  89        ret = pcim_enable_device(pdev);
  90        if (ret)
  91                return ret;
  92
  93        dws = devm_kzalloc(&pdev->dev, sizeof(*dws), GFP_KERNEL);
  94        if (!dws)
  95                return -ENOMEM;
  96
  97        /* Get basic io resource and map it */
  98        dws->paddr = pci_resource_start(pdev, pci_bar);
  99        pci_set_master(pdev);
 100
 101        ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev));
 102        if (ret)
 103                return ret;
 104
 105        ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
 106        if (ret < 0)
 107                return ret;
 108
 109        dws->regs = pcim_iomap_table(pdev)[pci_bar];
 110        dws->irq = pci_irq_vector(pdev, 0);
 111
 112        /*
 113         * Specific handling for platforms, like dma setup,
 114         * clock rate, FIFO depth.
 115         */
 116        if (desc) {
 117                dws->num_cs = desc->num_cs;
 118                dws->bus_num = desc->bus_num;
 119                dws->max_freq = desc->max_freq;
 120
 121                if (desc->setup) {
 122                        ret = desc->setup(dws);
 123                        if (ret)
 124                                goto err_free_irq_vectors;
 125                }
 126        } else {
 127                ret = -ENODEV;
 128                goto err_free_irq_vectors;
 129        }
 130
 131        ret = dw_spi_add_host(&pdev->dev, dws);
 132        if (ret)
 133                goto err_free_irq_vectors;
 134
 135        /* PCI hook and SPI hook use the same drv data */
 136        pci_set_drvdata(pdev, dws);
 137
 138        dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n",
 139                pdev->vendor, pdev->device);
 140
 141        pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
 142        pm_runtime_use_autosuspend(&pdev->dev);
 143        pm_runtime_put_autosuspend(&pdev->dev);
 144        pm_runtime_allow(&pdev->dev);
 145
 146        return 0;
 147
 148err_free_irq_vectors:
 149        pci_free_irq_vectors(pdev);
 150        return ret;
 151}
 152
 153static void spi_pci_remove(struct pci_dev *pdev)
 154{
 155        struct dw_spi *dws = pci_get_drvdata(pdev);
 156
 157        pm_runtime_forbid(&pdev->dev);
 158        pm_runtime_get_noresume(&pdev->dev);
 159
 160        dw_spi_remove_host(dws);
 161        pci_free_irq_vectors(pdev);
 162}
 163
 164#ifdef CONFIG_PM_SLEEP
 165static int spi_suspend(struct device *dev)
 166{
 167        struct dw_spi *dws = dev_get_drvdata(dev);
 168
 169        return dw_spi_suspend_host(dws);
 170}
 171
 172static int spi_resume(struct device *dev)
 173{
 174        struct dw_spi *dws = dev_get_drvdata(dev);
 175
 176        return dw_spi_resume_host(dws);
 177}
 178#endif
 179
 180static SIMPLE_DEV_PM_OPS(dw_spi_pm_ops, spi_suspend, spi_resume);
 181
 182static const struct pci_device_id pci_ids[] = {
 183        /* Intel MID platform SPI controller 0 */
 184        /*
 185         * The access to the device 8086:0801 is disabled by HW, since it's
 186         * exclusively used by SCU to communicate with MSIC.
 187         */
 188        /* Intel MID platform SPI controller 1 */
 189        { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc_1},
 190        /* Intel MID platform SPI controller 2 */
 191        { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&spi_pci_mid_desc_2},
 192        /* Intel Elkhart Lake PSE SPI controllers */
 193        { PCI_VDEVICE(INTEL, 0x4b84), (kernel_ulong_t)&spi_pci_ehl_desc},
 194        { PCI_VDEVICE(INTEL, 0x4b85), (kernel_ulong_t)&spi_pci_ehl_desc},
 195        { PCI_VDEVICE(INTEL, 0x4b86), (kernel_ulong_t)&spi_pci_ehl_desc},
 196        { PCI_VDEVICE(INTEL, 0x4b87), (kernel_ulong_t)&spi_pci_ehl_desc},
 197        {},
 198};
 199MODULE_DEVICE_TABLE(pci, pci_ids);
 200
 201static struct pci_driver dw_spi_driver = {
 202        .name =         DRIVER_NAME,
 203        .id_table =     pci_ids,
 204        .probe =        spi_pci_probe,
 205        .remove =       spi_pci_remove,
 206        .driver         = {
 207                .pm     = &dw_spi_pm_ops,
 208        },
 209};
 210
 211module_pci_driver(dw_spi_driver);
 212
 213MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
 214MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
 215MODULE_LICENSE("GPL v2");
 216