162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Linux multi-function-device driver (MFD) for the integrated peripherals
462306a36Sopenharmony_ci * of the VIA VX855 chipset
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2009 VIA Technologies, Inc.
762306a36Sopenharmony_ci * Copyright (C) 2010 One Laptop per Child
862306a36Sopenharmony_ci * Author: Harald Welte <HaraldWelte@viatech.com>
962306a36Sopenharmony_ci * All rights reserved.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci#include <linux/mfd/core.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* offset into pci config space indicating the 16bit register containing
2062306a36Sopenharmony_ci * the power management IO space base */
2162306a36Sopenharmony_ci#define VX855_CFG_PMIO_OFFSET	0x88
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* ACPI I/O Space registers */
2462306a36Sopenharmony_ci#define VX855_PMIO_ACPI		0x00
2562306a36Sopenharmony_ci#define VX855_PMIO_ACPI_LEN	0x0b
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Processor Power Management */
2862306a36Sopenharmony_ci#define VX855_PMIO_PPM		0x10
2962306a36Sopenharmony_ci#define VX855_PMIO_PPM_LEN	0x08
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* General Purpose Power Management */
3262306a36Sopenharmony_ci#define VX855_PMIO_GPPM		0x20
3362306a36Sopenharmony_ci#define VX855_PMIO_R_GPI	0x48
3462306a36Sopenharmony_ci#define VX855_PMIO_R_GPO	0x4c
3562306a36Sopenharmony_ci#define VX855_PMIO_GPPM_LEN	0x33
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define VSPIC_MMIO_SIZE	0x1000
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic struct resource vx855_gpio_resources[] = {
4062306a36Sopenharmony_ci	{
4162306a36Sopenharmony_ci		.flags = IORESOURCE_IO,
4262306a36Sopenharmony_ci	},
4362306a36Sopenharmony_ci	{
4462306a36Sopenharmony_ci		.flags = IORESOURCE_IO,
4562306a36Sopenharmony_ci	},
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic const struct mfd_cell vx855_cells[] = {
4962306a36Sopenharmony_ci	{
5062306a36Sopenharmony_ci		.name = "vx855_gpio",
5162306a36Sopenharmony_ci		.num_resources = ARRAY_SIZE(vx855_gpio_resources),
5262306a36Sopenharmony_ci		.resources = vx855_gpio_resources,
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		/* we must ignore resource conflicts, for reasons outlined in
5562306a36Sopenharmony_ci		 * the vx855_gpio driver */
5662306a36Sopenharmony_ci		.ignore_resource_conflicts = true,
5762306a36Sopenharmony_ci	},
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int vx855_probe(struct pci_dev *pdev,
6162306a36Sopenharmony_ci				 const struct pci_device_id *id)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	int ret;
6462306a36Sopenharmony_ci	u16 gpio_io_offset;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	ret = pci_enable_device(pdev);
6762306a36Sopenharmony_ci	if (ret)
6862306a36Sopenharmony_ci		return -ENODEV;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	pci_read_config_word(pdev, VX855_CFG_PMIO_OFFSET, &gpio_io_offset);
7162306a36Sopenharmony_ci	if (!gpio_io_offset) {
7262306a36Sopenharmony_ci		dev_warn(&pdev->dev,
7362306a36Sopenharmony_ci			"BIOS did not assign PMIO base offset?!?\n");
7462306a36Sopenharmony_ci		ret = -ENODEV;
7562306a36Sopenharmony_ci		goto out;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* mask out the lowest seven bits, as they are always zero, but
7962306a36Sopenharmony_ci	 * hardware returns them as 0x01 */
8062306a36Sopenharmony_ci	gpio_io_offset &= 0xff80;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* As the region identified here includes many non-GPIO things, we
8362306a36Sopenharmony_ci	 * only work with the specific registers that concern us. */
8462306a36Sopenharmony_ci	vx855_gpio_resources[0].start = gpio_io_offset + VX855_PMIO_R_GPI;
8562306a36Sopenharmony_ci	vx855_gpio_resources[0].end = vx855_gpio_resources[0].start + 3;
8662306a36Sopenharmony_ci	vx855_gpio_resources[1].start = gpio_io_offset + VX855_PMIO_R_GPO;
8762306a36Sopenharmony_ci	vx855_gpio_resources[1].end = vx855_gpio_resources[1].start + 3;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ret = mfd_add_devices(&pdev->dev, -1, vx855_cells, ARRAY_SIZE(vx855_cells),
9062306a36Sopenharmony_ci			NULL, 0, NULL);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* we always return -ENODEV here in order to enable other
9362306a36Sopenharmony_ci	 * drivers like old, not-yet-platform_device ported i2c-viapro */
9462306a36Sopenharmony_ci	return -ENODEV;
9562306a36Sopenharmony_ciout:
9662306a36Sopenharmony_ci	pci_disable_device(pdev);
9762306a36Sopenharmony_ci	return ret;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void vx855_remove(struct pci_dev *pdev)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	mfd_remove_devices(&pdev->dev);
10362306a36Sopenharmony_ci	pci_disable_device(pdev);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic const struct pci_device_id vx855_pci_tbl[] = {
10762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
10862306a36Sopenharmony_ci	{ 0, }
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, vx855_pci_tbl);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic struct pci_driver vx855_pci_driver = {
11362306a36Sopenharmony_ci	.name		= "vx855",
11462306a36Sopenharmony_ci	.id_table	= vx855_pci_tbl,
11562306a36Sopenharmony_ci	.probe		= vx855_probe,
11662306a36Sopenharmony_ci	.remove		= vx855_remove,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cimodule_pci_driver(vx855_pci_driver);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
12262306a36Sopenharmony_ciMODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
12362306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for the VIA VX855 chipset");
124