pinctrl: samsung: Use one GPIO chip per pin bank
This patch modifies the pinctrl-samsung driver to register one GPIO chip per pin bank, instead of a single chip for all pin banks of the controller. It simplifies GPIO accesses a lot (constant time instead of looping through the list of banks to find the right one) and should have a good effect on performance of any bit-banging driver. In addition it allows to reference GPIO pins by a phandle to the bank node and a local pin offset inside of the bank (similar to previous gpiolib driver), which is more clear and readable than using indices relative to the whole pin controller. Example: device { /* ... */ gpios = <&gpk0 4 0>; /* ... */ }; Signed-off-by: Tomasz Figa <t.figa@samsung.com> Reviewed-by: Kyungmin Park <kyungmin.park@samsung.com> Acked-by: Thomas Abraham <thomas.abraham@linaro.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
595be7268a
commit
d3a7b9e3a1
@ -48,6 +48,11 @@ struct pin_config {
|
|||||||
|
|
||||||
static unsigned int pin_base = 0;
|
static unsigned int pin_base = 0;
|
||||||
|
|
||||||
|
static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc)
|
||||||
|
{
|
||||||
|
return container_of(gc, struct samsung_pin_bank, gpio_chip);
|
||||||
|
}
|
||||||
|
|
||||||
/* check if the selector is a valid pin group selector */
|
/* check if the selector is a valid pin group selector */
|
||||||
static int samsung_get_group_count(struct pinctrl_dev *pctldev)
|
static int samsung_get_group_count(struct pinctrl_dev *pctldev)
|
||||||
{
|
{
|
||||||
@ -333,9 +338,12 @@ static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
|
|||||||
void __iomem *reg;
|
void __iomem *reg;
|
||||||
u32 data, pin_offset, mask, shift;
|
u32 data, pin_offset, mask, shift;
|
||||||
|
|
||||||
|
bank = gc_to_pin_bank(range->gc);
|
||||||
drvdata = pinctrl_dev_get_drvdata(pctldev);
|
drvdata = pinctrl_dev_get_drvdata(pctldev);
|
||||||
|
|
||||||
pin_to_reg_bank(drvdata, offset, ®, &pin_offset, &bank);
|
pin_offset = offset - bank->pin_base;
|
||||||
|
reg = drvdata->virt_base + bank->pctl_offset;
|
||||||
|
|
||||||
mask = (1 << bank->func_width) - 1;
|
mask = (1 << bank->func_width) - 1;
|
||||||
shift = pin_offset * bank->func_width;
|
shift = pin_offset * bank->func_width;
|
||||||
|
|
||||||
@ -469,17 +477,16 @@ static struct pinconf_ops samsung_pinconf_ops = {
|
|||||||
/* gpiolib gpio_set callback function */
|
/* gpiolib gpio_set callback function */
|
||||||
static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
|
static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
|
||||||
{
|
{
|
||||||
|
struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
|
||||||
void __iomem *reg;
|
void __iomem *reg;
|
||||||
u32 pin_offset, data;
|
u32 data;
|
||||||
struct samsung_pinctrl_drv_data *drvdata;
|
|
||||||
|
|
||||||
drvdata = dev_get_drvdata(gc->dev);
|
reg = bank->drvdata->virt_base + bank->pctl_offset;
|
||||||
|
|
||||||
pin_to_reg_bank(drvdata, offset, ®, &pin_offset, NULL);
|
|
||||||
data = readl(reg + DAT_REG);
|
data = readl(reg + DAT_REG);
|
||||||
data &= ~(1 << pin_offset);
|
data &= ~(1 << offset);
|
||||||
if (value)
|
if (value)
|
||||||
data |= 1 << pin_offset;
|
data |= 1 << offset;
|
||||||
writel(data, reg + DAT_REG);
|
writel(data, reg + DAT_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,14 +494,13 @@ static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
|
|||||||
static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
|
static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
|
||||||
{
|
{
|
||||||
void __iomem *reg;
|
void __iomem *reg;
|
||||||
u32 pin_offset, data;
|
u32 data;
|
||||||
struct samsung_pinctrl_drv_data *drvdata;
|
struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
|
||||||
|
|
||||||
drvdata = dev_get_drvdata(gc->dev);
|
reg = bank->drvdata->virt_base + bank->pctl_offset;
|
||||||
|
|
||||||
pin_to_reg_bank(drvdata, offset, ®, &pin_offset, NULL);
|
|
||||||
data = readl(reg + DAT_REG);
|
data = readl(reg + DAT_REG);
|
||||||
data >>= pin_offset;
|
data >>= offset;
|
||||||
data &= 1;
|
data &= 1;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -724,12 +730,16 @@ static int __init samsung_pinctrl_register(struct platform_device *pdev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
drvdata->grange.name = "samsung-pctrl-gpio-range";
|
for (bank = 0; bank < drvdata->ctrl->nr_banks; ++bank) {
|
||||||
drvdata->grange.id = 0;
|
pin_bank = &drvdata->ctrl->pin_banks[bank];
|
||||||
drvdata->grange.base = drvdata->ctrl->base;
|
pin_bank->grange.name = pin_bank->name;
|
||||||
drvdata->grange.npins = drvdata->ctrl->nr_pins;
|
pin_bank->grange.id = bank;
|
||||||
drvdata->grange.gc = drvdata->gc;
|
pin_bank->grange.pin_base = pin_bank->pin_base;
|
||||||
pinctrl_add_gpio_range(drvdata->pctl_dev, &drvdata->grange);
|
pin_bank->grange.base = pin_bank->gpio_chip.base;
|
||||||
|
pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
|
||||||
|
pin_bank->grange.gc = &pin_bank->gpio_chip;
|
||||||
|
pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange);
|
||||||
|
}
|
||||||
|
|
||||||
ret = samsung_pinctrl_parse_dt(pdev, drvdata);
|
ret = samsung_pinctrl_parse_dt(pdev, drvdata);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@ -740,49 +750,68 @@ static int __init samsung_pinctrl_register(struct platform_device *pdev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct gpio_chip samsung_gpiolib_chip = {
|
||||||
|
.set = samsung_gpio_set,
|
||||||
|
.get = samsung_gpio_get,
|
||||||
|
.direction_input = samsung_gpio_direction_input,
|
||||||
|
.direction_output = samsung_gpio_direction_output,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
/* register the gpiolib interface with the gpiolib subsystem */
|
/* register the gpiolib interface with the gpiolib subsystem */
|
||||||
static int __init samsung_gpiolib_register(struct platform_device *pdev,
|
static int __init samsung_gpiolib_register(struct platform_device *pdev,
|
||||||
struct samsung_pinctrl_drv_data *drvdata)
|
struct samsung_pinctrl_drv_data *drvdata)
|
||||||
{
|
{
|
||||||
|
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
|
||||||
|
struct samsung_pin_bank *bank = ctrl->pin_banks;
|
||||||
struct gpio_chip *gc;
|
struct gpio_chip *gc;
|
||||||
int ret;
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
|
for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
|
||||||
if (!gc) {
|
bank->gpio_chip = samsung_gpiolib_chip;
|
||||||
dev_err(&pdev->dev, "mem alloc for gpio_chip failed\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
drvdata->gc = gc;
|
gc = &bank->gpio_chip;
|
||||||
gc->base = drvdata->ctrl->base;
|
gc->base = ctrl->base + bank->pin_base;
|
||||||
gc->ngpio = drvdata->ctrl->nr_pins;
|
gc->ngpio = bank->nr_pins;
|
||||||
gc->dev = &pdev->dev;
|
gc->dev = &pdev->dev;
|
||||||
gc->set = samsung_gpio_set;
|
gc->of_node = bank->of_node;
|
||||||
gc->get = samsung_gpio_get;
|
gc->label = bank->name;
|
||||||
gc->direction_input = samsung_gpio_direction_input;
|
|
||||||
gc->direction_output = samsung_gpio_direction_output;
|
ret = gpiochip_add(gc);
|
||||||
gc->label = drvdata->ctrl->label;
|
if (ret) {
|
||||||
gc->owner = THIS_MODULE;
|
dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n",
|
||||||
ret = gpiochip_add(gc);
|
gc->label, ret);
|
||||||
if (ret) {
|
goto fail;
|
||||||
dev_err(&pdev->dev, "failed to register gpio_chip %s, error "
|
}
|
||||||
"code: %d\n", gc->label, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
for (--i, --bank; i >= 0; --i, --bank)
|
||||||
|
if (gpiochip_remove(&bank->gpio_chip))
|
||||||
|
dev_err(&pdev->dev, "gpio chip %s remove failed\n",
|
||||||
|
bank->gpio_chip.label);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unregister the gpiolib interface with the gpiolib subsystem */
|
/* unregister the gpiolib interface with the gpiolib subsystem */
|
||||||
static int __init samsung_gpiolib_unregister(struct platform_device *pdev,
|
static int __init samsung_gpiolib_unregister(struct platform_device *pdev,
|
||||||
struct samsung_pinctrl_drv_data *drvdata)
|
struct samsung_pinctrl_drv_data *drvdata)
|
||||||
{
|
{
|
||||||
int ret = gpiochip_remove(drvdata->gc);
|
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
|
||||||
if (ret) {
|
struct samsung_pin_bank *bank = ctrl->pin_banks;
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank)
|
||||||
|
ret = gpiochip_remove(&bank->gpio_chip);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
dev_err(&pdev->dev, "gpio chip remove failed\n");
|
dev_err(&pdev->dev, "gpio chip remove failed\n");
|
||||||
return ret;
|
|
||||||
}
|
return ret;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id samsung_pinctrl_dt_match[];
|
static const struct of_device_id samsung_pinctrl_dt_match[];
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/pinctrl/machine.h>
|
#include <linux/pinctrl/machine.h>
|
||||||
|
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
|
||||||
/* register offsets within a pin bank */
|
/* register offsets within a pin bank */
|
||||||
#define DAT_REG 0x4
|
#define DAT_REG 0x4
|
||||||
#define PUD_REG 0x8
|
#define PUD_REG 0x8
|
||||||
@ -113,6 +115,8 @@ struct samsung_pinctrl_drv_data;
|
|||||||
* @of_node: OF node of the bank.
|
* @of_node: OF node of the bank.
|
||||||
* @drvdata: link to controller driver data
|
* @drvdata: link to controller driver data
|
||||||
* @irq_domain: IRQ domain of the bank.
|
* @irq_domain: IRQ domain of the bank.
|
||||||
|
* @gpio_chip: GPIO chip of the bank.
|
||||||
|
* @grange: linux gpio pin range supported by this bank.
|
||||||
*/
|
*/
|
||||||
struct samsung_pin_bank {
|
struct samsung_pin_bank {
|
||||||
u32 pctl_offset;
|
u32 pctl_offset;
|
||||||
@ -129,6 +133,8 @@ struct samsung_pin_bank {
|
|||||||
struct device_node *of_node;
|
struct device_node *of_node;
|
||||||
struct samsung_pinctrl_drv_data *drvdata;
|
struct samsung_pinctrl_drv_data *drvdata;
|
||||||
struct irq_domain *irq_domain;
|
struct irq_domain *irq_domain;
|
||||||
|
struct gpio_chip gpio_chip;
|
||||||
|
struct pinctrl_gpio_range grange;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,8 +192,6 @@ struct samsung_pin_ctrl {
|
|||||||
* @nr_groups: number of such pin groups.
|
* @nr_groups: number of such pin groups.
|
||||||
* @pmx_functions: list of pin functions available to the driver.
|
* @pmx_functions: list of pin functions available to the driver.
|
||||||
* @nr_function: number of such pin functions.
|
* @nr_function: number of such pin functions.
|
||||||
* @gc: gpio_chip instance registered with gpiolib.
|
|
||||||
* @grange: linux gpio pin range supported by this controller.
|
|
||||||
*/
|
*/
|
||||||
struct samsung_pinctrl_drv_data {
|
struct samsung_pinctrl_drv_data {
|
||||||
void __iomem *virt_base;
|
void __iomem *virt_base;
|
||||||
@ -204,9 +208,6 @@ struct samsung_pinctrl_drv_data {
|
|||||||
unsigned int nr_functions;
|
unsigned int nr_functions;
|
||||||
|
|
||||||
struct irq_domain *wkup_irqd;
|
struct irq_domain *wkup_irqd;
|
||||||
|
|
||||||
struct gpio_chip *gc;
|
|
||||||
struct pinctrl_gpio_range grange;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user