/*
* Copyright (C) Maxime Coquelin 2015
* Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
* License terms: GNU General Public License (GPL), version 2
*
* Heavily based on Mediatek's pinctrl driver
*/
#include <linux/clk.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include "../core.h"
#include "../pinconf.h"
#include "../pinctrl-utils.h"
#include "pinctrl-stm32.h"
#define STM32_GPIO_MODER 0x00
#define STM32_GPIO_TYPER 0x04
#define STM32_GPIO_SPEEDR 0x08
#define STM32_GPIO_PUPDR 0x0c
#define STM32_GPIO_IDR 0x10
#define STM32_GPIO_ODR 0x14
#define STM32_GPIO_BSRR 0x18
#define STM32_GPIO_LCKR 0x1c
#define STM32_GPIO_AFRL 0x20
#define STM32_GPIO_AFRH 0x24
#define STM32_GPIO_PINS_PER_BANK 16
#define STM32_GPIO_IRQ_LINE 16
#define gpio_range_to_bank(chip) \
container_of(chip, struct stm32_gpio_bank, range)
static const char * const stm32_gpio_functions[] = {
"gpio", "af0", "af1",
"af2", "af3", "af4",
"af5", "af6", "af7",
"af8", "af9", "af10",
"af11", "af12", "af13",
"af14", "af15", "analog",
};
struct stm32_pinctrl_group {
const char *name;
unsigned long config;
unsigned pin;
};
struct stm32_gpio_bank {
void __iomem *base;
struct clk *clk;
spinlock_t lock;
struct gpio_chip gpio_chip;
struct pinctrl_gpio_range range;
struct fwnode_handle *fwnode;
struct irq_domain *domain;
};
struct stm32_pinctrl {
struct device *dev;
struct pinctrl_dev *pctl_dev;
struct pinctrl_desc pctl_desc;
struct stm32_pinctrl_group *groups;
unsigned ngroups;
const char **grp_names;
struct stm32_gpio_bank *banks;
unsigned nbanks;
const struct stm32_pinctrl_match_data *match_data;
struct irq_domain *domain;
struct regmap *regmap;
struct regmap_field *irqmux[STM32_GPIO_PINS_PER_BANK];
};
static inline int stm32_gpio_pin(int gpio)
{
return gpio % STM32_GPIO_PINS_PER_BANK;
}
static inline u32 stm32_gpio_get_mode(u32 function)
{
switch (function) {
case STM32_PIN_GPIO:
return 0;
case STM32_PIN_AF(0) ... STM32_PIN_AF(15):
return 2;
case STM32_PIN_ANALOG:
return 3;
}
return 0;
}
static inline u32 stm32_gpio_get_alt(u32 function)
{
switch (function) {
case STM32_PIN_GPIO:
return 0;
case STM32_PIN_AF(0) ... STM32_PIN_AF(15):
return function - 1;
case STM32_PIN_ANALOG:
return 0;
}
return 0;
}
/* GPIO functions */
static inline void __stm32_gpio_set(struct stm32_gpio_bank *bank,
unsigned offset, int value)
{
if (!value)
offset += STM32_GPIO_PINS_PER_BANK;
clk_enable(bank->clk);
writel_relaxed(BIT(offset), bank->base + STM32_GPIO_BSRR);
clk_disable(bank->clk);
}
static int stm32_gpio_request(struct gpio_chip *chip, unsigned offset)
{
return pinctrl_request_gpio(chip->base + offset);
}
static void stm32_gpio_free(struct gpio_chip *chip, unsigned offset)
{
pinctrl_free_gpio(chip->base + offset);
}
static int stm32_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct stm32_gpio_bank *bank = gpiochip_get_data(chip);
int ret;
clk_enable(bank->clk);
ret = !!(readl_relaxed(bank->base + STM32_GPIO_IDR) & BIT(offset));
clk_disable(bank->clk);
return ret;
}
static void stm32_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct stm32_gpio_bank *bank = gpiochip_get_data(chip);
__stm32_gpio_set(bank, offset, value);
}
static int stm32_gpio_direction_input(struct gpio_chip *chip, unsigned offset