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        /* Register hook to configure CTRLR0 */
  52        dws->update_cr0 = dw_spi_update_cr0;
  53
  54        dw_spi_dma_setup_mfld(dws);
  55
  56        return 0;
  57}
  58
  59static int spi_generic_init(struct dw_spi *dws)
  60{
  61        /* Register hook to configure CTRLR0 */
  62        dws->update_cr0 = dw_spi_update_cr0;
  63
  64        dw_spi_dma_setup_generic(dws);
  65
  66        return 0;
  67}
  68
  69static struct spi_pci_desc spi_pci_mid_desc_1 = {
  70        .setup = spi_mid_init,
  71        .num_cs = 5,
  72        .bus_num = 0,
  73};
  74
  75static struct spi_pci_desc spi_pci_mid_desc_2 = {
  76        .setup = spi_mid_init,
  77        .num_cs = 2,
  78        .bus_num = 1,
  79};
  80
  81static struct spi_pci_desc spi_pci_ehl_desc = {
  82        .setup = spi_generic_init,
  83        .num_cs = 2,
  84        .bus_num = -1,
  85        .max_freq = 100000000,
  86};
  87
  88static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  89{
  90        struct dw_spi *dws;
  91        struct spi_pci_desc *desc = (struct spi_pci_desc *)ent->driver_data;
  92        int pci_bar = 0;
  93        int ret;
  94
  95        ret = pcim_enable_device(pdev);
  96        if (ret)
  97                return ret;
  98
  99        dws = devm_kzalloc(&pdev->dev, sizeof(*dws), GFP_KERNEL);
 100        if (!dws)
 101                return -ENOMEM;
 102
 103        /* Get basic io resource and map it */
 104        dws->paddr = pci_resource_start(pdev, pci_bar);
 105        pci_set_master(pdev);
 106
 107        ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev));
 108        if (ret)
 109                return ret;
 110
 111        ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
 112        if (ret < 0)
 113                return ret;
 114
 115        dws->regs = pcim_iomap_table(pdev)[pci_bar];
 116        dws->irq = pci_irq_vector(pdev, 0);
 117
 118        /*
 119         * Specific handling for platforms, like dma setup,
 120         * clock rate, FIFO depth.
 121         */
 122        if (desc) {
 123                dws->num_cs = desc->num_cs;
 124                dws->bus_num = desc->bus_num;
 125                dws->max_freq = desc->max_freq;
 126
 127                if (desc->setup) {
 128                        ret = desc->setup(dws);
 129                        if (ret)
 130                                return ret;
 131                }
 132        } else {
 133                pci_free_irq_vectors(pdev);
 134                return -ENODEV;
 135        }
 136
 137        ret = dw_spi_add_host(&pdev->dev, dws);
 138        if (ret) {
 139                pci_free_irq_vectors(pdev);
 140                return ret;
 141        }
 142
 143        /* PCI hook and SPI hook use the same drv data */
 144        pci_set_drvdata(pdev, dws);
 145
 146        dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n",
 147                pdev->vendor, pdev->device);
 148
 149        pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
 150        pm_runtime_use_autosuspend(&pdev->dev);
 151        pm_runtime_put_autosuspend(&pdev->dev);
 152        pm_runtime_allow(&pdev->dev);
 153
 154        return 0;
 155}
 156
 157static void spi_pci_remove(struct pci_dev *pdev)
 158{
 159        struct dw_spi *dws = pci_get_drvdata(pdev);
 160
 161        pm_runtime_forbid(&pdev->dev);
 162        pm_runtime_get_noresume(&pdev->dev);
 163
 164        dw_spi_remove_host(dws);
 165        pci_free_irq_vectors(pdev);
 166}
 167
 168#ifdef CONFIG_PM_SLEEP
 169static int spi_suspend(struct device *dev)
 170{
 171        struct dw_spi *dws = dev_get_drvdata(dev);
 172
 173        return dw_spi_suspend_host(dws);
 174}
 175
 176static int spi_resume(struct device *dev)
 177{
 178        struct dw_spi *dws = dev_get_drvdata(dev);
 179
 180        return dw_spi_resume_host(dws);
 181}
 182#endif
 183
 184static SIMPLE_DEV_PM_OPS(dw_spi_pm_ops, spi_suspend, spi_resume);
 185
 186static const struct pci_device_id pci_ids[] = {
 187        /* Intel MID platform SPI controller 0 */
 188        /*
 189         * The access to the device 8086:0801 is disabled by HW, since it's
 190         * exclusively used by SCU to communicate with MSIC.
 191         */
 192        /* Intel MID platform SPI controller 1 */
 193        { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc_1},
 194        /* Intel MID platform SPI controller 2 */
 195        { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&spi_pci_mid_desc_2},
 196        /* Intel Elkhart Lake PSE SPI controllers */
 197        { PCI_VDEVICE(INTEL, 0x4b84), (kernel_ulong_t)&spi_pci_ehl_desc},
 198        { PCI_VDEVICE(INTEL, 0x4b85), (kernel_ulong_t)&spi_pci_ehl_desc},
 199        { PCI_VDEVICE(INTEL, 0x4b86), (kernel_ulong_t)&spi_pci_ehl_desc},
 200        { PCI_VDEVICE(INTEL, 0x4b87), (kernel_ulong_t)&spi_pci_ehl_desc},
 201        {},
 202};
 203MODULE_DEVICE_TABLE(pci, pci_ids);
 204
 205static struct pci_driver dw_spi_driver = {
 206        .name =         DRIVER_NAME,
 207        .id_table =     pci_ids,
 208        .probe =        spi_pci_probe,
 209        .remove =       spi_pci_remove,
 210        .driver         = {
 211                .pm     = &dw_spi_pm_ops,
 212        },
 213};
 214
 215module_pci_driver(dw_spi_driver);
 216
 217MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
 218MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
 219MODULE_LICENSE("GPL v2");
 220