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