lpc_sch.c 4.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 *  lpc_sch.c - LPC interface for Intel Poulsbo SCH
 *
 *  LPC bridge function of the Intel SCH contains many other
 *  functional units, such as Interrupt controllers, Timers,
 *  Power Management, System Management, GPIO, RTC, and LPC
 *  Configuration Registers.
 *
 *  Copyright (c) 2010 CompuLab Ltd
 *  Author: Denis Turischev <denis@compulab.co.il>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License 2 as published
 *  by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/mfd/core.h>

#define SMBASE		0x40
#define SMBUS_IO_SIZE	64

#define GPIOBASE	0x44
#define GPIO_IO_SIZE	64
39
#define GPIO_IO_SIZE_CENTERTON	128
40

41 42 43
#define WDTBASE		0x84
#define WDT_IO_SIZE	64

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static struct resource smbus_sch_resource = {
		.flags = IORESOURCE_IO,
};


static struct resource gpio_sch_resource = {
		.flags = IORESOURCE_IO,
};

static struct mfd_cell lpc_sch_cells[] = {
	{
		.name = "isch_smbus",
		.num_resources = 1,
		.resources = &smbus_sch_resource,
	},
	{
		.name = "sch_gpio",
		.num_resources = 1,
		.resources = &gpio_sch_resource,
	},
};

66 67 68 69 70 71
static struct resource wdt_sch_resource = {
		.flags = IORESOURCE_IO,
};

static struct mfd_cell tunnelcreek_cells[] = {
	{
72
		.name = "ie6xx_wdt",
73 74 75 76 77
		.num_resources = 1,
		.resources = &wdt_sch_resource,
	},
};

78
static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
79
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
80
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
81
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) },
82 83 84 85 86 87 88 89 90
	{ 0, }
};
MODULE_DEVICE_TABLE(pci, lpc_sch_ids);

static int __devinit lpc_sch_probe(struct pci_dev *dev,
				const struct pci_device_id *id)
{
	unsigned int base_addr_cfg;
	unsigned short base_addr;
91
	int i;
92
	int ret;
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

	pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
	if (!(base_addr_cfg & (1 << 31))) {
		dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
		return -ENODEV;
	}
	base_addr = (unsigned short)base_addr_cfg;
	if (base_addr == 0) {
		dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
		return -ENODEV;
	}

	smbus_sch_resource.start = base_addr;
	smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;

	pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
	if (!(base_addr_cfg & (1 << 31))) {
		dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
		return -ENODEV;
	}
	base_addr = (unsigned short)base_addr_cfg;
	if (base_addr == 0) {
		dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
		return -ENODEV;
	}

	gpio_sch_resource.start = base_addr;
120 121 122 123 124

	if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
		gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
	else
		gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
125

126 127 128
	for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
		lpc_sch_cells[i].id = id->device;

129
	ret = mfd_add_devices(&dev->dev, 0,
130 131
			      lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL,
			      0, NULL);
132 133 134
	if (ret)
		goto out_dev;

135 136
	if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC
	 || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
		pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
		if (!(base_addr_cfg & (1 << 31))) {
			dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
			ret = -ENODEV;
			goto out_dev;
		}
		base_addr = (unsigned short)base_addr_cfg;
		if (base_addr == 0) {
			dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
			ret = -ENODEV;
			goto out_dev;
		}

		wdt_sch_resource.start = base_addr;
		wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;

		for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
			tunnelcreek_cells[i].id = id->device;

		ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
157 158
				      ARRAY_SIZE(tunnelcreek_cells), NULL,
				      0, NULL);
159 160 161 162 163 164
	}

	return ret;
out_dev:
	mfd_remove_devices(&dev->dev);
	return ret;
165 166 167 168 169 170 171 172 173 174 175 176 177 178
}

static void __devexit lpc_sch_remove(struct pci_dev *dev)
{
	mfd_remove_devices(&dev->dev);
}

static struct pci_driver lpc_sch_driver = {
	.name		= "lpc_sch",
	.id_table	= lpc_sch_ids,
	.probe		= lpc_sch_probe,
	.remove		= __devexit_p(lpc_sch_remove),
};

A
Axel Lin 已提交
179
module_pci_driver(lpc_sch_driver);
180 181 182 183

MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
MODULE_LICENSE("GPL");