Commit d3a7b9e3 authored by Tomasz Figa's avatar Tomasz Figa Committed by Linus Walleij

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: default avatarTomasz Figa <t.figa@samsung.com>
Reviewed-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Acked-by: default avatarThomas Abraham <thomas.abraham@linaro.org>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 595be726
...@@ -48,6 +48,11 @@ struct pin_config { ...@@ -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, ...@@ -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, &reg, &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 = { ...@@ -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, &reg, &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) ...@@ -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, &reg, &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, ...@@ -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, ...@@ -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; gc = &bank->gpio_chip;
} gc->base = ctrl->base + bank->pin_base;
gc->ngpio = bank->nr_pins;
drvdata->gc = gc; gc->dev = &pdev->dev;
gc->base = drvdata->ctrl->base; gc->of_node = bank->of_node;
gc->ngpio = drvdata->ctrl->nr_pins; gc->label = bank->name;
gc->dev = &pdev->dev;
gc->set = samsung_gpio_set; ret = gpiochip_add(gc);
gc->get = samsung_gpio_get; if (ret) {
gc->direction_input = samsung_gpio_direction_input; dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n",
gc->direction_output = samsung_gpio_direction_output; gc->label, ret);
gc->label = drvdata->ctrl->label; goto fail;
gc->owner = THIS_MODULE; }
ret = gpiochip_add(gc);
if (ret) {
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 @@ ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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;
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment