18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * lpc_sch.c - LPC interface for Intel Poulsbo SCH 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * LPC bridge function of the Intel SCH contains many other 68c2ecf20Sopenharmony_ci * functional units, such as Interrupt controllers, Timers, 78c2ecf20Sopenharmony_ci * Power Management, System Management, GPIO, RTC, and LPC 88c2ecf20Sopenharmony_ci * Configuration Registers. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (c) 2010 CompuLab Ltd 118c2ecf20Sopenharmony_ci * Copyright (c) 2014 Intel Corp. 128c2ecf20Sopenharmony_ci * Author: Denis Turischev <denis@compulab.co.il> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/acpi.h> 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define SMBASE 0x40 238c2ecf20Sopenharmony_ci#define SMBUS_IO_SIZE 64 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define GPIO_BASE 0x44 268c2ecf20Sopenharmony_ci#define GPIO_IO_SIZE 64 278c2ecf20Sopenharmony_ci#define GPIO_IO_SIZE_CENTERTON 128 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define WDTBASE 0x84 308c2ecf20Sopenharmony_ci#define WDT_IO_SIZE 64 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cienum sch_chipsets { 338c2ecf20Sopenharmony_ci LPC_SCH = 0, /* Intel Poulsbo SCH */ 348c2ecf20Sopenharmony_ci LPC_ITC, /* Intel Tunnel Creek */ 358c2ecf20Sopenharmony_ci LPC_CENTERTON, /* Intel Centerton */ 368c2ecf20Sopenharmony_ci LPC_QUARK_X1000, /* Intel Quark X1000 */ 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct lpc_sch_info { 408c2ecf20Sopenharmony_ci unsigned int io_size_smbus; 418c2ecf20Sopenharmony_ci unsigned int io_size_gpio; 428c2ecf20Sopenharmony_ci unsigned int io_size_wdt; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct lpc_sch_info sch_chipset_info[] = { 468c2ecf20Sopenharmony_ci [LPC_SCH] = { 478c2ecf20Sopenharmony_ci .io_size_smbus = SMBUS_IO_SIZE, 488c2ecf20Sopenharmony_ci .io_size_gpio = GPIO_IO_SIZE, 498c2ecf20Sopenharmony_ci }, 508c2ecf20Sopenharmony_ci [LPC_ITC] = { 518c2ecf20Sopenharmony_ci .io_size_smbus = SMBUS_IO_SIZE, 528c2ecf20Sopenharmony_ci .io_size_gpio = GPIO_IO_SIZE, 538c2ecf20Sopenharmony_ci .io_size_wdt = WDT_IO_SIZE, 548c2ecf20Sopenharmony_ci }, 558c2ecf20Sopenharmony_ci [LPC_CENTERTON] = { 568c2ecf20Sopenharmony_ci .io_size_smbus = SMBUS_IO_SIZE, 578c2ecf20Sopenharmony_ci .io_size_gpio = GPIO_IO_SIZE_CENTERTON, 588c2ecf20Sopenharmony_ci .io_size_wdt = WDT_IO_SIZE, 598c2ecf20Sopenharmony_ci }, 608c2ecf20Sopenharmony_ci [LPC_QUARK_X1000] = { 618c2ecf20Sopenharmony_ci .io_size_gpio = GPIO_IO_SIZE, 628c2ecf20Sopenharmony_ci .io_size_wdt = WDT_IO_SIZE, 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic const struct pci_device_id lpc_sch_ids[] = { 678c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC), LPC_SCH }, 688c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC), LPC_ITC }, 698c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB), LPC_CENTERTON }, 708c2ecf20Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB), LPC_QUARK_X1000 }, 718c2ecf20Sopenharmony_ci { 0, } 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, lpc_sch_ids); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define LPC_NO_RESOURCE 1 768c2ecf20Sopenharmony_ci#define LPC_SKIP_RESOURCE 2 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int lpc_sch_get_io(struct pci_dev *pdev, int where, const char *name, 798c2ecf20Sopenharmony_ci struct resource *res, int size) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci unsigned int base_addr_cfg; 828c2ecf20Sopenharmony_ci unsigned short base_addr; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (size == 0) 858c2ecf20Sopenharmony_ci return LPC_NO_RESOURCE; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, where, &base_addr_cfg); 888c2ecf20Sopenharmony_ci base_addr = 0; 898c2ecf20Sopenharmony_ci if (!(base_addr_cfg & (1 << 31))) 908c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Decode of the %s I/O range disabled\n", 918c2ecf20Sopenharmony_ci name); 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci base_addr = (unsigned short)base_addr_cfg; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (base_addr == 0) { 968c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "I/O space for %s uninitialized\n", name); 978c2ecf20Sopenharmony_ci return LPC_SKIP_RESOURCE; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci res->start = base_addr; 1018c2ecf20Sopenharmony_ci res->end = base_addr + size - 1; 1028c2ecf20Sopenharmony_ci res->flags = IORESOURCE_IO; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int lpc_sch_populate_cell(struct pci_dev *pdev, int where, 1088c2ecf20Sopenharmony_ci const char *name, int size, int id, 1098c2ecf20Sopenharmony_ci struct mfd_cell *cell) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct resource *res; 1128c2ecf20Sopenharmony_ci int ret; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci res = devm_kzalloc(&pdev->dev, sizeof(*res), GFP_KERNEL); 1158c2ecf20Sopenharmony_ci if (!res) 1168c2ecf20Sopenharmony_ci return -ENOMEM; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = lpc_sch_get_io(pdev, where, name, res, size); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci memset(cell, 0, sizeof(*cell)); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci cell->name = name; 1258c2ecf20Sopenharmony_ci cell->resources = res; 1268c2ecf20Sopenharmony_ci cell->num_resources = 1; 1278c2ecf20Sopenharmony_ci cell->ignore_resource_conflicts = true; 1288c2ecf20Sopenharmony_ci cell->id = id; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct mfd_cell lpc_sch_cells[3]; 1368c2ecf20Sopenharmony_ci struct lpc_sch_info *info = &sch_chipset_info[id->driver_data]; 1378c2ecf20Sopenharmony_ci unsigned int cells = 0; 1388c2ecf20Sopenharmony_ci int ret; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci ret = lpc_sch_populate_cell(dev, SMBASE, "isch_smbus", 1418c2ecf20Sopenharmony_ci info->io_size_smbus, 1428c2ecf20Sopenharmony_ci id->device, &lpc_sch_cells[cells]); 1438c2ecf20Sopenharmony_ci if (ret < 0) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci if (ret == 0) 1468c2ecf20Sopenharmony_ci cells++; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = lpc_sch_populate_cell(dev, GPIO_BASE, "sch_gpio", 1498c2ecf20Sopenharmony_ci info->io_size_gpio, 1508c2ecf20Sopenharmony_ci id->device, &lpc_sch_cells[cells]); 1518c2ecf20Sopenharmony_ci if (ret < 0) 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci if (ret == 0) 1548c2ecf20Sopenharmony_ci cells++; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = lpc_sch_populate_cell(dev, WDTBASE, "ie6xx_wdt", 1578c2ecf20Sopenharmony_ci info->io_size_wdt, 1588c2ecf20Sopenharmony_ci id->device, &lpc_sch_cells[cells]); 1598c2ecf20Sopenharmony_ci if (ret < 0) 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci if (ret == 0) 1628c2ecf20Sopenharmony_ci cells++; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (cells == 0) { 1658c2ecf20Sopenharmony_ci dev_err(&dev->dev, "All decode registers disabled.\n"); 1668c2ecf20Sopenharmony_ci return -ENODEV; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void lpc_sch_remove(struct pci_dev *dev) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci mfd_remove_devices(&dev->dev); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic struct pci_driver lpc_sch_driver = { 1788c2ecf20Sopenharmony_ci .name = "lpc_sch", 1798c2ecf20Sopenharmony_ci .id_table = lpc_sch_ids, 1808c2ecf20Sopenharmony_ci .probe = lpc_sch_probe, 1818c2ecf20Sopenharmony_ci .remove = lpc_sch_remove, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cimodule_pci_driver(lpc_sch_driver); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 1878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH"); 1888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 189