linux/drivers/gpio/gpio-amdpt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * AMD Promontory GPIO driver
   4 *
   5 * Copyright (C) 2015 ASMedia Technology Inc.
   6 * Author: YD Tseng <yd_tseng@asmedia.com.tw>
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/gpio/driver.h>
  12#include <linux/spinlock.h>
  13#include <linux/acpi.h>
  14#include <linux/platform_device.h>
  15
  16#define PT_TOTAL_GPIO 8
  17#define PT_TOTAL_GPIO_EX 24
  18
  19/* PCI-E MMIO register offsets */
  20#define PT_DIRECTION_REG   0x00
  21#define PT_INPUTDATA_REG   0x04
  22#define PT_OUTPUTDATA_REG  0x08
  23#define PT_CLOCKRATE_REG   0x0C
  24#define PT_SYNC_REG        0x28
  25
  26struct pt_gpio_chip {
  27        struct gpio_chip         gc;
  28        void __iomem             *reg_base;
  29};
  30
  31static int pt_gpio_request(struct gpio_chip *gc, unsigned offset)
  32{
  33        struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc);
  34        unsigned long flags;
  35        u32 using_pins;
  36
  37        dev_dbg(gc->parent, "pt_gpio_request offset=%x\n", offset);
  38
  39        spin_lock_irqsave(&gc->bgpio_lock, flags);
  40
  41        using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
  42        if (using_pins & BIT(offset)) {
  43                dev_warn(gc->parent, "PT GPIO pin %x reconfigured\n",
  44                         offset);
  45                spin_unlock_irqrestore(&gc->bgpio_lock, flags);
  46                return -EINVAL;
  47        }
  48
  49        writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG);
  50
  51        spin_unlock_irqrestore(&gc->bgpio_lock, flags);
  52
  53        return 0;
  54}
  55
  56static void pt_gpio_free(struct gpio_chip *gc, unsigned offset)
  57{
  58        struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc);
  59        unsigned long flags;
  60        u32 using_pins;
  61
  62        spin_lock_irqsave(&gc->bgpio_lock, flags);
  63
  64        using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
  65        using_pins &= ~BIT(offset);
  66        writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG);
  67
  68        spin_unlock_irqrestore(&gc->bgpio_lock, flags);
  69
  70        dev_dbg(gc->parent, "pt_gpio_free offset=%x\n", offset);
  71}
  72
  73static int pt_gpio_probe(struct platform_device *pdev)
  74{
  75        struct device *dev = &pdev->dev;
  76        struct pt_gpio_chip *pt_gpio;
  77        int ret = 0;
  78
  79        if (!ACPI_COMPANION(dev)) {
  80                dev_err(dev, "PT GPIO device node not found\n");
  81                return -ENODEV;
  82        }
  83
  84        pt_gpio = devm_kzalloc(dev, sizeof(struct pt_gpio_chip), GFP_KERNEL);
  85        if (!pt_gpio)
  86                return -ENOMEM;
  87
  88        pt_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0);
  89        if (IS_ERR(pt_gpio->reg_base)) {
  90                dev_err(dev, "Failed to map MMIO resource for PT GPIO.\n");
  91                return PTR_ERR(pt_gpio->reg_base);
  92        }
  93
  94        ret = bgpio_init(&pt_gpio->gc, dev, 4,
  95                         pt_gpio->reg_base + PT_INPUTDATA_REG,
  96                         pt_gpio->reg_base + PT_OUTPUTDATA_REG, NULL,
  97                         pt_gpio->reg_base + PT_DIRECTION_REG, NULL,
  98                         BGPIOF_READ_OUTPUT_REG_SET);
  99        if (ret) {
 100                dev_err(dev, "bgpio_init failed\n");
 101                return ret;
 102        }
 103
 104        pt_gpio->gc.owner            = THIS_MODULE;
 105        pt_gpio->gc.request          = pt_gpio_request;
 106        pt_gpio->gc.free             = pt_gpio_free;
 107        pt_gpio->gc.ngpio            = (uintptr_t)device_get_match_data(dev);
 108
 109        ret = gpiochip_add_data(&pt_gpio->gc, pt_gpio);
 110        if (ret) {
 111                dev_err(dev, "Failed to register GPIO lib\n");
 112                return ret;
 113        }
 114
 115        platform_set_drvdata(pdev, pt_gpio);
 116
 117        /* initialize register setting */
 118        writel(0, pt_gpio->reg_base + PT_SYNC_REG);
 119        writel(0, pt_gpio->reg_base + PT_CLOCKRATE_REG);
 120
 121        dev_dbg(dev, "PT GPIO driver loaded\n");
 122        return ret;
 123}
 124
 125static int pt_gpio_remove(struct platform_device *pdev)
 126{
 127        struct pt_gpio_chip *pt_gpio = platform_get_drvdata(pdev);
 128
 129        gpiochip_remove(&pt_gpio->gc);
 130
 131        return 0;
 132}
 133
 134static const struct acpi_device_id pt_gpio_acpi_match[] = {
 135        { "AMDF030", PT_TOTAL_GPIO },
 136        { "AMDIF030", PT_TOTAL_GPIO },
 137        { "AMDIF031", PT_TOTAL_GPIO_EX },
 138        { },
 139};
 140MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match);
 141
 142static struct platform_driver pt_gpio_driver = {
 143        .driver = {
 144                .name = "pt-gpio",
 145                .acpi_match_table = ACPI_PTR(pt_gpio_acpi_match),
 146        },
 147        .probe = pt_gpio_probe,
 148        .remove = pt_gpio_remove,
 149};
 150
 151module_platform_driver(pt_gpio_driver);
 152
 153MODULE_LICENSE("GPL");
 154MODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>");
 155MODULE_DESCRIPTION("AMD Promontory GPIO Driver");
 156