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

lpc_sch.c (4510B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  lpc_sch.c - LPC interface for Intel Poulsbo SCH
      4 *
      5 *  LPC bridge function of the Intel SCH contains many other
      6 *  functional units, such as Interrupt controllers, Timers,
      7 *  Power Management, System Management, GPIO, RTC, and LPC
      8 *  Configuration Registers.
      9 *
     10 *  Copyright (c) 2010 CompuLab Ltd
     11 *  Copyright (c) 2014 Intel Corp.
     12 *  Author: Denis Turischev <denis@compulab.co.il>
     13 */
     14
     15#include <linux/kernel.h>
     16#include <linux/module.h>
     17#include <linux/errno.h>
     18#include <linux/acpi.h>
     19#include <linux/pci.h>
     20#include <linux/mfd/core.h>
     21
     22#define SMBASE		0x40
     23#define SMBUS_IO_SIZE	64
     24
     25#define GPIO_BASE	0x44
     26#define GPIO_IO_SIZE	64
     27#define GPIO_IO_SIZE_CENTERTON	128
     28
     29#define WDTBASE		0x84
     30#define WDT_IO_SIZE	64
     31
     32enum sch_chipsets {
     33	LPC_SCH = 0,		/* Intel Poulsbo SCH */
     34	LPC_ITC,		/* Intel Tunnel Creek */
     35	LPC_CENTERTON,		/* Intel Centerton */
     36	LPC_QUARK_X1000,	/* Intel Quark X1000 */
     37};
     38
     39struct lpc_sch_info {
     40	unsigned int io_size_smbus;
     41	unsigned int io_size_gpio;
     42	unsigned int io_size_wdt;
     43};
     44
     45static struct lpc_sch_info sch_chipset_info[] = {
     46	[LPC_SCH] = {
     47		.io_size_smbus = SMBUS_IO_SIZE,
     48		.io_size_gpio = GPIO_IO_SIZE,
     49	},
     50	[LPC_ITC] = {
     51		.io_size_smbus = SMBUS_IO_SIZE,
     52		.io_size_gpio = GPIO_IO_SIZE,
     53		.io_size_wdt = WDT_IO_SIZE,
     54	},
     55	[LPC_CENTERTON] = {
     56		.io_size_smbus = SMBUS_IO_SIZE,
     57		.io_size_gpio = GPIO_IO_SIZE_CENTERTON,
     58		.io_size_wdt = WDT_IO_SIZE,
     59	},
     60	[LPC_QUARK_X1000] = {
     61		.io_size_gpio = GPIO_IO_SIZE,
     62		.io_size_wdt = WDT_IO_SIZE,
     63	},
     64};
     65
     66static const struct pci_device_id lpc_sch_ids[] = {
     67	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC), LPC_SCH },
     68	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC), LPC_ITC },
     69	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB), LPC_CENTERTON },
     70	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB), LPC_QUARK_X1000 },
     71	{ 0, }
     72};
     73MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
     74
     75#define LPC_NO_RESOURCE		1
     76#define LPC_SKIP_RESOURCE	2
     77
     78static int lpc_sch_get_io(struct pci_dev *pdev, int where, const char *name,
     79			  struct resource *res, int size)
     80{
     81	unsigned int base_addr_cfg;
     82	unsigned short base_addr;
     83
     84	if (size == 0)
     85		return LPC_NO_RESOURCE;
     86
     87	pci_read_config_dword(pdev, where, &base_addr_cfg);
     88	base_addr = 0;
     89	if (!(base_addr_cfg & (1 << 31)))
     90		dev_warn(&pdev->dev, "Decode of the %s I/O range disabled\n",
     91			 name);
     92	else
     93		base_addr = (unsigned short)base_addr_cfg;
     94
     95	if (base_addr == 0) {
     96		dev_warn(&pdev->dev, "I/O space for %s uninitialized\n", name);
     97		return LPC_SKIP_RESOURCE;
     98	}
     99
    100	res->start = base_addr;
    101	res->end = base_addr + size - 1;
    102	res->flags = IORESOURCE_IO;
    103
    104	return 0;
    105}
    106
    107static int lpc_sch_populate_cell(struct pci_dev *pdev, int where,
    108				 const char *name, int size, int id,
    109				 struct mfd_cell *cell)
    110{
    111	struct resource *res;
    112	int ret;
    113
    114	res = devm_kzalloc(&pdev->dev, sizeof(*res), GFP_KERNEL);
    115	if (!res)
    116		return -ENOMEM;
    117
    118	ret = lpc_sch_get_io(pdev, where, name, res, size);
    119	if (ret)
    120		return ret;
    121
    122	memset(cell, 0, sizeof(*cell));
    123
    124	cell->name = name;
    125	cell->resources = res;
    126	cell->num_resources = 1;
    127	cell->ignore_resource_conflicts = true;
    128	cell->id = id;
    129
    130	return 0;
    131}
    132
    133static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id)
    134{
    135	struct mfd_cell lpc_sch_cells[3];
    136	struct lpc_sch_info *info = &sch_chipset_info[id->driver_data];
    137	unsigned int cells = 0;
    138	int ret;
    139
    140	ret = lpc_sch_populate_cell(dev, SMBASE, "isch_smbus",
    141				    info->io_size_smbus,
    142				    id->device, &lpc_sch_cells[cells]);
    143	if (ret < 0)
    144		return ret;
    145	if (ret == 0)
    146		cells++;
    147
    148	ret = lpc_sch_populate_cell(dev, GPIO_BASE, "sch_gpio",
    149				    info->io_size_gpio,
    150				    id->device, &lpc_sch_cells[cells]);
    151	if (ret < 0)
    152		return ret;
    153	if (ret == 0)
    154		cells++;
    155
    156	ret = lpc_sch_populate_cell(dev, WDTBASE, "ie6xx_wdt",
    157				    info->io_size_wdt,
    158				    id->device, &lpc_sch_cells[cells]);
    159	if (ret < 0)
    160		return ret;
    161	if (ret == 0)
    162		cells++;
    163
    164	if (cells == 0) {
    165		dev_err(&dev->dev, "All decode registers disabled.\n");
    166		return -ENODEV;
    167	}
    168
    169	return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL);
    170}
    171
    172static void lpc_sch_remove(struct pci_dev *dev)
    173{
    174	mfd_remove_devices(&dev->dev);
    175}
    176
    177static struct pci_driver lpc_sch_driver = {
    178	.name		= "lpc_sch",
    179	.id_table	= lpc_sch_ids,
    180	.probe		= lpc_sch_probe,
    181	.remove		= lpc_sch_remove,
    182};
    183
    184module_pci_driver(lpc_sch_driver);
    185
    186MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
    187MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
    188MODULE_LICENSE("GPL");