android_kernel_modules_leno.../drivers/misc/rawio/rawio_pci.c

236 lines
5.3 KiB
C

/*
* rawio_pci.c - a driver to read/write pci configuration space registers based
* on the rawio framework.
*
* 1: byte, 2: word, 4: dword
*
* read pci config space registers
* echo "r[1|2|4] pci <domain> <bus> <dev> <func> <reg> [<len>]" >
* /sys/kernel/debug/rawio_cmd
* cat /sys/kernel/debug/rawio_output
* e.g. echo "r1 pci 0 0 3 0 8 12" > /sys/kernel/debug/rawio_cmd
* cat /sys/kernel/debug/rawio_output
*
* write a pci config space register:
* echo "w[1|2|4] pci <domain> <bus> <dev> <func> <reg> <val>" >
* /sys/kernel/debug/rawio_output
* cat /sys/kernel/debug/rawio_output
* e.g. echo "w pci 0 0 0x11 2 0x10 0xffffffff" > /sys/kernel/debug/rawio_cmd
* cat /sys/kernel/debug/rawio_output
*
*
* Copyright (c) 2013 Bin Gao <bin.gao@intel.com>
*
* This file is released under the GPLv2
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pm_runtime.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include "rawio.h"
static int pci_prepare(int pci_domain, unsigned int pci_bus,
u8 pci_dev, u8 pci_func, enum width width,
u16 pci_reg, u16 len, struct pci_dev **ppdev)
{
struct pci_dev *pdev;
int ret;
if (((width == WIDTH_2) && (pci_reg & 0x1)) ||
((width == WIDTH_4) && (pci_reg & 0x3))) {
rawio_err("register address requires 2 bytes aligned for 16 bit access, and 4 bytes aligned for 32 bit access\n");
return -EINVAL;
}
pdev = pci_get_domain_bus_and_slot(pci_domain, pci_bus,
PCI_DEVFN(pci_dev, pci_func));
if (!pdev) {
rawio_err("pci device %04x:%02x:%02x.%01x doesn't exist\n",
pci_domain, pci_bus, pci_dev, pci_func);
return -ENODEV;
}
if (((pci_reg >= 0x100) && !pci_is_pcie(pdev)) ||
(pci_reg >= 0x1000)) {
rawio_err("register address is out of range\n");
return -EINVAL;
}
if ((((pci_reg + len * width) >= 0x100) && !pci_is_pcie(pdev)) ||
((pci_reg + len * width) >= 0x1000)) {
rawio_err("register address is out of range\n");
return -EINVAL;
}
ret = pm_runtime_get_sync(&pdev->dev);
if ((ret >= 0) || (ret == -EACCES))
goto out;
rawio_err("can't put pci device %04x:%02x:%02x.%01xinto running state, pm_runtime_get_sync() returned %d\n",
pci_domain, pci_bus, pci_dev, pci_func, ret);
return -EBUSY;
out:
*ppdev = pdev;
return 0;
}
static void pci_finish(struct pci_dev *pdev)
{
pm_runtime_put_sync(&pdev->dev);
}
static int rawio_pci_read(struct rawio_driver *driver, int width,
u64 *input, u8 *postfix, int input_num,
void **output, int *output_num)
{
int i, ret, pci_domain;
struct pci_dev *pdev;
unsigned int pci_bus;
u8 pci_dev, pci_func;
u16 pci_reg, len;
void *buf;
pci_domain = (int)input[0];
pci_bus = (unsigned int)input[1];
pci_dev = (u8)input[2];
pci_func = (u8)input[3];
pci_reg = (u16)input[4];
len = 1;
if (input_num == 6)
len = (u16)input[5];
ret = pci_prepare(pci_domain, pci_bus, pci_dev, pci_func,
width, pci_reg, len, &pdev);
if (ret)
return ret;
buf = kzalloc(width * len, GFP_KERNEL);
if (buf == NULL) {
rawio_err("can't alloc memory\n");
pci_finish(pdev);
return -ENOMEM;
}
for (i = 0; i < len; i++) {
switch (width) {
case WIDTH_1:
pci_read_config_byte(pdev, pci_reg + i, (u8 *)buf + i);
break;
case WIDTH_2:
pci_read_config_word(pdev, pci_reg + i * 2,
(u16 *)buf + i);
break;
case WIDTH_4:
pci_read_config_dword(pdev, pci_reg + i * 4,
(u32 *)buf + i);
break;
default:
break;
}
}
pci_finish(pdev);
*output = buf;
*output_num = len;
return 0;
}
static int rawio_pci_write(struct rawio_driver *driver, int width,
u64 *input, u8 *postfix, int input_num)
{
int ret, pci_domain;
struct pci_dev *pdev;
unsigned int pci_bus;
u8 pci_dev, pci_func;
u16 pci_reg;
u32 value;
pci_domain = (int)input[0];
pci_bus = (unsigned int)input[1];
pci_dev = (u8)input[2];
pci_func = (u8)input[3];
pci_reg = (u16)input[4];
value = (u32) input[5];
ret = pci_prepare(pci_domain, pci_bus, pci_dev, pci_func,
width, pci_reg, 1, &pdev);
if (ret)
return ret;
switch (width) {
case WIDTH_1:
pci_write_config_byte(pdev, pci_reg, (u8) value);
break;
case WIDTH_2:
pci_write_config_word(pdev, pci_reg, (u16) value);
break;
case WIDTH_4:
pci_write_config_dword(pdev, pci_reg, value);
break;
default:
break;
}
pci_finish(pdev);
return 0;
}
static struct rawio_ops rawio_pci_ops = {
rawio_pci_read,
NULL,
rawio_pci_write,
};
static struct rawio_driver rawio_pci = {
{NULL, NULL},
"pci",
/* read */
6, /* max args */
{TYPE_S32, TYPE_U32, TYPE_U8, TYPE_U8, TYPE_U16, TYPE_S16}, /* types */
5, /* min args */
{ 0, }, /* postfix */
/* write */
6, /* max args */
{TYPE_S32, TYPE_U32, TYPE_U8, TYPE_U8, TYPE_U16, TYPE_U32}, /* types */
6, /* min args */
{ 0, },
4, /* index of address arg */
WIDTH_1 | WIDTH_2 | WIDTH_4, /* supported width */
WIDTH_4, /* default width */
"r[1|2|4] pci <domain> <bus> <dev> <func> <reg> [<len>]\n"
"w[1|2|4] pci <domain> <bus> <dev> <func> <reg> <val>\n",
&rawio_pci_ops,
NULL
};
static int __init rawio_pci_init(void)
{
if (rawio_register_driver(&rawio_pci))
return -ENODEV;
return 0;
}
module_init(rawio_pci_init);
static void __exit rawio_pci_exit(void)
{
rawio_unregister_driver(&rawio_pci);
}
module_exit(rawio_pci_exit);
MODULE_DESCRIPTION("Rawio PCI driver");
MODULE_VERSION("1.0");
MODULE_AUTHOR("Bin Gao <bin.gao@intel.com>");
MODULE_LICENSE("GPL v2");