mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
![Serge Semin](/assets/img/avatar_default.png)
Mostly due to a long driver history it's methods and macro names look a
bit messy. In particularly that concerns the code their prefixes. A
biggest part of the driver functions and macros have got the dw_spi/DW_SPI
prefixes. But there are some entities which have been just
"spi_/SPI_"-prefixed. Especially that concerns the CSR and their fields
macro definitions. It makes the code harder to comprehend since such
methods and macros can be easily confused with the global SPI-subsystem
exports. In this case the only possible way to more or less quickly
distinguish one naming space from another is either by context or by the
argument type, which most of the times isn't that easy anyway. In addition
to that a new DW SSI IP-core support has been added in the framework of
commit e539f435cb
("spi: dw: Add support for DesignWare DWC_ssi"), which
introduced a new set or macro-prefixes to describe CTRLR0-specific fields
and worsen the situation. Finally there are methods with
no DW SPI driver-reference prefix at all, that make the code reading even
harder. So in order to ease the driver hacking let's bring the code naming
to a common base:
1) Each method is supposed to have "dw_spi_" prefix so to be easily
distinguished from the kernel API, e.g. SPI-subsystem methods and macros.
(Exception is the local implementation of the readl/writel methods since
being just the regspace accessors.)
2) Each generically used macro should have DW_SPI_-prefix thus being
easily comprehended as the local driver definition.
3) DW APB SSI and DW SSI specific macros should have prefixes as DW_PSSI_
and DW_HSSI_ respectively so referring to the system buses they support
(APB and AHB similarly to the DT clocks naming like pclk, hclk).
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20211115181917.7521-4-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Mark Brown <broonie@kernel.org>
215 lines
5 KiB
C
215 lines
5 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* PCI interface driver for DW SPI Core
|
|
*
|
|
* Copyright (c) 2009, 2014 Intel Corporation.
|
|
*/
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/module.h>
|
|
|
|
#include "spi-dw.h"
|
|
|
|
#define DRIVER_NAME "dw_spi_pci"
|
|
|
|
/* HW info for MRST Clk Control Unit, 32b reg per controller */
|
|
#define MRST_SPI_CLK_BASE 100000000 /* 100m */
|
|
#define MRST_CLK_SPI_REG 0xff11d86c
|
|
#define CLK_SPI_BDIV_OFFSET 0
|
|
#define CLK_SPI_BDIV_MASK 0x00000007
|
|
#define CLK_SPI_CDIV_OFFSET 9
|
|
#define CLK_SPI_CDIV_MASK 0x00000e00
|
|
#define CLK_SPI_DISABLE_OFFSET 8
|
|
|
|
struct dw_spi_pci_desc {
|
|
int (*setup)(struct dw_spi *);
|
|
u16 num_cs;
|
|
u16 bus_num;
|
|
u32 max_freq;
|
|
};
|
|
|
|
static int dw_spi_pci_mid_init(struct dw_spi *dws)
|
|
{
|
|
void __iomem *clk_reg;
|
|
u32 clk_cdiv;
|
|
|
|
clk_reg = ioremap(MRST_CLK_SPI_REG, 16);
|
|
if (!clk_reg)
|
|
return -ENOMEM;
|
|
|
|
/* Get SPI controller operating freq info */
|
|
clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32));
|
|
clk_cdiv &= CLK_SPI_CDIV_MASK;
|
|
clk_cdiv >>= CLK_SPI_CDIV_OFFSET;
|
|
dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1);
|
|
|
|
iounmap(clk_reg);
|
|
|
|
dw_spi_dma_setup_mfld(dws);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dw_spi_pci_generic_init(struct dw_spi *dws)
|
|
{
|
|
dw_spi_dma_setup_generic(dws);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct dw_spi_pci_desc dw_spi_pci_mid_desc_1 = {
|
|
.setup = dw_spi_pci_mid_init,
|
|
.num_cs = 5,
|
|
.bus_num = 0,
|
|
};
|
|
|
|
static struct dw_spi_pci_desc dw_spi_pci_mid_desc_2 = {
|
|
.setup = dw_spi_pci_mid_init,
|
|
.num_cs = 2,
|
|
.bus_num = 1,
|
|
};
|
|
|
|
static struct dw_spi_pci_desc dw_spi_pci_ehl_desc = {
|
|
.setup = dw_spi_pci_generic_init,
|
|
.num_cs = 2,
|
|
.bus_num = -1,
|
|
.max_freq = 100000000,
|
|
};
|
|
|
|
static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
{
|
|
struct dw_spi_pci_desc *desc = (struct dw_spi_pci_desc *)ent->driver_data;
|
|
struct dw_spi *dws;
|
|
int pci_bar = 0;
|
|
int ret;
|
|
|
|
ret = pcim_enable_device(pdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dws = devm_kzalloc(&pdev->dev, sizeof(*dws), GFP_KERNEL);
|
|
if (!dws)
|
|
return -ENOMEM;
|
|
|
|
/* Get basic io resource and map it */
|
|
dws->paddr = pci_resource_start(pdev, pci_bar);
|
|
pci_set_master(pdev);
|
|
|
|
ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
dws->regs = pcim_iomap_table(pdev)[pci_bar];
|
|
dws->irq = pci_irq_vector(pdev, 0);
|
|
|
|
/*
|
|
* Specific handling for platforms, like dma setup,
|
|
* clock rate, FIFO depth.
|
|
*/
|
|
if (desc) {
|
|
dws->num_cs = desc->num_cs;
|
|
dws->bus_num = desc->bus_num;
|
|
dws->max_freq = desc->max_freq;
|
|
|
|
if (desc->setup) {
|
|
ret = desc->setup(dws);
|
|
if (ret)
|
|
goto err_free_irq_vectors;
|
|
}
|
|
} else {
|
|
ret = -ENODEV;
|
|
goto err_free_irq_vectors;
|
|
}
|
|
|
|
ret = dw_spi_add_host(&pdev->dev, dws);
|
|
if (ret)
|
|
goto err_free_irq_vectors;
|
|
|
|
/* PCI hook and SPI hook use the same drv data */
|
|
pci_set_drvdata(pdev, dws);
|
|
|
|
dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n",
|
|
pdev->vendor, pdev->device);
|
|
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
|
pm_runtime_use_autosuspend(&pdev->dev);
|
|
pm_runtime_put_autosuspend(&pdev->dev);
|
|
pm_runtime_allow(&pdev->dev);
|
|
|
|
return 0;
|
|
|
|
err_free_irq_vectors:
|
|
pci_free_irq_vectors(pdev);
|
|
return ret;
|
|
}
|
|
|
|
static void dw_spi_pci_remove(struct pci_dev *pdev)
|
|
{
|
|
struct dw_spi *dws = pci_get_drvdata(pdev);
|
|
|
|
pm_runtime_forbid(&pdev->dev);
|
|
pm_runtime_get_noresume(&pdev->dev);
|
|
|
|
dw_spi_remove_host(dws);
|
|
pci_free_irq_vectors(pdev);
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int dw_spi_pci_suspend(struct device *dev)
|
|
{
|
|
struct dw_spi *dws = dev_get_drvdata(dev);
|
|
|
|
return dw_spi_suspend_host(dws);
|
|
}
|
|
|
|
static int dw_spi_pci_resume(struct device *dev)
|
|
{
|
|
struct dw_spi *dws = dev_get_drvdata(dev);
|
|
|
|
return dw_spi_resume_host(dws);
|
|
}
|
|
#endif
|
|
|
|
static SIMPLE_DEV_PM_OPS(dw_spi_pci_pm_ops, dw_spi_pci_suspend, dw_spi_pci_resume);
|
|
|
|
static const struct pci_device_id dw_spi_pci_ids[] = {
|
|
/* Intel MID platform SPI controller 0 */
|
|
/*
|
|
* The access to the device 8086:0801 is disabled by HW, since it's
|
|
* exclusively used by SCU to communicate with MSIC.
|
|
*/
|
|
/* Intel MID platform SPI controller 1 */
|
|
{ PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&dw_spi_pci_mid_desc_1},
|
|
/* Intel MID platform SPI controller 2 */
|
|
{ PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&dw_spi_pci_mid_desc_2},
|
|
/* Intel Elkhart Lake PSE SPI controllers */
|
|
{ PCI_VDEVICE(INTEL, 0x4b84), (kernel_ulong_t)&dw_spi_pci_ehl_desc},
|
|
{ PCI_VDEVICE(INTEL, 0x4b85), (kernel_ulong_t)&dw_spi_pci_ehl_desc},
|
|
{ PCI_VDEVICE(INTEL, 0x4b86), (kernel_ulong_t)&dw_spi_pci_ehl_desc},
|
|
{ PCI_VDEVICE(INTEL, 0x4b87), (kernel_ulong_t)&dw_spi_pci_ehl_desc},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(pci, dw_spi_pci_ids);
|
|
|
|
static struct pci_driver dw_spi_pci_driver = {
|
|
.name = DRIVER_NAME,
|
|
.id_table = dw_spi_pci_ids,
|
|
.probe = dw_spi_pci_probe,
|
|
.remove = dw_spi_pci_remove,
|
|
.driver = {
|
|
.pm = &dw_spi_pci_pm_ops,
|
|
},
|
|
};
|
|
module_pci_driver(dw_spi_pci_driver);
|
|
|
|
MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
|
|
MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_IMPORT_NS(SPI_DW_CORE);
|