1e82c60aeSDenis Turischev /* 2e82c60aeSDenis Turischev * lpc_sch.c - LPC interface for Intel Poulsbo SCH 3e82c60aeSDenis Turischev * 4e82c60aeSDenis Turischev * LPC bridge function of the Intel SCH contains many other 5e82c60aeSDenis Turischev * functional units, such as Interrupt controllers, Timers, 6e82c60aeSDenis Turischev * Power Management, System Management, GPIO, RTC, and LPC 7e82c60aeSDenis Turischev * Configuration Registers. 8e82c60aeSDenis Turischev * 9e82c60aeSDenis Turischev * Copyright (c) 2010 CompuLab Ltd 1085de80e8SAndy Shevchenko * Copyright (c) 2014 Intel Corp. 11e82c60aeSDenis Turischev * Author: Denis Turischev <denis@compulab.co.il> 12e82c60aeSDenis Turischev * 13e82c60aeSDenis Turischev * This program is free software; you can redistribute it and/or modify 14e82c60aeSDenis Turischev * it under the terms of the GNU General Public License 2 as published 15e82c60aeSDenis Turischev * by the Free Software Foundation. 16e82c60aeSDenis Turischev * 17e82c60aeSDenis Turischev * This program is distributed in the hope that it will be useful, 18e82c60aeSDenis Turischev * but WITHOUT ANY WARRANTY; without even the implied warranty of 19e82c60aeSDenis Turischev * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20e82c60aeSDenis Turischev * GNU General Public License for more details. 21e82c60aeSDenis Turischev */ 22e82c60aeSDenis Turischev 23e82c60aeSDenis Turischev #include <linux/kernel.h> 24e82c60aeSDenis Turischev #include <linux/module.h> 25e82c60aeSDenis Turischev #include <linux/errno.h> 26e82c60aeSDenis Turischev #include <linux/acpi.h> 27e82c60aeSDenis Turischev #include <linux/pci.h> 28e82c60aeSDenis Turischev #include <linux/mfd/core.h> 29e82c60aeSDenis Turischev 30e82c60aeSDenis Turischev #define SMBASE 0x40 31e82c60aeSDenis Turischev #define SMBUS_IO_SIZE 64 32e82c60aeSDenis Turischev 33e82c60aeSDenis Turischev #define GPIOBASE 0x44 34e82c60aeSDenis Turischev #define GPIO_IO_SIZE 64 358ee3c2a7SSeth Heasley #define GPIO_IO_SIZE_CENTERTON 128 36e82c60aeSDenis Turischev 37ec689a8aSAndy Shevchenko /* Intel Quark X1000 GPIO IRQ Number */ 38ec689a8aSAndy Shevchenko #define GPIO_IRQ_QUARK_X1000 9 39ec689a8aSAndy Shevchenko 4019921ef6SAlexander Stein #define WDTBASE 0x84 4119921ef6SAlexander Stein #define WDT_IO_SIZE 64 4219921ef6SAlexander Stein 43b24512c8SAndy Shevchenko enum sch_chipsets { 44b24512c8SAndy Shevchenko LPC_SCH = 0, /* Intel Poulsbo SCH */ 45b24512c8SAndy Shevchenko LPC_ITC, /* Intel Tunnel Creek */ 46b24512c8SAndy Shevchenko LPC_CENTERTON, /* Intel Centerton */ 47ec689a8aSAndy Shevchenko LPC_QUARK_X1000, /* Intel Quark X1000 */ 48e82c60aeSDenis Turischev }; 49e82c60aeSDenis Turischev 50b24512c8SAndy Shevchenko struct lpc_sch_info { 51b24512c8SAndy Shevchenko unsigned int io_size_smbus; 52b24512c8SAndy Shevchenko unsigned int io_size_gpio; 53b24512c8SAndy Shevchenko unsigned int io_size_wdt; 54ec689a8aSAndy Shevchenko int irq_gpio; 55e82c60aeSDenis Turischev }; 56e82c60aeSDenis Turischev 57b24512c8SAndy Shevchenko static struct lpc_sch_info sch_chipset_info[] = { 58b24512c8SAndy Shevchenko [LPC_SCH] = { 59b24512c8SAndy Shevchenko .io_size_smbus = SMBUS_IO_SIZE, 60b24512c8SAndy Shevchenko .io_size_gpio = GPIO_IO_SIZE, 61ec689a8aSAndy Shevchenko .irq_gpio = -1, 62b24512c8SAndy Shevchenko }, 63b24512c8SAndy Shevchenko [LPC_ITC] = { 64b24512c8SAndy Shevchenko .io_size_smbus = SMBUS_IO_SIZE, 65b24512c8SAndy Shevchenko .io_size_gpio = GPIO_IO_SIZE, 66b24512c8SAndy Shevchenko .io_size_wdt = WDT_IO_SIZE, 67ec689a8aSAndy Shevchenko .irq_gpio = -1, 68b24512c8SAndy Shevchenko }, 69b24512c8SAndy Shevchenko [LPC_CENTERTON] = { 70b24512c8SAndy Shevchenko .io_size_smbus = SMBUS_IO_SIZE, 71b24512c8SAndy Shevchenko .io_size_gpio = GPIO_IO_SIZE_CENTERTON, 72b24512c8SAndy Shevchenko .io_size_wdt = WDT_IO_SIZE, 73ec689a8aSAndy Shevchenko .irq_gpio = -1, 74ec689a8aSAndy Shevchenko }, 75ec689a8aSAndy Shevchenko [LPC_QUARK_X1000] = { 76ec689a8aSAndy Shevchenko .io_size_gpio = GPIO_IO_SIZE, 77ec689a8aSAndy Shevchenko .irq_gpio = GPIO_IRQ_QUARK_X1000, 78b24512c8SAndy Shevchenko }, 7919921ef6SAlexander Stein }; 8019921ef6SAlexander Stein 8136fcd06cSJingoo Han static const struct pci_device_id lpc_sch_ids[] = { 82b24512c8SAndy Shevchenko { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC), LPC_SCH }, 83b24512c8SAndy Shevchenko { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC), LPC_ITC }, 84b24512c8SAndy Shevchenko { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB), LPC_CENTERTON }, 85ec689a8aSAndy Shevchenko { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB), LPC_QUARK_X1000 }, 86e82c60aeSDenis Turischev { 0, } 87e82c60aeSDenis Turischev }; 88e82c60aeSDenis Turischev MODULE_DEVICE_TABLE(pci, lpc_sch_ids); 89e82c60aeSDenis Turischev 90b24512c8SAndy Shevchenko #define LPC_NO_RESOURCE 1 91b24512c8SAndy Shevchenko #define LPC_SKIP_RESOURCE 2 92b24512c8SAndy Shevchenko 93b24512c8SAndy Shevchenko static int lpc_sch_get_io(struct pci_dev *pdev, int where, const char *name, 94b24512c8SAndy Shevchenko struct resource *res, int size) 95e82c60aeSDenis Turischev { 96e82c60aeSDenis Turischev unsigned int base_addr_cfg; 97e82c60aeSDenis Turischev unsigned short base_addr; 98b24512c8SAndy Shevchenko 99b24512c8SAndy Shevchenko if (size == 0) 100b24512c8SAndy Shevchenko return LPC_NO_RESOURCE; 101b24512c8SAndy Shevchenko 102b24512c8SAndy Shevchenko pci_read_config_dword(pdev, where, &base_addr_cfg); 103b24512c8SAndy Shevchenko base_addr = 0; 104b24512c8SAndy Shevchenko if (!(base_addr_cfg & (1 << 31))) 105b24512c8SAndy Shevchenko dev_warn(&pdev->dev, "Decode of the %s I/O range disabled\n", 106b24512c8SAndy Shevchenko name); 107b24512c8SAndy Shevchenko else 108b24512c8SAndy Shevchenko base_addr = (unsigned short)base_addr_cfg; 109b24512c8SAndy Shevchenko 110b24512c8SAndy Shevchenko if (base_addr == 0) { 111b24512c8SAndy Shevchenko dev_warn(&pdev->dev, "I/O space for %s uninitialized\n", name); 112b24512c8SAndy Shevchenko return LPC_SKIP_RESOURCE; 113b24512c8SAndy Shevchenko } 114b24512c8SAndy Shevchenko 115b24512c8SAndy Shevchenko res->start = base_addr; 116b24512c8SAndy Shevchenko res->end = base_addr + size - 1; 117b24512c8SAndy Shevchenko res->flags = IORESOURCE_IO; 118b24512c8SAndy Shevchenko 119b24512c8SAndy Shevchenko return 0; 120b24512c8SAndy Shevchenko } 121b24512c8SAndy Shevchenko 122b24512c8SAndy Shevchenko static int lpc_sch_populate_cell(struct pci_dev *pdev, int where, 123ec689a8aSAndy Shevchenko const char *name, int size, int irq, 124ec689a8aSAndy Shevchenko int id, struct mfd_cell *cell) 125b24512c8SAndy Shevchenko { 126b24512c8SAndy Shevchenko struct resource *res; 12719921ef6SAlexander Stein int ret; 128e82c60aeSDenis Turischev 129ec689a8aSAndy Shevchenko res = devm_kcalloc(&pdev->dev, 2, sizeof(*res), GFP_KERNEL); 130b24512c8SAndy Shevchenko if (!res) 131b24512c8SAndy Shevchenko return -ENOMEM; 132e82c60aeSDenis Turischev 133b24512c8SAndy Shevchenko ret = lpc_sch_get_io(pdev, where, name, res, size); 134b24512c8SAndy Shevchenko if (ret) 135b24512c8SAndy Shevchenko return ret; 136b24512c8SAndy Shevchenko 137b24512c8SAndy Shevchenko memset(cell, 0, sizeof(*cell)); 138b24512c8SAndy Shevchenko 139b24512c8SAndy Shevchenko cell->name = name; 140b24512c8SAndy Shevchenko cell->resources = res; 141b24512c8SAndy Shevchenko cell->num_resources = 1; 142b24512c8SAndy Shevchenko cell->ignore_resource_conflicts = true; 143b24512c8SAndy Shevchenko cell->id = id; 144b24512c8SAndy Shevchenko 145ec689a8aSAndy Shevchenko /* Check if we need to add an IRQ resource */ 146ec689a8aSAndy Shevchenko if (irq < 0) 147ec689a8aSAndy Shevchenko return 0; 148ec689a8aSAndy Shevchenko 149ec689a8aSAndy Shevchenko res++; 150ec689a8aSAndy Shevchenko 151ec689a8aSAndy Shevchenko res->start = irq; 152ec689a8aSAndy Shevchenko res->end = irq; 153ec689a8aSAndy Shevchenko res->flags = IORESOURCE_IRQ; 154ec689a8aSAndy Shevchenko 155ec689a8aSAndy Shevchenko cell->num_resources++; 156ec689a8aSAndy Shevchenko 157b24512c8SAndy Shevchenko return 0; 1585829e9b6SDarren Hart } 159e82c60aeSDenis Turischev 160b24512c8SAndy Shevchenko static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id) 161b24512c8SAndy Shevchenko { 162b24512c8SAndy Shevchenko struct mfd_cell lpc_sch_cells[3]; 163b24512c8SAndy Shevchenko struct lpc_sch_info *info = &sch_chipset_info[id->driver_data]; 164b24512c8SAndy Shevchenko unsigned int cells = 0; 165b24512c8SAndy Shevchenko int ret; 1665829e9b6SDarren Hart 167b24512c8SAndy Shevchenko ret = lpc_sch_populate_cell(dev, SMBASE, "isch_smbus", 168ec689a8aSAndy Shevchenko info->io_size_smbus, -1, 169b24512c8SAndy Shevchenko id->device, &lpc_sch_cells[cells]); 170b24512c8SAndy Shevchenko if (ret < 0) 171b24512c8SAndy Shevchenko return ret; 172b24512c8SAndy Shevchenko if (ret == 0) 173b24512c8SAndy Shevchenko cells++; 17419921ef6SAlexander Stein 175b24512c8SAndy Shevchenko ret = lpc_sch_populate_cell(dev, GPIOBASE, "sch_gpio", 176ec689a8aSAndy Shevchenko info->io_size_gpio, info->irq_gpio, 177b24512c8SAndy Shevchenko id->device, &lpc_sch_cells[cells]); 178b24512c8SAndy Shevchenko if (ret < 0) 179b24512c8SAndy Shevchenko return ret; 180b24512c8SAndy Shevchenko if (ret == 0) 181b24512c8SAndy Shevchenko cells++; 18219921ef6SAlexander Stein 183b24512c8SAndy Shevchenko ret = lpc_sch_populate_cell(dev, WDTBASE, "ie6xx_wdt", 184ec689a8aSAndy Shevchenko info->io_size_wdt, -1, 185b24512c8SAndy Shevchenko id->device, &lpc_sch_cells[cells]); 186b24512c8SAndy Shevchenko if (ret < 0) 187b24512c8SAndy Shevchenko return ret; 188b24512c8SAndy Shevchenko if (ret == 0) 189b24512c8SAndy Shevchenko cells++; 1905829e9b6SDarren Hart 1915829e9b6SDarren Hart if (cells == 0) { 1925829e9b6SDarren Hart dev_err(&dev->dev, "All decode registers disabled.\n"); 1935829e9b6SDarren Hart return -ENODEV; 1945829e9b6SDarren Hart } 1955829e9b6SDarren Hart 196bde3e706SAndy Shevchenko return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); 197e82c60aeSDenis Turischev } 198e82c60aeSDenis Turischev 1994740f73fSBill Pemberton static void lpc_sch_remove(struct pci_dev *dev) 200e82c60aeSDenis Turischev { 201e82c60aeSDenis Turischev mfd_remove_devices(&dev->dev); 202e82c60aeSDenis Turischev } 203e82c60aeSDenis Turischev 204e82c60aeSDenis Turischev static struct pci_driver lpc_sch_driver = { 205e82c60aeSDenis Turischev .name = "lpc_sch", 206e82c60aeSDenis Turischev .id_table = lpc_sch_ids, 207e82c60aeSDenis Turischev .probe = lpc_sch_probe, 20884449216SBill Pemberton .remove = lpc_sch_remove, 209e82c60aeSDenis Turischev }; 210e82c60aeSDenis Turischev 21138a36f5aSAxel Lin module_pci_driver(lpc_sch_driver); 212e82c60aeSDenis Turischev 213e82c60aeSDenis Turischev MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 214e82c60aeSDenis Turischev MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH"); 215e82c60aeSDenis Turischev MODULE_LICENSE("GPL"); 216