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