linux/drivers/gpio/gpio-reg.c
Andy Shevchenko d74c0863fd gpio: reg: Add missing header(s)
Do not imply that some of the generic headers may be always included.
Instead, include explicitly what we are direct user of.

While at it, split out the GPIO group of headers.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
2023-03-06 12:33:02 +02:00

192 lines
4.7 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* gpio-reg: single register individually fixed-direction GPIOs
*
* Copyright (C) 2016 Russell King
*/
#include <linux/bits.h>
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/irqdomain.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/gpio-reg.h>
struct gpio_reg {
struct gpio_chip gc;
spinlock_t lock;
u32 direction;
u32 out;
void __iomem *reg;
struct irq_domain *irqdomain;
const int *irqs;
};
#define to_gpio_reg(x) container_of(x, struct gpio_reg, gc)
static int gpio_reg_get_direction(struct gpio_chip *gc, unsigned offset)
{
struct gpio_reg *r = to_gpio_reg(gc);
return r->direction & BIT(offset) ? GPIO_LINE_DIRECTION_IN :
GPIO_LINE_DIRECTION_OUT;
}
static int gpio_reg_direction_output(struct gpio_chip *gc, unsigned offset,
int value)
{
struct gpio_reg *r = to_gpio_reg(gc);
if (r->direction & BIT(offset))
return -ENOTSUPP;
gc->set(gc, offset, value);
return 0;
}
static int gpio_reg_direction_input(struct gpio_chip *gc, unsigned offset)
{
struct gpio_reg *r = to_gpio_reg(gc);
return r->direction & BIT(offset) ? 0 : -ENOTSUPP;
}
static void gpio_reg_set(struct gpio_chip *gc, unsigned offset, int value)
{
struct gpio_reg *r = to_gpio_reg(gc);
unsigned long flags;
u32 val, mask = BIT(offset);
spin_lock_irqsave(&r->lock, flags);
val = r->out;
if (value)
val |= mask;
else
val &= ~mask;
r->out = val;
writel_relaxed(val, r->reg);
spin_unlock_irqrestore(&r->lock, flags);
}
static int gpio_reg_get(struct gpio_chip *gc, unsigned offset)
{
struct gpio_reg *r = to_gpio_reg(gc);
u32 val, mask = BIT(offset);
if (r->direction & mask) {
/*
* double-read the value, some registers latch after the
* first read.
*/
readl_relaxed(r->reg);
val = readl_relaxed(r->reg);
} else {
val = r->out;
}
return !!(val & mask);
}
static void gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct gpio_reg *r = to_gpio_reg(gc);
unsigned long flags;
spin_lock_irqsave(&r->lock, flags);
r->out = (r->out & ~*mask) | (*bits & *mask);
writel_relaxed(r->out, r->reg);
spin_unlock_irqrestore(&r->lock, flags);
}
static int gpio_reg_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct gpio_reg *r = to_gpio_reg(gc);
int irq = r->irqs[offset];
if (irq >= 0 && r->irqdomain)
irq = irq_find_mapping(r->irqdomain, irq);
return irq;
}
/**
* gpio_reg_init - add a fixed in/out register as gpio
* @dev: optional struct device associated with this register
* @base: start gpio number, or -1 to allocate
* @num: number of GPIOs, maximum 32
* @label: GPIO chip label
* @direction: bitmask of fixed direction, one per GPIO signal, 1 = in
* @def_out: initial GPIO output value
* @names: array of %num strings describing each GPIO signal or %NULL
* @irqdom: irq domain or %NULL
* @irqs: array of %num ints describing the interrupt mapping for each
* GPIO signal, or %NULL. If @irqdom is %NULL, then this
* describes the Linux interrupt number, otherwise it describes
* the hardware interrupt number in the specified irq domain.
*
* Add a single-register GPIO device containing up to 32 GPIO signals,
* where each GPIO has a fixed input or output configuration. Only
* input GPIOs are assumed to be readable from the register, and only
* then after a double-read. Output values are assumed not to be
* readable.
*/
struct gpio_chip *gpio_reg_init(struct device *dev, void __iomem *reg,
int base, int num, const char *label, u32 direction, u32 def_out,
const char *const *names, struct irq_domain *irqdom, const int *irqs)
{
struct gpio_reg *r;
int ret;
if (dev)
r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL);
else
r = kzalloc(sizeof(*r), GFP_KERNEL);
if (!r)
return ERR_PTR(-ENOMEM);
spin_lock_init(&r->lock);
r->gc.label = label;
r->gc.get_direction = gpio_reg_get_direction;
r->gc.direction_input = gpio_reg_direction_input;
r->gc.direction_output = gpio_reg_direction_output;
r->gc.set = gpio_reg_set;
r->gc.get = gpio_reg_get;
r->gc.set_multiple = gpio_reg_set_multiple;
if (irqs)
r->gc.to_irq = gpio_reg_to_irq;
r->gc.base = base;
r->gc.ngpio = num;
r->gc.names = names;
r->direction = direction;
r->out = def_out;
r->reg = reg;
r->irqs = irqs;
if (dev)
ret = devm_gpiochip_add_data(dev, &r->gc, r);
else
ret = gpiochip_add_data(&r->gc, r);
return ret ? ERR_PTR(ret) : &r->gc;
}
int gpio_reg_resume(struct gpio_chip *gc)
{
struct gpio_reg *r = to_gpio_reg(gc);
unsigned long flags;
spin_lock_irqsave(&r->lock, flags);
writel_relaxed(r->out, r->reg);
spin_unlock_irqrestore(&r->lock, flags);
return 0;
}