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

intel_vr_nor.c (6911B)


      1/*
      2 * drivers/mtd/maps/intel_vr_nor.c
      3 *
      4 * An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel
      5 * Vermilion Range chipset.
      6 *
      7 * The Vermilion Range Expansion Bus supports four chip selects, each of which
      8 * has 64MiB of address space.  The 2nd BAR of the Expansion Bus PCI Device
      9 * is a 256MiB memory region containing the address spaces for all four of the
     10 * chip selects, with start addresses hardcoded on 64MiB boundaries.
     11 *
     12 * This map driver only supports NOR flash on chip select 0.  The buswidth
     13 * (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing
     14 * and Control Register for Chip Select 0 (EXP_TIMING_CS0).  This driver does
     15 * not modify the value in the EXP_TIMING_CS0 register except to enable writing
     16 * and disable boot acceleration.  The timing parameters in the register are
     17 * assumed to have been properly initialized by the BIOS.  The reset default
     18 * timing parameters are maximally conservative (slow), so access to the flash
     19 * will be slower than it should be if the BIOS has not initialized the timing
     20 * parameters.
     21 *
     22 * Author: Andy Lowe <alowe@mvista.com>
     23 *
     24 * 2006 (c) MontaVista Software, Inc. This file is licensed under
     25 * the terms of the GNU General Public License version 2. This program
     26 * is licensed "as is" without any warranty of any kind, whether express
     27 * or implied.
     28 */
     29
     30#include <linux/module.h>
     31#include <linux/kernel.h>
     32#include <linux/slab.h>
     33#include <linux/pci.h>
     34#include <linux/mtd/mtd.h>
     35#include <linux/mtd/map.h>
     36#include <linux/mtd/partitions.h>
     37#include <linux/mtd/cfi.h>
     38#include <linux/mtd/flashchip.h>
     39
     40#define DRV_NAME "vr_nor"
     41
     42struct vr_nor_mtd {
     43	void __iomem *csr_base;
     44	struct map_info map;
     45	struct mtd_info *info;
     46	struct pci_dev *dev;
     47};
     48
     49/* Expansion Bus Configuration and Status Registers are in BAR 0 */
     50#define EXP_CSR_MBAR 0
     51/* Expansion Bus Memory Window is BAR 1 */
     52#define EXP_WIN_MBAR 1
     53/* Maximum address space for Chip Select 0 is 64MiB */
     54#define CS0_SIZE 0x04000000
     55/* Chip Select 0 is at offset 0 in the Memory Window */
     56#define CS0_START 0x0
     57/* Chip Select 0 Timing Register is at offset 0 in CSR */
     58#define EXP_TIMING_CS0 0x00
     59#define TIMING_CS_EN		(1 << 31)	/* Chip Select Enable */
     60#define TIMING_BOOT_ACCEL_DIS	(1 <<  8)	/* Boot Acceleration Disable */
     61#define TIMING_WR_EN		(1 <<  1)	/* Write Enable */
     62#define TIMING_BYTE_EN		(1 <<  0)	/* 8-bit vs 16-bit bus */
     63#define TIMING_MASK		0x3FFF0000
     64
     65static void vr_nor_destroy_partitions(struct vr_nor_mtd *p)
     66{
     67	mtd_device_unregister(p->info);
     68}
     69
     70static int vr_nor_init_partitions(struct vr_nor_mtd *p)
     71{
     72	/* register the flash bank */
     73	/* partition the flash bank */
     74	return mtd_device_register(p->info, NULL, 0);
     75}
     76
     77static void vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
     78{
     79	map_destroy(p->info);
     80}
     81
     82static int vr_nor_mtd_setup(struct vr_nor_mtd *p)
     83{
     84	static const char * const probe_types[] =
     85	    { "cfi_probe", "jedec_probe", NULL };
     86	const char * const *type;
     87
     88	for (type = probe_types; !p->info && *type; type++)
     89		p->info = do_map_probe(*type, &p->map);
     90	if (!p->info)
     91		return -ENODEV;
     92
     93	p->info->dev.parent = &p->dev->dev;
     94
     95	return 0;
     96}
     97
     98static void vr_nor_destroy_maps(struct vr_nor_mtd *p)
     99{
    100	unsigned int exp_timing_cs0;
    101
    102	/* write-protect the flash bank */
    103	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
    104	exp_timing_cs0 &= ~TIMING_WR_EN;
    105	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
    106
    107	/* unmap the flash window */
    108	iounmap(p->map.virt);
    109
    110	/* unmap the csr window */
    111	iounmap(p->csr_base);
    112}
    113
    114/*
    115 * Initialize the map_info structure and map the flash.
    116 * Returns 0 on success, nonzero otherwise.
    117 */
    118static int vr_nor_init_maps(struct vr_nor_mtd *p)
    119{
    120	unsigned long csr_phys, csr_len;
    121	unsigned long win_phys, win_len;
    122	unsigned int exp_timing_cs0;
    123	int err;
    124
    125	csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR);
    126	csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR);
    127	win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR);
    128	win_len = pci_resource_len(p->dev, EXP_WIN_MBAR);
    129
    130	if (!csr_phys || !csr_len || !win_phys || !win_len)
    131		return -ENODEV;
    132
    133	if (win_len < (CS0_START + CS0_SIZE))
    134		return -ENXIO;
    135
    136	p->csr_base = ioremap(csr_phys, csr_len);
    137	if (!p->csr_base)
    138		return -ENOMEM;
    139
    140	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
    141	if (!(exp_timing_cs0 & TIMING_CS_EN)) {
    142		dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
    143		       "is disabled.\n");
    144		err = -ENODEV;
    145		goto release;
    146	}
    147	if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) {
    148		dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
    149		       "is configured for maximally slow access times.\n");
    150	}
    151	p->map.name = DRV_NAME;
    152	p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2;
    153	p->map.phys = win_phys + CS0_START;
    154	p->map.size = CS0_SIZE;
    155	p->map.virt = ioremap(p->map.phys, p->map.size);
    156	if (!p->map.virt) {
    157		err = -ENOMEM;
    158		goto release;
    159	}
    160	simple_map_init(&p->map);
    161
    162	/* Enable writes to flash bank */
    163	exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN;
    164	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
    165
    166	return 0;
    167
    168      release:
    169	iounmap(p->csr_base);
    170	return err;
    171}
    172
    173static const struct pci_device_id vr_nor_pci_ids[] = {
    174	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},
    175	{0,}
    176};
    177
    178static void vr_nor_pci_remove(struct pci_dev *dev)
    179{
    180	struct vr_nor_mtd *p = pci_get_drvdata(dev);
    181
    182	vr_nor_destroy_partitions(p);
    183	vr_nor_destroy_mtd_setup(p);
    184	vr_nor_destroy_maps(p);
    185	kfree(p);
    186	pci_release_regions(dev);
    187	pci_disable_device(dev);
    188}
    189
    190static int vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
    191{
    192	struct vr_nor_mtd *p = NULL;
    193	unsigned int exp_timing_cs0;
    194	int err;
    195
    196	err = pci_enable_device(dev);
    197	if (err)
    198		goto out;
    199
    200	err = pci_request_regions(dev, DRV_NAME);
    201	if (err)
    202		goto disable_dev;
    203
    204	p = kzalloc(sizeof(*p), GFP_KERNEL);
    205	err = -ENOMEM;
    206	if (!p)
    207		goto release;
    208
    209	p->dev = dev;
    210
    211	err = vr_nor_init_maps(p);
    212	if (err)
    213		goto release;
    214
    215	err = vr_nor_mtd_setup(p);
    216	if (err)
    217		goto destroy_maps;
    218
    219	err = vr_nor_init_partitions(p);
    220	if (err)
    221		goto destroy_mtd_setup;
    222
    223	pci_set_drvdata(dev, p);
    224
    225	return 0;
    226
    227      destroy_mtd_setup:
    228	map_destroy(p->info);
    229
    230      destroy_maps:
    231	/* write-protect the flash bank */
    232	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
    233	exp_timing_cs0 &= ~TIMING_WR_EN;
    234	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
    235
    236	/* unmap the flash window */
    237	iounmap(p->map.virt);
    238
    239	/* unmap the csr window */
    240	iounmap(p->csr_base);
    241
    242      release:
    243	kfree(p);
    244	pci_release_regions(dev);
    245
    246      disable_dev:
    247	pci_disable_device(dev);
    248
    249      out:
    250	return err;
    251}
    252
    253static struct pci_driver vr_nor_pci_driver = {
    254	.name = DRV_NAME,
    255	.probe = vr_nor_pci_probe,
    256	.remove = vr_nor_pci_remove,
    257	.id_table = vr_nor_pci_ids,
    258};
    259
    260module_pci_driver(vr_nor_pci_driver);
    261
    262MODULE_AUTHOR("Andy Lowe");
    263MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");
    264MODULE_LICENSE("GPL");
    265MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids);