cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

alcor_pci.c (9157B)


      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_alloc(&alcor_pci_idr, 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		ret = -ENOMEM;
    284		goto error_free_ida;
    285	}
    286
    287	if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
    288		dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
    289		ret = -ENODEV;
    290		goto error_release_regions;
    291	}
    292
    293	priv->iobase = pcim_iomap(pdev, bar, 0);
    294	if (!priv->iobase) {
    295		ret = -ENOMEM;
    296		goto error_release_regions;
    297	}
    298
    299	/* make sure irqs are disabled */
    300	alcor_write32(priv, 0, AU6601_REG_INT_ENABLE);
    301	alcor_write32(priv, 0, AU6601_MS_INT_ENABLE);
    302
    303	ret = dma_set_mask_and_coherent(priv->dev, AU6601_SDMA_MASK);
    304	if (ret) {
    305		dev_err(priv->dev, "Failed to set DMA mask\n");
    306		goto error_release_regions;
    307	}
    308
    309	pci_set_master(pdev);
    310	pci_set_drvdata(pdev, priv);
    311	alcor_pci_init_check_aspm(priv);
    312
    313	for (i = 0; i < ARRAY_SIZE(alcor_pci_cells); i++) {
    314		alcor_pci_cells[i].platform_data = priv;
    315		alcor_pci_cells[i].pdata_size = sizeof(*priv);
    316	}
    317	ret = mfd_add_devices(&pdev->dev, priv->id, alcor_pci_cells,
    318			ARRAY_SIZE(alcor_pci_cells), NULL, 0, NULL);
    319	if (ret < 0)
    320		goto error_clear_drvdata;
    321
    322	alcor_pci_aspm_ctrl(priv, 0);
    323
    324	return 0;
    325
    326error_clear_drvdata:
    327	pci_clear_master(pdev);
    328	pci_set_drvdata(pdev, NULL);
    329error_release_regions:
    330	pci_release_regions(pdev);
    331error_free_ida:
    332	ida_free(&alcor_pci_idr, priv->id);
    333	return ret;
    334}
    335
    336static void alcor_pci_remove(struct pci_dev *pdev)
    337{
    338	struct alcor_pci_priv *priv;
    339
    340	priv = pci_get_drvdata(pdev);
    341
    342	alcor_pci_aspm_ctrl(priv, 1);
    343
    344	mfd_remove_devices(&pdev->dev);
    345
    346	ida_free(&alcor_pci_idr, priv->id);
    347
    348	pci_release_regions(pdev);
    349	pci_clear_master(pdev);
    350	pci_set_drvdata(pdev, NULL);
    351}
    352
    353#ifdef CONFIG_PM_SLEEP
    354static int alcor_suspend(struct device *dev)
    355{
    356	struct alcor_pci_priv *priv = dev_get_drvdata(dev);
    357
    358	alcor_pci_aspm_ctrl(priv, 1);
    359	return 0;
    360}
    361
    362static int alcor_resume(struct device *dev)
    363{
    364
    365	struct alcor_pci_priv *priv = dev_get_drvdata(dev);
    366
    367	alcor_pci_aspm_ctrl(priv, 0);
    368	return 0;
    369}
    370#endif /* CONFIG_PM_SLEEP */
    371
    372static SIMPLE_DEV_PM_OPS(alcor_pci_pm_ops, alcor_suspend, alcor_resume);
    373
    374static struct pci_driver alcor_driver = {
    375	.name	=	DRV_NAME_ALCOR_PCI,
    376	.id_table =	pci_ids,
    377	.probe	=	alcor_pci_probe,
    378	.remove =	alcor_pci_remove,
    379	.driver	=	{
    380		.pm	= &alcor_pci_pm_ops
    381	},
    382};
    383
    384module_pci_driver(alcor_driver);
    385
    386MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
    387MODULE_DESCRIPTION("PCI driver for Alcor Micro AU6601 Secure Digital Host Controller Interface");
    388MODULE_LICENSE("GPL");