mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: "Raphaël Poggi" <poggi.raph@gmail.com>
To: barebox@lists.infradead.org
Cc: "Raphaël Poggi" <poggi.raph@gmail.com>
Subject: [PATCH v3 2/3] pinctrl: at91: add pinctrl driver
Date: Tue,  2 Sep 2014 13:07:59 +0200	[thread overview]
Message-ID: <1409656080-19280-3-git-send-email-poggi.raph@gmail.com> (raw)
In-Reply-To: <1409656080-19280-1-git-send-email-poggi.raph@gmail.com>

This driver is based on mach-at91/gpio.c and linux pinctrl driver.
The driver contains the gpio and pinctrl parts (like in linux) because the two parts
share some structures and logics.

Signed-off-by: Raphaël Poggi <poggi.raph@gmail.com>
---
 drivers/pinctrl/Kconfig        |    6 +
 drivers/pinctrl/Makefile       |    1 +
 drivers/pinctrl/pinctrl-at91.c |  527 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 534 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-at91.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index dffaa4e..ce55c7b 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -7,6 +7,12 @@ config PINCTRL
 	  from the devicetree. Legacy drivers here may not need this core
 	  support but instead provide their own SoC specific APIs
 
+config PINCTRL_AT91
+	select PINCTRL
+	bool
+	help
+	    The pinmux controller found on AT91 SoCs.
+
 config PINCTRL_IMX_IOMUX_V1
 	select PINCTRL if OFDEVICE
 	bool
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 566ba11..3ea8649 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_PINCTRL)	+= pinctrl.o
+obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
 obj-$(CONFIG_PINCTRL_IMX_IOMUX_V1) += imx-iomux-v1.o
 obj-$(CONFIG_PINCTRL_IMX_IOMUX_V2) += imx-iomux-v2.o
 obj-$(CONFIG_PINCTRL_IMX_IOMUX_V3) += imx-iomux-v3.o
diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c
new file mode 100644
index 0000000..433862a
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-at91.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2005 HP Labs
+ * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ * Copyright (C) 2014 Raphaël Poggi
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <command.h>
+#include <complete.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <errno.h>
+#include <io.h>
+#include <gpio.h>
+#include <init.h>
+#include <driver.h>
+#include <getopt.h>
+
+#include <mach/at91_pio.h>
+#include <mach/gpio.h>
+
+#include <pinctrl.h>
+
+struct at91_pinctrl {
+	struct pinctrl_device pctl;
+	struct at91_pinctrl_mux_ops	*ops;
+};
+
+struct at91_gpio_chip {
+	struct gpio_chip	chip;
+	void __iomem		*regbase;	/* PIO bank virtual address */
+	struct at91_pinctrl_mux_ops *ops;	/* ops */
+};
+
+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,
+};
+
+#define MAX_GPIO_BANKS		5
+#define to_at91_pinctrl(c) container_of(c, struct at91_pinctrl, pctl);
+#define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip)
+
+#define PULL_UP         (1 << 0)
+#define MULTI_DRIVE     (1 << 1)
+#define DEGLITCH        (1 << 2)
+#define PULL_DOWN       (1 << 3)
+#define DIS_SCHMIT      (1 << 4)
+#define DEBOUNCE        (1 << 16)
+#define DEBOUNCE_VAL_SHIFT      17
+#define DEBOUNCE_VAL    (0x3fff << DEBOUNCE_VAL_SHIFT)
+
+static int gpio_banks;
+
+static struct at91_gpio_chip gpio_chip[MAX_GPIO_BANKS];
+
+static inline void __iomem *pin_to_controller(struct at91_pinctrl *info, unsigned pin)
+{
+	pin /= MAX_NB_GPIO_PER_BANK;
+	if (likely(pin < gpio_banks))
+		return gpio_chip[pin].regbase;
+
+	return NULL;
+}
+
+/**
+ * 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
+ * @set_deglitch: enable/disable deglitch
+ * @set_debounce: enable/disable debounce
+ * @set_pulldown: enable/disable pulldown
+ * @disable_schmitt_trig: disable schmitt trigger
+ */
+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);
+	bool (*get_deglitch)(void __iomem *pio, unsigned pin);
+	void (*set_deglitch)(void __iomem *pio, unsigned mask, bool in_on);
+	bool (*get_debounce)(void __iomem *pio, unsigned pin, u32 *div);
+	void (*set_debounce)(void __iomem *pio, unsigned mask, bool in_on, u32 div);
+	bool (*get_pulldown)(void __iomem *pio, unsigned pin);
+	void (*set_pulldown)(void __iomem *pio, unsigned mask, bool in_on);
+	bool (*get_schmitt_trig)(void __iomem *pio, unsigned pin);
+	void (*disable_schmitt_trig)(void __iomem *pio, unsigned mask);
+};
+
+static enum at91_mux at91_mux_pio3_get_periph(void __iomem *pio, unsigned mask)
+{
+	unsigned select;
+
+	if (__raw_readl(pio + PIO_PSR) & mask)
+		return AT91_MUX_GPIO;
+
+	select = !!(__raw_readl(pio + PIO_ABCDSR1) & mask);
+	select |= (!!(__raw_readl(pio + PIO_ABCDSR2) & mask) << 1);
+
+	return select + 1;
+}
+
+static enum at91_mux at91_mux_get_periph(void __iomem *pio, unsigned mask)
+{
+	unsigned select;
+
+	if (__raw_readl(pio + PIO_PSR) & mask)
+		return AT91_MUX_GPIO;
+
+	select = __raw_readl(pio + PIO_ABSR) & mask;
+
+	return select + 1;
+}
+
+static bool at91_mux_get_deglitch(void __iomem *pio, unsigned pin)
+{
+	return (__raw_readl(pio + PIO_IFSR) >> pin) & 0x1;
+}
+
+static bool at91_mux_pio3_get_debounce(void __iomem *pio, unsigned pin, u32 *div)
+{
+	*div = __raw_readl(pio + PIO_SCDR);
+
+	return (__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1;
+}
+
+static bool at91_mux_pio3_get_pulldown(void __iomem *pio, unsigned pin)
+{
+	return (__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1;
+}
+
+static bool at91_mux_pio3_get_schmitt_trig(void __iomem *pio, unsigned pin)
+{
+	return (__raw_readl(pio + PIO_SCHMITT) >> pin) & 0x1;
+}
+
+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,
+	.get_deglitch	= at91_mux_get_deglitch,
+	.set_deglitch	= at91_mux_set_deglitch,
+};
+
+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,
+	.get_deglitch	= at91_mux_get_deglitch,
+	.set_deglitch	= at91_mux_pio3_set_deglitch,
+	.get_debounce	= at91_mux_pio3_get_debounce,
+	.set_debounce	= at91_mux_pio3_set_debounce,
+	.get_pulldown	= at91_mux_pio3_get_pulldown,
+	.set_pulldown	= at91_mux_pio3_set_pulldown,
+	.get_schmitt_trig = at91_mux_pio3_get_schmitt_trig,
+	.disable_schmitt_trig = at91_mux_pio3_disable_schmitt_trig,
+};
+
+static int at91_mux_pin(struct at91_pinctrl *info, unsigned pin, enum at91_mux mux, int use_pullup)
+{
+	void __iomem	*pio = pin_to_controller(info, pin);
+	unsigned mask = pin_to_mask(pin);
+
+	if (!info)
+		return -EINVAL;
+
+	if (!pio)
+		return -EINVAL;
+
+	at91_mux_disable_interrupt(pio, mask);
+
+	switch (mux) {
+	case AT91_MUX_GPIO:
+		at91_mux_gpio_enable(pio, mask);
+		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 (mux)
+		at91_mux_gpio_disable(pio, mask);
+
+	if (use_pullup >= 0)
+		at91_mux_set_pullup(pio, mask, use_pullup);
+
+	return 0;
+}
+
+static struct of_device_id at91_pinctrl_dt_ids[] = {
+	{
+		.compatible = "atmel,at91rm9200-pinctrl",
+		.data = (unsigned long)&at91rm9200_ops,
+	}, {
+		.compatible = "atmel,at91sam9x5-pinctrl",
+		.data = (unsigned long)&at91sam9x5_ops,
+	}, {
+		/* sentinel */
+	}
+};
+
+static struct at91_pinctrl_mux_ops *at91_pinctrl_get_driver_data(struct device_d *dev)
+{
+	struct at91_pinctrl_mux_ops *ops_data = NULL;
+	int rc;
+
+	if (dev->device_node) {
+		const struct of_device_id *match;
+		match = of_match_node(at91_pinctrl_dt_ids, dev->device_node);
+		if (!match)
+			ops_data = NULL;
+		else
+			ops_data = (struct at91_pinctrl_mux_ops *)match->data;
+	} else {
+		rc = dev_get_drvdata(dev, (unsigned long *)&ops_data);
+		if (rc)
+			ops_data = NULL;
+	}
+
+	return ops_data;
+}
+
+static int at91_pinctrl_set_conf(struct at91_pinctrl *info, unsigned int pin_num, unsigned int mux, unsigned int conf)
+{
+	unsigned int mask;
+	void __iomem *pio;
+
+	pio = pin_to_controller(info, pin_num);
+	mask = pin_to_mask(pin_num);
+
+	if (conf & PULL_UP && conf & PULL_DOWN)
+		return -EINVAL;
+
+	at91_mux_set_pullup(pio, mask, conf & PULL_UP);
+	at91_mux_set_multidrive(pio, mask, conf & MULTI_DRIVE);
+	if (info->ops->set_deglitch)
+		info->ops->set_deglitch(pio, mask, conf & DEGLITCH);
+	if (info->ops->set_debounce)
+		info->ops->set_debounce(pio, mask, conf & DEBOUNCE,
+			(conf & DEBOUNCE_VAL) >> DEBOUNCE_VAL_SHIFT);
+	if (info->ops->set_pulldown)
+		info->ops->set_pulldown(pio, mask, conf & PULL_DOWN);
+	if (info->ops->disable_schmitt_trig && conf & DIS_SCHMIT)
+		info->ops->disable_schmitt_trig(pio, mask);
+
+	return 0;
+}
+
+static int at91_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np)
+{
+	struct at91_pinctrl *info;
+	const __be32 *list;
+	int i, size;
+	int ret = 0;
+	int bank_num, pin_num, mux, conf;
+
+	info = to_at91_pinctrl(pdev);
+
+	list = of_get_property(np, "atmel,pins", &size);
+	size /= sizeof(*list);
+
+	if (!size || size % 4) {
+		dev_err(pdev->dev, "wrong pins number or pins and configs should be by 4\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < size; i += 4) {
+		bank_num = be32_to_cpu(*list++);
+		pin_num = be32_to_cpu(*list++);
+		mux = be32_to_cpu(*list++);
+		conf = be32_to_cpu(*list++);
+
+		ret = at91_mux_pin(info, pin_num, mux, conf & PULL_UP);
+		if (ret) {
+			dev_err(pdev->dev, "failed to mux pin %d\n", pin_num);
+			return ret;
+		}
+
+		ret = at91_pinctrl_set_conf(info, pin_num, mux, conf);
+		if (ret) {
+			dev_err(pdev->dev, "failed to set conf on pin %d\n", pin_num);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static struct pinctrl_ops at91_pinctrl_ops = {
+	.set_state = at91_pinctrl_set_state,
+};
+
+static int at91_pinctrl_probe(struct device_d *dev)
+{
+	struct at91_pinctrl *info;
+	int ret;
+
+	info = xzalloc(sizeof(struct at91_pinctrl));
+
+	info->ops = at91_pinctrl_get_driver_data(dev);
+	if (!info->ops) {
+		dev_err(dev, "failed to retrieve driver data\n");
+		return -ENODEV;
+	}
+
+	info->pctl.dev = dev;
+	info->pctl.ops = &at91_pinctrl_ops;
+
+	ret = pinctrl_register(&info->pctl);
+	if (ret)
+		return ret;
+
+	dev_info(dev, "AT91 pinctrl registered\n");
+
+	return 0;
+}
+
+static struct platform_device_id at91_pinctrl_ids[] = {
+	{
+		.name = "at91rm9200-pinctrl",
+		.driver_data = (unsigned long)&at91rm9200_ops,
+	}, {
+		.name = "at91sam9x5-pinctrl",
+		.driver_data = (unsigned long)&at91sam9x5_ops,
+	}, {
+		/* sentinel */
+	},
+};
+
+static struct driver_d at91_pinctrl_driver = {
+	.name = "pinctrl-at91",
+	.probe = at91_pinctrl_probe,
+	.id_table = at91_pinctrl_ids,
+	.of_compatible = DRV_OF_COMPAT(at91_pinctrl_dt_ids),
+};
+
+static int at91_pinctrl_init(void)
+{
+	return platform_driver_register(&at91_pinctrl_driver);
+}
+coredevice_initcall(at91_pinctrl_init);
+
+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;
+
+	return at91_mux_gpio_get(pio, mask);
+}
+
+static void at91_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+	void __iomem *pio = at91_gpio->regbase;
+	unsigned mask = 1 << offset;
+
+	at91_mux_gpio_set(pio, mask, value);
+}
+
+static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+		int value)
+{
+	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+	void __iomem *pio = at91_gpio->regbase;
+	unsigned mask = 1 << offset;
+
+	at91_mux_gpio_set(pio, mask, value);
+	__raw_writel(mask, pio + PIO_OER);
+
+	return 0;
+}
+
+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;
+
+	__raw_writel(mask, pio + PIO_ODR);
+
+	return 0;
+}
+
+static int at91_gpio_request(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;
+
+	dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__,
+		 'A' + pin_to_bank(chip->base), offset, chip->base + offset);
+	at91_mux_gpio_enable(pio, mask);
+
+	return 0;
+}
+
+static void at91_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__,
+		 'A' + pin_to_bank(chip->base), offset, chip->base + offset);
+}
+
+static struct gpio_ops at91_gpio_ops = {
+	.request = at91_gpio_request,
+	.free = at91_gpio_free,
+	.direction_input = at91_gpio_direction_input,
+	.direction_output = at91_gpio_direction_output,
+	.get = at91_gpio_get,
+	.set = at91_gpio_set,
+};
+
+static struct of_device_id at91_gpio_dt_ids[] = {
+	{
+		.compatible = "atmel,at91rm9200-gpio",
+	}, {
+		.compatible = "atmel,at91sam9x5-gpio",
+	}, {
+		/* sentinel */
+	},
+};
+
+static int at91_gpio_probe(struct device_d *dev)
+{
+	struct at91_gpio_chip *at91_gpio;
+	struct clk *clk;
+	int ret;
+	int alias_idx = of_alias_get_id(dev->device_node, "gpio");
+
+	BUG_ON(dev->id > MAX_GPIO_BANKS);
+
+	at91_gpio = &gpio_chip[alias_idx];
+
+	clk = clk_get(dev, NULL);
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		dev_err(dev, "clock not found: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_enable(clk);
+	if (ret < 0) {
+		dev_err(dev, "clock failed to enable: %d\n", ret);
+		clk_put(clk);
+		return ret;
+	}
+
+	gpio_banks = max(gpio_banks, alias_idx + 1);
+	at91_gpio->regbase = dev_request_mem_region(dev, 0);
+
+	at91_gpio->chip.ops = &at91_gpio_ops;
+	at91_gpio->chip.ngpio = MAX_NB_GPIO_PER_BANK;
+	at91_gpio->chip.dev = dev;
+	at91_gpio->chip.base = dev->id * MAX_NB_GPIO_PER_BANK;
+
+	ret = gpiochip_add(&at91_gpio->chip);
+	if (ret) {
+		dev_err(dev, "couldn't add gpiochip, ret = %d\n", ret);
+		return ret;
+	}
+
+	dev_info(dev, "AT91 gpio driver registered\n");
+
+	return 0;
+}
+
+static struct platform_device_id at91_gpio_ids[] = {
+	{
+		.name = "at91rm9200-gpio",
+	}, {
+		.name = "at91sam9x5-gpio",
+	}, {
+		/* sentinel */
+	},
+};
+
+static struct driver_d at91_gpio_driver = {
+	.name = "gpio-at91",
+	.probe = at91_gpio_probe,
+	.id_table = at91_gpio_ids,
+	.of_compatible	= DRV_OF_COMPAT(at91_gpio_dt_ids),
+};
+
+static int at91_gpio_init(void)
+{
+	return platform_driver_register(&at91_gpio_driver);
+}
+coredevice_initcall(at91_gpio_init);
-- 
1.7.9.5


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  parent reply	other threads:[~2014-09-02 11:08 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-09-02 11:07 [PATCH v3 0/3] Add atmel " Raphaël Poggi
2014-09-02 11:07 ` [PATCH v3 1/3] arm: mach-at91: move gpio.h to include folder Raphaël Poggi
2014-09-03  7:00   ` Sascha Hauer
2014-09-02 11:07 ` Raphaël Poggi [this message]
2014-09-03  6:56   ` [PATCH v3 2/3] pinctrl: at91: add pinctrl driver Sascha Hauer
2014-09-03 11:23     ` Raphaël Poggi
2014-09-03 13:52       ` Sascha Hauer
2014-09-03 15:10         ` Raphaël Poggi
2014-09-04  9:26           ` Sascha Hauer
2014-09-04  9:41             ` Raphaël Poggi
2014-09-02 11:08 ` [PATCH v3 3/3] at91sam9g45: add device tree gpio clocks Raphaël Poggi
2014-09-03  6:46   ` Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1409656080-19280-3-git-send-email-poggi.raph@gmail.com \
    --to=poggi.raph@gmail.com \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox