File Index
Files and directories
Sign in
FILE

gpio-amd-fch.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpio/gpio-amd-fch.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2
  3/*
  4 * GPIO driver for the AMD G series FCH (eg. GX-412TC)
  5 *
  6 * Copyright (C) 2018 metux IT consult
  7 * Author: Enrico Weigelt, metux IT consult <info@metux.net>
  8 *
  9 */
 10
 11#include <linux/bitfield.h>
 12#include <linux/err.h>
 13#include <linux/io.h>
 14#include <linux/kernel.h>
 15#include <linux/module.h>
 16#include <linux/platform_device.h>
 17#include <linux/gpio/driver.h>
 18#include <linux/platform_data/gpio/gpio-amd-fch.h>
 19#include <linux/spinlock.h>
 20
 21#define AMD_FCH_MMIO_BASE		0xFED80000
 22#define AMD_FCH_GPIO_BANK0_BASE		0x1500
 23#define AMD_FCH_GPIO_SIZE		0x0300
 24
 25#define AMD_FCH_GPIO_FLAG_DIRECTION	BIT(23)
 26#define AMD_FCH_GPIO_FLAG_WRITE		BIT(22)
 27#define AMD_FCH_GPIO_FLAG_READ		BIT(16)
 28
 29static const struct resource amd_fch_gpio_iores =
 30	DEFINE_RES_MEM_NAMED(
 31		AMD_FCH_MMIO_BASE + AMD_FCH_GPIO_BANK0_BASE,
 32		AMD_FCH_GPIO_SIZE,
 33		"amd-fch-gpio-iomem");
 34
 35struct amd_fch_gpio_priv {
 36	struct gpio_chip		gc;
 37	void __iomem			*base;
 38	struct amd_fch_gpio_pdata	*pdata;
 39	spinlock_t			lock;
 40};
 41
 42static void __iomem *amd_fch_gpio_addr(struct amd_fch_gpio_priv *priv,
 43				       unsigned int gpio)
 44{
 45	return priv->base + priv->pdata->gpio_reg[gpio]*sizeof(u32);
 46}
 47
 48static int amd_fch_gpio_direction_input(struct gpio_chip *gc,
 49					unsigned int offset)
 50{
 51	unsigned long flags;
 52	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
 53	void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
 54
 55	spin_lock_irqsave(&priv->lock, flags);
 56	writel_relaxed(readl_relaxed(ptr) & ~AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
 57	spin_unlock_irqrestore(&priv->lock, flags);
 58
 59	return 0;
 60}
 61
 62static int amd_fch_gpio_direction_output(struct gpio_chip *gc,
 63					 unsigned int gpio, int value)
 64{
 65	unsigned long flags;
 66	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
 67	void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
 68	u32 val;
 69
 70	spin_lock_irqsave(&priv->lock, flags);
 71
 72	val = readl_relaxed(ptr);
 73	if (value)
 74		val |= AMD_FCH_GPIO_FLAG_WRITE;
 75	else
 76		val &= ~AMD_FCH_GPIO_FLAG_WRITE;
 77
 78	writel_relaxed(val | AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
 79
 80	spin_unlock_irqrestore(&priv->lock, flags);
 81
 82	return 0;
 83}
 84
 85static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
 86{
 87	int ret;
 88	unsigned long flags;
 89	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
 90	void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
 91
 92	spin_lock_irqsave(&priv->lock, flags);
 93	ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION);
 94	spin_unlock_irqrestore(&priv->lock, flags);
 95
 96	return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
 97}
 98
 99static int amd_fch_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
100{
101	unsigned long flags;
102	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
103	void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
104	u32 mask;
105
106	spin_lock_irqsave(&priv->lock, flags);
107
108	mask = readl_relaxed(ptr);
109	if (value)
110		mask |= AMD_FCH_GPIO_FLAG_WRITE;
111	else
112		mask &= ~AMD_FCH_GPIO_FLAG_WRITE;
113	writel_relaxed(mask, ptr);
114
115	spin_unlock_irqrestore(&priv->lock, flags);
116
117	return 0;
118}
119
120static int amd_fch_gpio_get(struct gpio_chip *gc,
121			    unsigned int offset)
122{
123	unsigned long flags;
124	u32 val;
125	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
126	void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
127
128	spin_lock_irqsave(&priv->lock, flags);
129	val = readl_relaxed(ptr);
130	spin_unlock_irqrestore(&priv->lock, flags);
131
132	return FIELD_GET(AMD_FCH_GPIO_FLAG_READ, val);
133}
134
135static int amd_fch_gpio_request(struct gpio_chip *chip,
136				unsigned int gpio_pin)
137{
138	return 0;
139}
140
141static int amd_fch_gpio_probe(struct platform_device *pdev)
142{
143	struct amd_fch_gpio_priv *priv;
144	struct amd_fch_gpio_pdata *pdata;
145
146	pdata = dev_get_platdata(&pdev->dev);
147	if (!pdata) {
148		dev_err(&pdev->dev, "no platform_data\n");
149		return -ENOENT;
150	}
151
152	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
153	if (!priv)
154		return -ENOMEM;
155
156	priv->pdata	= pdata;
157
158	priv->gc.owner			= THIS_MODULE;
159	priv->gc.parent			= &pdev->dev;
160	priv->gc.label			= dev_name(&pdev->dev);
161	priv->gc.ngpio			= priv->pdata->gpio_num;
162	priv->gc.names			= priv->pdata->gpio_names;
163	priv->gc.base			= -1;
164	priv->gc.request		= amd_fch_gpio_request;
165	priv->gc.direction_input	= amd_fch_gpio_direction_input;
166	priv->gc.direction_output	= amd_fch_gpio_direction_output;
167	priv->gc.get_direction		= amd_fch_gpio_get_direction;
168	priv->gc.get			= amd_fch_gpio_get;
169	priv->gc.set			= amd_fch_gpio_set;
170
171	spin_lock_init(&priv->lock);
172
173	priv->base = devm_ioremap_resource(&pdev->dev, &amd_fch_gpio_iores);
174	if (IS_ERR(priv->base))
175		return PTR_ERR(priv->base);
176
177	platform_set_drvdata(pdev, priv);
178
179	return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
180}
181
182static struct platform_driver amd_fch_gpio_driver = {
183	.driver = {
184		.name = AMD_FCH_GPIO_DRIVER_NAME,
185	},
186	.probe = amd_fch_gpio_probe,
187};
188
189module_platform_driver(amd_fch_gpio_driver);
190
191MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
192MODULE_DESCRIPTION("AMD G-series FCH GPIO driver");
193MODULE_LICENSE("GPL");
194MODULE_ALIAS("platform:" AMD_FCH_GPIO_DRIVER_NAME);
~/ Read-only file preview.