mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 13:41:51 +00:00
484b322676
The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
154 lines
3.8 KiB
C
154 lines
3.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* AMD Promontory GPIO driver
|
|
*
|
|
* Copyright (C) 2015 ASMedia Technology Inc.
|
|
* Author: YD Tseng <yd_tseng@asmedia.com.tw>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#define PT_TOTAL_GPIO 8
|
|
#define PT_TOTAL_GPIO_EX 24
|
|
|
|
/* PCI-E MMIO register offsets */
|
|
#define PT_DIRECTION_REG 0x00
|
|
#define PT_INPUTDATA_REG 0x04
|
|
#define PT_OUTPUTDATA_REG 0x08
|
|
#define PT_CLOCKRATE_REG 0x0C
|
|
#define PT_SYNC_REG 0x28
|
|
|
|
struct pt_gpio_chip {
|
|
struct gpio_chip gc;
|
|
void __iomem *reg_base;
|
|
};
|
|
|
|
static int pt_gpio_request(struct gpio_chip *gc, unsigned offset)
|
|
{
|
|
struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc);
|
|
unsigned long flags;
|
|
u32 using_pins;
|
|
|
|
dev_dbg(gc->parent, "pt_gpio_request offset=%x\n", offset);
|
|
|
|
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
|
|
|
|
using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
|
|
if (using_pins & BIT(offset)) {
|
|
dev_warn(gc->parent, "PT GPIO pin %x reconfigured\n",
|
|
offset);
|
|
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
writel(using_pins | BIT(offset), pt_gpio->reg_base + PT_SYNC_REG);
|
|
|
|
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pt_gpio_free(struct gpio_chip *gc, unsigned offset)
|
|
{
|
|
struct pt_gpio_chip *pt_gpio = gpiochip_get_data(gc);
|
|
unsigned long flags;
|
|
u32 using_pins;
|
|
|
|
raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
|
|
|
|
using_pins = readl(pt_gpio->reg_base + PT_SYNC_REG);
|
|
using_pins &= ~BIT(offset);
|
|
writel(using_pins, pt_gpio->reg_base + PT_SYNC_REG);
|
|
|
|
raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
|
|
|
dev_dbg(gc->parent, "pt_gpio_free offset=%x\n", offset);
|
|
}
|
|
|
|
static int pt_gpio_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct pt_gpio_chip *pt_gpio;
|
|
int ret = 0;
|
|
|
|
if (!ACPI_COMPANION(dev)) {
|
|
dev_err(dev, "PT GPIO device node not found\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
pt_gpio = devm_kzalloc(dev, sizeof(struct pt_gpio_chip), GFP_KERNEL);
|
|
if (!pt_gpio)
|
|
return -ENOMEM;
|
|
|
|
pt_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(pt_gpio->reg_base)) {
|
|
dev_err(dev, "Failed to map MMIO resource for PT GPIO.\n");
|
|
return PTR_ERR(pt_gpio->reg_base);
|
|
}
|
|
|
|
ret = bgpio_init(&pt_gpio->gc, dev, 4,
|
|
pt_gpio->reg_base + PT_INPUTDATA_REG,
|
|
pt_gpio->reg_base + PT_OUTPUTDATA_REG, NULL,
|
|
pt_gpio->reg_base + PT_DIRECTION_REG, NULL,
|
|
BGPIOF_READ_OUTPUT_REG_SET);
|
|
if (ret) {
|
|
dev_err(dev, "bgpio_init failed\n");
|
|
return ret;
|
|
}
|
|
|
|
pt_gpio->gc.owner = THIS_MODULE;
|
|
pt_gpio->gc.request = pt_gpio_request;
|
|
pt_gpio->gc.free = pt_gpio_free;
|
|
pt_gpio->gc.ngpio = (uintptr_t)device_get_match_data(dev);
|
|
|
|
ret = gpiochip_add_data(&pt_gpio->gc, pt_gpio);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to register GPIO lib\n");
|
|
return ret;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, pt_gpio);
|
|
|
|
/* initialize register setting */
|
|
writel(0, pt_gpio->reg_base + PT_SYNC_REG);
|
|
writel(0, pt_gpio->reg_base + PT_CLOCKRATE_REG);
|
|
|
|
dev_dbg(dev, "PT GPIO driver loaded\n");
|
|
return ret;
|
|
}
|
|
|
|
static void pt_gpio_remove(struct platform_device *pdev)
|
|
{
|
|
struct pt_gpio_chip *pt_gpio = platform_get_drvdata(pdev);
|
|
|
|
gpiochip_remove(&pt_gpio->gc);
|
|
}
|
|
|
|
static const struct acpi_device_id pt_gpio_acpi_match[] = {
|
|
{ "AMDF030", PT_TOTAL_GPIO },
|
|
{ "AMDIF030", PT_TOTAL_GPIO },
|
|
{ "AMDIF031", PT_TOTAL_GPIO_EX },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, pt_gpio_acpi_match);
|
|
|
|
static struct platform_driver pt_gpio_driver = {
|
|
.driver = {
|
|
.name = "pt-gpio",
|
|
.acpi_match_table = ACPI_PTR(pt_gpio_acpi_match),
|
|
},
|
|
.probe = pt_gpio_probe,
|
|
.remove_new = pt_gpio_remove,
|
|
};
|
|
|
|
module_platform_driver(pt_gpio_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("YD Tseng <yd_tseng@asmedia.com.tw>");
|
|
MODULE_DESCRIPTION("AMD Promontory GPIO Driver");
|