mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-04 09:26:58 +07:00
6732ae5cb4
This is also include the gpio controller as the IP share both. Each soc will have to describe the SoC limitation and pin configuration via DT. This will allow to do not need to touch the C code when adding new SoC if the IP version is supported. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
1491 lines
38 KiB
C
1491 lines
38 KiB
C
/*
|
|
* at91 pinctrl driver based on at91 pinmux core
|
|
*
|
|
* Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
|
|
*
|
|
* Under GPLv2 only
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/err.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/io.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/pinctrl/machine.h>
|
|
#include <linux/pinctrl/pinconf.h>
|
|
#include <linux/pinctrl/pinctrl.h>
|
|
#include <linux/pinctrl/pinmux.h>
|
|
/* Since we request GPIOs from ourself */
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
|
#include <asm/mach/irq.h>
|
|
|
|
#include <mach/hardware.h>
|
|
#include <mach/at91_pio.h>
|
|
|
|
#include "core.h"
|
|
|
|
#define MAX_NB_GPIO_PER_BANK 32
|
|
|
|
struct at91_pinctrl_mux_ops;
|
|
|
|
struct at91_gpio_chip {
|
|
struct gpio_chip chip;
|
|
struct pinctrl_gpio_range range;
|
|
struct at91_gpio_chip *next; /* Bank sharing same clock */
|
|
int pioc_hwirq; /* PIO bank interrupt identifier on AIC */
|
|
int pioc_virq; /* PIO bank Linux virtual interrupt */
|
|
int pioc_idx; /* PIO bank index */
|
|
void __iomem *regbase; /* PIO bank virtual address */
|
|
struct clk *clock; /* associated clock */
|
|
struct irq_domain *domain; /* associated irq domain */
|
|
struct at91_pinctrl_mux_ops *ops; /* ops */
|
|
};
|
|
|
|
#define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip)
|
|
|
|
static struct at91_gpio_chip *gpio_chips[MAX_GPIO_BANKS];
|
|
|
|
static int gpio_banks;
|
|
|
|
#define PULL_UP (0 << 1)
|
|
#define MULTI_DRIVE (1 << 1)
|
|
|
|
/**
|
|
* struct at91_pmx_func - describes AT91 pinmux functions
|
|
* @name: the name of this specific function
|
|
* @groups: corresponding pin groups
|
|
* @ngroups: the number of groups
|
|
*/
|
|
struct at91_pmx_func {
|
|
const char *name;
|
|
const char **groups;
|
|
unsigned ngroups;
|
|
};
|
|
|
|
enum at91_mux {
|
|
AT91_MUX_GPIO = 0,
|
|
AT91_MUX_PERIPH_A = 1,
|
|
AT91_MUX_PERIPH_B = 2,
|
|
AT91_MUX_PERIPH_C = 3,
|
|
AT91_MUX_PERIPH_D = 4,
|
|
};
|
|
|
|
/**
|
|
* struct at91_pmx_pin - describes an At91 pin mux
|
|
* @bank: the bank of the pin
|
|
* @pin: the pin number in the @bank
|
|
* @mux: the mux mode : gpio or periph_x of the pin i.e. alternate function.
|
|
* @conf: the configuration of the pin: PULL_UP, MULTIDRIVE etc...
|
|
*/
|
|
struct at91_pmx_pin {
|
|
uint32_t bank;
|
|
uint32_t pin;
|
|
enum at91_mux mux;
|
|
unsigned long conf;
|
|
};
|
|
|
|
/**
|
|
* struct at91_pin_group - describes an At91 pin group
|
|
* @name: the name of this specific pin group
|
|
* @pins_conf: the mux mode for each pin in this group. The size of this
|
|
* array is the same as pins.
|
|
* @pins: an array of discrete physical pins used in this group, taken
|
|
* from the driver-local pin enumeration space
|
|
* @npins: the number of pins in this group array, i.e. the number of
|
|
* elements in .pins so we can iterate over that array
|
|
*/
|
|
struct at91_pin_group {
|
|
const char *name;
|
|
struct at91_pmx_pin *pins_conf;
|
|
unsigned int *pins;
|
|
unsigned npins;
|
|
};
|
|
|
|
/**
|
|
* struct at91_pinctrl_mux_ops - describes an At91 mux ops group
|
|
* on new IP with support for periph C and D the way to mux in
|
|
* periph A and B has changed
|
|
* So provide the right call back
|
|
* if not present means the IP does not support it
|
|
* @get_periph: return the periph mode configured
|
|
* @mux_A_periph: mux as periph A
|
|
* @mux_B_periph: mux as periph B
|
|
* @mux_C_periph: mux as periph C
|
|
* @mux_D_periph: mux as periph D
|
|
* @irq_type: return irq type
|
|
*/
|
|
struct at91_pinctrl_mux_ops {
|
|
enum at91_mux (*get_periph)(void __iomem *pio, unsigned mask);
|
|
void (*mux_A_periph)(void __iomem *pio, unsigned mask);
|
|
void (*mux_B_periph)(void __iomem *pio, unsigned mask);
|
|
void (*mux_C_periph)(void __iomem *pio, unsigned mask);
|
|
void (*mux_D_periph)(void __iomem *pio, unsigned mask);
|
|
/* irq */
|
|
int (*irq_type)(struct irq_data *d, unsigned type);
|
|
};
|
|
|
|
static int gpio_irq_type(struct irq_data *d, unsigned type);
|
|
static int alt_gpio_irq_type(struct irq_data *d, unsigned type);
|
|
|
|
struct at91_pinctrl {
|
|
struct device *dev;
|
|
struct pinctrl_dev *pctl;
|
|
|
|
int nbanks;
|
|
|
|
uint32_t *mux_mask;
|
|
int nmux;
|
|
|
|
struct at91_pmx_func *functions;
|
|
int nfunctions;
|
|
|
|
struct at91_pin_group *groups;
|
|
int ngroups;
|
|
|
|
struct at91_pinctrl_mux_ops *ops;
|
|
};
|
|
|
|
static const inline struct at91_pin_group *at91_pinctrl_find_group_by_name(
|
|
const struct at91_pinctrl *info,
|
|
const char *name)
|
|
{
|
|
const struct at91_pin_group *grp = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < info->ngroups; i++) {
|
|
if (strcmp(info->groups[i].name, name))
|
|
continue;
|
|
|
|
grp = &info->groups[i];
|
|
dev_dbg(info->dev, "%s: %d 0:%d\n", name, grp->npins, grp->pins[0]);
|
|
break;
|
|
}
|
|
|
|
return grp;
|
|
}
|
|
|
|
static int at91_get_groups_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
return info->ngroups;
|
|
}
|
|
|
|
static const char *at91_get_group_name(struct pinctrl_dev *pctldev,
|
|
unsigned selector)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
return info->groups[selector].name;
|
|
}
|
|
|
|
static int at91_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
|
|
const unsigned **pins,
|
|
unsigned *npins)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
if (selector >= info->ngroups)
|
|
return -EINVAL;
|
|
|
|
*pins = info->groups[selector].pins;
|
|
*npins = info->groups[selector].npins;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void at91_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
|
|
unsigned offset)
|
|
{
|
|
seq_printf(s, "%s", dev_name(pctldev->dev));
|
|
}
|
|
|
|
static int at91_dt_node_to_map(struct pinctrl_dev *pctldev,
|
|
struct device_node *np,
|
|
struct pinctrl_map **map, unsigned *num_maps)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
const struct at91_pin_group *grp;
|
|
struct pinctrl_map *new_map;
|
|
struct device_node *parent;
|
|
int map_num = 1;
|
|
int i;
|
|
struct at91_pmx_pin *pin;
|
|
|
|
/*
|
|
* first find the group of this node and check if we need create
|
|
* config maps for pins
|
|
*/
|
|
grp = at91_pinctrl_find_group_by_name(info, np->name);
|
|
if (!grp) {
|
|
dev_err(info->dev, "unable to find group for node %s\n",
|
|
np->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
map_num += grp->npins;
|
|
new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, GFP_KERNEL);
|
|
if (!new_map)
|
|
return -ENOMEM;
|
|
|
|
*map = new_map;
|
|
*num_maps = map_num;
|
|
|
|
/* create mux map */
|
|
parent = of_get_parent(np);
|
|
if (!parent) {
|
|
kfree(new_map);
|
|
return -EINVAL;
|
|
}
|
|
new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
|
|
new_map[0].data.mux.function = parent->name;
|
|
new_map[0].data.mux.group = np->name;
|
|
of_node_put(parent);
|
|
|
|
/* create config map */
|
|
new_map++;
|
|
for (i = 0; i < grp->npins; i++) {
|
|
pin = &grp->pins_conf[i];
|
|
|
|
new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
|
|
new_map[i].data.configs.group_or_pin =
|
|
pin_get_name(pctldev, grp->pins[i]);
|
|
new_map[i].data.configs.configs = &grp->pins_conf[i].conf;
|
|
new_map[i].data.configs.num_configs = 1;
|
|
}
|
|
|
|
dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
|
|
(*map)->data.mux.function, (*map)->data.mux.group, map_num);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void at91_dt_free_map(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_map *map, unsigned num_maps)
|
|
{
|
|
}
|
|
|
|
static struct pinctrl_ops at91_pctrl_ops = {
|
|
.get_groups_count = at91_get_groups_count,
|
|
.get_group_name = at91_get_group_name,
|
|
.get_group_pins = at91_get_group_pins,
|
|
.pin_dbg_show = at91_pin_dbg_show,
|
|
.dt_node_to_map = at91_dt_node_to_map,
|
|
.dt_free_map = at91_dt_free_map,
|
|
};
|
|
|
|
static void __iomem * pin_to_controller(struct at91_pinctrl *info,
|
|
unsigned int bank)
|
|
{
|
|
return gpio_chips[bank]->regbase;
|
|
}
|
|
|
|
static inline int pin_to_bank(unsigned pin)
|
|
{
|
|
return pin /= MAX_NB_GPIO_PER_BANK;
|
|
}
|
|
|
|
static unsigned pin_to_mask(unsigned int pin)
|
|
{
|
|
return 1 << pin;
|
|
}
|
|
|
|
static void at91_mux_disable_interrupt(void __iomem *pio, unsigned mask)
|
|
{
|
|
writel_relaxed(mask, pio + PIO_IDR);
|
|
}
|
|
|
|
static unsigned at91_mux_get_pullup(void __iomem *pio, unsigned pin)
|
|
{
|
|
return (readl_relaxed(pio + PIO_PUSR) >> pin) & 0x1;
|
|
}
|
|
|
|
static void at91_mux_set_pullup(void __iomem *pio, unsigned mask, bool on)
|
|
{
|
|
writel_relaxed(mask, pio + (on ? PIO_PUER : PIO_PUDR));
|
|
}
|
|
|
|
static unsigned at91_mux_get_multidrive(void __iomem *pio, unsigned pin)
|
|
{
|
|
return (readl_relaxed(pio + PIO_MDSR) >> pin) & 0x1;
|
|
}
|
|
|
|
static void at91_mux_set_multidrive(void __iomem *pio, unsigned mask, bool on)
|
|
{
|
|
writel_relaxed(mask, pio + (on ? PIO_MDER : PIO_MDDR));
|
|
}
|
|
|
|
static void at91_mux_set_A_periph(void __iomem *pio, unsigned mask)
|
|
{
|
|
writel_relaxed(mask, pio + PIO_ASR);
|
|
}
|
|
|
|
static void at91_mux_set_B_periph(void __iomem *pio, unsigned mask)
|
|
{
|
|
writel_relaxed(mask, pio + PIO_BSR);
|
|
}
|
|
|
|
static void at91_mux_pio3_set_A_periph(void __iomem *pio, unsigned mask)
|
|
{
|
|
|
|
writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) & ~mask,
|
|
pio + PIO_ABCDSR1);
|
|
writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) & ~mask,
|
|
pio + PIO_ABCDSR2);
|
|
}
|
|
|
|
static void at91_mux_pio3_set_B_periph(void __iomem *pio, unsigned mask)
|
|
{
|
|
writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) | mask,
|
|
pio + PIO_ABCDSR1);
|
|
writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) & ~mask,
|
|
pio + PIO_ABCDSR2);
|
|
}
|
|
|
|
static void at91_mux_pio3_set_C_periph(void __iomem *pio, unsigned mask)
|
|
{
|
|
writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) & ~mask, pio + PIO_ABCDSR1);
|
|
writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2);
|
|
}
|
|
|
|
static void at91_mux_pio3_set_D_periph(void __iomem *pio, unsigned mask)
|
|
{
|
|
writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) | mask, pio + PIO_ABCDSR1);
|
|
writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2);
|
|
}
|
|
|
|
static enum at91_mux at91_mux_pio3_get_periph(void __iomem *pio, unsigned mask)
|
|
{
|
|
unsigned select;
|
|
|
|
if (readl_relaxed(pio + PIO_PSR) & mask)
|
|
return AT91_MUX_GPIO;
|
|
|
|
select = !!(readl_relaxed(pio + PIO_ABCDSR1) & mask);
|
|
select |= (!!(readl_relaxed(pio + PIO_ABCDSR2) & mask) << 1);
|
|
|
|
return select + 1;
|
|
}
|
|
|
|
static enum at91_mux at91_mux_get_periph(void __iomem *pio, unsigned mask)
|
|
{
|
|
unsigned select;
|
|
|
|
if (readl_relaxed(pio + PIO_PSR) & mask)
|
|
return AT91_MUX_GPIO;
|
|
|
|
select = readl_relaxed(pio + PIO_ABSR) & mask;
|
|
|
|
return select + 1;
|
|
}
|
|
|
|
static struct at91_pinctrl_mux_ops at91rm9200_ops = {
|
|
.get_periph = at91_mux_get_periph,
|
|
.mux_A_periph = at91_mux_set_A_periph,
|
|
.mux_B_periph = at91_mux_set_B_periph,
|
|
.irq_type = gpio_irq_type,
|
|
};
|
|
|
|
static struct at91_pinctrl_mux_ops at91sam9x5_ops = {
|
|
.get_periph = at91_mux_pio3_get_periph,
|
|
.mux_A_periph = at91_mux_pio3_set_A_periph,
|
|
.mux_B_periph = at91_mux_pio3_set_B_periph,
|
|
.mux_C_periph = at91_mux_pio3_set_C_periph,
|
|
.mux_D_periph = at91_mux_pio3_set_D_periph,
|
|
.irq_type = alt_gpio_irq_type,
|
|
};
|
|
|
|
static void at91_pin_dbg(const struct device *dev, const struct at91_pmx_pin *pin)
|
|
{
|
|
if (pin->mux) {
|
|
dev_dbg(dev, "pio%c%d configured as periph%c with conf = 0x%lu\n",
|
|
pin->bank + 'A', pin->pin, pin->mux - 1 + 'A', pin->conf);
|
|
} else {
|
|
dev_dbg(dev, "pio%c%d configured as gpio with conf = 0x%lu\n",
|
|
pin->bank + 'A', pin->pin, pin->conf);
|
|
}
|
|
}
|
|
|
|
static int pin_check_config(struct at91_pinctrl *info, const char* name,
|
|
int index, const struct at91_pmx_pin *pin)
|
|
{
|
|
int mux;
|
|
|
|
/* check if it's a valid config */
|
|
if (pin->bank >= info->nbanks) {
|
|
dev_err(info->dev, "%s: pin conf %d bank_id %d >= nbanks %d\n",
|
|
name, index, pin->bank, info->nbanks);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (pin->pin >= MAX_NB_GPIO_PER_BANK) {
|
|
dev_err(info->dev, "%s: pin conf %d pin_bank_id %d >= %d\n",
|
|
name, index, pin->pin, MAX_NB_GPIO_PER_BANK);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!pin->mux)
|
|
return 0;
|
|
|
|
mux = pin->mux - 1;
|
|
|
|
if (mux >= info->nmux) {
|
|
dev_err(info->dev, "%s: pin conf %d mux_id %d >= nmux %d\n",
|
|
name, index, mux, info->nmux);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!(info->mux_mask[pin->bank * info->nmux + mux] & 1 << pin->pin)) {
|
|
dev_err(info->dev, "%s: pin conf %d mux_id %d not supported for pio%c%d\n",
|
|
name, index, mux, pin->bank + 'A', pin->pin);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void at91_mux_gpio_disable(void __iomem *pio, unsigned mask)
|
|
{
|
|
writel_relaxed(mask, pio + PIO_PDR);
|
|
}
|
|
|
|
static void at91_mux_gpio_enable(void __iomem *pio, unsigned mask, bool input)
|
|
{
|
|
writel_relaxed(mask, pio + PIO_PER);
|
|
writel_relaxed(mask, pio + (input ? PIO_ODR : PIO_OER));
|
|
}
|
|
|
|
static int at91_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector,
|
|
unsigned group)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
const struct at91_pmx_pin *pins_conf = info->groups[group].pins_conf;
|
|
const struct at91_pmx_pin *pin;
|
|
uint32_t npins = info->groups[group].npins;
|
|
int i, ret;
|
|
unsigned mask;
|
|
void __iomem *pio;
|
|
|
|
dev_dbg(info->dev, "enable function %s group %s\n",
|
|
info->functions[selector].name, info->groups[group].name);
|
|
|
|
/* first check that all the pins of the group are valid with a valid
|
|
* paramter */
|
|
for (i = 0; i < npins; i++) {
|
|
pin = &pins_conf[i];
|
|
ret = pin_check_config(info, info->groups[group].name, i, pin);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < npins; i++) {
|
|
pin = &pins_conf[i];
|
|
at91_pin_dbg(info->dev, pin);
|
|
pio = pin_to_controller(info, pin->bank);
|
|
mask = pin_to_mask(pin->pin);
|
|
at91_mux_disable_interrupt(pio, mask);
|
|
switch(pin->mux) {
|
|
case AT91_MUX_GPIO:
|
|
at91_mux_gpio_enable(pio, mask, 1);
|
|
break;
|
|
case AT91_MUX_PERIPH_A:
|
|
info->ops->mux_A_periph(pio, mask);
|
|
break;
|
|
case AT91_MUX_PERIPH_B:
|
|
info->ops->mux_B_periph(pio, mask);
|
|
break;
|
|
case AT91_MUX_PERIPH_C:
|
|
if (!info->ops->mux_C_periph)
|
|
return -EINVAL;
|
|
info->ops->mux_C_periph(pio, mask);
|
|
break;
|
|
case AT91_MUX_PERIPH_D:
|
|
if (!info->ops->mux_D_periph)
|
|
return -EINVAL;
|
|
info->ops->mux_D_periph(pio, mask);
|
|
break;
|
|
}
|
|
if (pin->mux)
|
|
at91_mux_gpio_disable(pio, mask);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void at91_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector,
|
|
unsigned group)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
const struct at91_pmx_pin *pins_conf = info->groups[group].pins_conf;
|
|
const struct at91_pmx_pin *pin;
|
|
uint32_t npins = info->groups[group].npins;
|
|
int i;
|
|
unsigned mask;
|
|
void __iomem *pio;
|
|
|
|
for (i = 0; i < npins; i++) {
|
|
pin = &pins_conf[i];
|
|
at91_pin_dbg(info->dev, pin);
|
|
pio = pin_to_controller(info, pin->bank);
|
|
mask = pin_to_mask(pin->pin);
|
|
at91_mux_gpio_enable(pio, mask, 1);
|
|
}
|
|
}
|
|
|
|
static int at91_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
return info->nfunctions;
|
|
}
|
|
|
|
static const char *at91_pmx_get_func_name(struct pinctrl_dev *pctldev,
|
|
unsigned selector)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
return info->functions[selector].name;
|
|
}
|
|
|
|
static int at91_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
|
|
const char * const **groups,
|
|
unsigned * const num_groups)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
*groups = info->functions[selector].groups;
|
|
*num_groups = info->functions[selector].ngroups;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at91_gpio_request_enable(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_gpio_range *range,
|
|
unsigned offset)
|
|
{
|
|
struct at91_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
|
|
struct at91_gpio_chip *at91_chip;
|
|
struct gpio_chip *chip;
|
|
unsigned mask;
|
|
|
|
if (!range) {
|
|
dev_err(npct->dev, "invalid range\n");
|
|
return -EINVAL;
|
|
}
|
|
if (!range->gc) {
|
|
dev_err(npct->dev, "missing GPIO chip in range\n");
|
|
return -EINVAL;
|
|
}
|
|
chip = range->gc;
|
|
at91_chip = container_of(chip, struct at91_gpio_chip, chip);
|
|
|
|
dev_dbg(npct->dev, "enable pin %u as GPIO\n", offset);
|
|
|
|
mask = 1 << (offset - chip->base);
|
|
|
|
dev_dbg(npct->dev, "enable pin %u as PIO%c%d 0x%x\n",
|
|
offset, 'A' + range->id, offset - chip->base, mask);
|
|
|
|
writel_relaxed(mask, at91_chip->regbase + PIO_PER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void at91_gpio_disable_free(struct pinctrl_dev *pctldev,
|
|
struct pinctrl_gpio_range *range,
|
|
unsigned offset)
|
|
{
|
|
struct at91_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
|
|
|
|
dev_dbg(npct->dev, "disable pin %u as GPIO\n", offset);
|
|
/* Set the pin to some default state, GPIO is usually default */
|
|
}
|
|
|
|
static struct pinmux_ops at91_pmx_ops = {
|
|
.get_functions_count = at91_pmx_get_funcs_count,
|
|
.get_function_name = at91_pmx_get_func_name,
|
|
.get_function_groups = at91_pmx_get_groups,
|
|
.enable = at91_pmx_enable,
|
|
.disable = at91_pmx_disable,
|
|
.gpio_request_enable = at91_gpio_request_enable,
|
|
.gpio_disable_free = at91_gpio_disable_free,
|
|
};
|
|
|
|
static int at91_pinconf_get(struct pinctrl_dev *pctldev,
|
|
unsigned pin_id, unsigned long *config)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
void __iomem *pio;
|
|
unsigned pin;
|
|
|
|
dev_dbg(info->dev, "%s:%d, pin_id=%d, config=0x%lx", __func__, __LINE__, pin_id, *config);
|
|
pio = pin_to_controller(info, pin_to_bank(pin_id));
|
|
pin = pin_id % MAX_NB_GPIO_PER_BANK;
|
|
|
|
if (at91_mux_get_multidrive(pio, pin))
|
|
*config |= MULTI_DRIVE;
|
|
|
|
if (at91_mux_get_pullup(pio, pin))
|
|
*config |= PULL_UP;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int at91_pinconf_set(struct pinctrl_dev *pctldev,
|
|
unsigned pin_id, unsigned long config)
|
|
{
|
|
struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
|
unsigned mask;
|
|
void __iomem *pio;
|
|
|
|
dev_dbg(info->dev, "%s:%d, pin_id=%d, config=0x%lx", __func__, __LINE__, pin_id, config);
|
|
pio = pin_to_controller(info, pin_to_bank(pin_id));
|
|
mask = pin_to_mask(pin_id % MAX_NB_GPIO_PER_BANK);
|
|
|
|
at91_mux_set_pullup(pio, mask, config & PULL_UP);
|
|
at91_mux_set_multidrive(pio, mask, config & MULTI_DRIVE);
|
|
return 0;
|
|
}
|
|
|
|
static void at91_pinconf_dbg_show(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s, unsigned pin_id)
|
|
{
|
|
|
|
}
|
|
|
|
static void at91_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
|
|
struct seq_file *s, unsigned group)
|
|
{
|
|
}
|
|
|
|
struct pinconf_ops at91_pinconf_ops = {
|
|
.pin_config_get = at91_pinconf_get,
|
|
.pin_config_set = at91_pinconf_set,
|
|
.pin_config_dbg_show = at91_pinconf_dbg_show,
|
|
.pin_config_group_dbg_show = at91_pinconf_group_dbg_show,
|
|
};
|
|
|
|
static struct pinctrl_desc at91_pinctrl_desc = {
|
|
.pctlops = &at91_pctrl_ops,
|
|
.pmxops = &at91_pmx_ops,
|
|
.confops = &at91_pinconf_ops,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static const char *gpio_compat = "atmel,at91rm9200-gpio";
|
|
|
|
static void __devinit at91_pinctrl_child_count(struct at91_pinctrl *info,
|
|
struct device_node *np)
|
|
{
|
|
struct device_node *child;
|
|
|
|
for_each_child_of_node(np, child) {
|
|
if (of_device_is_compatible(child, gpio_compat)) {
|
|
info->nbanks++;
|
|
} else {
|
|
info->nfunctions++;
|
|
info->ngroups += of_get_child_count(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int __devinit at91_pinctrl_mux_mask(struct at91_pinctrl *info,
|
|
struct device_node *np)
|
|
{
|
|
int ret = 0;
|
|
int size;
|
|
const const __be32 *list;
|
|
|
|
list = of_get_property(np, "atmel,mux-mask", &size);
|
|
if (!list) {
|
|
dev_err(info->dev, "can not read the mux-mask of %d\n", size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
size /= sizeof(*list);
|
|
if (!size || size % info->nbanks) {
|
|
dev_err(info->dev, "wrong mux mask array should be by %d\n", info->nbanks);
|
|
return -EINVAL;
|
|
}
|
|
info->nmux = size / info->nbanks;
|
|
|
|
info->mux_mask = devm_kzalloc(info->dev, sizeof(u32) * size, GFP_KERNEL);
|
|
if (!info->mux_mask) {
|
|
dev_err(info->dev, "could not alloc mux_mask\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = of_property_read_u32_array(np, "atmel,mux-mask",
|
|
info->mux_mask, size);
|
|
if (ret)
|
|
dev_err(info->dev, "can not read the mux-mask of %d\n", size);
|
|
return ret;
|
|
}
|
|
|
|
static int __devinit at91_pinctrl_parse_groups(struct device_node *np,
|
|
struct at91_pin_group *grp,
|
|
struct at91_pinctrl *info,
|
|
u32 index)
|
|
{
|
|
struct at91_pmx_pin *pin;
|
|
int size;
|
|
const const __be32 *list;
|
|
int i, j;
|
|
|
|
dev_dbg(info->dev, "group(%d): %s\n", index, np->name);
|
|
|
|
/* Initialise group */
|
|
grp->name = np->name;
|
|
|
|
/*
|
|
* the binding format is atmel,pins = <bank pin mux CONFIG ...>,
|
|
* do sanity check and calculate pins number
|
|
*/
|
|
list = of_get_property(np, "atmel,pins", &size);
|
|
/* we do not check return since it's safe node passed down */
|
|
size /= sizeof(*list);
|
|
if (!size || size % 4) {
|
|
dev_err(info->dev, "wrong pins number or pins and configs should be by 4\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
grp->npins = size / 4;
|
|
pin = grp->pins_conf = devm_kzalloc(info->dev, grp->npins * sizeof(struct at91_pmx_pin),
|
|
GFP_KERNEL);
|
|
grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
|
|
GFP_KERNEL);
|
|
if (!grp->pins_conf || !grp->pins)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0, j = 0; i < size; i += 4, j++) {
|
|
pin->bank = be32_to_cpu(*list++);
|
|
pin->pin = be32_to_cpu(*list++);
|
|
grp->pins[j] = pin->bank * MAX_NB_GPIO_PER_BANK + pin->pin;
|
|
pin->mux = be32_to_cpu(*list++);
|
|
pin->conf = be32_to_cpu(*list++);
|
|
|
|
at91_pin_dbg(info->dev, pin);
|
|
pin++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __devinit at91_pinctrl_parse_functions(struct device_node *np,
|
|
struct at91_pinctrl *info, u32 index)
|
|
{
|
|
struct device_node *child;
|
|
struct at91_pmx_func *func;
|
|
struct at91_pin_group *grp;
|
|
int ret;
|
|
static u32 grp_index;
|
|
u32 i = 0;
|
|
|
|
dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name);
|
|
|
|
func = &info->functions[index];
|
|
|
|
/* Initialise function */
|
|
func->name = np->name;
|
|
func->ngroups = of_get_child_count(np);
|
|
if (func->ngroups <= 0) {
|
|
dev_err(info->dev, "no groups defined\n");
|
|
return -EINVAL;
|
|
}
|
|
func->groups = devm_kzalloc(info->dev,
|
|
func->ngroups * sizeof(char *), GFP_KERNEL);
|
|
if (!func->groups)
|
|
return -ENOMEM;
|
|
|
|
for_each_child_of_node(np, child) {
|
|
func->groups[i] = child->name;
|
|
grp = &info->groups[grp_index++];
|
|
ret = at91_pinctrl_parse_groups(child, grp, info, i++);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct of_device_id at91_pinctrl_of_match[] __devinitdata = {
|
|
{ .compatible = "atmel,at91sam9x5-pinctrl", .data = &at91sam9x5_ops },
|
|
{ .compatible = "atmel,at91rm9200-pinctrl", .data = &at91rm9200_ops },
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
static int __devinit at91_pinctrl_probe_dt(struct platform_device *pdev,
|
|
struct at91_pinctrl *info)
|
|
{
|
|
int ret = 0;
|
|
int i, j;
|
|
uint32_t *tmp;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct device_node *child;
|
|
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
info->dev = &pdev->dev;
|
|
info->ops =
|
|
of_match_device(at91_pinctrl_of_match, &pdev->dev)->data;
|
|
at91_pinctrl_child_count(info, np);
|
|
|
|
if (info->nbanks < 1) {
|
|
dev_err(&pdev->dev, "you need to specify atleast one gpio-controller\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = at91_pinctrl_mux_mask(info, np);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dev_dbg(&pdev->dev, "nmux = %d\n", info->nmux);
|
|
|
|
dev_dbg(&pdev->dev, "mux-mask\n");
|
|
tmp = info->mux_mask;
|
|
for (i = 0; i < info->nbanks; i++) {
|
|
for (j = 0; j < info->nmux; j++, tmp++) {
|
|
dev_dbg(&pdev->dev, "%d:%d\t0x%x\n", i, j, tmp[0]);
|
|
}
|
|
}
|
|
|
|
dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
|
|
dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
|
|
info->functions = devm_kzalloc(&pdev->dev, info->nfunctions * sizeof(struct at91_pmx_func),
|
|
GFP_KERNEL);
|
|
if (!info->functions)
|
|
return -ENOMEM;
|
|
|
|
info->groups = devm_kzalloc(&pdev->dev, info->ngroups * sizeof(struct at91_pin_group),
|
|
GFP_KERNEL);
|
|
if (!info->groups)
|
|
return -ENOMEM;
|
|
|
|
dev_dbg(&pdev->dev, "nbanks = %d\n", info->nbanks);
|
|
dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
|
|
dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
|
|
|
|
i = 0;
|
|
|
|
for_each_child_of_node(np, child) {
|
|
if (of_device_is_compatible(child, gpio_compat))
|
|
continue;
|
|
ret = at91_pinctrl_parse_functions(child, info, i++);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to parse function\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __devinit at91_pinctrl_probe(struct platform_device *pdev)
|
|
{
|
|
struct at91_pinctrl *info;
|
|
struct pinctrl_pin_desc *pdesc;
|
|
int ret, i, j ,k;
|
|
|
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
|
if (!info)
|
|
return -ENOMEM;
|
|
|
|
ret = at91_pinctrl_probe_dt(pdev, info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* We need all the GPIO drivers to probe FIRST, or we will not be able
|
|
* to obtain references to the struct gpio_chip * for them, and we
|
|
* need this to proceed.
|
|
*/
|
|
for (i = 0; i < info->nbanks; i++) {
|
|
if (!gpio_chips[i]) {
|
|
dev_warn(&pdev->dev, "GPIO chip %d not registered yet\n", i);
|
|
devm_kfree(&pdev->dev, info);
|
|
return -EPROBE_DEFER;
|
|
}
|
|
}
|
|
|
|
at91_pinctrl_desc.name = dev_name(&pdev->dev);
|
|
at91_pinctrl_desc.npins = info->nbanks * MAX_NB_GPIO_PER_BANK;
|
|
at91_pinctrl_desc.pins = pdesc =
|
|
devm_kzalloc(&pdev->dev, sizeof(*pdesc) * at91_pinctrl_desc.npins, GFP_KERNEL);
|
|
|
|
if (!at91_pinctrl_desc.pins)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0 , k = 0; i < info->nbanks; i++) {
|
|
for (j = 0; j < MAX_NB_GPIO_PER_BANK; j++, k++) {
|
|
pdesc->number = k;
|
|
pdesc->name = kasprintf(GFP_KERNEL, "pio%c%d", i + 'A', j);
|
|
pdesc++;
|
|
}
|
|
}
|
|
|
|
platform_set_drvdata(pdev, info);
|
|
info->pctl = pinctrl_register(&at91_pinctrl_desc, &pdev->dev, info);
|
|
|
|
if (!info->pctl) {
|
|
dev_err(&pdev->dev, "could not register AT91 pinctrl driver\n");
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
/* We will handle a range of GPIO pins */
|
|
for (i = 0; i < info->nbanks; i++)
|
|
pinctrl_add_gpio_range(info->pctl, &gpio_chips[i]->range);
|
|
|
|
dev_info(&pdev->dev, "initialized AT91 pinctrl driver\n");
|
|
|
|
return 0;
|
|
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
int __devexit at91_pinctrl_remove(struct platform_device *pdev)
|
|
{
|
|
struct at91_pinctrl *info = platform_get_drvdata(pdev);
|
|
|
|
pinctrl_unregister(info->pctl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int at91_gpio_request(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
/*
|
|
* Map back to global GPIO space and request muxing, the direction
|
|
* parameter does not matter for this controller.
|
|
*/
|
|
int gpio = chip->base + offset;
|
|
int bank = chip->base / chip->ngpio;
|
|
|
|
dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__,
|
|
'A' + bank, offset, gpio);
|
|
|
|
return pinctrl_request_gpio(gpio);
|
|
}
|
|
|
|
static void at91_gpio_free(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
int gpio = chip->base + offset;
|
|
|
|
pinctrl_free_gpio(gpio);
|
|
}
|
|
|
|
static int at91_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
unsigned mask = 1 << offset;
|
|
|
|
writel_relaxed(mask, pio + PIO_ODR);
|
|
return 0;
|
|
}
|
|
|
|
static int at91_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
unsigned mask = 1 << offset;
|
|
u32 pdsr;
|
|
|
|
pdsr = readl_relaxed(pio + PIO_PDSR);
|
|
return (pdsr & mask) != 0;
|
|
}
|
|
|
|
static void at91_gpio_set(struct gpio_chip *chip, unsigned offset,
|
|
int val)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
unsigned mask = 1 << offset;
|
|
|
|
writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
|
|
}
|
|
|
|
static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
|
|
int val)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
unsigned mask = 1 << offset;
|
|
|
|
writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR));
|
|
writel_relaxed(mask, pio + PIO_OER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int at91_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
|
|
int virq;
|
|
|
|
if (offset < chip->ngpio)
|
|
virq = irq_create_mapping(at91_gpio->domain, offset);
|
|
else
|
|
virq = -ENXIO;
|
|
|
|
dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n",
|
|
chip->label, offset + chip->base, virq);
|
|
return virq;
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
static void at91_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|
{
|
|
enum at91_mux mode;
|
|
int i;
|
|
struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
|
|
for (i = 0; i < chip->ngpio; i++) {
|
|
unsigned pin = chip->base + i;
|
|
unsigned mask = pin_to_mask(pin);
|
|
const char *gpio_label;
|
|
u32 pdsr;
|
|
|
|
gpio_label = gpiochip_is_requested(chip, i);
|
|
if (!gpio_label)
|
|
continue;
|
|
mode = at91_gpio->ops->get_periph(pio, mask);
|
|
seq_printf(s, "[%s] GPIO%s%d: ",
|
|
gpio_label, chip->label, i);
|
|
if (mode == AT91_MUX_GPIO) {
|
|
pdsr = readl_relaxed(pio + PIO_PDSR);
|
|
|
|
seq_printf(s, "[gpio] %s\n",
|
|
pdsr & mask ?
|
|
"set" : "clear");
|
|
} else {
|
|
seq_printf(s, "[periph %c]\n",
|
|
mode + 'A' - 1);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
#define at91_gpio_dbg_show NULL
|
|
#endif
|
|
|
|
/* Several AIC controller irqs are dispatched through this GPIO handler.
|
|
* To use any AT91_PIN_* as an externally triggered IRQ, first call
|
|
* at91_set_gpio_input() then maybe enable its glitch filter.
|
|
* Then just request_irq() with the pin ID; it works like any ARM IRQ
|
|
* handler.
|
|
* First implementation always triggers on rising and falling edges
|
|
* whereas the newer PIO3 can be additionally configured to trigger on
|
|
* level, edge with any polarity.
|
|
*
|
|
* Alternatively, certain pins may be used directly as IRQ0..IRQ6 after
|
|
* configuring them with at91_set_a_periph() or at91_set_b_periph().
|
|
* IRQ0..IRQ6 should be configurable, e.g. level vs edge triggering.
|
|
*/
|
|
|
|
static void gpio_irq_mask(struct irq_data *d)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
unsigned mask = 1 << d->hwirq;
|
|
|
|
if (pio)
|
|
writel_relaxed(mask, pio + PIO_IDR);
|
|
}
|
|
|
|
static void gpio_irq_unmask(struct irq_data *d)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
unsigned mask = 1 << d->hwirq;
|
|
|
|
if (pio)
|
|
writel_relaxed(mask, pio + PIO_IER);
|
|
}
|
|
|
|
static int gpio_irq_type(struct irq_data *d, unsigned type)
|
|
{
|
|
switch (type) {
|
|
case IRQ_TYPE_NONE:
|
|
case IRQ_TYPE_EDGE_BOTH:
|
|
return 0;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* Alternate irq type for PIO3 support */
|
|
static int alt_gpio_irq_type(struct irq_data *d, unsigned type)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
unsigned mask = 1 << d->hwirq;
|
|
|
|
switch (type) {
|
|
case IRQ_TYPE_EDGE_RISING:
|
|
writel_relaxed(mask, pio + PIO_ESR);
|
|
writel_relaxed(mask, pio + PIO_REHLSR);
|
|
break;
|
|
case IRQ_TYPE_EDGE_FALLING:
|
|
writel_relaxed(mask, pio + PIO_ESR);
|
|
writel_relaxed(mask, pio + PIO_FELLSR);
|
|
break;
|
|
case IRQ_TYPE_LEVEL_LOW:
|
|
writel_relaxed(mask, pio + PIO_LSR);
|
|
writel_relaxed(mask, pio + PIO_FELLSR);
|
|
break;
|
|
case IRQ_TYPE_LEVEL_HIGH:
|
|
writel_relaxed(mask, pio + PIO_LSR);
|
|
writel_relaxed(mask, pio + PIO_REHLSR);
|
|
break;
|
|
case IRQ_TYPE_EDGE_BOTH:
|
|
/*
|
|
* disable additional interrupt modes:
|
|
* fall back to default behavior
|
|
*/
|
|
writel_relaxed(mask, pio + PIO_AIMDR);
|
|
return 0;
|
|
case IRQ_TYPE_NONE:
|
|
default:
|
|
pr_warn("AT91: No type for irq %d\n", gpio_to_irq(d->irq));
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* enable additional interrupt modes */
|
|
writel_relaxed(mask, pio + PIO_AIMER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int gpio_irq_set_wake(struct irq_data *d, unsigned state)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d);
|
|
unsigned bank = at91_gpio->pioc_idx;
|
|
|
|
if (unlikely(bank >= MAX_GPIO_BANKS))
|
|
return -EINVAL;
|
|
|
|
irq_set_irq_wake(at91_gpio->pioc_virq, state);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
#define gpio_irq_set_wake NULL
|
|
#endif
|
|
|
|
static struct irq_chip gpio_irqchip = {
|
|
.name = "GPIO",
|
|
.irq_disable = gpio_irq_mask,
|
|
.irq_mask = gpio_irq_mask,
|
|
.irq_unmask = gpio_irq_unmask,
|
|
/* .irq_set_type is set dynamically */
|
|
.irq_set_wake = gpio_irq_set_wake,
|
|
};
|
|
|
|
static void gpio_irq_handler(unsigned irq, struct irq_desc *desc)
|
|
{
|
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
|
struct irq_data *idata = irq_desc_get_irq_data(desc);
|
|
struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(idata);
|
|
void __iomem *pio = at91_gpio->regbase;
|
|
unsigned long isr;
|
|
int n;
|
|
|
|
chained_irq_enter(chip, desc);
|
|
for (;;) {
|
|
/* Reading ISR acks pending (edge triggered) GPIO interrupts.
|
|
* When there none are pending, we're finished unless we need
|
|
* to process multiple banks (like ID_PIOCDE on sam9263).
|
|
*/
|
|
isr = readl_relaxed(pio + PIO_ISR) & readl_relaxed(pio + PIO_IMR);
|
|
if (!isr) {
|
|
if (!at91_gpio->next)
|
|
break;
|
|
at91_gpio = at91_gpio->next;
|
|
pio = at91_gpio->regbase;
|
|
continue;
|
|
}
|
|
|
|
n = find_first_bit(&isr, BITS_PER_LONG);
|
|
while (n < BITS_PER_LONG) {
|
|
generic_handle_irq(irq_find_mapping(at91_gpio->domain, n));
|
|
n = find_next_bit(&isr, BITS_PER_LONG, n + 1);
|
|
}
|
|
}
|
|
chained_irq_exit(chip, desc);
|
|
/* now it may re-trigger */
|
|
}
|
|
|
|
/*
|
|
* This lock class tells lockdep that GPIO irqs are in a different
|
|
* category than their parents, so it won't report false recursion.
|
|
*/
|
|
static struct lock_class_key gpio_lock_class;
|
|
|
|
static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq,
|
|
irq_hw_number_t hw)
|
|
{
|
|
struct at91_gpio_chip *at91_gpio = h->host_data;
|
|
|
|
irq_set_lockdep_class(virq, &gpio_lock_class);
|
|
|
|
/*
|
|
* Can use the "simple" and not "edge" handler since it's
|
|
* shorter, and the AIC handles interrupts sanely.
|
|
*/
|
|
irq_set_chip_and_handler(virq, &gpio_irqchip,
|
|
handle_simple_irq);
|
|
set_irq_flags(virq, IRQF_VALID);
|
|
irq_set_chip_data(virq, at91_gpio);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct irq_domain_ops at91_gpio_ops = {
|
|
.map = at91_gpio_irq_map,
|
|
.xlate = irq_domain_xlate_twocell,
|
|
};
|
|
|
|
static int at91_gpio_of_irq_setup(struct device_node *node,
|
|
struct at91_gpio_chip *at91_gpio)
|
|
{
|
|
struct at91_gpio_chip *prev = NULL;
|
|
struct irq_data *d = irq_get_irq_data(at91_gpio->pioc_virq);
|
|
|
|
at91_gpio->pioc_hwirq = irqd_to_hwirq(d);
|
|
|
|
/* Setup proper .irq_set_type function */
|
|
gpio_irqchip.irq_set_type = at91_gpio->ops->irq_type;
|
|
|
|
/* Disable irqs of this PIO controller */
|
|
writel_relaxed(~0, at91_gpio->regbase + PIO_IDR);
|
|
|
|
/* Setup irq domain */
|
|
at91_gpio->domain = irq_domain_add_linear(node, at91_gpio->chip.ngpio,
|
|
&at91_gpio_ops, at91_gpio);
|
|
if (!at91_gpio->domain)
|
|
panic("at91_gpio.%d: couldn't allocate irq domain (DT).\n",
|
|
at91_gpio->pioc_idx);
|
|
|
|
/* Setup chained handler */
|
|
if (at91_gpio->pioc_idx)
|
|
prev = gpio_chips[at91_gpio->pioc_idx - 1];
|
|
|
|
/* The toplevel handler handles one bank of GPIOs, except
|
|
* on some SoC it can handles up to three...
|
|
* We only set up the handler for the first of the list.
|
|
*/
|
|
if (prev && prev->next == at91_gpio)
|
|
return 0;
|
|
|
|
irq_set_chip_data(at91_gpio->pioc_virq, at91_gpio);
|
|
irq_set_chained_handler(at91_gpio->pioc_virq, gpio_irq_handler);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This structure is replicated for each GPIO block allocated at probe time */
|
|
static struct gpio_chip at91_gpio_template = {
|
|
.request = at91_gpio_request,
|
|
.free = at91_gpio_free,
|
|
.direction_input = at91_gpio_direction_input,
|
|
.get = at91_gpio_get,
|
|
.direction_output = at91_gpio_direction_output,
|
|
.set = at91_gpio_set,
|
|
.to_irq = at91_gpio_to_irq,
|
|
.dbg_show = at91_gpio_dbg_show,
|
|
.can_sleep = 0,
|
|
.ngpio = MAX_NB_GPIO_PER_BANK,
|
|
};
|
|
|
|
static void __devinit at91_gpio_probe_fixup(void)
|
|
{
|
|
unsigned i;
|
|
struct at91_gpio_chip *at91_gpio, *last = NULL;
|
|
|
|
for (i = 0; i < gpio_banks; i++) {
|
|
at91_gpio = gpio_chips[i];
|
|
|
|
/*
|
|
* GPIO controller are grouped on some SoC:
|
|
* PIOC, PIOD and PIOE can share the same IRQ line
|
|
*/
|
|
if (last && last->pioc_virq == at91_gpio->pioc_virq)
|
|
last->next = at91_gpio;
|
|
last = at91_gpio;
|
|
}
|
|
}
|
|
|
|
static struct of_device_id at91_gpio_of_match[] __devinitdata = {
|
|
{ .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, },
|
|
{ .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops },
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
static int __devinit at91_gpio_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct resource *res;
|
|
struct at91_gpio_chip *at91_chip = NULL;
|
|
struct gpio_chip *chip;
|
|
struct pinctrl_gpio_range *range;
|
|
int ret = 0;
|
|
int irq;
|
|
int alias_idx = of_alias_get_id(np, "gpio");
|
|
uint32_t ngpio;
|
|
|
|
BUG_ON(alias_idx >= ARRAY_SIZE(gpio_chips));
|
|
if (gpio_chips[alias_idx]) {
|
|
ret = -EBUSY;
|
|
goto err;
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
ret = -ENOENT;
|
|
goto err;
|
|
}
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0) {
|
|
ret = irq;
|
|
goto err;
|
|
}
|
|
|
|
at91_chip = devm_kzalloc(&pdev->dev, sizeof(*at91_chip), GFP_KERNEL);
|
|
if (!at91_chip) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
at91_chip->regbase = devm_request_and_ioremap(&pdev->dev, res);
|
|
if (!at91_chip->regbase) {
|
|
dev_err(&pdev->dev, "failed to map registers, ignoring.\n");
|
|
ret = -EBUSY;
|
|
goto err;
|
|
}
|
|
|
|
at91_chip->ops =
|
|
of_match_device(at91_gpio_of_match, &pdev->dev)->data;
|
|
at91_chip->pioc_virq = irq;
|
|
at91_chip->pioc_idx = alias_idx;
|
|
|
|
at91_chip->clock = clk_get(&pdev->dev, NULL);
|
|
if (IS_ERR(at91_chip->clock)) {
|
|
dev_err(&pdev->dev, "failed to get clock, ignoring.\n");
|
|
goto err;
|
|
}
|
|
|
|
if (clk_prepare(at91_chip->clock))
|
|
goto clk_prep_err;
|
|
|
|
/* enable PIO controller's clock */
|
|
if (clk_enable(at91_chip->clock)) {
|
|
dev_err(&pdev->dev, "failed to enable clock, ignoring.\n");
|
|
goto clk_err;
|
|
}
|
|
|
|
at91_chip->chip = at91_gpio_template;
|
|
|
|
chip = &at91_chip->chip;
|
|
chip->of_node = np;
|
|
chip->label = dev_name(&pdev->dev);
|
|
chip->dev = &pdev->dev;
|
|
chip->owner = THIS_MODULE;
|
|
chip->base = alias_idx * MAX_NB_GPIO_PER_BANK;
|
|
|
|
if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) {
|
|
if (ngpio >= MAX_NB_GPIO_PER_BANK)
|
|
pr_err("at91_gpio.%d, gpio-nb >= %d failback to %d\n",
|
|
alias_idx, MAX_NB_GPIO_PER_BANK, MAX_NB_GPIO_PER_BANK);
|
|
else
|
|
chip->ngpio = ngpio;
|
|
}
|
|
|
|
range = &at91_chip->range;
|
|
range->name = chip->label;
|
|
range->id = alias_idx;
|
|
range->pin_base = range->base = range->id * MAX_NB_GPIO_PER_BANK;
|
|
|
|
range->npins = chip->ngpio;
|
|
range->gc = chip;
|
|
|
|
ret = gpiochip_add(chip);
|
|
if (ret)
|
|
goto clk_err;
|
|
|
|
gpio_chips[alias_idx] = at91_chip;
|
|
gpio_banks = max(gpio_banks, alias_idx + 1);
|
|
|
|
at91_gpio_probe_fixup();
|
|
|
|
at91_gpio_of_irq_setup(np, at91_chip);
|
|
|
|
dev_info(&pdev->dev, "at address %p\n", at91_chip->regbase);
|
|
|
|
return 0;
|
|
|
|
clk_err:
|
|
clk_unprepare(at91_chip->clock);
|
|
clk_prep_err:
|
|
clk_put(at91_chip->clock);
|
|
err:
|
|
dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct platform_driver at91_gpio_driver = {
|
|
.driver = {
|
|
.name = "gpio-at91",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(at91_gpio_of_match),
|
|
},
|
|
.probe = at91_gpio_probe,
|
|
};
|
|
|
|
static struct platform_driver at91_pinctrl_driver = {
|
|
.driver = {
|
|
.name = "pinctrl-at91",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(at91_pinctrl_of_match),
|
|
},
|
|
.probe = at91_pinctrl_probe,
|
|
.remove = __devexit_p(at91_pinctrl_remove),
|
|
};
|
|
|
|
static int __init at91_pinctrl_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = platform_driver_register(&at91_gpio_driver);
|
|
if (ret)
|
|
return ret;
|
|
return platform_driver_register(&at91_pinctrl_driver);
|
|
}
|
|
arch_initcall(at91_pinctrl_init);
|
|
|
|
static void __exit at91_pinctrl_exit(void)
|
|
{
|
|
platform_driver_unregister(&at91_pinctrl_driver);
|
|
}
|
|
|
|
module_exit(at91_pinctrl_exit);
|
|
MODULE_AUTHOR("Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>");
|
|
MODULE_DESCRIPTION("Atmel AT91 pinctrl driver");
|
|
MODULE_LICENSE("GPL v2");
|