18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GPIO interface for Intel Poulsbo SCH 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010 CompuLab Ltd 68c2ecf20Sopenharmony_ci * Author: Denis Turischev <denis@compulab.co.il> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/acpi.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define GEN 0x00 198c2ecf20Sopenharmony_ci#define GIO 0x04 208c2ecf20Sopenharmony_ci#define GLV 0x08 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct sch_gpio { 238c2ecf20Sopenharmony_ci struct gpio_chip chip; 248c2ecf20Sopenharmony_ci spinlock_t lock; 258c2ecf20Sopenharmony_ci unsigned short iobase; 268c2ecf20Sopenharmony_ci unsigned short resume_base; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic unsigned int sch_gpio_offset(struct sch_gpio *sch, unsigned int gpio, 308c2ecf20Sopenharmony_ci unsigned int reg) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci unsigned int base = 0; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (gpio >= sch->resume_base) { 358c2ecf20Sopenharmony_ci gpio -= sch->resume_base; 368c2ecf20Sopenharmony_ci base += 0x20; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return base + reg + gpio / 8; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic unsigned int sch_gpio_bit(struct sch_gpio *sch, unsigned int gpio) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci if (gpio >= sch->resume_base) 458c2ecf20Sopenharmony_ci gpio -= sch->resume_base; 468c2ecf20Sopenharmony_ci return gpio % 8; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int sch_gpio_reg_get(struct sch_gpio *sch, unsigned int gpio, unsigned int reg) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci unsigned short offset, bit; 528c2ecf20Sopenharmony_ci u8 reg_val; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci offset = sch_gpio_offset(sch, gpio, reg); 558c2ecf20Sopenharmony_ci bit = sch_gpio_bit(sch, gpio); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci reg_val = !!(inb(sch->iobase + offset) & BIT(bit)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return reg_val; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void sch_gpio_reg_set(struct sch_gpio *sch, unsigned int gpio, unsigned int reg, 638c2ecf20Sopenharmony_ci int val) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci unsigned short offset, bit; 668c2ecf20Sopenharmony_ci u8 reg_val; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci offset = sch_gpio_offset(sch, gpio, reg); 698c2ecf20Sopenharmony_ci bit = sch_gpio_bit(sch, gpio); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci reg_val = inb(sch->iobase + offset); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (val) 748c2ecf20Sopenharmony_ci outb(reg_val | BIT(bit), sch->iobase + offset); 758c2ecf20Sopenharmony_ci else 768c2ecf20Sopenharmony_ci outb((reg_val & ~BIT(bit)), sch->iobase + offset); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct sch_gpio *sch = gpiochip_get_data(gc); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci spin_lock(&sch->lock); 848c2ecf20Sopenharmony_ci sch_gpio_reg_set(sch, gpio_num, GIO, 1); 858c2ecf20Sopenharmony_ci spin_unlock(&sch->lock); 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct sch_gpio *sch = gpiochip_get_data(gc); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return sch_gpio_reg_get(sch, gpio_num, GLV); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct sch_gpio *sch = gpiochip_get_data(gc); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci spin_lock(&sch->lock); 1018c2ecf20Sopenharmony_ci sch_gpio_reg_set(sch, gpio_num, GLV, val); 1028c2ecf20Sopenharmony_ci spin_unlock(&sch->lock); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, 1068c2ecf20Sopenharmony_ci int val) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct sch_gpio *sch = gpiochip_get_data(gc); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci spin_lock(&sch->lock); 1118c2ecf20Sopenharmony_ci sch_gpio_reg_set(sch, gpio_num, GIO, 0); 1128c2ecf20Sopenharmony_ci spin_unlock(&sch->lock); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * according to the datasheet, writing to the level register has no 1168c2ecf20Sopenharmony_ci * effect when GPIO is programmed as input. 1178c2ecf20Sopenharmony_ci * Actually the the level register is read-only when configured as input. 1188c2ecf20Sopenharmony_ci * Thus presetting the output level before switching to output is _NOT_ possible. 1198c2ecf20Sopenharmony_ci * Hence we set the level after configuring the GPIO as output. 1208c2ecf20Sopenharmony_ci * But we cannot prevent a short low pulse if direction is set to high 1218c2ecf20Sopenharmony_ci * and an external pull-up is connected. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci sch_gpio_set(gc, gpio_num, val); 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct sch_gpio *sch = gpiochip_get_data(gc); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (sch_gpio_reg_get(sch, gpio_num, GIO)) 1328c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct gpio_chip sch_gpio_chip = { 1388c2ecf20Sopenharmony_ci .label = "sch_gpio", 1398c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1408c2ecf20Sopenharmony_ci .direction_input = sch_gpio_direction_in, 1418c2ecf20Sopenharmony_ci .get = sch_gpio_get, 1428c2ecf20Sopenharmony_ci .direction_output = sch_gpio_direction_out, 1438c2ecf20Sopenharmony_ci .set = sch_gpio_set, 1448c2ecf20Sopenharmony_ci .get_direction = sch_gpio_get_direction, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int sch_gpio_probe(struct platform_device *pdev) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct sch_gpio *sch; 1508c2ecf20Sopenharmony_ci struct resource *res; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL); 1538c2ecf20Sopenharmony_ci if (!sch) 1548c2ecf20Sopenharmony_ci return -ENOMEM; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IO, 0); 1578c2ecf20Sopenharmony_ci if (!res) 1588c2ecf20Sopenharmony_ci return -EBUSY; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!devm_request_region(&pdev->dev, res->start, resource_size(res), 1618c2ecf20Sopenharmony_ci pdev->name)) 1628c2ecf20Sopenharmony_ci return -EBUSY; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci spin_lock_init(&sch->lock); 1658c2ecf20Sopenharmony_ci sch->iobase = res->start; 1668c2ecf20Sopenharmony_ci sch->chip = sch_gpio_chip; 1678c2ecf20Sopenharmony_ci sch->chip.label = dev_name(&pdev->dev); 1688c2ecf20Sopenharmony_ci sch->chip.parent = &pdev->dev; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci switch (pdev->id) { 1718c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_SCH_LPC: 1728c2ecf20Sopenharmony_ci sch->resume_base = 10; 1738c2ecf20Sopenharmony_ci sch->chip.ngpio = 14; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* 1768c2ecf20Sopenharmony_ci * GPIO[6:0] enabled by default 1778c2ecf20Sopenharmony_ci * GPIO7 is configured by the CMC as SLPIOVR 1788c2ecf20Sopenharmony_ci * Enable GPIO[9:8] core powered gpios explicitly 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci sch_gpio_reg_set(sch, 8, GEN, 1); 1818c2ecf20Sopenharmony_ci sch_gpio_reg_set(sch, 9, GEN, 1); 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * SUS_GPIO[2:0] enabled by default 1848c2ecf20Sopenharmony_ci * Enable SUS_GPIO3 resume powered gpio explicitly 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci sch_gpio_reg_set(sch, 13, GEN, 1); 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_ITC_LPC: 1908c2ecf20Sopenharmony_ci sch->resume_base = 5; 1918c2ecf20Sopenharmony_ci sch->chip.ngpio = 14; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_CENTERTON_ILB: 1958c2ecf20Sopenharmony_ci sch->resume_base = 21; 1968c2ecf20Sopenharmony_ci sch->chip.ngpio = 30; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB: 2008c2ecf20Sopenharmony_ci sch->resume_base = 2; 2018c2ecf20Sopenharmony_ci sch->chip.ngpio = 8; 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci default: 2058c2ecf20Sopenharmony_ci return -ENODEV; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sch); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return devm_gpiochip_add_data(&pdev->dev, &sch->chip, sch); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic struct platform_driver sch_gpio_driver = { 2148c2ecf20Sopenharmony_ci .driver = { 2158c2ecf20Sopenharmony_ci .name = "sch_gpio", 2168c2ecf20Sopenharmony_ci }, 2178c2ecf20Sopenharmony_ci .probe = sch_gpio_probe, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cimodule_platform_driver(sch_gpio_driver); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 2238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); 2248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2258c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:sch_gpio"); 226