* [PATCH 3/4] pinctrl: add abx500 pinctrl driver core
From: Linus Walleij @ 2013-01-15 9:43 UTC (permalink / raw)
To: linux-arm-kernel
From: Patrice Chotard <patrice.chotard@st.com>
This adds the AB8500 core driver, which will be utilized by
the follow-on drivers for different ABx500 variants.
Sselect the driver from the DBX500_SOC, as this chip is
powering and clocking that SoC.
Cc: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
Hi Sam, it'd be great if you could ACK this so we could proceed
with rewriting the GPIO driver.
---
arch/arm/mach-ux500/Kconfig | 1 +
arch/arm/mach-ux500/board-mop500.c | 19 +-
drivers/pinctrl/Kconfig | 7 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-abx500.c | 1233 ++++++++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-abx500.h | 229 ++++++
include/linux/mfd/abx500/ab8500-gpio.h | 15 +-
include/linux/mfd/abx500/ab8500.h | 2 +-
8 files changed, 1486 insertions(+), 21 deletions(-)
create mode 100644 drivers/pinctrl/pinctrl-abx500.c
create mode 100644 drivers/pinctrl/pinctrl-abx500.h
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index 5dea906..2c090d7 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -11,6 +11,7 @@ config UX500_SOC_COMMON
select COMMON_CLK
select PINCTRL
select PINCTRL_NOMADIK
+ select PINCTRL_ABX500
select PL310_ERRATA_753970 if CACHE_PL310
config UX500_SOC_DB8500
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index d453522..b6f14ee 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -90,26 +90,9 @@ static struct platform_device snowball_gpio_en_3v3_regulator_dev = {
},
};
-static struct ab8500_gpio_platform_data ab8500_gpio_pdata = {
+static struct abx500_gpio_platform_data ab8500_gpio_pdata = {
.gpio_base = MOP500_AB8500_PIN_GPIO(1),
.irq_base = MOP500_AB8500_VIR_GPIO_IRQ_BASE,
- /* config_reg is the initial configuration of ab8500 pins.
- * The pins can be configured as GPIO or alt functions based
- * on value present in GpioSel1 to GpioSel6 and AlternatFunction
- * register. This is the array of 7 configuration settings.
- * One has to compile time decide these settings. Below is the
- * explanation of these setting
- * GpioSel1 = 0x00 => Pins GPIO1 to GPIO8 are not used as GPIO
- * GpioSel2 = 0x1E => Pins GPIO10 to GPIO13 are configured as GPIO
- * GpioSel3 = 0x80 => Pin GPIO24 is configured as GPIO
- * GpioSel4 = 0x01 => Pin GPIo25 is configured as GPIO
- * GpioSel5 = 0x7A => Pins GPIO34, GPIO36 to GPIO39 are conf as GPIO
- * GpioSel6 = 0x00 => Pins GPIO41 & GPIo42 are not configured as GPIO
- * AlternaFunction = 0x00 => If Pins GPIO10 to 13 are not configured
- * as GPIO then this register selectes the alternate fucntions
- */
- .config_reg = {0x00, 0x1E, 0x80, 0x01,
- 0x7A, 0x00, 0x00},
};
/* ab8500-codec */
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c31aeb0..ec819d8 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -26,6 +26,13 @@ config DEBUG_PINCTRL
help
Say Y here to add some extra checks and diagnostics to PINCTRL calls.
+config PINCTRL_ABX500
+ bool "ST-Ericsson ABx500 family Mixed Signal Circuit gpio functions"
+ depends on AB8500_CORE
+ select GENERIC_PINCONF
+ help
+ Select this to enable the ABx500 family IC GPIO driver
+
config PINCTRL_AT91
bool "AT91 pinctrl driver"
depends on OF
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index fc4606f..d10bf87 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -9,6 +9,7 @@ ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_PINCTRL) += devicetree.o
endif
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
+obj-$(CONFIG_PINCTRL_ABX500) += pinctrl-abx500.o
obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o
obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o
diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c
new file mode 100644
index 0000000..ac89e10
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-abx500.c
@@ -0,0 +1,1233 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2013
+ *
+ * Author: Patrice Chotard <patrice.chotard@st.com>
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+#include "pinctrl-abx500.h"
+
+/*
+ * The AB9540 and AB8540 GPIO support are extended versions
+ * of the AB8500 GPIO support.
+ * The AB9540 supports an additional (7th) register so that
+ * more GPIO may be configured and used.
+ * The AB8540 supports 4 new gpios (GPIOx_VBAT) that have
+ * internal pull-up and pull-down capabilities.
+ */
+
+/*
+ * GPIO registers offset
+ * Bank: 0x10
+ */
+#define AB8500_GPIO_SEL1_REG 0x00
+#define AB8500_GPIO_SEL2_REG 0x01
+#define AB8500_GPIO_SEL3_REG 0x02
+#define AB8500_GPIO_SEL4_REG 0x03
+#define AB8500_GPIO_SEL5_REG 0x04
+#define AB8500_GPIO_SEL6_REG 0x05
+#define AB9540_GPIO_SEL7_REG 0x06
+
+#define AB8500_GPIO_DIR1_REG 0x10
+#define AB8500_GPIO_DIR2_REG 0x11
+#define AB8500_GPIO_DIR3_REG 0x12
+#define AB8500_GPIO_DIR4_REG 0x13
+#define AB8500_GPIO_DIR5_REG 0x14
+#define AB8500_GPIO_DIR6_REG 0x15
+#define AB9540_GPIO_DIR7_REG 0x16
+
+#define AB8500_GPIO_OUT1_REG 0x20
+#define AB8500_GPIO_OUT2_REG 0x21
+#define AB8500_GPIO_OUT3_REG 0x22
+#define AB8500_GPIO_OUT4_REG 0x23
+#define AB8500_GPIO_OUT5_REG 0x24
+#define AB8500_GPIO_OUT6_REG 0x25
+#define AB9540_GPIO_OUT7_REG 0x26
+
+#define AB8500_GPIO_PUD1_REG 0x30
+#define AB8500_GPIO_PUD2_REG 0x31
+#define AB8500_GPIO_PUD3_REG 0x32
+#define AB8500_GPIO_PUD4_REG 0x33
+#define AB8500_GPIO_PUD5_REG 0x34
+#define AB8500_GPIO_PUD6_REG 0x35
+#define AB9540_GPIO_PUD7_REG 0x36
+
+#define AB8500_GPIO_IN1_REG 0x40
+#define AB8500_GPIO_IN2_REG 0x41
+#define AB8500_GPIO_IN3_REG 0x42
+#define AB8500_GPIO_IN4_REG 0x43
+#define AB8500_GPIO_IN5_REG 0x44
+#define AB8500_GPIO_IN6_REG 0x45
+#define AB9540_GPIO_IN7_REG 0x46
+#define AB8540_GPIO_VINSEL_REG 0x47
+#define AB8540_GPIO_PULL_UPDOWN_REG 0x48
+#define AB8500_GPIO_ALTFUN_REG 0x50
+#define AB8500_NUM_VIR_GPIO_IRQ 16
+#define AB8540_GPIO_PULL_UPDOWN_MASK 0x03
+#define AB8540_GPIO_VINSEL_MASK 0x03
+#define AB8540_GPIOX_VBAT_START 51
+#define AB8540_GPIOX_VBAT_END 54
+
+enum abx500_gpio_action {
+ NONE,
+ STARTUP,
+ SHUTDOWN,
+ MASK,
+ UNMASK
+};
+
+struct abx500_pinctrl {
+ struct device *dev;
+ struct pinctrl_dev *pctldev;
+ struct abx500_pinctrl_soc_data *soc;
+ struct gpio_chip chip;
+ struct ab8500 *parent;
+ struct mutex lock;
+ u32 irq_base;
+ enum abx500_gpio_action irq_action;
+ u16 rising;
+ u16 falling;
+ struct abx500_gpio_irq_cluster *irq_cluster;
+ int irq_cluster_size;
+ int irq_gpio_rising_offset;
+ int irq_gpio_falling_offset;
+ int irq_gpio_factor;
+};
+
+/**
+ * to_abx500_pinctrl() - get the pointer to abx500_pinctrl
+ * @chip: Member of the structure abx500_pinctrl
+ */
+static inline struct abx500_pinctrl *to_abx500_pinctrl(struct gpio_chip *chip)
+{
+ return container_of(chip, struct abx500_pinctrl, chip);
+}
+
+static int abx500_gpio_get_bit(struct gpio_chip *chip, u8 reg,
+ unsigned offset, bool *bit)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ u8 pos = offset % 8;
+ u8 val;
+ int ret;
+
+ reg += offset / 8;
+ ret = abx500_get_register_interruptible(pct->dev,
+ AB8500_MISC, reg, &val);
+
+ *bit = !!(val & BIT(pos));
+
+ if (ret < 0)
+ dev_err(pct->dev,
+ "%s read reg =%x, offset=%x failed\n",
+ __func__, reg, offset);
+
+ return ret;
+}
+
+static int abx500_gpio_set_bits(struct gpio_chip *chip, u8 reg,
+ unsigned offset, int val)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ u8 pos = offset % 8;
+ int ret;
+
+ reg += offset / 8;
+ ret = abx500_mask_and_set_register_interruptible(pct->dev,
+ AB8500_MISC, reg, 1 << pos, val << pos);
+ if (ret < 0)
+ dev_err(pct->dev, "%s write failed\n", __func__);
+ return ret;
+}
+/**
+ * abx500_gpio_get() - Get the particular GPIO value
+ * @chip: Gpio device
+ * @offset: GPIO number to read
+ */
+static int abx500_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ u8 mask = 1 << (offset % 8);
+ u8 reg = AB8500_GPIO_IN1_REG + (offset / 8);
+ int ret;
+ u8 data;
+ ret = abx500_get_register_interruptible(pct->dev, AB8500_MISC,
+ reg, &data);
+ if (ret < 0) {
+ dev_err(pct->dev, "%s read failed\n", __func__);
+ return ret;
+ }
+ return (data & mask) >> (offset % 8);
+}
+
+static void abx500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ int ret;
+ /* Write the data */
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
+ if (ret < 0)
+ dev_err(pct->dev, "%s write failed\n", __func__);
+}
+
+static int abx500_config_pull_updown(struct abx500_pinctrl *pct,
+ int offset, enum abx500_gpio_pull_updown val)
+{
+ u8 pos;
+ int ret;
+ struct pullud *pullud;
+
+ if (!pct->soc->pullud) {
+ dev_err(pct->dev, "%s AB chip doesn't support pull up/down feature",
+ __func__);
+ ret = -EPERM;
+ goto out;
+ }
+
+ pullud = pct->soc->pullud;
+
+ if ((offset < pullud->first_pin)
+ || (offset > pullud->last_pin)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pos = offset << 1;
+
+ ret = abx500_mask_and_set_register_interruptible(pct->dev,
+ AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG,
+ AB8540_GPIO_PULL_UPDOWN_MASK << pos, val << pos);
+
+out:
+ if (ret < 0)
+ dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+}
+
+static int abx500_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset,
+ int val)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ struct pullud *pullud = pct->soc->pullud;
+ unsigned gpio;
+ int ret;
+ /* set direction as output */
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 1);
+ if (ret < 0)
+ return ret;
+
+ /* disable pull down */
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, 1);
+ if (ret < 0)
+ return ret;
+
+ /* if supported, disable both pull down and pull up */
+ gpio = offset + 1;
+ if (pullud && gpio >= pullud->first_pin && gpio <= pullud->last_pin) {
+ ret = abx500_config_pull_updown(pct,
+ offset,
+ ABX500_GPIO_PULL_NONE);
+ if (ret < 0)
+ return ret;
+ }
+ /* set the output as 1 or 0 */
+ return abx500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
+}
+
+static int abx500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ /* set the register as input */
+ return abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 0);
+}
+
+static int abx500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ int base = pct->irq_base;
+ int i;
+
+ for (i = 0; i < pct->irq_cluster_size; i++) {
+ struct abx500_gpio_irq_cluster *cluster =
+ &pct->irq_cluster[i];
+
+ if (offset >= cluster->start && offset <= cluster->end)
+ return base + offset - cluster->start;
+
+ /* Advance by the number of gpios in this cluster */
+ base += cluster->end + cluster->offset - cluster->start + 1;
+ }
+
+ return -EINVAL;
+}
+
+static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
+ unsigned gpio, int alt_setting)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct alternate_functions af = pct->soc->alternate_functions[gpio];
+ int ret;
+ int val;
+ unsigned offset;
+ const char *modes[] = {
+ [ABX500_DEFAULT] = "default",
+ [ABX500_ALT_A] = "altA",
+ [ABX500_ALT_B] = "altB",
+ [ABX500_ALT_C] = "altC",
+ };
+
+ /* sanity check */
+ if (((alt_setting == ABX500_ALT_A) && (af.gpiosel_bit == UNUSED)) ||
+ ((alt_setting == ABX500_ALT_B) && (af.alt_bit1 == UNUSED)) ||
+ ((alt_setting == ABX500_ALT_C) && (af.alt_bit2 == UNUSED))) {
+ dev_dbg(pct->dev, "pin %d doesn't support %s mode\n", gpio,
+ modes[alt_setting]);
+ return -EINVAL;
+ }
+
+ /* on ABx5xx, there is no GPIO0, so adjust the offset */
+ offset = gpio - 1;
+ switch (alt_setting) {
+ case ABX500_DEFAULT:
+ /*
+ * for ABx5xx family, default mode is always selected by
+ * writing 0 to GPIOSELx register, except for pins which
+ * support at least ALT_B mode, default mode is selected
+ * by writing 1 to GPIOSELx register
+ */
+ val = 0;
+ if (af.alt_bit1 != UNUSED)
+ val++;
+
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, val);
+ break;
+ case ABX500_ALT_A:
+ /*
+ * for ABx5xx family, alt_a mode is always selected by
+ * writing 1 to GPIOSELx register, except for pins which
+ * support at least ALT_B mode, alt_a mode is selected
+ * by writing 0 to GPIOSELx register and 0 in ALTFUNC
+ * register
+ */
+ if (af.alt_bit1 != UNUSED) {
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, 0);
+ ret = abx500_gpio_set_bits(chip,
+ AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit1,
+ !!(af.alta_val && BIT(0)));
+ if (af.alt_bit2 != UNUSED)
+ ret = abx500_gpio_set_bits(chip,
+ AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit2,
+ !!(af.alta_val && BIT(1)));
+ } else
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, 1);
+ break;
+ case ABX500_ALT_B:
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, 0);
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit1, !!(af.altb_val && BIT(0)));
+ if (af.alt_bit2 != UNUSED)
+ ret = abx500_gpio_set_bits(chip,
+ AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit2,
+ !!(af.altb_val && BIT(1)));
+ break;
+ case ABX500_ALT_C:
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, 0);
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit2, !!(af.altc_val && BIT(0)));
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit2, !!(af.altc_val && BIT(1)));
+ break;
+
+ default:
+ dev_dbg(pct->dev, "unknow alt_setting %d\n", alt_setting);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
+ unsigned gpio)
+{
+ u8 mode;
+ bool bit_mode;
+ bool alt_bit1;
+ bool alt_bit2;
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct alternate_functions af = pct->soc->alternate_functions[gpio];
+
+ /*
+ * if gpiosel_bit is set to unused,
+ * it means no GPIO or special case
+ */
+ if (af.gpiosel_bit == UNUSED)
+ return ABX500_DEFAULT;
+
+ /* read GpioSelx register */
+ abx500_gpio_get_bit(chip, AB8500_GPIO_SEL1_REG + (gpio / 8),
+ af.gpiosel_bit, &bit_mode);
+ mode = bit_mode;
+
+ /* sanity check */
+ if ((af.alt_bit1 < UNUSED) || (af.alt_bit1 > 7) ||
+ (af.alt_bit2 < UNUSED) || (af.alt_bit2 > 7)) {
+ dev_err(pct->dev,
+ "alt_bitX value not in correct range (-1 to 7)\n");
+ return -EINVAL;
+ }
+ /* if alt_bit2 is used, alt_bit1 must be used too */
+ if ((af.alt_bit2 != UNUSED) && (af.alt_bit1 == UNUSED)) {
+ dev_err(pct->dev,
+ "if alt_bit2 is used, alt_bit1 can't be unused\n");
+ return -EINVAL;
+ }
+
+ /* check if pin use AlternateFunction register */
+ if ((af.alt_bit1 == UNUSED) && (af.alt_bit1 == UNUSED))
+ return mode;
+ /*
+ * if pin GPIOSEL bit is set and pin supports alternate function,
+ * it means DEFAULT mode
+ */
+ if (mode)
+ return ABX500_DEFAULT;
+ /*
+ * pin use the AlternatFunction register
+ * read alt_bit1 value
+ */
+ abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit1, &alt_bit1);
+
+ if (af.alt_bit2 != UNUSED)
+ /* read alt_bit2 value */
+ abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2,
+ &alt_bit2);
+ else
+ alt_bit2 = 0;
+
+ mode = (alt_bit2 << 1) + alt_bit1;
+ if (mode == af.alta_val)
+ return ABX500_ALT_A;
+ else if (mode == af.altb_val)
+ return ABX500_ALT_B;
+ else
+ return ABX500_ALT_C;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/seq_file.h>
+
+static void abx500_gpio_dbg_show_one(struct seq_file *s,
+ struct pinctrl_dev *pctldev, struct gpio_chip *chip,
+ unsigned offset, unsigned gpio)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ const char *label = gpiochip_is_requested(chip, offset - 1);
+ u8 gpio_offset = offset - 1;
+ int mode = -1;
+ bool is_out;
+ bool pull;
+ const char *modes[] = {
+ [ABX500_DEFAULT] = "default",
+ [ABX500_ALT_A] = "altA",
+ [ABX500_ALT_B] = "altB",
+ [ABX500_ALT_C] = "altC",
+ };
+
+ abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, gpio_offset, &is_out);
+ abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG, gpio_offset, &pull);
+
+ if (pctldev)
+ mode = abx500_get_mode(pctldev, chip, offset);
+
+ seq_printf(s, " gpio-%-3d (%-20.20s) %-3s %-9s %s",
+ gpio, label ?: "(none)",
+ is_out ? "out" : "in ",
+ is_out ?
+ (chip->get
+ ? (chip->get(chip, offset) ? "hi" : "lo")
+ : "? ")
+ : (pull ? "pull up" : "pull down"),
+ (mode < 0) ? "unknown" : modes[mode]);
+
+ if (label && !is_out) {
+ int irq = gpio_to_irq(gpio);
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ if (irq >= 0 && desc->action) {
+ char *trigger;
+ int irq_offset = irq - pct->irq_base;
+
+ if (pct->rising & BIT(irq_offset))
+ trigger = "edge-rising";
+ else if (pct->falling & BIT(irq_offset))
+ trigger = "edge-falling";
+ else
+ trigger = "edge-undefined";
+
+ seq_printf(s, " irq-%d %s", irq, trigger);
+ }
+ }
+}
+
+static void abx500_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ unsigned i;
+ unsigned gpio = chip->base;
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ struct pinctrl_dev *pctldev = pct->pctldev;
+
+ for (i = 0; i < chip->ngpio; i++, gpio++) {
+ /* On AB8500, there is no GPIO0, the first is the GPIO 1 */
+ abx500_gpio_dbg_show_one(s, pctldev, chip, i + 1, gpio);
+ seq_printf(s, "\n");
+ }
+}
+
+#else
+static inline void abx500_gpio_dbg_show_one(struct seq_file *s,
+ struct pinctrl_dev *pctldev,
+ struct gpio_chip *chip,
+ unsigned offset, unsigned gpio)
+{
+}
+#define abx500_gpio_dbg_show NULL
+#endif
+
+int abx500_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+
+ return pinctrl_request_gpio(gpio);
+}
+
+void abx500_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+
+ pinctrl_free_gpio(gpio);
+}
+
+static struct gpio_chip abx500gpio_chip = {
+ .label = "abx500-gpio",
+ .owner = THIS_MODULE,
+ .request = abx500_gpio_request,
+ .free = abx500_gpio_free,
+ .direction_input = abx500_gpio_direction_input,
+ .get = abx500_gpio_get,
+ .direction_output = abx500_gpio_direction_output,
+ .set = abx500_gpio_set,
+ .to_irq = abx500_gpio_to_irq,
+ .dbg_show = abx500_gpio_dbg_show,
+};
+
+static unsigned int irq_to_rising(unsigned int irq)
+{
+ struct abx500_pinctrl *pct = irq_get_chip_data(irq);
+ int offset = irq - pct->irq_base;
+ int new_irq;
+
+ new_irq = offset * pct->irq_gpio_factor
+ + pct->irq_gpio_rising_offset
+ + pct->parent->irq_base;
+
+ return new_irq;
+}
+
+static unsigned int irq_to_falling(unsigned int irq)
+{
+ struct abx500_pinctrl *pct = irq_get_chip_data(irq);
+ int offset = irq - pct->irq_base;
+ int new_irq;
+
+ new_irq = offset * pct->irq_gpio_factor
+ + pct->irq_gpio_falling_offset
+ + pct->parent->irq_base;
+ return new_irq;
+
+}
+
+static unsigned int rising_to_irq(unsigned int irq, void *dev)
+{
+ struct abx500_pinctrl *pct = dev;
+ int offset, new_irq;
+
+ offset = irq - pct->irq_gpio_rising_offset
+ - pct->parent->irq_base;
+ new_irq = (offset / pct->irq_gpio_factor)
+ + pct->irq_base;
+
+ return new_irq;
+}
+
+static unsigned int falling_to_irq(unsigned int irq, void *dev)
+{
+ struct abx500_pinctrl *pct = dev;
+ int offset, new_irq;
+
+ offset = irq - pct->irq_gpio_falling_offset
+ - pct->parent->irq_base;
+ new_irq = (offset / pct->irq_gpio_factor)
+ + pct->irq_base;
+
+ return new_irq;
+}
+
+/*
+ * IRQ handler
+ */
+
+static irqreturn_t handle_rising(int irq, void *dev)
+{
+
+ handle_nested_irq(rising_to_irq(irq , dev));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_falling(int irq, void *dev)
+{
+
+ handle_nested_irq(falling_to_irq(irq, dev));
+ return IRQ_HANDLED;
+}
+
+static void abx500_gpio_irq_lock(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ mutex_lock(&pct->lock);
+}
+
+static void abx500_gpio_irq_sync_unlock(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
+ int offset = irq - pct->irq_base;
+ bool rising = pct->rising & BIT(offset);
+ bool falling = pct->falling & BIT(offset);
+ int ret;
+
+ switch (pct->irq_action) {
+ case STARTUP:
+ if (rising)
+ ret = request_threaded_irq(irq_to_rising(irq),
+ NULL, handle_rising,
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
+ "abx500-gpio-r", pct);
+ if (falling)
+ ret = request_threaded_irq(irq_to_falling(irq),
+ NULL, handle_falling,
+ IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND,
+ "abx500-gpio-f", pct);
+ break;
+ case SHUTDOWN:
+ if (rising)
+ free_irq(irq_to_rising(irq), pct);
+ if (falling)
+ free_irq(irq_to_falling(irq), pct);
+ break;
+ case MASK:
+ if (rising)
+ disable_irq(irq_to_rising(irq));
+ if (falling)
+ disable_irq(irq_to_falling(irq));
+ break;
+ case UNMASK:
+ if (rising)
+ enable_irq(irq_to_rising(irq));
+ if (falling)
+ enable_irq(irq_to_falling(irq));
+ break;
+ case NONE:
+ break;
+ }
+ pct->irq_action = NONE;
+ pct->rising &= ~(BIT(offset));
+ pct->falling &= ~(BIT(offset));
+ mutex_unlock(&pct->lock);
+}
+
+
+static void abx500_gpio_irq_mask(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ pct->irq_action = MASK;
+}
+
+static void abx500_gpio_irq_unmask(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ pct->irq_action = UNMASK;
+}
+
+static int abx500_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
+ int offset = irq - pct->irq_base;
+
+ if (type == IRQ_TYPE_EDGE_BOTH) {
+ pct->rising = BIT(offset);
+ pct->falling = BIT(offset);
+ } else if (type == IRQ_TYPE_EDGE_RISING) {
+ pct->rising = BIT(offset);
+ } else {
+ pct->falling = BIT(offset);
+ }
+ return 0;
+}
+
+static unsigned int abx500_gpio_irq_startup(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ pct->irq_action = STARTUP;
+ return 0;
+}
+
+static void abx500_gpio_irq_shutdown(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ pct->irq_action = SHUTDOWN;
+}
+
+static struct irq_chip abx500_gpio_irq_chip = {
+ .name = "abx500-gpio",
+ .irq_startup = abx500_gpio_irq_startup,
+ .irq_shutdown = abx500_gpio_irq_shutdown,
+ .irq_bus_lock = abx500_gpio_irq_lock,
+ .irq_bus_sync_unlock = abx500_gpio_irq_sync_unlock,
+ .irq_mask = abx500_gpio_irq_mask,
+ .irq_unmask = abx500_gpio_irq_unmask,
+ .irq_set_type = abx500_gpio_irq_set_type,
+};
+
+static int abx500_gpio_irq_init(struct abx500_pinctrl *pct)
+{
+ u32 base = pct->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ ; irq++) {
+ irq_set_chip_data(irq, pct);
+ irq_set_chip_and_handler(irq, &abx500_gpio_irq_chip,
+ handle_simple_irq);
+ irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void abx500_gpio_irq_remove(struct abx500_pinctrl *pct)
+{
+ int base = pct->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+ }
+}
+
+static int abx500_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ return pct->soc->nfunctions;
+}
+
+static const char *abx500_pmx_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ return pct->soc->functions[function].name;
+}
+
+static int abx500_pmx_get_func_groups(struct pinctrl_dev *pctldev,
+ unsigned function,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = pct->soc->functions[function].groups;
+ *num_groups = pct->soc->functions[function].ngroups;
+
+ return 0;
+}
+
+static void abx500_disable_lazy_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ int irq;
+ int offset;
+ bool rising;
+ bool falling;
+
+ /*
+ * check if gpio has interrupt capability and convert
+ * gpio number to irq
+ * On ABx5xx, there is no GPIO0, GPIO1 is the
+ * first one, so adjust gpio number
+ */
+ gpio--;
+ irq = gpio_to_irq(gpio + chip->base);
+ if (irq < 0)
+ return;
+
+ offset = irq - pct->irq_base;
+ rising = pct->rising & BIT(offset);
+ falling = pct->falling & BIT(offset);
+
+ /* nothing to do ?*/
+ if (!rising && !falling)
+ return;
+
+ if (rising) {
+ disable_irq(irq_to_rising(irq));
+ free_irq(irq_to_rising(irq), pct);
+ }
+ if (falling) {
+ disable_irq(irq_to_falling(irq));
+ free_irq(irq_to_falling(irq), pct);
+ }
+}
+
+static int abx500_pmx_enable(struct pinctrl_dev *pctldev, unsigned function,
+ unsigned group)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct gpio_chip *chip = &pct->chip;
+ const struct abx500_pingroup *g;
+ int i;
+ int ret = 0;
+
+ g = &pct->soc->groups[group];
+ if (g->altsetting < 0)
+ return -EINVAL;
+
+ dev_dbg(pct->dev, "enable group %s, %u pins\n", g->name, g->npins);
+
+ for (i = 0; i < g->npins; i++) {
+ dev_dbg(pct->dev, "setting pin %d to altsetting %d\n",
+ g->pins[i], g->altsetting);
+
+ abx500_disable_lazy_irq(chip, g->pins[i]);
+ ret = abx500_set_mode(pctldev, chip, g->pins[i], g->altsetting);
+ }
+ return ret;
+}
+
+static void abx500_pmx_disable(struct pinctrl_dev *pctldev,
+ unsigned function, unsigned group)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ const struct abx500_pingroup *g;
+
+ g = &pct->soc->groups[group];
+ if (g->altsetting < 0)
+ return;
+
+ /* Poke out the mux, set the pin to some default state? */
+ dev_dbg(pct->dev, "disable group %s, %u pins\n", g->name, g->npins);
+}
+
+int abx500_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ u8 reg = AB8500_GPIO_SEL1_REG + (offset / 8);
+ u8 pos = offset % 8;
+ int ret;
+
+ if (!range) {
+ dev_err(pct->dev, "invalid range\n");
+ return -EINVAL;
+ }
+ if (!range->gc) {
+ dev_err(pct->dev, "missing GPIO chip in range\n");
+ return -EINVAL;
+ }
+ dev_dbg(pct->dev, "enable pin %u as GPIO\n", offset);
+
+ /*
+ * by default, for ABx5xx family, GPIO mode is selected by
+ * writing 1 in GPIOSELx registers
+ */
+ ret = abx500_mask_and_set_register_interruptible(pct->dev,
+ AB8500_MISC, reg, 1 << pos, 1 << pos);
+
+ if (ret < 0) {
+ dev_err(pct->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ return ret;
+}
+
+static void abx500_gpio_disable_free(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+}
+
+static struct pinmux_ops abx500_pinmux_ops = {
+ .get_functions_count = abx500_pmx_get_funcs_cnt,
+ .get_function_name = abx500_pmx_get_func_name,
+ .get_function_groups = abx500_pmx_get_func_groups,
+ .enable = abx500_pmx_enable,
+ .disable = abx500_pmx_disable,
+ .gpio_request_enable = abx500_gpio_request_enable,
+ .gpio_disable_free = abx500_gpio_disable_free,
+};
+
+static int abx500_get_groups_cnt(struct pinctrl_dev *pctldev)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ return pct->soc->ngroups;
+}
+
+static const char *abx500_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ return pct->soc->groups[selector].name;
+}
+
+static int abx500_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned selector,
+ const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ *pins = pct->soc->groups[selector].pins;
+ *num_pins = pct->soc->groups[selector].npins;
+ return 0;
+}
+
+static void abx500_pin_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned offset)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct gpio_chip *chip = &pct->chip;
+
+ abx500_gpio_dbg_show_one(s, pctldev, chip, offset,
+ chip->base + offset - 1);
+}
+
+static struct pinctrl_ops abx500_pinctrl_ops = {
+ .get_groups_count = abx500_get_groups_cnt,
+ .get_group_name = abx500_get_group_name,
+ .get_group_pins = abx500_get_group_pins,
+ .pin_dbg_show = abx500_pin_dbg_show,
+};
+
+int abx500_pin_config_get(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ unsigned long *config)
+{
+ /* Not implemented */
+ return -EINVAL;
+}
+
+int abx500_pin_config_set(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ unsigned long config)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct pullud *pullud = pct->soc->pullud;
+ struct gpio_chip *chip = &pct->chip;
+ unsigned offset;
+ int ret;
+ enum pin_config_param param = pinconf_to_config_param(config);
+ enum pin_config_param argument = pinconf_to_config_argument(config);
+
+ dev_dbg(chip->dev, "pin %d [%#lx]: %s %s\n",
+ pin, config, (param == PIN_CONFIG_OUTPUT) ? "output " : "input",
+ (param == PIN_CONFIG_OUTPUT) ? (argument ? "high" : "low") :
+ (argument ? "pull up" : "pull down"));
+ /* on ABx500, there is no GPIO0, so adjust the offset */
+ offset = pin - 1;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ /*
+ * if argument = 1 set the pull down
+ * else clear the pull down
+ */
+ ret = abx500_gpio_direction_input(chip, offset);
+ /* check if pin supports pull updown feature */
+ if (pullud && pin >= pullud->first_pin && pin <= pullud->last_pin)
+ ret = abx500_config_pull_updown(pct,
+ offset,
+ argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE);
+ else
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG,
+ offset, argument ? 0 : 1);
+ break;
+ case PIN_CONFIG_OUTPUT:
+ ret = abx500_gpio_direction_output(chip, offset, argument);
+ break;
+ default:
+ dev_err(chip->dev, "illegal configuration requested\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static struct pinconf_ops abx500_pinconf_ops = {
+ .pin_config_get = abx500_pin_config_get,
+ .pin_config_set = abx500_pin_config_set,
+};
+
+static struct pinctrl_desc abx500_pinctrl_desc = {
+ .name = "pinctrl-abx500",
+ .pctlops = &abx500_pinctrl_ops,
+ .pmxops = &abx500_pinmux_ops,
+ .confops = &abx500_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+static int abx500_get_gpio_num(struct abx500_pinctrl_soc_data *soc)
+{
+ unsigned int lowest = 0;
+ unsigned int highest = 0;
+ unsigned int npins = 0;
+ int i;
+
+ /*
+ * Compute number of GPIOs from the last SoC gpio range descriptors
+ * These ranges may include "holes" but the GPIO number space shall
+ * still be homogeneous, so we need to detect and account for any
+ * such holes so that these are included in the number of GPIO pins.
+ */
+ for (i = 0; i < soc->gpio_num_ranges; i++) {
+ unsigned gstart;
+ unsigned gend;
+ const struct abx500_pinrange *p;
+
+ p = &soc->gpio_ranges[i];
+ gstart = p->offset;
+ gend = p->offset + p->npins - 1;
+
+ if (i == 0) {
+ /* First iteration, set start values */
+ lowest = gstart;
+ highest = gend;
+ } else {
+ if (gstart < lowest)
+ lowest = gstart;
+ if (gend > highest)
+ highest = gend;
+ }
+ }
+ /* this gives the absolute number of pins */
+ npins = highest - lowest + 1;
+ return npins;
+}
+
+static int __devinit abx500_gpio_probe(struct platform_device *pdev)
+{
+ struct ab8500_platform_data *abx500_pdata =
+ dev_get_platdata(pdev->dev.parent);
+ struct abx500_gpio_platform_data *pdata;
+ struct abx500_pinctrl *pct;
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
+ int ret;
+ int i;
+
+ pdata = abx500_pdata->gpio;
+ if (!pdata) {
+ dev_err(&pdev->dev, "gpio platform data missing\n");
+ return -ENODEV;
+ }
+
+ pct = devm_kzalloc(&pdev->dev, sizeof(struct abx500_pinctrl),
+ GFP_KERNEL);
+ if (pct == NULL) {
+ dev_err(&pdev->dev,
+ "failed to allocate memory for pct\n");
+ return -ENOMEM;
+ }
+
+ pct->dev = &pdev->dev;
+ pct->parent = dev_get_drvdata(pdev->dev.parent);
+ pct->chip = abx500gpio_chip;
+ pct->chip.dev = &pdev->dev;
+ pct->chip.base = pdata->gpio_base;
+ pct->irq_base = pdata->irq_base;
+
+ /* initialize the lock */
+ mutex_init(&pct->lock);
+
+ /* Poke in other ASIC variants here */
+ switch (platid->driver_data) {
+ case PINCTRL_AB8500:
+ abx500_pinctrl_ab8500_init(&pct->soc);
+ break;
+ case PINCTRL_AB8540:
+ abx500_pinctrl_ab8540_init(&pct->soc);
+ break;
+ case PINCTRL_AB9540:
+ abx500_pinctrl_ab9540_init(&pct->soc);
+ break;
+ case PINCTRL_AB8505:
+ abx500_pinctrl_ab8505_init(&pct->soc);
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported pinctrl sub driver (%d)\n",
+ (int) platid->driver_data);
+ return -EINVAL;
+ }
+
+ if (!pct->soc) {
+ dev_err(&pdev->dev, "Invalid SOC data\n");
+ return -EINVAL;
+ }
+
+ pct->chip.ngpio = abx500_get_gpio_num(pct->soc);
+ pct->irq_cluster = pct->soc->gpio_irq_cluster;
+ pct->irq_cluster_size = pct->soc->ngpio_irq_cluster;
+ pct->irq_gpio_rising_offset = pct->soc->irq_gpio_rising_offset;
+ pct->irq_gpio_falling_offset = pct->soc->irq_gpio_falling_offset;
+ pct->irq_gpio_factor = pct->soc->irq_gpio_factor;
+
+ ret = abx500_gpio_irq_init(pct);
+ if (ret)
+ goto out_free;
+ ret = gpiochip_add(&pct->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n",
+ ret);
+ goto out_rem_irq;
+ }
+ dev_info(&pdev->dev, "added gpiochip\n");
+
+ abx500_pinctrl_desc.pins = pct->soc->pins;
+ abx500_pinctrl_desc.npins = pct->soc->npins;
+ pct->pctldev = pinctrl_register(&abx500_pinctrl_desc, &pdev->dev, pct);
+ if (!pct->pctldev) {
+ dev_err(&pdev->dev,
+ "could not register abx500 pinctrl driver\n");
+ goto out_rem_chip;
+ }
+ dev_info(&pdev->dev, "registered pin controller\n");
+
+ /* We will handle a range of GPIO pins */
+ for (i = 0; i < pct->soc->gpio_num_ranges; i++) {
+ const struct abx500_pinrange *p = &pct->soc->gpio_ranges[i];
+
+ ret = gpiochip_add_pin_range(&pct->chip,
+ dev_name(&pdev->dev),
+ p->offset, p->offset, p->npins);
+ if (ret < 0)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, pct);
+ dev_info(&pdev->dev, "initialized abx500 pinctrl driver\n");
+
+ return 0;
+
+out_rem_chip:
+ ret = gpiochip_remove(&pct->chip);
+ if (ret)
+ dev_info(&pdev->dev, "failed to remove gpiochip\n");
+out_rem_irq:
+ abx500_gpio_irq_remove(pct);
+out_free:
+ mutex_destroy(&pct->lock);
+ return ret;
+}
+
+/*
+ * abx500_gpio_remove() - remove Ab8500-gpio driver
+ * @pdev : Platform device registered
+ */
+static int __devexit abx500_gpio_remove(struct platform_device *pdev)
+{
+ struct abx500_pinctrl *pct = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = gpiochip_remove(&pct->chip);
+ if (ret < 0) {
+ dev_err(pct->dev, "unable to remove gpiochip: %d\n",
+ ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ mutex_destroy(&pct->lock);
+ kfree(pct);
+
+ return 0;
+}
+
+static const struct platform_device_id abx500_pinctrl_id[] = {
+ { "pinctrl-ab8500", PINCTRL_AB8500 },
+ { "pinctrl-ab8540", PINCTRL_AB8540 },
+ { "pinctrl-ab9540", PINCTRL_AB9540 },
+ { "pinctrl-ab8505", PINCTRL_AB8505 },
+ { },
+};
+
+static struct platform_driver abx500_gpio_driver = {
+ .driver = {
+ .name = "abx500-gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = abx500_gpio_probe,
+ .remove = __devexit_p(abx500_gpio_remove),
+ .id_table = abx500_pinctrl_id,
+};
+
+static int __init abx500_gpio_init(void)
+{
+ return platform_driver_register(&abx500_gpio_driver);
+}
+core_initcall(abx500_gpio_init);
+
+MODULE_AUTHOR("Patrice Chotard <patrice.chotard@st.com>");
+MODULE_DESCRIPTION("Driver allows to use AxB5xx unused pins to be used as GPIO");
+MODULE_ALIAS("platform:abx500-gpio");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-abx500.h b/drivers/pinctrl/pinctrl-abx500.h
new file mode 100644
index 0000000..359cb30
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-abx500.h
@@ -0,0 +1,229 @@
+#ifndef PINCTRL_PINCTRL_ABx5O0_H
+#define PINCTRL_PINCTRL_ABx500_H
+
+/* Package definitions */
+#define PINCTRL_AB8500 0
+#define PINCTRL_AB8540 1
+#define PINCTRL_AB9540 2
+#define PINCTRL_AB8505 3
+
+/* pins alternate function */
+enum abx500_pin_func {
+ ABX500_DEFAULT,
+ ABX500_ALT_A,
+ ABX500_ALT_B,
+ ABX500_ALT_C,
+};
+
+/**
+ * struct abx500_function - ABx500 pinctrl mux function
+ * @name: The name of the function, exported to pinctrl core.
+ * @groups: An array of pin groups that may select this function.
+ * @ngroups: The number of entries in @groups.
+ */
+struct abx500_function {
+ const char *name;
+ const char * const *groups;
+ unsigned ngroups;
+};
+
+/**
+ * struct abx500_pingroup - describes a ABx500 pin group
+ * @name: the name of this specific pin group
+ * @pins: an array of discrete physical pins used in this group, taken
+ * from the driver-local pin enumeration space
+ * @num_pins: the number of pins in this group array, i.e. the number of
+ * elements in .pins so we can iterate over that array
+ * @altsetting: the altsetting to apply to all pins in this group to
+ * configure them to be used by a function
+ */
+struct abx500_pingroup {
+ const char *name;
+ const unsigned int *pins;
+ const unsigned npins;
+ int altsetting;
+};
+
+#define ALTERNATE_FUNCTIONS(pin, sel_bit, alt1, alt2, alta, altb, altc) \
+{ \
+ .pin_number = pin, \
+ .gpiosel_bit = sel_bit, \
+ .alt_bit1 = alt1, \
+ .alt_bit2 = alt2, \
+ .alta_val = alta, \
+ .altb_val = altb, \
+ .altc_val = altc, \
+}
+
+#define UNUSED -1
+/**
+ * struct alternate_functions
+ * @pin_number: The pin number
+ * @gpiosel_bit: Control bit in GPIOSEL register,
+ * @alt_bit1: First AlternateFunction bit used to select the
+ * alternate function
+ * @alt_bit2: Second AlternateFunction bit used to select the
+ * alternate function
+ *
+ * these 3 following fields are necessary due to none
+ * coherency on how to select the altA, altB and altC
+ * function between the ABx500 SOC family when using
+ * alternatfunc register.
+ * @alta_val: value to write in alternatfunc to select altA function
+ * @altb_val: value to write in alternatfunc to select altB function
+ * @altc_val: value to write in alternatfunc to select altC function
+ */
+struct alternate_functions {
+ unsigned pin_number;
+ s8 gpiosel_bit;
+ s8 alt_bit1;
+ s8 alt_bit2;
+ u8 alta_val;
+ u8 altb_val;
+ u8 altc_val;
+};
+
+/**
+ * struct pullud - specific pull up/down feature
+ * @first_pin: The pin number of the first pins which support
+ * specific pull up/down
+ * @last_pin: The pin number of the last pins
+ */
+struct pullud {
+ unsigned first_pin;
+ unsigned last_pin;
+};
+
+#define GPIO_IRQ_CLUSTER(a, b, c) \
+{ \
+ .start = a, \
+ .end = b, \
+ .offset = c, \
+}
+
+/**
+ * struct abx500_gpio_irq_cluster - indicates GPIOs which are interrupt
+ * capable
+ * @start: The pin number of the first pin interrupt capable
+ * @end: The pin number of the last pin interrupt capable
+ * @offset: offset used to compute specific setting strategy of
+ * the interrupt line
+ */
+
+struct abx500_gpio_irq_cluster {
+ int start;
+ int end;
+ int offset;
+};
+
+/**
+ * struct abx500_pinrange - map pin numbers to GPIO offsets
+ * @offset: offset into the GPIO local numberspace, incidentally
+ * identical to the offset into the local pin numberspace
+ * @npins: number of pins to map from both offsets
+ */
+struct abx500_pinrange {
+ unsigned int offset;
+ unsigned int npins;
+};
+
+#define ABX500_PINRANGE(a, b) { .offset = a, .npins = b }
+
+/**
+ * struct abx500_pinctrl_soc_data - ABx500 pin controller per-SoC configuration
+ * @gpio_ranges: An array of GPIO ranges for this SoC
+ * @gpio_num_ranges: The number of GPIO ranges for this SoC
+ * @pins: An array describing all pins the pin controller affects.
+ * All pins which are also GPIOs must be listed first within the
+ * array, and be numbered identically to the GPIO controller's
+ * numbering.
+ * @npins: The number of entries in @pins.
+ * @functions: The functions supported on this SoC.
+ * @nfunction: The number of entries in @functions.
+ * @groups: An array describing all pin groups the pin SoC supports.
+ * @ngroups: The number of entries in @groups.
+ * @alternate_functions: array describing pins which supports alternate and
+ * how to set it.
+ * @pullud: array describing pins which supports pull up/down
+ * specific registers.
+ * @gpio_irq_cluster: An array of GPIO interrupt capable for this SoC
+ * @ngpio_irq_cluster: The number of GPIO inetrrupt capable for this SoC
+ * @irq_gpio_rising_offset: Interrupt offset used as base to compute specific
+ * setting strategy of the rising interrupt line
+ * @irq_gpio_falling_offset: Interrupt offset used as base to compute specific
+ * setting strategy of the falling interrupt line
+ * @irq_gpio_factor: Factor used to compute specific setting strategy of
+ * the interrupt line
+ */
+
+struct abx500_pinctrl_soc_data {
+ const struct abx500_pinrange *gpio_ranges;
+ unsigned gpio_num_ranges;
+ const struct pinctrl_pin_desc *pins;
+ unsigned npins;
+ const struct abx500_function *functions;
+ unsigned nfunctions;
+ const struct abx500_pingroup *groups;
+ unsigned ngroups;
+ struct alternate_functions *alternate_functions;
+ struct pullud *pullud;
+ struct abx500_gpio_irq_cluster *gpio_irq_cluster;
+ unsigned ngpio_irq_cluster;
+ int irq_gpio_rising_offset;
+ int irq_gpio_falling_offset;
+ int irq_gpio_factor;
+};
+
+#ifdef CONFIG_PINCTRL_AB8500
+
+void abx500_pinctrl_ab8500_init(struct abx500_pinctrl_soc_data **soc);
+
+#else
+
+static inline void
+abx500_pinctrl_ab8500_init(struct abx500_pinctrl_soc_data **soc)
+{
+}
+
+#endif
+
+#ifdef CONFIG_PINCTRL_AB8540
+
+void abx500_pinctrl_ab8540_init(struct abx500_pinctrl_soc_data **soc);
+
+#else
+
+static inline void
+abx500_pinctrl_ab8540_init(struct abx500_pinctrl_soc_data **soc)
+{
+}
+
+#endif
+
+#ifdef CONFIG_PINCTRL_AB9540
+
+void abx500_pinctrl_ab9540_init(struct abx500_pinctrl_soc_data **soc);
+
+#else
+
+static inline void
+abx500_pinctrl_ab9540_init(struct abx500_pinctrl_soc_data **soc)
+{
+}
+
+#endif
+
+#ifdef CONFIG_PINCTRL_AB8505
+
+void abx500_pinctrl_ab8505_init(struct abx500_pinctrl_soc_data **soc);
+
+#else
+
+static inline void
+abx500_pinctrl_ab8505_init(struct abx500_pinctrl_soc_data **soc)
+{
+}
+
+#endif
+
+#endif /* PINCTRL_PINCTRL_ABx500_H */
diff --git a/include/linux/mfd/abx500/ab8500-gpio.h b/include/linux/mfd/abx500/ab8500-gpio.h
index 2387c20..e8c8281 100644
--- a/include/linux/mfd/abx500/ab8500-gpio.h
+++ b/include/linux/mfd/abx500/ab8500-gpio.h
@@ -14,10 +14,21 @@
* registers.
*/
-struct ab8500_gpio_platform_data {
+struct abx500_gpio_platform_data {
int gpio_base;
u32 irq_base;
- u8 config_reg[8];
+};
+
+enum abx500_gpio_pull_updown {
+ ABX500_GPIO_PULL_DOWN = 0x0,
+ ABX500_GPIO_PULL_NONE = 0x1,
+ ABX500_GPIO_PULL_UP = 0x3,
+};
+
+enum abx500_gpio_vinsel {
+ ABX500_GPIO_VINSEL_VBAT = 0x0,
+ ABX500_GPIO_VINSEL_VIN_1V8 = 0x1,
+ ABX500_GPIO_VINSEL_VDD_BIF = 0x2,
};
#endif /* _AB8500_GPIO_H */
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index e640ea0..fc05344 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -385,7 +385,7 @@ struct ab8500_platform_data {
struct ab8500_regulator_reg_init *regulator_reg_init;
int num_regulator;
struct regulator_init_data *regulator;
- struct ab8500_gpio_platform_data *gpio;
+ struct abx500_gpio_platform_data *gpio;
struct ab8500_codec_platform_data *codec;
};
--
1.7.11.3
^ permalink raw reply related
* [PATCH 2/4] gpio: delete AB8500 driver
From: Linus Walleij @ 2013-01-15 9:42 UTC (permalink / raw)
To: linux-arm-kernel
From: Linus Walleij <linus.walleij@linaro.org>
The AB8500 GPIO driver has been marked BROKEN for ages, and we
have something better in store: a shiny new pinctrl driver. So
let use delete this old driver as the first step.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
drivers/gpio/Kconfig | 6 -
drivers/gpio/Makefile | 1 -
drivers/gpio/gpio-ab8500.c | 520 ---------------------------------------------
3 files changed, 527 deletions(-)
delete mode 100644 drivers/gpio/gpio-ab8500.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 682de75..e5116fa 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -657,12 +657,6 @@ config GPIO_JANZ_TTL
This driver provides support for driving the pins in output
mode only. Input mode is not supported.
-config GPIO_AB8500
- bool "ST-Ericsson AB8500 Mixed Signal Circuit gpio functions"
- depends on AB8500_CORE && BROKEN
- help
- Select this to enable the AB8500 IC GPIO driver
-
config GPIO_TPS6586X
bool "TPS6586X GPIO"
depends on MFD_TPS6586X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index c5aebd0..45a388c 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -10,7 +10,6 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
-obj-$(CONFIG_GPIO_AB8500) += gpio-ab8500.o
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
diff --git a/drivers/gpio/gpio-ab8500.c b/drivers/gpio/gpio-ab8500.c
deleted file mode 100644
index 983ad42..0000000
--- a/drivers/gpio/gpio-ab8500.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2011
- *
- * Author: BIBEK BASU <bibek.basu@stericsson.com>
- * License terms: GNU General Public License (GPL) version 2
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/mfd/ab8500.h>
-#include <linux/mfd/abx500.h>
-#include <linux/mfd/ab8500/gpio.h>
-
-/*
- * GPIO registers offset
- * Bank: 0x10
- */
-#define AB8500_GPIO_SEL1_REG 0x00
-#define AB8500_GPIO_SEL2_REG 0x01
-#define AB8500_GPIO_SEL3_REG 0x02
-#define AB8500_GPIO_SEL4_REG 0x03
-#define AB8500_GPIO_SEL5_REG 0x04
-#define AB8500_GPIO_SEL6_REG 0x05
-
-#define AB8500_GPIO_DIR1_REG 0x10
-#define AB8500_GPIO_DIR2_REG 0x11
-#define AB8500_GPIO_DIR3_REG 0x12
-#define AB8500_GPIO_DIR4_REG 0x13
-#define AB8500_GPIO_DIR5_REG 0x14
-#define AB8500_GPIO_DIR6_REG 0x15
-
-#define AB8500_GPIO_OUT1_REG 0x20
-#define AB8500_GPIO_OUT2_REG 0x21
-#define AB8500_GPIO_OUT3_REG 0x22
-#define AB8500_GPIO_OUT4_REG 0x23
-#define AB8500_GPIO_OUT5_REG 0x24
-#define AB8500_GPIO_OUT6_REG 0x25
-
-#define AB8500_GPIO_PUD1_REG 0x30
-#define AB8500_GPIO_PUD2_REG 0x31
-#define AB8500_GPIO_PUD3_REG 0x32
-#define AB8500_GPIO_PUD4_REG 0x33
-#define AB8500_GPIO_PUD5_REG 0x34
-#define AB8500_GPIO_PUD6_REG 0x35
-
-#define AB8500_GPIO_IN1_REG 0x40
-#define AB8500_GPIO_IN2_REG 0x41
-#define AB8500_GPIO_IN3_REG 0x42
-#define AB8500_GPIO_IN4_REG 0x43
-#define AB8500_GPIO_IN5_REG 0x44
-#define AB8500_GPIO_IN6_REG 0x45
-#define AB8500_GPIO_ALTFUN_REG 0x45
-#define ALTFUN_REG_INDEX 6
-#define AB8500_NUM_GPIO 42
-#define AB8500_NUM_VIR_GPIO_IRQ 16
-
-enum ab8500_gpio_action {
- NONE,
- STARTUP,
- SHUTDOWN,
- MASK,
- UNMASK
-};
-
-struct ab8500_gpio {
- struct gpio_chip chip;
- struct ab8500 *parent;
- struct device *dev;
- struct mutex lock;
- u32 irq_base;
- enum ab8500_gpio_action irq_action;
- u16 rising;
- u16 falling;
-};
-/**
- * to_ab8500_gpio() - get the pointer to ab8500_gpio
- * @chip: Member of the structure ab8500_gpio
- */
-static inline struct ab8500_gpio *to_ab8500_gpio(struct gpio_chip *chip)
-{
- return container_of(chip, struct ab8500_gpio, chip);
-}
-
-static int ab8500_gpio_set_bits(struct gpio_chip *chip, u8 reg,
- unsigned offset, int val)
-{
- struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
- u8 pos = offset % 8;
- int ret;
-
- reg = reg + (offset / 8);
- ret = abx500_mask_and_set_register_interruptible(ab8500_gpio->dev,
- AB8500_MISC, reg, 1 << pos, val << pos);
- if (ret < 0)
- dev_err(ab8500_gpio->dev, "%s write failed\n", __func__);
- return ret;
-}
-/**
- * ab8500_gpio_get() - Get the particular GPIO value
- * @chip: Gpio device
- * @offset: GPIO number to read
- */
-static int ab8500_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
- u8 mask = 1 << (offset % 8);
- u8 reg = AB8500_GPIO_OUT1_REG + (offset / 8);
- int ret;
- u8 data;
- ret = abx500_get_register_interruptible(ab8500_gpio->dev, AB8500_MISC,
- reg, &data);
- if (ret < 0) {
- dev_err(ab8500_gpio->dev, "%s read failed\n", __func__);
- return ret;
- }
- return (data & mask) >> (offset % 8);
-}
-
-static void ab8500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
-{
- struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
- int ret;
- /* Write the data */
- ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, 1);
- if (ret < 0)
- dev_err(ab8500_gpio->dev, "%s write failed\n", __func__);
-}
-
-static int ab8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
- int val)
-{
- int ret;
- /* set direction as output */
- ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 1);
- if (ret < 0)
- return ret;
- /* disable pull down */
- ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, 1);
- if (ret < 0)
- return ret;
- /* set the output as 1 or 0 */
- return ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
-
-}
-
-static int ab8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- /* set the register as input */
- return ab8500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 0);
-}
-
-static int ab8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- /*
- * Only some GPIOs are interrupt capable, and they are
- * organized in discontiguous clusters:
- *
- * GPIO6 to GPIO13
- * GPIO24 and GPIO25
- * GPIO36 to GPIO41
- */
- static struct ab8500_gpio_irq_cluster {
- int start;
- int end;
- } clusters[] = {
- {.start = 6, .end = 13},
- {.start = 24, .end = 25},
- {.start = 36, .end = 41},
- };
- struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
- int base = ab8500_gpio->irq_base;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(clusters); i++) {
- struct ab8500_gpio_irq_cluster *cluster = &clusters[i];
-
- if (offset >= cluster->start && offset <= cluster->end)
- return base + offset - cluster->start;
-
- /* Advance by the number of gpios in this cluster */
- base += cluster->end - cluster->start + 1;
- }
-
- return -EINVAL;
-}
-
-static struct gpio_chip ab8500gpio_chip = {
- .label = "ab8500_gpio",
- .owner = THIS_MODULE,
- .direction_input = ab8500_gpio_direction_input,
- .get = ab8500_gpio_get,
- .direction_output = ab8500_gpio_direction_output,
- .set = ab8500_gpio_set,
- .to_irq = ab8500_gpio_to_irq,
-};
-
-static unsigned int irq_to_rising(unsigned int irq)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- int offset = irq - ab8500_gpio->irq_base;
- int new_irq = offset + AB8500_INT_GPIO6R
- + ab8500_gpio->parent->irq_base;
- return new_irq;
-}
-
-static unsigned int irq_to_falling(unsigned int irq)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- int offset = irq - ab8500_gpio->irq_base;
- int new_irq = offset + AB8500_INT_GPIO6F
- + ab8500_gpio->parent->irq_base;
- return new_irq;
-
-}
-
-static unsigned int rising_to_irq(unsigned int irq, void *dev)
-{
- struct ab8500_gpio *ab8500_gpio = dev;
- int offset = irq - AB8500_INT_GPIO6R
- - ab8500_gpio->parent->irq_base ;
- int new_irq = offset + ab8500_gpio->irq_base;
- return new_irq;
-}
-
-static unsigned int falling_to_irq(unsigned int irq, void *dev)
-{
- struct ab8500_gpio *ab8500_gpio = dev;
- int offset = irq - AB8500_INT_GPIO6F
- - ab8500_gpio->parent->irq_base ;
- int new_irq = offset + ab8500_gpio->irq_base;
- return new_irq;
-
-}
-
-/*
- * IRQ handler
- */
-
-static irqreturn_t handle_rising(int irq, void *dev)
-{
-
- handle_nested_irq(rising_to_irq(irq , dev));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t handle_falling(int irq, void *dev)
-{
-
- handle_nested_irq(falling_to_irq(irq, dev));
- return IRQ_HANDLED;
-}
-
-static void ab8500_gpio_irq_lock(unsigned int irq)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- mutex_lock(&ab8500_gpio->lock);
-}
-
-static void ab8500_gpio_irq_sync_unlock(unsigned int irq)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- int offset = irq - ab8500_gpio->irq_base;
- bool rising = ab8500_gpio->rising & BIT(offset);
- bool falling = ab8500_gpio->falling & BIT(offset);
- int ret;
-
- switch (ab8500_gpio->irq_action) {
- case STARTUP:
- if (rising)
- ret = request_threaded_irq(irq_to_rising(irq),
- NULL, handle_rising,
- IRQF_TRIGGER_RISING,
- "ab8500-gpio-r", ab8500_gpio);
- if (falling)
- ret = request_threaded_irq(irq_to_falling(irq),
- NULL, handle_falling,
- IRQF_TRIGGER_FALLING,
- "ab8500-gpio-f", ab8500_gpio);
- break;
- case SHUTDOWN:
- if (rising)
- free_irq(irq_to_rising(irq), ab8500_gpio);
- if (falling)
- free_irq(irq_to_falling(irq), ab8500_gpio);
- break;
- case MASK:
- if (rising)
- disable_irq(irq_to_rising(irq));
- if (falling)
- disable_irq(irq_to_falling(irq));
- break;
- case UNMASK:
- if (rising)
- enable_irq(irq_to_rising(irq));
- if (falling)
- enable_irq(irq_to_falling(irq));
- break;
- case NONE:
- break;
- }
- ab8500_gpio->irq_action = NONE;
- ab8500_gpio->rising &= ~(BIT(offset));
- ab8500_gpio->falling &= ~(BIT(offset));
- mutex_unlock(&ab8500_gpio->lock);
-}
-
-
-static void ab8500_gpio_irq_mask(unsigned int irq)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- ab8500_gpio->irq_action = MASK;
-}
-
-static void ab8500_gpio_irq_unmask(unsigned int irq)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- ab8500_gpio->irq_action = UNMASK;
-}
-
-static int ab8500_gpio_irq_set_type(unsigned int irq, unsigned int type)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- int offset = irq - ab8500_gpio->irq_base;
-
- if (type == IRQ_TYPE_EDGE_BOTH) {
- ab8500_gpio->rising = BIT(offset);
- ab8500_gpio->falling = BIT(offset);
- } else if (type == IRQ_TYPE_EDGE_RISING) {
- ab8500_gpio->rising = BIT(offset);
- } else {
- ab8500_gpio->falling = BIT(offset);
- }
- return 0;
-}
-
-unsigned int ab8500_gpio_irq_startup(unsigned int irq)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- ab8500_gpio->irq_action = STARTUP;
- return 0;
-}
-
-void ab8500_gpio_irq_shutdown(unsigned int irq)
-{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
- ab8500_gpio->irq_action = SHUTDOWN;
-}
-
-static struct irq_chip ab8500_gpio_irq_chip = {
- .name = "ab8500-gpio",
- .startup = ab8500_gpio_irq_startup,
- .shutdown = ab8500_gpio_irq_shutdown,
- .bus_lock = ab8500_gpio_irq_lock,
- .bus_sync_unlock = ab8500_gpio_irq_sync_unlock,
- .mask = ab8500_gpio_irq_mask,
- .unmask = ab8500_gpio_irq_unmask,
- .set_type = ab8500_gpio_irq_set_type,
-};
-
-static int ab8500_gpio_irq_init(struct ab8500_gpio *ab8500_gpio)
-{
- u32 base = ab8500_gpio->irq_base;
- int irq;
-
- for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ ; irq++) {
- set_irq_chip_data(irq, ab8500_gpio);
- set_irq_chip_and_handler(irq, &ab8500_gpio_irq_chip,
- handle_simple_irq);
- set_irq_nested_thread(irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
-#else
- set_irq_noprobe(irq);
-#endif
- }
-
- return 0;
-}
-
-static void ab8500_gpio_irq_remove(struct ab8500_gpio *ab8500_gpio)
-{
- int base = ab8500_gpio->irq_base;
- int irq;
-
- for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ; irq++) {
-#ifdef CONFIG_ARM
- set_irq_flags(irq, 0);
-#endif
- set_irq_chip_and_handler(irq, NULL, NULL);
- set_irq_chip_data(irq, NULL);
- }
-}
-
-static int ab8500_gpio_probe(struct platform_device *pdev)
-{
- struct ab8500_platform_data *ab8500_pdata =
- dev_get_platdata(pdev->dev.parent);
- struct ab8500_gpio_platform_data *pdata;
- struct ab8500_gpio *ab8500_gpio;
- int ret;
- int i;
-
- pdata = ab8500_pdata->gpio;
- if (!pdata) {
- dev_err(&pdev->dev, "gpio platform data missing\n");
- return -ENODEV;
- }
-
- ab8500_gpio = kzalloc(sizeof(struct ab8500_gpio), GFP_KERNEL);
- if (ab8500_gpio == NULL) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
- return -ENOMEM;
- }
- ab8500_gpio->dev = &pdev->dev;
- ab8500_gpio->parent = dev_get_drvdata(pdev->dev.parent);
- ab8500_gpio->chip = ab8500gpio_chip;
- ab8500_gpio->chip.ngpio = AB8500_NUM_GPIO;
- ab8500_gpio->chip.dev = &pdev->dev;
- ab8500_gpio->chip.base = pdata->gpio_base;
- ab8500_gpio->irq_base = pdata->irq_base;
- /* initialize the lock */
- mutex_init(&ab8500_gpio->lock);
- /*
- * AB8500 core will handle and clear the IRQ
- * configre GPIO based on config-reg value.
- * These values are for selecting the PINs as
- * GPIO or alternate function
- */
- for (i = AB8500_GPIO_SEL1_REG; i <= AB8500_GPIO_SEL6_REG; i++) {
- ret = abx500_set_register_interruptible(ab8500_gpio->dev,
- AB8500_MISC, i,
- pdata->config_reg[i]);
- if (ret < 0)
- goto out_free;
- }
- ret = abx500_set_register_interruptible(ab8500_gpio->dev, AB8500_MISC,
- AB8500_GPIO_ALTFUN_REG,
- pdata->config_reg[ALTFUN_REG_INDEX]);
- if (ret < 0)
- goto out_free;
-
- ret = ab8500_gpio_irq_init(ab8500_gpio);
- if (ret)
- goto out_free;
- ret = gpiochip_add(&ab8500_gpio->chip);
- if (ret) {
- dev_err(&pdev->dev, "unable to add gpiochip: %d\n",
- ret);
- goto out_rem_irq;
- }
- platform_set_drvdata(pdev, ab8500_gpio);
- return 0;
-
-out_rem_irq:
- ab8500_gpio_irq_remove(ab8500_gpio);
-out_free:
- mutex_destroy(&ab8500_gpio->lock);
- kfree(ab8500_gpio);
- return ret;
-}
-
-/*
- * ab8500_gpio_remove() - remove Ab8500-gpio driver
- * @pdev : Platform device registered
- */
-static int ab8500_gpio_remove(struct platform_device *pdev)
-{
- struct ab8500_gpio *ab8500_gpio = platform_get_drvdata(pdev);
- int ret;
-
- ret = gpiochip_remove(&ab8500_gpio->chip);
- if (ret < 0) {
- dev_err(ab8500_gpio->dev, "unable to remove gpiochip: %d\n",
- ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, NULL);
- mutex_destroy(&ab8500_gpio->lock);
- kfree(ab8500_gpio);
-
- return 0;
-}
-
-static struct platform_driver ab8500_gpio_driver = {
- .driver = {
- .name = "ab8500-gpio",
- .owner = THIS_MODULE,
- },
- .probe = ab8500_gpio_probe,
- .remove = ab8500_gpio_remove,
-};
-
-static int __init ab8500_gpio_init(void)
-{
- return platform_driver_register(&ab8500_gpio_driver);
-}
-arch_initcall(ab8500_gpio_init);
-
-static void __exit ab8500_gpio_exit(void)
-{
- platform_driver_unregister(&ab8500_gpio_driver);
-}
-module_exit(ab8500_gpio_exit);
-
-MODULE_AUTHOR("BIBEK BASU <bibek.basu@stericsson.com>");
-MODULE_DESCRIPTION("Driver allows to use AB8500 unused pins to be used as GPIO");
-MODULE_ALIAS("platform:ab8500-gpio");
-MODULE_LICENSE("GPL v2");
--
1.7.11.3
^ permalink raw reply related
* [PATCH 1/4] mfd: ab8500: update header file and version detection
From: Linus Walleij @ 2013-01-15 9:42 UTC (permalink / raw)
To: linux-arm-kernel
From: Linus Walleij <linus.walleij@linaro.org>
This updates the AB8500 register map with defines for a few
new chip variants and adds version detection helpers to handle
the different variants.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
include/linux/mfd/abx500/ab8500.h | 277 ++++++++++++++++++++++++++++++--------
1 file changed, 222 insertions(+), 55 deletions(-)
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index 1cb5698..e640ea0 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -24,7 +24,7 @@ enum ab8500_version {
AB8500_VERSION_AB8500 = 0x0,
AB8500_VERSION_AB8505 = 0x1,
AB8500_VERSION_AB9540 = 0x2,
- AB8500_VERSION_AB8540 = 0x3,
+ AB8500_VERSION_AB8540 = 0x4,
AB8500_VERSION_UNDEFINED,
};
@@ -32,6 +32,7 @@ enum ab8500_version {
#define AB8500_CUTEARLY 0x00
#define AB8500_CUT1P0 0x10
#define AB8500_CUT1P1 0x11
+#define AB8500_CUT1P2 0x12 /* Only valid for AB8540 */
#define AB8500_CUT2P0 0x20
#define AB8500_CUT3P0 0x30
#define AB8500_CUT3P3 0x33
@@ -39,6 +40,7 @@ enum ab8500_version {
/*
* AB8500 bank addresses
*/
+#define AB8500_M_FSM_RANK 0x0
#define AB8500_SYS_CTRL1_BLOCK 0x1
#define AB8500_SYS_CTRL2_BLOCK 0x2
#define AB8500_REGU_CTRL1 0x3
@@ -58,6 +60,7 @@ enum ab8500_version {
#define AB8500_DEVELOPMENT 0x11
#define AB8500_DEBUG 0x12
#define AB8500_PROD_TEST 0x13
+#define AB8500_STE_TEST 0x14
#define AB8500_OTP_EMUL 0x15
/*
@@ -65,11 +68,11 @@ enum ab8500_version {
* Values used to index into array ab8500_irq_regoffset[] defined in
* drivers/mdf/ab8500-core.c
*/
-/* Definitions for AB8500 and AB9540 */
+/* Definitions for AB8500, AB9540 and AB8540 */
/* ab8500_irq_regoffset[0] -> IT[Source|Latch|Mask]1 */
#define AB8500_INT_MAIN_EXT_CH_NOT_OK 0 /* not 8505/9540 */
-#define AB8500_INT_UN_PLUG_TV_DET 1 /* not 8505/9540 */
-#define AB8500_INT_PLUG_TV_DET 2 /* not 8505/9540 */
+#define AB8500_INT_UN_PLUG_TV_DET 1 /* not 8505/9540/8540 */
+#define AB8500_INT_PLUG_TV_DET 2 /* not 8505/9540/8540 */
#define AB8500_INT_TEMP_WARM 3
#define AB8500_INT_PON_KEY2DB_F 4
#define AB8500_INT_PON_KEY2DB_R 5
@@ -77,18 +80,19 @@ enum ab8500_version {
#define AB8500_INT_PON_KEY1DB_R 7
/* ab8500_irq_regoffset[1] -> IT[Source|Latch|Mask]2 */
#define AB8500_INT_BATT_OVV 8
-#define AB8500_INT_MAIN_CH_UNPLUG_DET 10 /* not 8505 */
-#define AB8500_INT_MAIN_CH_PLUG_DET 11 /* not 8505 */
+#define AB8500_INT_MAIN_CH_UNPLUG_DET 10 /* not 8505/8540 */
+#define AB8500_INT_MAIN_CH_PLUG_DET 11 /* not 8505/8540 */
#define AB8500_INT_VBUS_DET_F 14
#define AB8500_INT_VBUS_DET_R 15
/* ab8500_irq_regoffset[2] -> IT[Source|Latch|Mask]3 */
#define AB8500_INT_VBUS_CH_DROP_END 16
#define AB8500_INT_RTC_60S 17
#define AB8500_INT_RTC_ALARM 18
+#define AB8540_INT_BIF_INT 19
#define AB8500_INT_BAT_CTRL_INDB 20
#define AB8500_INT_CH_WD_EXP 21
#define AB8500_INT_VBUS_OVV 22
-#define AB8500_INT_MAIN_CH_DROP_END 23 /* not 8505/9540 */
+#define AB8500_INT_MAIN_CH_DROP_END 23 /* not 8505/9540/8540 */
/* ab8500_irq_regoffset[3] -> IT[Source|Latch|Mask]4 */
#define AB8500_INT_CCN_CONV_ACC 24
#define AB8500_INT_INT_AUD 25
@@ -99,7 +103,7 @@ enum ab8500_version {
#define AB8500_INT_BUP_CHG_NOT_OK 30
#define AB8500_INT_BUP_CHG_OK 31
/* ab8500_irq_regoffset[4] -> IT[Source|Latch|Mask]5 */
-#define AB8500_INT_GP_HW_ADC_CONV_END 32 /* not 8505 */
+#define AB8500_INT_GP_HW_ADC_CONV_END 32 /* not 8505/8540 */
#define AB8500_INT_ACC_DETECT_1DB_F 33
#define AB8500_INT_ACC_DETECT_1DB_R 34
#define AB8500_INT_ACC_DETECT_22DB_F 35
@@ -108,23 +112,23 @@ enum ab8500_version {
#define AB8500_INT_ACC_DETECT_21DB_R 38
#define AB8500_INT_GP_SW_ADC_CONV_END 39
/* ab8500_irq_regoffset[5] -> IT[Source|Latch|Mask]7 */
-#define AB8500_INT_GPIO6R 40 /* not 8505/9540 */
-#define AB8500_INT_GPIO7R 41 /* not 8505/9540 */
-#define AB8500_INT_GPIO8R 42 /* not 8505/9540 */
-#define AB8500_INT_GPIO9R 43 /* not 8505/9540 */
-#define AB8500_INT_GPIO10R 44
-#define AB8500_INT_GPIO11R 45
-#define AB8500_INT_GPIO12R 46 /* not 8505 */
-#define AB8500_INT_GPIO13R 47
+#define AB8500_INT_GPIO6R 40 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO7R 41 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO8R 42 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO9R 43 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO10R 44 /* not 8540 */
+#define AB8500_INT_GPIO11R 45 /* not 8540 */
+#define AB8500_INT_GPIO12R 46 /* not 8505/8540 */
+#define AB8500_INT_GPIO13R 47 /* not 8540 */
/* ab8500_irq_regoffset[6] -> IT[Source|Latch|Mask]8 */
-#define AB8500_INT_GPIO24R 48 /* not 8505 */
-#define AB8500_INT_GPIO25R 49 /* not 8505 */
-#define AB8500_INT_GPIO36R 50 /* not 8505/9540 */
-#define AB8500_INT_GPIO37R 51 /* not 8505/9540 */
-#define AB8500_INT_GPIO38R 52 /* not 8505/9540 */
-#define AB8500_INT_GPIO39R 53 /* not 8505/9540 */
-#define AB8500_INT_GPIO40R 54
-#define AB8500_INT_GPIO41R 55
+#define AB8500_INT_GPIO24R 48 /* not 8505/8540 */
+#define AB8500_INT_GPIO25R 49 /* not 8505/8540 */
+#define AB8500_INT_GPIO36R 50 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO37R 51 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO38R 52 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO39R 53 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO40R 54 /* not 8540 */
+#define AB8500_INT_GPIO41R 55 /* not 8540 */
/* ab8500_irq_regoffset[7] -> IT[Source|Latch|Mask]9 */
#define AB8500_INT_GPIO6F 56 /* not 8505/9540 */
#define AB8500_INT_GPIO7F 57 /* not 8505/9540 */
@@ -135,14 +139,14 @@ enum ab8500_version {
#define AB8500_INT_GPIO12F 62 /* not 8505 */
#define AB8500_INT_GPIO13F 63
/* ab8500_irq_regoffset[8] -> IT[Source|Latch|Mask]10 */
-#define AB8500_INT_GPIO24F 64 /* not 8505 */
-#define AB8500_INT_GPIO25F 65 /* not 8505 */
-#define AB8500_INT_GPIO36F 66 /* not 8505/9540 */
-#define AB8500_INT_GPIO37F 67 /* not 8505/9540 */
-#define AB8500_INT_GPIO38F 68 /* not 8505/9540 */
-#define AB8500_INT_GPIO39F 69 /* not 8505/9540 */
-#define AB8500_INT_GPIO40F 70
-#define AB8500_INT_GPIO41F 71
+#define AB8500_INT_GPIO24F 64 /* not 8505/8540 */
+#define AB8500_INT_GPIO25F 65 /* not 8505/8540 */
+#define AB8500_INT_GPIO36F 66 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO37F 67 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO38F 68 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO39F 69 /* not 8505/9540/8540 */
+#define AB8500_INT_GPIO40F 70 /* not 8540 */
+#define AB8500_INT_GPIO41F 71 /* not 8540 */
/* ab8500_irq_regoffset[9] -> IT[Source|Latch|Mask]12 */
#define AB8500_INT_ADP_SOURCE_ERROR 72
#define AB8500_INT_ADP_SINK_ERROR 73
@@ -160,42 +164,44 @@ enum ab8500_version {
#define AB8500_INT_SRP_DETECT 88
#define AB8500_INT_USB_CHARGER_NOT_OKR 89
#define AB8500_INT_ID_WAKEUP_R 90
+#define AB8500_INT_ID_DET_PLUGR 91 /* 8505/9540 cut2.0 */
#define AB8500_INT_ID_DET_R1R 92
#define AB8500_INT_ID_DET_R2R 93
#define AB8500_INT_ID_DET_R3R 94
#define AB8500_INT_ID_DET_R4R 95
/* ab8500_irq_regoffset[12] -> IT[Source|Latch|Mask]21 */
-#define AB8500_INT_ID_WAKEUP_F 96
-#define AB8500_INT_ID_DET_R1F 98
-#define AB8500_INT_ID_DET_R2F 99
-#define AB8500_INT_ID_DET_R3F 100
-#define AB8500_INT_ID_DET_R4F 101
-#define AB8500_INT_CHAUTORESTARTAFTSEC 102
+#define AB8500_INT_ID_WAKEUP_F 96 /* not 8505/9540 */
+#define AB8500_INT_ID_DET_PLUGF 97 /* 8505/9540 cut2.0 */
+#define AB8500_INT_ID_DET_R1F 98 /* not 8505/9540 */
+#define AB8500_INT_ID_DET_R2F 99 /* not 8505/9540 */
+#define AB8500_INT_ID_DET_R3F 100 /* not 8505/9540 */
+#define AB8500_INT_ID_DET_R4F 101 /* not 8505/9540 */
+#define AB8500_INT_CHAUTORESTARTAFTSEC 102 /* not 8505/9540 */
#define AB8500_INT_CHSTOPBYSEC 103
/* ab8500_irq_regoffset[13] -> IT[Source|Latch|Mask]22 */
#define AB8500_INT_USB_CH_TH_PROT_F 104
-#define AB8500_INT_USB_CH_TH_PROT_R 105
+#define AB8500_INT_USB_CH_TH_PROT_R 105
#define AB8500_INT_MAIN_CH_TH_PROT_F 106 /* not 8505/9540 */
#define AB8500_INT_MAIN_CH_TH_PROT_R 107 /* not 8505/9540 */
#define AB8500_INT_CHCURLIMNOHSCHIRP 109
#define AB8500_INT_CHCURLIMHSCHIRP 110
#define AB8500_INT_XTAL32K_KO 111
-/* Definitions for AB9540 */
+/* Definitions for AB9540 / AB8505 */
/* ab8500_irq_regoffset[14] -> IT[Source|Latch|Mask]13 */
-#define AB9540_INT_GPIO50R 113
-#define AB9540_INT_GPIO51R 114 /* not 8505 */
-#define AB9540_INT_GPIO52R 115
-#define AB9540_INT_GPIO53R 116
-#define AB9540_INT_GPIO54R 117 /* not 8505 */
+#define AB9540_INT_GPIO50R 113 /* not 8540 */
+#define AB9540_INT_GPIO51R 114 /* not 8505/8540 */
+#define AB9540_INT_GPIO52R 115 /* not 8540 */
+#define AB9540_INT_GPIO53R 116 /* not 8540 */
+#define AB9540_INT_GPIO54R 117 /* not 8505/8540 */
#define AB9540_INT_IEXT_CH_RF_BFN_R 118
-#define AB9540_INT_IEXT_CH_RF_BFN_F 119
/* ab8500_irq_regoffset[15] -> IT[Source|Latch|Mask]14 */
-#define AB9540_INT_GPIO50F 121
-#define AB9540_INT_GPIO51F 122 /* not 8505 */
-#define AB9540_INT_GPIO52F 123
-#define AB9540_INT_GPIO53F 124
-#define AB9540_INT_GPIO54F 125 /* not 8505 */
+#define AB9540_INT_GPIO50F 121 /* not 8540 */
+#define AB9540_INT_GPIO51F 122 /* not 8505/8540 */
+#define AB9540_INT_GPIO52F 123 /* not 8540 */
+#define AB9540_INT_GPIO53F 124 /* not 8540 */
+#define AB9540_INT_GPIO54F 125 /* not 8505/8540 */
+#define AB9540_INT_IEXT_CH_RF_BFN_F 126
/* ab8500_irq_regoffset[16] -> IT[Source|Latch|Mask]25 */
#define AB8505_INT_KEYSTUCK 128
#define AB8505_INT_IKR 129
@@ -204,6 +210,87 @@ enum ab8500_version {
#define AB8505_INT_KEYDEGLITCH 132
#define AB8505_INT_MODPWRSTATUSF 134
#define AB8505_INT_MODPWRSTATUSR 135
+/* ab8500_irq_regoffset[17] -> IT[Source|Latch|Mask]6 */
+#define AB8500_INT_HOOK_DET_NEG_F 138
+#define AB8500_INT_HOOK_DET_NEG_R 139
+#define AB8500_INT_HOOK_DET_POS_F 140
+#define AB8500_INT_HOOK_DET_POS_R 141
+#define AB8500_INT_PLUG_DET_COMP_F 142
+#define AB8500_INT_PLUG_DET_COMP_R 143
+/* ab8500_irq_regoffset[18] -> IT[Source|Latch|Mask]23 */
+#define AB8505_INT_COLL 144
+#define AB8505_INT_RESERR 145
+#define AB8505_INT_FRAERR 146
+#define AB8505_INT_COMERR 147
+#define AB8505_INT_SPDSET 148
+#define AB8505_INT_DSENT 149
+#define AB8505_INT_DREC 150
+#define AB8505_INT_ACC_INT 151
+/* ab8500_irq_regoffset[19] -> IT[Source|Latch|Mask]24 */
+#define AB8505_INT_NOPINT 152
+/* ab8540_irq_regoffset[20] -> IT[Source|Latch|Mask]26 */
+#define AB8540_INT_IDPLUGDETCOMPF 160
+#define AB8540_INT_IDPLUGDETCOMPR 161
+#define AB8540_INT_FMDETCOMPLOF 162
+#define AB8540_INT_FMDETCOMPLOR 163
+#define AB8540_INT_FMDETCOMPHIF 164
+#define AB8540_INT_FMDETCOMPHIR 165
+#define AB8540_INT_ID5VDETCOMPF 166
+#define AB8540_INT_ID5VDETCOMPR 167
+/* ab8540_irq_regoffset[21] -> IT[Source|Latch|Mask]27 */
+#define AB8540_INT_GPIO43F 168
+#define AB8540_INT_GPIO43R 169
+#define AB8540_INT_GPIO44F 170
+#define AB8540_INT_GPIO44R 171
+#define AB8540_INT_KEYPOSDETCOMPF 172
+#define AB8540_INT_KEYPOSDETCOMPR 173
+#define AB8540_INT_KEYNEGDETCOMPF 174
+#define AB8540_INT_KEYNEGDETCOMPR 175
+/* ab8540_irq_regoffset[22] -> IT[Source|Latch|Mask]28 */
+#define AB8540_INT_GPIO1VBATF 176
+#define AB8540_INT_GPIO1VBATR 177
+#define AB8540_INT_GPIO2VBATF 178
+#define AB8540_INT_GPIO2VBATR 179
+#define AB8540_INT_GPIO3VBATF 180
+#define AB8540_INT_GPIO3VBATR 181
+#define AB8540_INT_GPIO4VBATF 182
+#define AB8540_INT_GPIO4VBATR 183
+/* ab8540_irq_regoffset[23] -> IT[Source|Latch|Mask]29 */
+#define AB8540_INT_SYSCLKREQ2F 184
+#define AB8540_INT_SYSCLKREQ2R 185
+#define AB8540_INT_SYSCLKREQ3F 186
+#define AB8540_INT_SYSCLKREQ3R 187
+#define AB8540_INT_SYSCLKREQ4F 188
+#define AB8540_INT_SYSCLKREQ4R 189
+#define AB8540_INT_SYSCLKREQ5F 190
+#define AB8540_INT_SYSCLKREQ5R 191
+/* ab8540_irq_regoffset[24] -> IT[Source|Latch|Mask]30 */
+#define AB8540_INT_PWMOUT1F 192
+#define AB8540_INT_PWMOUT1R 193
+#define AB8540_INT_PWMCTRL0F 194
+#define AB8540_INT_PWMCTRL0R 195
+#define AB8540_INT_PWMCTRL1F 196
+#define AB8540_INT_PWMCTRL1R 197
+#define AB8540_INT_SYSCLKREQ6F 198
+#define AB8540_INT_SYSCLKREQ6R 199
+/* ab8540_irq_regoffset[25] -> IT[Source|Latch|Mask]31 */
+#define AB8540_INT_PWMEXTVIBRA1F 200
+#define AB8540_INT_PWMEXTVIBRA1R 201
+#define AB8540_INT_PWMEXTVIBRA2F 202
+#define AB8540_INT_PWMEXTVIBRA2R 203
+#define AB8540_INT_PWMOUT2F 204
+#define AB8540_INT_PWMOUT2R 205
+#define AB8540_INT_PWMOUT3F 206
+#define AB8540_INT_PWMOUT3R 207
+/* ab8540_irq_regoffset[26] -> IT[Source|Latch|Mask]32 */
+#define AB8540_INT_ADDATA2F 208
+#define AB8540_INT_ADDATA2R 209
+#define AB8540_INT_DADATA2F 210
+#define AB8540_INT_DADATA2R 211
+#define AB8540_INT_FSYNC2F 212
+#define AB8540_INT_FSYNC2R 213
+#define AB8540_INT_BITCLK2F 214
+#define AB8540_INT_BITCLK2R 215
/*
* AB8500_AB9540_NR_IRQS is used when configuring the IRQ numbers for the
@@ -213,13 +300,24 @@ enum ab8500_version {
* which is larger.
*/
#define AB8500_NR_IRQS 112
-#define AB8505_NR_IRQS 136
-#define AB9540_NR_IRQS 136
+#define AB8505_NR_IRQS 153
+#define AB9540_NR_IRQS 153
+#define AB8540_NR_IRQS 216
/* This is set to the roof of any AB8500 chip variant IRQ counts */
-#define AB8500_MAX_NR_IRQS AB9540_NR_IRQS
+#define AB8500_MAX_NR_IRQS AB8540_NR_IRQS
#define AB8500_NUM_IRQ_REGS 14
-#define AB9540_NUM_IRQ_REGS 17
+#define AB9540_NUM_IRQ_REGS 20
+#define AB8540_NUM_IRQ_REGS 27
+
+/* Turn On Status Event */
+#define AB8500_POR_ON_VBAT 0x01
+#define AB8500_POW_KEY_1_ON 0x02
+#define AB8500_POW_KEY_2_ON 0x04
+#define AB8500_RTC_ALARM 0x08
+#define AB8500_MAIN_CH_DET 0x10
+#define AB8500_VBUS_DET 0x20
+#define AB8500_USB_ID_DET 0x40
/**
* struct ab8500 - ab8500 internal structure
@@ -335,10 +433,79 @@ static inline int is_ab8500_2p0_or_earlier(struct ab8500 *ab)
return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT2P0));
}
+static inline int is_ab8500_3p3_or_earlier(struct ab8500 *ab)
+{
+ return (is_ab8500(ab) && (ab->chip_id <= AB8500_CUT3P3));
+}
+
/* exclude also ab8505, ab9540... */
static inline int is_ab8500_2p0(struct ab8500 *ab)
{
return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
}
+static inline int is_ab8505_1p0_or_earlier(struct ab8500 *ab)
+{
+ return (is_ab8505(ab) && (ab->chip_id <= AB8500_CUT1P0));
+}
+
+static inline int is_ab8505_2p0(struct ab8500 *ab)
+{
+ return (is_ab8505(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
+
+static inline int is_ab9540_1p0_or_earlier(struct ab8500 *ab)
+{
+ return (is_ab9540(ab) && (ab->chip_id <= AB8500_CUT1P0));
+}
+
+static inline int is_ab9540_2p0(struct ab8500 *ab)
+{
+ return (is_ab9540(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
+
+/*
+ * Be careful, the marketing name for this chip is 2.1
+ * but the value read from the chip is 3.0 (0x30)
+ */
+static inline int is_ab9540_3p0(struct ab8500 *ab)
+{
+ return (is_ab9540(ab) && (ab->chip_id == AB8500_CUT3P0));
+}
+
+static inline int is_ab8540_1p0_or_earlier(struct ab8500 *ab)
+{
+ return is_ab8540(ab) && (ab->chip_id <= AB8500_CUT1P0);
+}
+
+static inline int is_ab8540_1p1_or_earlier(struct ab8500 *ab)
+{
+ return is_ab8540(ab) && (ab->chip_id <= AB8500_CUT1P1);
+}
+
+static inline int is_ab8540_1p2_or_earlier(struct ab8500 *ab)
+{
+ return is_ab8540(ab) && (ab->chip_id <= AB8500_CUT1P2);
+}
+
+static inline int is_ab8540_2p0_or_earlier(struct ab8500 *ab)
+{
+ return is_ab8540(ab) && (ab->chip_id <= AB8500_CUT2P0);
+}
+
+static inline int is_ab8540_2p0(struct ab8500 *ab)
+{
+ return is_ab8540(ab) && (ab->chip_id == AB8500_CUT2P0);
+}
+
+static inline int is_ab8505_2p0_earlier(struct ab8500 *ab)
+{
+ return (is_ab8505(ab) && (ab->chip_id < AB8500_CUT2P0));
+}
+
+static inline int is_ab9540_2p0_or_earlier(struct ab8500 *ab)
+{
+ return (is_ab9540(ab) && (ab->chip_id < AB8500_CUT2P0));
+}
+
#endif /* MFD_AB8500_H */
--
1.7.11.3
^ permalink raw reply related
* [PATCH v5 6/9] ARM: davinci: Remoteproc driver support for OMAP-L138 DSP
From: Sekhar Nori @ 2013-01-15 9:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <13514BD7FAEBA745BBD7D8A672905C14311FE538@DFLE08.ent.ti.com>
On 1/12/2013 7:56 AM, Tivy, Robert wrote:
>> From: Ohad Ben-Cohen [mailto:ohad at wizery.com]
>> Sent: Friday, January 11, 2013 4:26 AM
>> On Fri, Jan 11, 2013 at 2:23 AM, Robert Tivy <rtivy@ti.com> wrote:
>>> +static int davinci_rproc_probe(struct platform_device *pdev)
>>> +{
>>> + struct da8xx_rproc_pdata *pdata = pdev->dev.platform_data;
>>> + struct davinci_rproc *drproc;
>>> + struct rproc *rproc;
>>> + struct clk *dsp_clk;
>>> + int ret;
>>> +
>>> + if (!fw_name) {
>>> + dev_err(&pdev->dev, "No firmware file specified\n");
>>> +
>>> + return -EINVAL;
>>> + }
>> There are a few issues with this fw_name module param:
>>
>> 1. Usually we don't rely on users providing the firmware file name for
>> drivers to work. Drivers should know the name beforehand, and if there
>> may be several different instances of firmwares (for different cores
>> you may have), then it's just better to get it from the platform data.
>
> Is this suggesting that there be separate platform device instances for each different potential fw, and that each platform device instance hardcodes the fw filename?
I am not convinced firmware name should be in platform data (or DT)
since it is not hardware specific. User can choose multiple different
firmwares to load on the DSP depending the application he is running all
for the same platform (da850 evm).
>
>>
>> 2. You may still want to have such a module param in order to be able
>> to override the default firmware name (for debugging purposes?), but
>> I'm not sure it should be davinci-specific. if we do want it to be
>> then please prefix the name with 'davinci'.
>
> Sekhar asked that there not be a default fw name, so there's conflicting feedback on this point. I prefer to have a default name plus the module parameter override (but don't have much opinion on whether it should be davinci-specific (and passed with davinci_remoteproc.ko) or general (and passed with remoteproc.ko), please advise).
Rob, I don't remember objecting to a default firmware name if module
parameter is not passed. On 29th November 2012 you wrote:
"
Sounds OK. I propose then to have the above be the default firmware
name, along with a module parameter that will override if specified.
"
and I wrote back:
"
Sounds good.
"
As you can see, there was no objection from me.
>
> Since the fw file (i.e., DSP program) is typically paired with a particular Linux app, I like the ability to specify the fw filename at runtime, depending on the Linux app I need to run.
Right, and platform data is not the way to achieve this.
Thanks,
Sekhar
^ permalink raw reply
* [PATCH 04/18] power: ab8500_fg: Replace msleep() with usleep_range() for greater accuracy
From: Linus Walleij @ 2013-01-15 8:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130115084821.GT12385@gmail.com>
On Tue, Jan 15, 2013 at 9:48 AM, Lee Jones <lee.jones@linaro.org> wrote:
> On Mon, 14 Jan 2013, Joe Perches wrote:
>> > - msleep(5);
>> > + usleep_range(5000, 5001);
>>
>> If you're going to give a range that small
>> you might as well use usleep instead.
>>
>> Otherwise, add some tolerance to allow any
>> other coalesced wakeup to occur.
>
> I can't increase the tolerance, as I don't know how that would
> effect the running of the system, and the person who would know
> is off on parental leave.
>
> What I can tell you is we're only using usleep_range() because
> there is no usleep in the kernel. At least that's what we've
> been led to believe:
>
> Documentation/timers/timers-howto.txt:
>
> - Why is there no "usleep" / What is a good range?
> Since usleep_range is built on top of hrtimers, the
> wakeup will be very precise (ish), thus a simple
> usleep function would likely introduce a large number
> of undesired interrupts.
And I think the above is why we have this in the kernel:
arch/arm/mach-davinci/board-da850-evm.c:
usleep_range(1000, 1000);
arch/arm/mach-tegra/pcie.c: usleep_range(1000, 1000);
drivers/clk/clk-wm831x.c: usleep_range(2000, 2000);
drivers/media/i2c/m5mols/m5mols_core.c: usleep_range(200, 200);
drivers/media/i2c/s5k6aa.c: usleep_range(4000, 4000);
drivers/media/i2c/smiapp/smiapp-core.c: usleep_range(1000, 1000);
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c:
usleep_range(1000, 1000);
There are quite a few of these.
Let's ping John Stultz for some clarification ...
Yours,
Linus Walleij
^ permalink raw reply
* OMAP baseline test results for v3.8-rc3
From: Felipe Balbi @ 2013-01-15 8:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130115073225.GB14505@arwen.pp.htv.fi>
Hi,
(quick parens here)
I think n900's problems will go away if you add omap_i2c_wait_for_ardy()
from my patchset.
On Tue, Jan 15, 2013 at 09:32:25AM +0200, Felipe Balbi wrote:
> > [ 4.281280] twl 1-0048: PIH (irq 23) chaining IRQs 338..346
> > [ 4.281433] twl 1-0048: power (irq 343) chaining IRQs 346..353
> > [ 4.281616] i2c i2c-1: master_xfer[0] W, addr=0x49, len=1
> > [ 4.281677] i2c i2c-1: master_xfer[1] R, addr=0x49, len=1
> > [ 4.281707] omap_i2c omap_i2c.1: addr: 0x0049, len: 1, flags: 0x0, stop: 0
> > [ 4.281799] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
> > [ 4.281829] omap_i2c omap_i2c.1: addr: 0x0049, len: 1, flags: 0x1, stop: 1
> > [ 4.281921] omap_i2c omap_i2c.1: IRQ (ISR = 0x0008)
> > [ 4.296905] i2c i2c-1: master_xfer[0] W, addr=0x49, len=2
> > [ 4.296936] omap_i2c omap_i2c.1: addr: 0x0049, len: 2, flags: 0x0, stop: 1
> > [ 4.296997] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
> > [ 4.313476] twl4030_gpio twl4030_gpio: gpio (irq 338) chaining IRQs 354..371
> >
> > This log excerpt has only 3 transfers, but the time duration is already
> > 10x longer compared to vanilla 3.8-rc3.
>
> weird, there's nothing extremely expensive added by my patchset, I'll go
> over them and try to figure out what's going on.
>
> Thanks for notifying me about it.
Here's from v3.8-rc3 vanilla (just added two small prints):
[ 1.688079] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x0, stop: 0
[ 1.688110] omap_i2c omap_i2c.1: starting transfer
[ 1.688140] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 1.688201] omap_i2c omap_i2c.1: IRQ (ISR = 0x0004)
[ 1.688232] omap_i2c omap_i2c.1: finishing transfer
[ 1.688232] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x1, stop: 1
[ 1.688262] omap_i2c omap_i2c.1: starting transfer
[ 1.688354] omap_i2c omap_i2c.1: IRQ (ISR = 0x0008)
[ 1.688354] omap_i2c omap_i2c.1: IRQ (ISR = 0x0004)
[ 1.688385] omap_i2c omap_i2c.1: finishing transfer
[ 1.688415] i2c i2c-1: master_xfer[0] W, addr=0x48, len=2
[ 1.688446] omap_i2c omap_i2c.1: addr: 0x0048, len: 2, flags: 0x0, stop: 1
[ 1.688446] omap_i2c omap_i2c.1: starting transfer
[ 1.688476] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 1.688568] omap_i2c omap_i2c.1: IRQ (ISR = 0x0004)
[ 1.688598] omap_i2c omap_i2c.1: finishing transfer
[ 1.688629] i2c i2c-1: master_xfer[0] W, addr=0x48, len=1
[ 1.688659] i2c i2c-1: master_xfer[1] R, addr=0x48, len=6
[ 1.688659] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x0, stop: 0
[ 1.688690] omap_i2c omap_i2c.1: starting transfer
[ 1.688720] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 1.688781] omap_i2c omap_i2c.1: IRQ (ISR = 0x0004)
[ 1.688812] omap_i2c omap_i2c.1: finishing transfer
[ 1.688812] omap_i2c omap_i2c.1: addr: 0x0048, len: 6, flags: 0x1, stop: 1
and here's from my patchset:
[ 1.993865] omap_i2c omap_i2c.1: addr: 0x0049, len: 1, flags: 0x0, stop: 0
[ 1.993896] omap_i2c omap_i2c.1: starting transfer
[ 1.993927] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 1.993957] omap_i2c omap_i2c.1: finished transfer
[ 1.993988] omap_i2c omap_i2c.1: addr: 0x0049, len: 1, flags: 0x1, stop: 1
[ 1.993988] omap_i2c omap_i2c.1: starting transfer
[ 1.994079] omap_i2c omap_i2c.1: IRQ (ISR = 0x0008)
[ 1.994110] omap_i2c omap_i2c.1: finished transfer
[ 2.009490] i2c i2c-1: master_xfer[0] W, addr=0x49, len=2
[ 2.009490] omap_i2c omap_i2c.1: addr: 0x0049, len: 2, flags: 0x0, stop: 1
[ 2.009521] omap_i2c omap_i2c.1: starting transfer
[ 2.009552] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 2.009582] omap_i2c omap_i2c.1: finished transfer
[ 2.025054] i2c i2c-1: master_xfer[0] W, addr=0x49, len=1
[ 2.025054] i2c i2c-1: master_xfer[1] R, addr=0x49, len=1
[ 2.025085] omap_i2c omap_i2c.1: addr: 0x0049, len: 1, flags: 0x0, stop: 0
[ 2.025085] omap_i2c omap_i2c.1: starting transfer
[ 2.025146] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 2.025177] omap_i2c omap_i2c.1: finished transfer
[ 2.025177] omap_i2c omap_i2c.1: addr: 0x0049, len: 1, flags: 0x1, stop: 1
[ 2.025207] omap_i2c omap_i2c.1: starting transfer
[ 2.025268] omap_i2c omap_i2c.1: IRQ (ISR = 0x0008)
[ 2.025329] omap_i2c omap_i2c.1: finished transfer
[ 2.040679] i2c i2c-1: master_xfer[0] W, addr=0x49, len=2
[ 2.040710] omap_i2c omap_i2c.1: addr: 0x0049, len: 2, flags: 0x0, stop: 1
[ 2.040710] omap_i2c omap_i2c.1: starting transfer
[ 2.040771] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 2.040802] omap_i2c omap_i2c.1: finished transfer
Looks like the transfer itself is faster with my patchset. I don't have
an idea of what's causing extra delay, for whatever reason client driver
seems to take longer to start more i2c transfers. I've added extra
prints to see what's going on:
| [ 1.818725] omap_i2c omap_i2c.1: pm_runtime_get_sync()
| [ 1.818725] omap_i2c omap_i2c.1: omap_i2c_wait_for_bb()
| [ 1.818725] omap_i2c omap_i2c.1: omap_i2c_xfer_msg()
| [ 1.818756] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x0, stop: 0
| [ 1.818756] omap_i2c omap_i2c.1: starting transfer
| [ 1.818786] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
| [ 1.818847] omap_i2c omap_i2c.1: finished transfer
| [ 1.818847] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x1, stop: 1
| [ 1.818847] omap_i2c omap_i2c.1: starting transfer
| [ 1.818939] omap_i2c omap_i2c.1: IRQ (ISR = 0x0008)
| [ 1.818969] omap_i2c omap_i2c.1: finished transfer
| [ 1.819000] omap_i2c omap_i2c.1: waiting for ARDY
| [ 1.819000] omap_i2c omap_i2c.1: ARDY
| [ 1.819000] omap_i2c omap_i2c.1: 2. omap_i2c_wait_for_bb()
| [ 1.819030] omap_i2c omap_i2c.1: pm_runtime_mark_last_busy()
| [ 1.819030] omap_i2c omap_i2c.1: return
| [ 1.819061] i2c i2c-1: master_xfer[0] W, addr=0x48, len=1
| [ 1.819061] i2c i2c-1: master_xfer[1] R, addr=0x48, len=1
| [ 1.819091] omap_i2c omap_i2c.1: pm_runtime_get_sync()
| [ 1.819091] omap_i2c omap_i2c.1: omap_i2c_wait_for_bb()
| [ 1.819122] omap_i2c omap_i2c.1: omap_i2c_xfer_msg()
| [ 1.819122] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x0, stop: 0
| [ 1.819122] omap_i2c omap_i2c.1: starting transfer
| [ 1.819183] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
| [ 1.819213] omap_i2c omap_i2c.1: finished transfer
| [ 1.819213] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x1, stop: 1
| [ 1.819244] omap_i2c omap_i2c.1: starting transfer
| [ 1.819305] omap_i2c omap_i2c.1: IRQ (ISR = 0x0008)
| [ 1.819366] omap_i2c omap_i2c.1: finished transfer
| [ 1.819366] omap_i2c omap_i2c.1: waiting for ARDY
| [ 1.819366] omap_i2c omap_i2c.1: ARDY
| [ 1.819396] omap_i2c omap_i2c.1: 2. omap_i2c_wait_for_bb()
| [ 1.819396] omap_i2c omap_i2c.1: pm_runtime_mark_last_busy()
| [ 1.819396] omap_i2c omap_i2c.1: return
| [ 1.819427] i2c i2c-1: master_xfer[0] W, addr=0x48, len=1
| [ 1.819458] i2c i2c-1: master_xfer[1] R, addr=0x48, len=1
| [ 1.819458] omap_i2c omap_i2c.1: pm_runtime_get_sync()
| [ 1.819458] omap_i2c omap_i2c.1: omap_i2c_wait_for_bb()
| [ 1.819488] omap_i2c omap_i2c.1: omap_i2c_xfer_msg()
| [ 1.819488] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x0, stop: 0
| [ 1.819519] omap_i2c omap_i2c.1: starting transfer
| [ 1.819549] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
| [ 1.819580] omap_i2c omap_i2c.1: finished transfer
| [ 1.819580] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x1, stop: 1
| [ 1.819610] omap_i2c omap_i2c.1: starting transfer
| [ 1.819671] omap_i2c omap_i2c.1: IRQ (ISR = 0x0008)
| [ 1.819732] omap_i2c omap_i2c.1: finished transfer
| [ 1.819732] omap_i2c omap_i2c.1: waiting for ARDY
| [ 1.819732] omap_i2c omap_i2c.1: ARDY
| [ 1.819763] omap_i2c omap_i2c.1: 2. omap_i2c_wait_for_bb()
| [ 1.819763] omap_i2c omap_i2c.1: pm_runtime_mark_last_busy()
| [ 1.819793] omap_i2c omap_i2c.1: return
as you can see, from pm_runtime_get_sync() to return, the time spent is
about the same in all cases (around 305us).
one thing I note, however, is that after we start waiting for ARDY,
there's no point in "omap_i2c_wait_for_bb()" since I only exit
omap_i2c_xfer_msg() after all bytes have been unloaded on SDA line.
Removing that saves some extra time (about 30us):
[ 1.751983] omap_i2c omap_i2c.1: pm_runtime_get_sync()
[ 1.751983] omap_i2c omap_i2c.1: omap_i2c_xfer_msg()
[ 1.751983] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x0, stop: 0
[ 1.752014] omap_i2c omap_i2c.1: starting transfer
[ 1.752044] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 1.752075] omap_i2c omap_i2c.1: finished transfer
[ 1.752105] omap_i2c omap_i2c.1: addr: 0x0048, len: 1, flags: 0x1, stop: 1
[ 1.752105] omap_i2c omap_i2c.1: starting transfer
[ 1.752197] omap_i2c omap_i2c.1: IRQ (ISR = 0x0008)
[ 1.752227] omap_i2c omap_i2c.1: finished transfer
[ 1.752227] omap_i2c omap_i2c.1: waiting for ARDY
[ 1.752258] omap_i2c omap_i2c.1: ARDY
[ 1.752258] omap_i2c omap_i2c.1: pm_runtime_mark_last_busy()
[ 1.752258] omap_i2c omap_i2c.1: return
[ 1.752288] i2c i2c-1: master_xfer[0] W, addr=0x48, len=2
[ 1.752319] omap_i2c omap_i2c.1: pm_runtime_get_sync()
[ 1.752319] omap_i2c omap_i2c.1: omap_i2c_xfer_msg()
[ 1.752349] omap_i2c omap_i2c.1: addr: 0x0048, len: 2, flags: 0x0, stop: 1
[ 1.752349] omap_i2c omap_i2c.1: starting transfer
[ 1.752380] omap_i2c omap_i2c.1: IRQ (ISR = 0x0010)
[ 1.752410] omap_i2c omap_i2c.1: finished transfer
[ 1.752441] omap_i2c omap_i2c.1: waiting for ARDY
[ 1.752441] omap_i2c omap_i2c.1: ARDY
[ 1.752471] omap_i2c omap_i2c.1: pm_runtime_mark_last_busy()
[ 1.752471] omap_i2c omap_i2c.1: return
For whatever reason, in some cases client driver takes longer to
initiate a transfer and I don't think there's anything we can do about
that during boot up.
Anyway, whenever I can see a big delay, it's always from "return" to the
next "i2c i2c-1: master_xfer[0] W, addr=0x48, len=1" message.
--
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130115/ed31a283/attachment.sig>
^ permalink raw reply
* rtc: vt8500: Fix year field in vt8500_rtc_set_time
From: Alessandro Zummo @ 2013-01-15 8:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358234563.6868.13.camel@gitbox>
On Tue, 15 Jan 2013 20:22:43 +1300
Tony Prisk <linux@prisktech.co.nz> wrote:
> Alessandro,
>
> This patch was sent out at the same time as two other RTC fixes for
> vt8500 but this one seems to have stopped somewhere along the way while
> the other two have been accepted.
Hi, Andrew usually picks up the patches directly from the rtc mailing list,
unless there's an arch specific tree to carry it.
This one seems fine so I'm sure it's just an overlook
--
Best regards,
Alessandro Zummo,
Tower Technologies - Torino, Italy
http://www.towertech.it
^ permalink raw reply
* [GIT PULL] ste_dma40 updates for 3.9
From: Fabio Baltieri @ 2013-01-15 8:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOesGMg6FqsaqZiGeK14n4==dF-y=iMYSGRDBYSwkGWO0J69Yg@mail.gmail.com>
Hello Olof,
On Mon, Jan 14, 2013 at 10:48:15PM -0800, Olof Johansson wrote:
> > This contains a series of updates and fixes for the ste_dma40 driver.
> >
> > The driver is specific for the ux500, and the patches were acked by both Linus
> > Walleij, and Vinod Koul (dmaengine), who agreed to push this through arm-soc.
> >
> > Can you please pull those in for -next?
>
> While I don't mind taking this through arm-soc, I don't see a strong
> reason to do so?
>
> This series of patches only modify the ste_dma40 driver, there are no
> corresponding changes under arch/arm that need to be coordinated or
> considered w.r.t. merge conflicts. I.e. they all seem nicely isolated
> to only the driver.
>
> So is there a specific reason for why these shouldn't just go in
> through the dmaengine tree?
The idea was to have this in the same tree as other ux500 specific
driver patches to easy up testing a bit, but now that you point it out
I see that current arm-soc tree is pretty clear of driver patches (mmci
was what I had in mind) and those should end up to other trees anyway,
so you're probably right and these should go in dmaengine tree.
Vinod, would you please consider pulling these into dmaengine's next
tree?
Thanks,
Fabio
--
Fabio Baltieri
^ permalink raw reply
* [GIT PULL] ste_dma40 updates for 3.9
From: Linus Walleij @ 2013-01-15 8:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOesGMg6FqsaqZiGeK14n4==dF-y=iMYSGRDBYSwkGWO0J69Yg@mail.gmail.com>
On Tue, Jan 15, 2013 at 7:48 AM, Olof Johansson <olof@lixom.net> wrote:
> This series of patches only modify the ste_dma40 driver, there are no
> corresponding changes under arch/arm that need to be coordinated or
> considered w.r.t. merge conflicts. I.e. they all seem nicely isolated
> to only the driver.
>
> So is there a specific reason for why these shouldn't just go in
> through the dmaengine tree?
One reason would be if there are DMA bindings to device tree coming
this merge window, as I'm told, and it implicates a lot of platform code
changes on top of this as we adopt to it.
But maybe this will be wholly confined to the DMAengine tree?
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 04/18] power: ab8500_fg: Replace msleep() with usleep_range() for greater accuracy
From: Lee Jones @ 2013-01-15 8:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358183857.19400.13.camel@joe-AO722>
On Mon, 14 Jan 2013, Joe Perches wrote:
> On Fri, 2013-01-11 at 13:12 +0000, Lee Jones wrote:
> > Doing so provides a greater degree of accuracy when dealing with
> > time-frames between 1us and 20ms. msleep() is only accurate for
> > wake-ups greater than 20ms.
> []
> > diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> []
> > @@ -956,7 +956,7 @@ static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
> > do {
> > vbat += ab8500_fg_bat_voltage(di);
> > i++;
> > - msleep(5);
> > + usleep_range(5000, 5001);
>
> If you're going to give a range that small
> you might as well use usleep instead.
>
> Otherwise, add some tolerance to allow any
> other coalesced wakeup to occur.
I can't increase the tolerance, as I don't know how that would
effect the running of the system, and the person who would know
is off on parental leave.
What I can tell you is we're only using usleep_range() because
there is no usleep in the kernel. At least that's what we've
been led to believe:
Documentation/timers/timers-howto.txt:
- Why is there no "usleep" / What is a good range?
Since usleep_range is built on top of hrtimers, the
wakeup will be very precise (ish), thus a simple
usleep function would likely introduce a large number
of undesired interrupts.
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [RFC PATCH 7/7] ARM: dts: omap: Add omap control usb data
From: Kishon Vijay Abraham I @ 2013-01-15 8:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>
Add omap control usb data in omap4 device tree file. This will have the
register address of registers to power on the PHY and to write to
mailbox.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
arch/arm/boot/dts/omap4.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index b7e2ba3..5d770be 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -545,5 +545,13 @@
ram_bits = <12>;
ti,has_mailbox;
};
+
+ omap_control_usb at 4a002300 {
+ compatible = "ti,omap-control-usb";
+ reg = <0x4a002300 0x4>,
+ <0x4a00233c 0x4>;
+ reg-names = "control_dev_conf", "otghs_control";
+ ti,has_mailbox;
+ };
};
};
--
1.7.9.5
^ permalink raw reply related
* [RFC PATCH 6/7] ARM: dts: omap: Add omap-usb2 dt data
From: Kishon Vijay Abraham I @ 2013-01-15 8:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>
Add omap-usb2 data node in omap4 device tree file. Since omap-usb2 is
connected to ocp2scp, omap-usb2 dt data is added as a child node
of ocp2scp.
Acked-by: Felipe Balbi <balbi@ti.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
arch/arm/boot/dts/omap4.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 7bbf1fb..b7e2ba3 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -438,6 +438,10 @@
#size-cells = <1>;
ranges;
ti,hwmods = "ocp2scp_usb_phy";
+ usb2phy at 4a0ad080 {
+ compatible = "ti,omap-usb2";
+ reg = <0x4a0ad080 0x58>;
+ };
};
timer1: timer at 4a318000 {
--
1.7.9.5
^ permalink raw reply related
* [RFC PATCH 5/7] ARM: dts: omap: Add usb_otg and glue data
From: Kishon Vijay Abraham I @ 2013-01-15 8:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>
Add usb otg data node in omap4/omap3 device tree file. Also update
the node with board specific setting in omapx-<board>.dts file.
The dt data specifies among others the interface type (ULPI or UTMI), mode
which is mostly OTG, power that specifies the amount of power this can supply
when in host mode.
Acked-by: Felipe Balbi <balbi@ti.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
arch/arm/boot/dts/omap3-beagle-xm.dts | 6 ++++++
arch/arm/boot/dts/omap3-evm.dts | 6 ++++++
arch/arm/boot/dts/omap3-overo.dtsi | 6 ++++++
arch/arm/boot/dts/omap3.dtsi | 11 +++++++++++
arch/arm/boot/dts/omap4-panda.dts | 6 ++++++
arch/arm/boot/dts/omap4-sdp.dts | 6 ++++++
arch/arm/boot/dts/omap4.dtsi | 12 ++++++++++++
7 files changed, 53 insertions(+)
diff --git a/arch/arm/boot/dts/omap3-beagle-xm.dts b/arch/arm/boot/dts/omap3-beagle-xm.dts
index 3705a81..cb07583 100644
--- a/arch/arm/boot/dts/omap3-beagle-xm.dts
+++ b/arch/arm/boot/dts/omap3-beagle-xm.dts
@@ -107,3 +107,9 @@
*/
ti,pulldowns = <0x03a1c4>;
};
+
+&usb_otg_hs {
+ interface_type = <0>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap3-evm.dts b/arch/arm/boot/dts/omap3-evm.dts
index e8ba1c2..afb9ba9 100644
--- a/arch/arm/boot/dts/omap3-evm.dts
+++ b/arch/arm/boot/dts/omap3-evm.dts
@@ -59,3 +59,9 @@
&twl_gpio {
ti,use-leds;
};
+
+&usb_otg_hs {
+ interface_type = <0>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap3-overo.dtsi b/arch/arm/boot/dts/omap3-overo.dtsi
index 89808ce..4b3d157 100644
--- a/arch/arm/boot/dts/omap3-overo.dtsi
+++ b/arch/arm/boot/dts/omap3-overo.dtsi
@@ -55,3 +55,9 @@
&twl_gpio {
ti,use-leds;
};
+
+&usb_otg_hs {
+ interface_type = <0>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
index 1acc261..8d03736 100644
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -397,5 +397,16 @@
ti,timer-alwon;
ti,timer-secure;
};
+
+ usb_otg_hs: usb_otg_hs at 480ab000 {
+ compatible = "ti,omap3-musb";
+ reg = <0x480ab000 0x1000>;
+ interrupts = <0 92 0x4>, <0 93 0x4>;
+ interrupt-names = "mc", "dma";
+ ti,hwmods = "usb_otg_hs";
+ multipoint = <1>;
+ num_eps = <16>;
+ ram_bits = <12>;
+ };
};
};
diff --git a/arch/arm/boot/dts/omap4-panda.dts b/arch/arm/boot/dts/omap4-panda.dts
index 4122efe..612c9bb 100644
--- a/arch/arm/boot/dts/omap4-panda.dts
+++ b/arch/arm/boot/dts/omap4-panda.dts
@@ -206,3 +206,9 @@
&twl_usb_comparator {
usb-supply = <&vusb>;
};
+
+&usb_otg_hs {
+ interface_type = <1>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts
index 43e5258..582d7ee 100644
--- a/arch/arm/boot/dts/omap4-sdp.dts
+++ b/arch/arm/boot/dts/omap4-sdp.dts
@@ -428,3 +428,9 @@
&twl_usb_comparator {
usb-supply = <&vusb>;
};
+
+&usb_otg_hs {
+ interface_type = <1>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 739bb79..7bbf1fb 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -529,5 +529,17 @@
ti,hwmods = "timer11";
ti,timer-pwm;
};
+
+ usb_otg_hs: usb_otg_hs at 4a0ab000 {
+ compatible = "ti,omap4-musb";
+ reg = <0x4a0ab000 0x7ff>;
+ interrupts = <0 92 0x4>, <0 93 0x4>;
+ interrupt-names = "mc", "dma";
+ ti,hwmods = "usb_otg_hs";
+ multipoint = <1>;
+ num_eps = <16>;
+ ram_bits = <12>;
+ ti,has_mailbox;
+ };
};
};
--
1.7.9.5
^ permalink raw reply related
* [RFC PATCH 4/7] drivers: usb: start using the control module driver
From: Kishon Vijay Abraham I @ 2013-01-15 8:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>
Start using the control module driver for powering on the PHY and for
writing to the mailbox instead of writing to the control module
registers on their own.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
Documentation/devicetree/bindings/usb/omap-usb.txt | 4 ++
Documentation/devicetree/bindings/usb/usb-phy.txt | 7 +--
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 13 ----
drivers/usb/musb/Kconfig | 1 +
drivers/usb/musb/omap2430.c | 64 ++++++++------------
drivers/usb/musb/omap2430.h | 9 ---
drivers/usb/phy/Kconfig | 1 +
drivers/usb/phy/omap-usb2.c | 38 +++---------
include/linux/usb/omap_usb.h | 4 +-
9 files changed, 42 insertions(+), 99 deletions(-)
diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt
index d58dae3..3f0152b 100644
--- a/Documentation/devicetree/bindings/usb/omap-usb.txt
+++ b/Documentation/devicetree/bindings/usb/omap-usb.txt
@@ -3,6 +3,9 @@ OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
OMAP MUSB GLUE
- compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
- ti,hwmods : must be "usb_otg_hs"
+ - ti,has_mailbox : to specify that omap uses an external mailbox
+ (in control module) to communicate with the musb core during device connect
+ and disconnect.
- multipoint : Should be "1" indicating the musb controller supports
multipoint. This is a MUSB configuration-specific setting.
- num_eps : Specifies the number of endpoints. This is also a
@@ -20,6 +23,7 @@ SOC specific device node entry
usb_otg_hs: usb_otg_hs at 4a0ab000 {
compatible = "ti,omap4-musb";
ti,hwmods = "usb_otg_hs";
+ ti,has_mailbox;
multipoint = <1>;
num_eps = <16>;
ram_bits = <12>;
diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
index 80d4148..ee14cb7 100644
--- a/Documentation/devicetree/bindings/usb/usb-phy.txt
+++ b/Documentation/devicetree/bindings/usb/usb-phy.txt
@@ -4,14 +4,11 @@ OMAP USB2 PHY
Required properties:
- compatible: Should be "ti,omap-usb2"
- - reg : Address and length of the register set for the device. Also
-add the address of control module dev conf register until a driver for
-control module is added
+ - reg : Address and length of the register set for the device.
This is usually a subnode of ocp2scp to which it is connected.
usb2phy at 4a0ad080 {
compatible = "ti,omap-usb2";
- reg = <0x4a0ad080 0x58>,
- <0x4a002300 0x4>;
+ reg = <0x4a0ad080 0x58>;
};
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 129d508..103f4ba 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -2698,13 +2698,6 @@ static struct resource omap44xx_usb_phy_and_pll_addrs[] = {
.end = 0x4a0ae000,
.flags = IORESOURCE_MEM,
},
- {
- /* XXX: Remove this once control module driver is in place */
- .name = "ctrl_dev",
- .start = 0x4a002300,
- .end = 0x4a002303,
- .flags = IORESOURCE_MEM,
- },
{ }
};
@@ -6152,12 +6145,6 @@ static struct omap_hwmod_addr_space omap44xx_usb_otg_hs_addrs[] = {
.pa_end = 0x4a0ab7ff,
.flags = ADDR_TYPE_RT
},
- {
- /* XXX: Remove this once control module driver is in place */
- .pa_start = 0x4a00233c,
- .pa_end = 0x4a00233f,
- .flags = ADDR_TYPE_RT
- },
{ }
};
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 23a0b7f..de6e5ce 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -11,6 +11,7 @@ config USB_MUSB_HDRC
select NOP_USB_XCEIV if (SOC_TI81XX || SOC_AM33XX)
select TWL4030_USB if MACH_OMAP_3430SDP
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
+ select OMAP_CONTROL_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
select USB_OTG_UTILS
help
Say Y here if your system has a dual role high speed USB
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index da00af4..3e7ceef 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -37,6 +37,7 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/usb/musb-omap.h>
+#include <linux/usb/omap_control_usb.h>
#include "musb_core.h"
#include "omap2430.h"
@@ -46,7 +47,7 @@ struct omap2430_glue {
struct platform_device *musb;
enum omap_musb_vbus_id_status status;
struct work_struct omap_musb_mailbox_work;
- u32 __iomem *control_otghs;
+ struct device *control_otghs;
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
@@ -54,26 +55,6 @@ struct omap2430_glue *_glue;
static struct timer_list musb_idle_timer;
-/**
- * omap4_usb_phy_mailbox - write to usb otg mailbox
- * @glue: struct omap2430_glue *
- * @val: the value to be written to the mailbox
- *
- * On detection of a device (ID pin is grounded), this API should be called
- * to set AVALID, VBUSVALID and ID pin is grounded.
- *
- * When OMAP is connected to a host (OMAP in device mode), this API
- * is called to set AVALID, VBUSVALID and ID pin in high impedance.
- *
- * XXX: This function will be removed once we have a seperate driver for
- * control module
- */
-static void omap4_usb_phy_mailbox(struct omap2430_glue *glue, u32 val)
-{
- if (glue->control_otghs)
- writel(val, glue->control_otghs);
-}
-
static void musb_do_idle(unsigned long _musb)
{
struct musb *musb = (void *)_musb;
@@ -269,7 +250,6 @@ EXPORT_SYMBOL_GPL(omap_musb_mailbox);
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
{
- u32 val;
struct musb *musb = glue_to_musb(glue);
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *pdata = dev->platform_data;
@@ -285,8 +265,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
musb->xceiv->last_event = USB_EVENT_ID;
if (musb->gadget_driver) {
pm_runtime_get_sync(dev);
- val = AVALID | VBUSVALID;
- omap4_usb_phy_mailbox(glue, val);
+ if (!IS_ERR(glue->control_otghs))
+ omap_control_usb_host_mode(glue->control_otghs);
omap2430_musb_set_vbus(musb, 1);
}
break;
@@ -299,8 +279,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
musb->xceiv->last_event = USB_EVENT_VBUS;
if (musb->gadget_driver)
pm_runtime_get_sync(dev);
- val = IDDIG | AVALID | VBUSVALID;
- omap4_usb_phy_mailbox(glue, val);
+ if (!IS_ERR(glue->control_otghs))
+ omap_control_usb_device_mode(glue->control_otghs);
break;
case OMAP_MUSB_ID_FLOAT:
@@ -317,8 +297,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
if (musb->xceiv->otg->set_vbus)
otg_set_vbus(musb->xceiv->otg, 0);
}
- val = SESSEND | IDDIG;
- omap4_usb_phy_mailbox(glue, val);
+ if (!IS_ERR(glue->control_otghs))
+ omap_control_usb_set_sessionend(glue->control_otghs);
break;
default:
dev_dbg(dev, "ID float\n");
@@ -415,7 +395,6 @@ err1:
static void omap2430_musb_enable(struct musb *musb)
{
u8 devctl;
- u32 val;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
@@ -425,8 +404,8 @@ static void omap2430_musb_enable(struct musb *musb)
switch (glue->status) {
case OMAP_MUSB_ID_GROUND:
- val = AVALID | VBUSVALID;
- omap4_usb_phy_mailbox(glue, val);
+ if (!IS_ERR(glue->control_otghs))
+ omap_control_usb_host_mode(glue->control_otghs);
if (data->interface_type != MUSB_INTERFACE_UTMI)
break;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
@@ -445,8 +424,8 @@ static void omap2430_musb_enable(struct musb *musb)
break;
case OMAP_MUSB_VBUS_VALID:
- val = IDDIG | AVALID | VBUSVALID;
- omap4_usb_phy_mailbox(glue, val);
+ if (!IS_ERR(glue->control_otghs))
+ omap_control_usb_device_mode(glue->control_otghs);
break;
default:
@@ -456,13 +435,12 @@ static void omap2430_musb_enable(struct musb *musb)
static void omap2430_musb_disable(struct musb *musb)
{
- u32 val;
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
if (glue->status != OMAP_MUSB_UNKNOWN) {
- val = SESSEND | IDDIG;
- omap4_usb_phy_mailbox(glue, val);
+ if (!IS_ERR(glue->control_otghs))
+ omap_control_usb_set_sessionend(glue->control_otghs);
}
}
@@ -523,9 +501,6 @@ static int omap2430_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- glue->control_otghs = devm_request_and_ioremap(&pdev->dev, res);
- if (glue->control_otghs == NULL)
- dev_dbg(&pdev->dev, "Failed to obtain control memory\n");
if (np) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
@@ -558,11 +533,22 @@ static int omap2430_probe(struct platform_device *pdev)
of_property_read_u32(np, "ram_bits", (u32 *)&config->ram_bits);
of_property_read_u32(np, "power", (u32 *)&pdata->power);
config->multipoint = of_property_read_bool(np, "multipoint");
+ pdata->has_mailbox = of_property_read_bool(np,
+ "ti,has_mailbox");
pdata->board_data = data;
pdata->config = config;
}
+ if (pdata->has_mailbox) {
+ glue->control_otghs = get_omap_control_dev();
+ if (IS_ERR(glue->control_otghs)) {
+ dev_vdbg(&pdev->dev, "Failed to get control device\n");
+ return -ENODEV;
+ }
+ } else {
+ glue->control_otghs = ERR_PTR(-ENODEV);
+ }
pdata->platform_ops = &omap2430_ops;
platform_set_drvdata(pdev, glue);
diff --git a/drivers/usb/musb/omap2430.h b/drivers/usb/musb/omap2430.h
index 8ef6566..1b5e83a 100644
--- a/drivers/usb/musb/omap2430.h
+++ b/drivers/usb/musb/omap2430.h
@@ -49,13 +49,4 @@
#define OTG_FORCESTDBY 0x414
# define ENABLEFORCE (1 << 0)
-/*
- * Control Module bit definitions
- * XXX: Will be removed once we have a driver for control module.
- */
-#define AVALID BIT(0)
-#define BVALID BIT(1)
-#define VBUSVALID BIT(2)
-#define SESSEND BIT(3)
-#define IDDIG BIT(4)
#endif /* __MUSB_OMAP243X_H__ */
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index a7277ee..16bdaca 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -8,6 +8,7 @@ config OMAP_USB2
tristate "OMAP USB2 PHY Driver"
depends on ARCH_OMAP2PLUS
select USB_OTG_UTILS
+ select OMAP_CONTROL_USB
help
Enable this to support the transceiver that is part of SOC. This
driver takes care of all the PHY functionality apart from comparator.
diff --git a/drivers/usb/phy/omap-usb2.c b/drivers/usb/phy/omap-usb2.c
index 26ae8f4..2152ce4 100644
--- a/drivers/usb/phy/omap-usb2.c
+++ b/drivers/usb/phy/omap-usb2.c
@@ -27,6 +27,7 @@
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
+#include <linux/usb/omap_control_usb.h>
/**
* omap_usb2_set_comparator - links the comparator present in the sytem with
@@ -52,29 +53,6 @@ int omap_usb2_set_comparator(struct phy_companion *comparator)
}
EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
-/**
- * omap_usb_phy_power - power on/off the phy using control module reg
- * @phy: struct omap_usb *
- * @on: 0 or 1, based on powering on or off the PHY
- *
- * XXX: Remove this function once control module driver gets merged
- */
-static void omap_usb_phy_power(struct omap_usb *phy, int on)
-{
- u32 val;
-
- if (on) {
- val = readl(phy->control_dev);
- if (val & PHY_PD) {
- writel(~PHY_PD, phy->control_dev);
- /* XXX: add proper documentation for this delay */
- mdelay(200);
- }
- } else {
- writel(PHY_PD, phy->control_dev);
- }
-}
-
static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
{
struct omap_usb *phy = phy_to_omapusb(otg->phy);
@@ -124,7 +102,7 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend)
struct omap_usb *phy = phy_to_omapusb(x);
if (suspend && !phy->is_suspended) {
- omap_usb_phy_power(phy, 0);
+ omap_control_usb_phy_power(phy->control_dev, 0);
pm_runtime_put_sync(phy->dev);
phy->is_suspended = 1;
} else if (!suspend && phy->is_suspended) {
@@ -134,7 +112,7 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend)
ret);
return ret;
}
- omap_usb_phy_power(phy, 1);
+ omap_control_usb_phy_power(phy->control_dev, 1);
phy->is_suspended = 0;
}
@@ -168,14 +146,14 @@ static int omap_usb2_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- phy->control_dev = devm_request_and_ioremap(&pdev->dev, res);
- if (phy->control_dev == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENXIO;
+ phy->control_dev = get_omap_control_dev();
+ if (IS_ERR(phy->control_dev)) {
+ dev_dbg(&pdev->dev, "Failed to get control device\n");
+ return -ENODEV;
}
phy->is_suspended = 1;
- omap_usb_phy_power(phy, 0);
+ omap_control_usb_phy_power(phy->control_dev, 0);
otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral;
diff --git a/include/linux/usb/omap_usb.h b/include/linux/usb/omap_usb.h
index 0ea17f8..3db9b53 100644
--- a/include/linux/usb/omap_usb.h
+++ b/include/linux/usb/omap_usb.h
@@ -25,13 +25,11 @@ struct omap_usb {
struct usb_phy phy;
struct phy_companion *comparator;
struct device *dev;
- u32 __iomem *control_dev;
+ struct device *control_dev;
struct clk *wkupclk;
u8 is_suspended:1;
};
-#define PHY_PD 0x1
-
#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy)
#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)
--
1.7.9.5
^ permalink raw reply related
* [RFC PATCH 3/7] ARM: OMAP2: MUSB: Specify omap4 has mailbox
From: Kishon Vijay Abraham I @ 2013-01-15 8:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>
Added has_mailbox to the musb platform data to specify that omap uses
an external mailbox (in control module) to communicate with the musb
core during device connect and disconnect.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
arch/arm/mach-omap2/usb-musb.c | 3 +++
include/linux/usb/musb.h | 2 ++
2 files changed, 5 insertions(+)
diff --git a/arch/arm/mach-omap2/usb-musb.c b/arch/arm/mach-omap2/usb-musb.c
index 7b33b37..9d27e3f 100644
--- a/arch/arm/mach-omap2/usb-musb.c
+++ b/arch/arm/mach-omap2/usb-musb.c
@@ -85,6 +85,9 @@ void __init usb_musb_init(struct omap_musb_board_data *musb_board_data)
musb_plat.mode = board_data->mode;
musb_plat.extvbus = board_data->extvbus;
+ if (cpu_is_omap44xx())
+ musb_plat.has_mailbox = true;
+
if (soc_is_am35xx()) {
oh_name = "am35x_otg_hs";
name = "musb-am35x";
diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h
index eb50525..053c268 100644
--- a/include/linux/usb/musb.h
+++ b/include/linux/usb/musb.h
@@ -99,6 +99,8 @@ struct musb_hdrc_platform_data {
/* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
u8 mode;
+ u8 has_mailbox:1;
+
/* for clk_get() */
const char *clock;
--
1.7.9.5
^ permalink raw reply related
* [RFC PATCH 2/7] ARM: OMAP: devices: create device for usb part of control module
From: Kishon Vijay Abraham I @ 2013-01-15 8:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>
A seperate driver has been added to handle the usb part of control
module. A device for the above driver is created here, using the register
address information to be used by the driver for powering on the PHY and
for writing to the mailbox.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
arch/arm/mach-omap2/devices.c | 50 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index 5e304d0..a761faf4 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -20,6 +20,7 @@
#include <linux/pinctrl/machine.h>
#include <linux/platform_data/omap4-keypad.h>
#include <linux/platform_data/omap_ocp2scp.h>
+#include <linux/usb/omap_control_usb.h>
#include <asm/mach-types.h>
#include <asm/mach/map.h>
@@ -254,6 +255,54 @@ static inline void omap_init_camera(void)
#endif
}
+#if (defined(CONFIG_OMAP_CONTROL_USB) || \
+ defined(CONFIG_OMAP_CONTROL_USB_MODULE))
+
+static struct omap_control_usb_platform_data omap4_control_usb_pdata = {
+ .has_mailbox = true,
+};
+
+struct resource omap4_control_usb_res[] = {
+ {
+ .name = "control_dev_conf",
+ .start = 0x4a002300,
+ .end = 0x4a002303,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "otghs_control",
+ .start = 0x4a00233c,
+ .end = 0x4a00233f,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device omap4_control_usb = {
+ .name = "omap-control-usb",
+ .id = -1,
+ .dev = {
+ .platform_data = &omap4_control_usb_pdata,
+ },
+ .num_resources = 2,
+ .resource = omap4_control_usb_res,
+};
+
+static inline void __init omap_init_control_usb(void)
+{
+ int ret = 0;
+
+ if (cpu_is_omap44xx()) {
+ ret = platform_device_register(&omap4_control_usb);
+ if (ret)
+ pr_err("Error registering omap_control_usb device: %d\n"
+ , ret);
+ }
+}
+
+#else
+static inline void omap_init_control_usb(void) { }
+#endif /* CONFIG_OMAP_CONTROL_USB */
+
int __init omap4_keyboard_init(struct omap4_keypad_platform_data
*sdp4430_keypad_data, struct omap_board_data *bdata)
{
@@ -721,6 +770,7 @@ static int __init omap2_init_devices(void)
omap_init_mbox();
/* If dtb is there, the devices will be created dynamically */
if (!of_have_populated_dt()) {
+ omap_init_control_usb();
omap_init_dmic();
omap_init_mcpdm();
omap_init_mcspi();
--
1.7.9.5
^ permalink raw reply related
* [RFC PATCH 1/7] drivers: usb: phy: add a new driver for usb part of control module
From: Kishon Vijay Abraham I @ 2013-01-15 8:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>
Added a new driver for the usb part of control module. This has an API
to power on the USB2 phy and an API to write to the mailbox depending on
whether MUSB has to act in host mode or in device mode.
Writing to control module registers for doing the above task which was
previously done in omap glue and in omap-usb2 phy will be removed.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
Documentation/devicetree/bindings/usb/omap-usb.txt | 21 +-
drivers/usb/phy/Kconfig | 9 +
drivers/usb/phy/Makefile | 1 +
drivers/usb/phy/omap-control-usb.c | 204 ++++++++++++++++++++
include/linux/usb/omap_control_usb.h | 73 +++++++
5 files changed, 307 insertions(+), 1 deletion(-)
create mode 100644 drivers/usb/phy/omap-control-usb.c
create mode 100644 include/linux/usb/omap_control_usb.h
diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt
index 29a043e..d58dae3 100644
--- a/Documentation/devicetree/bindings/usb/omap-usb.txt
+++ b/Documentation/devicetree/bindings/usb/omap-usb.txt
@@ -1,4 +1,4 @@
-OMAP GLUE
+OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
OMAP MUSB GLUE
- compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
@@ -31,3 +31,22 @@ Board specific device node entry
mode = <3>;
power = <50>;
};
+
+OMAP CONTROL USB
+
+Required properties:
+ - compatible: Should be "ti,omap-control-usb"
+ - reg : Address and length of the register set for the device. It contains
+ the address of "control_dev_conf" and "otghs_control".
+ - reg-names: The names of the register addresses corresponding to the registers
+ filled in "reg".
+ - ti,has_mailbox: This is used to specify if the platform uses mailbox in
+ control module.
+
+omap_control_usb at 4a002300 {
+ compatible = "ti,omap-control-usb";
+ reg = <0x4a002300 0x4>,
+ <0x4a00233c 0x4>;
+ reg-names = "control_dev_conf", "otghs_control";
+ ti,has_mailbox;
+};
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 5de6e7f..a7277ee 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -14,6 +14,15 @@ config OMAP_USB2
The USB OTG controller communicates with the comparator using this
driver.
+config OMAP_CONTROL_USB
+ tristate "OMAP CONTROL USB Driver"
+ depends on ARCH_OMAP2PLUS
+ help
+ Enable this to add support for the USB part present in the control
+ module. This driver has API to power on the PHY and to write to the
+ mailbox. The mailbox is present only in omap4 and the register to
+ power on the PHY is present in omap4 and omap5.
+
config USB_ISP1301
tristate "NXP ISP1301 USB transceiver support"
depends on USB || USB_GADGET
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 1a579a8..0dea4d2 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -5,6 +5,7 @@
ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
obj-$(CONFIG_OMAP_USB2) += omap-usb2.o
+obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o
obj-$(CONFIG_USB_ISP1301) += isp1301.o
obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o
obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o
diff --git a/drivers/usb/phy/omap-control-usb.c b/drivers/usb/phy/omap-control-usb.c
new file mode 100644
index 0000000..bed41a9
--- /dev/null
+++ b/drivers/usb/phy/omap-control-usb.c
@@ -0,0 +1,204 @@
+/*
+ * omap-control-usb.c - The USB part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * 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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/usb/omap_control_usb.h>
+
+static struct omap_control_usb *control_usb;
+
+/**
+ * get_omap_control_dev - returns the device pointer for this control device
+ *
+ * This API should be called to get the device pointer for this control
+ * module device. This device pointer should be passed to all other API's
+ * in this driver.
+ *
+ * To be used by PHY driver and glue driver
+ */
+struct device *get_omap_control_dev(void)
+{
+ if (!control_usb)
+ return ERR_PTR(-ENODEV);
+
+ return control_usb->dev;
+}
+EXPORT_SYMBOL_GPL(get_omap_control_dev);
+
+/**
+ * omap_control_usb_phy_power - power on/off the phy using control module reg
+ * @dev: the control module device
+ * @on: 0 or 1, based on powering on or off the PHY
+ */
+void omap_control_usb_phy_power(struct device *dev, int on)
+{
+ u32 val;
+ struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+
+ if (on) {
+ val = readl(control_usb->dev_conf);
+ if (val & PHY_PD)
+ writel(~PHY_PD, control_usb->dev_conf);
+ } else {
+ writel(PHY_PD, control_usb->dev_conf);
+ }
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
+
+/**
+ * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
+ * @dev: struct device *
+ *
+ * This is an API to write to the mailbox register to notify the usb core that
+ * a usb device has been connected.
+ */
+void omap_control_usb_host_mode(struct device *dev)
+{
+ u32 val;
+ struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+
+ val = AVALID | VBUSVALID;
+
+ writel(val, control_usb->otghs_control);
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_host_mode);
+
+/**
+ * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
+ * impedance
+ * @dev: struct device *
+ *
+ * This is an API to write to the mailbox register to notify the usb core that
+ * it has been connected to a usb host.
+ */
+void omap_control_usb_device_mode(struct device *dev)
+{
+ u32 val;
+ struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+
+ val = IDDIG | AVALID | VBUSVALID;
+
+ writel(val, control_usb->otghs_control);
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_device_mode);
+
+/**
+ * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
+ * impedance
+ * @dev: struct device *
+ *
+ * This is an API to write to the mailbox register to notify the usb core
+ * it's now in disconnected state.
+ */
+void omap_control_usb_set_sessionend(struct device *dev)
+{
+ u32 val;
+ struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+
+ val = SESSEND | IDDIG;
+
+ writel(val, control_usb->otghs_control);
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_set_sessionend);
+
+static int omap_control_usb_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct device_node *np = pdev->dev.of_node;
+ struct omap_control_usb_platform_data *pdata = pdev->dev.platform_data;
+
+ control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
+ GFP_KERNEL);
+ if (!control_usb) {
+ dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
+ return -ENOMEM;
+ }
+
+ if (np) {
+ control_usb->has_mailbox = of_property_read_bool(np,
+ "ti,has_mailbox");
+ } else if (pdata) {
+ control_usb->has_mailbox = pdata->has_mailbox;
+ } else {
+ dev_err(&pdev->dev, "no pdata present\n");
+ return -EINVAL;
+ }
+
+ control_usb->dev = &pdev->dev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "control_dev_conf");
+ control_usb->dev_conf = devm_request_and_ioremap(&pdev->dev, res);
+ if (control_usb->dev_conf == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENXIO;
+ }
+
+ if (control_usb->has_mailbox) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "otghs_control");
+ control_usb->otghs_control = devm_request_and_ioremap(
+ &pdev->dev, res);
+ if (control_usb->otghs_control == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENXIO;
+ }
+ }
+
+ dev_set_drvdata(control_usb->dev, control_usb);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_control_usb_id_table[] = {
+ { .compatible = "ti,omap-control-usb" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
+#endif
+
+static struct platform_driver omap_control_usb_driver = {
+ .probe = omap_control_usb_probe,
+ .driver = {
+ .name = "omap-control-usb",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(omap_control_usb_id_table),
+ },
+};
+
+static int __init omap_control_usb_init(void)
+{
+ return platform_driver_register(&omap_control_usb_driver);
+}
+subsys_initcall(omap_control_usb_init);
+
+static void __exit omap_control_usb_exit(void)
+{
+ platform_driver_unregister(&omap_control_usb_driver);
+}
+module_exit(omap_control_usb_exit);
+
+MODULE_ALIAS("platform: omap_control_usb");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP CONTROL USB DRIVER");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/usb/omap_control_usb.h b/include/linux/usb/omap_control_usb.h
new file mode 100644
index 0000000..fbc7407
--- /dev/null
+++ b/include/linux/usb/omap_control_usb.h
@@ -0,0 +1,73 @@
+/*
+ * omap_control_usb.h - Header file for the USB part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * 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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __OMAP_CONTROL_USB_H__
+#define __OMAP_CONTROL_USB_H__
+
+struct omap_control_usb {
+ struct device *dev;
+
+ u32 __iomem *dev_conf;
+ u32 __iomem *otghs_control;
+
+ u8 has_mailbox:1;
+};
+
+struct omap_control_usb_platform_data {
+ u8 has_mailbox:1;
+};
+
+#define PHY_PD BIT(0)
+
+#define AVALID BIT(0)
+#define BVALID BIT(1)
+#define VBUSVALID BIT(2)
+#define SESSEND BIT(3)
+#define IDDIG BIT(4)
+
+#if (defined(CONFIG_OMAP_CONTROL_USB) || \
+ defined(CONFIG_OMAP_CONTROL_USB_MODULE))
+extern struct device *get_omap_control_dev(void);
+extern void omap_control_usb_phy_power(struct device *dev, int on);
+extern void omap_control_usb_host_mode(struct device *dev);
+extern void omap_control_usb_device_mode(struct device *dev);
+extern void omap_control_usb_set_sessionend(struct device *dev);
+#else
+static inline struct device *get_omap_control_dev()
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void omap_control_usb_phy_power(struct device *dev, int on)
+{
+}
+
+static inline void omap_control_usb_host_mode(struct device *dev)
+{
+}
+
+static inline void omap_control_usb_device_mode(struct device *dev)
+{
+}
+
+static inline void omap_control_usb_set_sessionend(struct device *dev)
+{
+}
+#endif
+
+#endif /* __OMAP_CONTROL_USB_H__ */
--
1.7.9.5
^ permalink raw reply related
* [RFC PATCH 0/7] usb: musb: add driver for control module
From: Kishon Vijay Abraham I @ 2013-01-15 8:42 UTC (permalink / raw)
To: linux-arm-kernel
Added a new driver for the usb part of control module. This has an API
to power on the USB2 phy and an API to write to the mailbox depending on
whether MUSB has to act in host mode or in device mode.
Writing to control module registers for doing the above task which was
previously done in omap glue and in omap-usb2 phy is removed.
Also added the dt data to get MUSB working in OMAP platforms.
This series has patches for both drivers and ARCH folders, so If it has to
be split I'll do it.
This series was developed on
git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git xceiv
Did basic enumeration testing in omap4 beagle, omap4 sdp and omap3 beagle.
Kishon Vijay Abraham I (7):
drivers: usb: phy: add a new driver for usb part of control module
ARM: OMAP: devices: create device for usb part of control module
ARM: OMAP2: MUSB: Specify omap4 has mailbox
drivers: usb: start using the control module driver
ARM: dts: omap: Add usb_otg and glue data
ARM: dts: omap: Add omap-usb2 dt data
ARM: dts: omap: Add omap control usb data
Documentation/devicetree/bindings/usb/omap-usb.txt | 25 ++-
Documentation/devicetree/bindings/usb/usb-phy.txt | 7 +-
arch/arm/boot/dts/omap3-beagle-xm.dts | 6 +
arch/arm/boot/dts/omap3-evm.dts | 6 +
arch/arm/boot/dts/omap3-overo.dtsi | 6 +
arch/arm/boot/dts/omap3.dtsi | 11 ++
arch/arm/boot/dts/omap4-panda.dts | 6 +
arch/arm/boot/dts/omap4-sdp.dts | 6 +
arch/arm/boot/dts/omap4.dtsi | 24 +++
arch/arm/mach-omap2/devices.c | 50 +++++
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 13 --
arch/arm/mach-omap2/usb-musb.c | 3 +
drivers/usb/musb/Kconfig | 1 +
drivers/usb/musb/omap2430.c | 64 +++---
drivers/usb/musb/omap2430.h | 9 -
drivers/usb/phy/Kconfig | 10 +
drivers/usb/phy/Makefile | 1 +
drivers/usb/phy/omap-control-usb.c | 204 ++++++++++++++++++++
drivers/usb/phy/omap-usb2.c | 38 +---
include/linux/usb/musb.h | 2 +
include/linux/usb/omap_control_usb.h | 73 +++++++
include/linux/usb/omap_usb.h | 4 +-
22 files changed, 469 insertions(+), 100 deletions(-)
create mode 100644 drivers/usb/phy/omap-control-usb.c
create mode 100644 include/linux/usb/omap_control_usb.h
--
1.7.9.5
^ permalink raw reply
* [PATCH 2/2] gpio: devm_gpio_* support should not depend on GPIOLIB
From: Shawn Guo @ 2013-01-15 8:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239056-703-1-git-send-email-shawn.guo@linaro.org>
Some architectures (e.g. blackfin) provide gpio API without requiring
GPIOLIB support (ARCH_WANT_OPTIONAL_GPIOLIB). devm_gpio_* functions
should also work for these architectures, since they do not really
depend on GPIOLIB.
Add a new option GPIO_DEVRES (enabled by default) to control the build
of devres.c, and move devm_gpio_* function declarations out from #ifdef
CONFIG_GPIOLIB in include/asm-generic/gpio.h, so that they can be
available to client drivers without unnecessary dependency on GPIOLIB.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
drivers/gpio/Kconfig | 3 +++
drivers/gpio/Makefile | 3 ++-
include/asm-generic/gpio.h | 17 ++++++++---------
3 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 682de75..d972932 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -30,6 +30,9 @@ config ARCH_REQUIRE_GPIOLIB
Selecting this from the architecture code will cause the gpiolib
code to always get built in.
+config GPIO_DEVRES
+ def_bool y
+ depends on HAS_IOMEM
menuconfig GPIOLIB
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index c5aebd0..36ca605 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -2,7 +2,8 @@
ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
-obj-$(CONFIG_GPIOLIB) += gpiolib.o devres.o
+obj-$(CONFIG_GPIO_DEVRES) += devres.o
+obj-$(CONFIG_GPIOLIB) += gpiolib.o
obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 2341014..71fd984 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -7,6 +7,8 @@
#include <linux/of.h>
#include <linux/pinctrl/pinctrl.h>
+struct device;
+
#ifdef CONFIG_GPIOLIB
#include <linux/compiler.h>
@@ -42,7 +44,6 @@ static inline bool gpio_is_valid(int number)
return number >= 0 && number < ARCH_NR_GPIOS;
}
-struct device;
struct gpio;
struct seq_file;
struct module;
@@ -192,12 +193,6 @@ extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *labe
extern int gpio_request_array(const struct gpio *array, size_t num);
extern void gpio_free_array(const struct gpio *array, size_t num);
-/* bindings for managed devices that want to request gpios */
-int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
-int devm_gpio_request_one(struct device *dev, unsigned gpio,
- unsigned long flags, const char *label);
-void devm_gpio_free(struct device *dev, unsigned int gpio);
-
#ifdef CONFIG_GPIO_SYSFS
/*
@@ -280,9 +275,13 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value)
#endif /* !CONFIG_GPIOLIB */
-#ifndef CONFIG_GPIO_SYSFS
+/* bindings for managed devices that want to request gpios */
+int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
+int devm_gpio_request_one(struct device *dev, unsigned gpio,
+ unsigned long flags, const char *label);
+void devm_gpio_free(struct device *dev, unsigned int gpio);
-struct device;
+#ifndef CONFIG_GPIO_SYSFS
/* sysfs support is only available with gpiolib, where it's optional */
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/2] gpio: fix warning of 'struct gpio_chip' declaration
From: Shawn Guo @ 2013-01-15 8:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358239056-703-1-git-send-email-shawn.guo@linaro.org>
The struct gpio_chip is only defined inside #ifdef CONFIG_GPIOLIB,
but it's referenced by gpiochip_add_pin_range() and
gpiochip_remove_pin_ranges() which are outside #ifdef CONFIG_GPIOLIB.
Thus, we see the following warning when building blackfin image, where
GPIOLIB is not required.
CC arch/blackfin/kernel/bfin_gpio.o
CC init/version.o
In file included from arch/blackfin/include/asm/gpio.h:321,
from arch/blackfin/kernel/bfin_gpio.c:15:
include/asm-generic/gpio.h:298: warning: ?struct gpio_chip? declared inside parameter list
include/asm-generic/gpio.h:298: warning: its scope is only this definition or declaration, which is probably not what you want
include/asm-generic/gpio.h:304: warning: ?struct gpio_chip? declared inside parameter list
Move pinctrl trunk into #ifdef CONFIG_GPIOLIB to fix the warning,
since it appears that pinctrl gpio range support depends on GPIOLIB.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
include/asm-generic/gpio.h | 74 ++++++++++++++++++++++----------------------
1 file changed, 37 insertions(+), 37 deletions(-)
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 20ca766..2341014 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -212,6 +212,43 @@ extern void gpio_unexport(unsigned gpio);
#endif /* CONFIG_GPIO_SYSFS */
+#ifdef CONFIG_PINCTRL
+
+/**
+ * struct gpio_pin_range - pin range controlled by a gpio chip
+ * @head: list for maintaining set of pin ranges, used internally
+ * @pctldev: pinctrl device which handles corresponding pins
+ * @range: actual range of pins controlled by a gpio controller
+ */
+
+struct gpio_pin_range {
+ struct list_head node;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_gpio_range range;
+};
+
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int gpio_offset, unsigned int pin_offset,
+ unsigned int npins);
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
+
+#else
+
+static inline int
+gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int gpio_offset, unsigned int pin_offset,
+ unsigned int npins)
+{
+ return 0;
+}
+
+static inline void
+gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+}
+
+#endif /* CONFIG_PINCTRL */
+
#else /* !CONFIG_GPIOLIB */
static inline bool gpio_is_valid(int number)
@@ -270,41 +307,4 @@ static inline void gpio_unexport(unsigned gpio)
}
#endif /* CONFIG_GPIO_SYSFS */
-#ifdef CONFIG_PINCTRL
-
-/**
- * struct gpio_pin_range - pin range controlled by a gpio chip
- * @head: list for maintaining set of pin ranges, used internally
- * @pctldev: pinctrl device which handles corresponding pins
- * @range: actual range of pins controlled by a gpio controller
- */
-
-struct gpio_pin_range {
- struct list_head node;
- struct pinctrl_dev *pctldev;
- struct pinctrl_gpio_range range;
-};
-
-int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
- unsigned int gpio_offset, unsigned int pin_offset,
- unsigned int npins);
-void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
-
-#else
-
-static inline int
-gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
- unsigned int gpio_offset, unsigned int pin_offset,
- unsigned int npins)
-{
- return 0;
-}
-
-static inline void
-gpiochip_remove_pin_ranges(struct gpio_chip *chip)
-{
-}
-
-#endif /* CONFIG_PINCTRL */
-
#endif /* _ASM_GENERIC_GPIO_H */
--
1.7.9.5
^ permalink raw reply related
* [PATCH 0/2] A couple of gpio fixes
From: Shawn Guo @ 2013-01-15 8:37 UTC (permalink / raw)
To: linux-arm-kernel
I recently sent a patch "mmc: slot-gpio: use devm_* managed functions
to ease users" for mmc-next inclusion, and got a error report [1] from
Fengguang (Thanks!), saying it breaks blackfin build. It seems to me
that the error is caused by devm_gpio_* unnecessary dependency on
GPIOLIB.
While at it, I also include a patch (#1) to fix the 'struct gpio_chip'
declaration warning that's already been there with blackfin build.
And the patch #2 remove the unnecessary dependency between devm_gpio_*
and GPIOLIB to get the reported error fixed.
[1] http://thread.gmane.org/gmane.linux.kernel.mmc/18650
Shawn Guo (2):
gpio: fix warning of 'struct gpio_chip' declaration
gpio: devm_gpio_* support should not depend on GPIOLIB
drivers/gpio/Kconfig | 3 ++
drivers/gpio/Makefile | 3 +-
include/asm-generic/gpio.h | 91 ++++++++++++++++++++++----------------------
3 files changed, 50 insertions(+), 47 deletions(-)
--
1.7.9.5
^ permalink raw reply
* OMAP baseline test results for v3.8-rc3
From: Peter Ujfalusi @ 2013-01-15 8:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.00.1301141756460.20390@utopia.booyaka.com>
Hi Paul,
On 01/14/2013 06:59 PM, Paul Walmsley wrote:
> Failing tests: needing investigation
> ------------------------------------
>
> Build tests:
>
> * rmk_3430_ldp_allnoconfig, rmk_4430_sdp_allnoconfig: twl4030 build failures
> - in drivers/mfd/twl-core.c
The following patch fixes these:
https://lkml.org/lkml/2012/12/24/3
--
P?ter
^ permalink raw reply
* [v3 9/9] ARM: tegra: Add initial support for Tegra 114 SoC.
From: Hiroshi Doyu @ 2013-01-15 8:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358237598-32413-1-git-send-email-hdoyu@nvidia.com>
Add new Tegra 114 SoC support.
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
---
arch/arm/mach-tegra/Kconfig | 10 +++++++
arch/arm/mach-tegra/Makefile | 1 +
arch/arm/mach-tegra/board-dt-tegra114.c | 48 +++++++++++++++++++++++++++++++
arch/arm/mach-tegra/common.c | 1 +
4 files changed, 60 insertions(+)
create mode 100644 arch/arm/mach-tegra/board-dt-tegra114.c
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 1ec7f80..d976ef2 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -44,6 +44,16 @@ config ARCH_TEGRA_3x_SOC
Support for NVIDIA Tegra T30 processor family, based on the
ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
+config ARCH_TEGRA_114_SOC
+ bool "Enable support for Tegra114 family"
+ select ARM_GIC
+ select CPU_V7
+ select ARM_L1_CACHE_SHIFT_6
+ select ARM_ARCH_TIMER
+ help
+ Support for NVIDIA Tegra T114 processor family, based on the
+ ARM CortexA15MP CPU
+
config TEGRA_PCI
bool "PCI Express support"
depends on ARCH_TEGRA_2x_SOC
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 660ee03..219c2ed 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_TEGRA_PCI) += pcie.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-dt-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_114_SOC) += board-dt-tegra114.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-harmony-pcie.o
diff --git a/arch/arm/mach-tegra/board-dt-tegra114.c b/arch/arm/mach-tegra/board-dt-tegra114.c
new file mode 100644
index 0000000..4c36dd0
--- /dev/null
+++ b/arch/arm/mach-tegra/board-dt-tegra114.c
@@ -0,0 +1,48 @@
+/*
+ * NVIDIA Tegra114 device tree board support
+ *
+ * Copyright (C) 2012 NVIDIA Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/clocksource.h>
+
+#include <asm/mach/arch.h>
+#include <asm/hardware/gic.h>
+
+#include "board.h"
+#include "common.h"
+
+static void __init tegra114_dt_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char * const tegra114_dt_board_compat[] = {
+ "nvidia,tegra114",
+ NULL,
+};
+
+DT_MACHINE_START(TEGRA114_DT, "NVIDIA Tegra114 (Flattened Device Tree)")
+ .smp = smp_ops(tegra_smp_ops),
+ .map_io = tegra_map_common_io,
+ .init_early = tegra30_init_early,
+ .init_irq = tegra_dt_init_irq,
+ .handle_irq = gic_handle_irq,
+ .init_time = clocksource_of_init,
+ .init_machine = tegra114_dt_init,
+ .init_late = tegra_init_late,
+ .restart = tegra_assert_system_reset,
+ .dt_compat = tegra114_dt_board_compat,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 3efe80b..b6bfc0c 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -59,6 +59,7 @@ u32 tegra_uart_config[4] = {
#ifdef CONFIG_OF
static const struct of_device_id tegra_dt_irq_match[] __initconst = {
+ { .compatible = "arm,cortex-a15-gic", .data = gic_of_init },
{ .compatible = "arm,cortex-a9-gic", .data = gic_of_init },
{ }
};
--
1.7.9.5
^ permalink raw reply related
* [v3 8/9] ARM: dt: tegra114: Add new board, Pluto
From: Hiroshi Doyu @ 2013-01-15 8:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358237598-32413-1-git-send-email-hdoyu@nvidia.com>
Add a new evaluation board, Pluto for Tegra 114 family.
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
---
arch/arm/boot/dts/Makefile | 3 ++-
arch/arm/boot/dts/tegra114-pluto.dts | 21 +++++++++++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/boot/dts/tegra114-pluto.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 04e43d2..32f47ce 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -148,7 +148,8 @@ dtb-$(CONFIG_ARCH_TEGRA) += tegra20-harmony.dtb \
tegra30-cardhu-a02.dtb \
tegra30-cardhu-a04.dtb \
tegra30-beaver.dtb \
- tegra114-dalmore.dtb
+ tegra114-dalmore.dtb \
+ tegra114-pluto.dtb
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
vexpress-v2p-ca9.dtb \
vexpress-v2p-ca15-tc1.dtb \
diff --git a/arch/arm/boot/dts/tegra114-pluto.dts b/arch/arm/boot/dts/tegra114-pluto.dts
new file mode 100644
index 0000000..9bea8f5
--- /dev/null
+++ b/arch/arm/boot/dts/tegra114-pluto.dts
@@ -0,0 +1,21 @@
+/dts-v1/;
+
+/include/ "tegra114.dtsi"
+
+/ {
+ model = "NVIDIA Tegra114 Pluto evaluation board";
+ compatible = "nvidia,pluto", "nvidia,tegra114";
+
+ memory {
+ reg = <0x80000000 0x40000000>;
+ };
+
+ serial at 70006300 {
+ status = "okay";
+ clock-frequency = <408000000>;
+ };
+
+ pmc {
+ nvidia,invert-interrupt;
+ };
+};
--
1.7.9.5
^ permalink raw reply related
* [v3 7/9] ARM: dt: tegra114: Add new board, Dalmore
From: Hiroshi Doyu @ 2013-01-15 8:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358237598-32413-1-git-send-email-hdoyu@nvidia.com>
Add a new evaluation board, Dalmore for Tegra 114 family.
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
---
arch/arm/boot/dts/Makefile | 3 ++-
arch/arm/boot/dts/tegra114-dalmore.dts | 21 +++++++++++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/boot/dts/tegra114-dalmore.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index b511bc1..04e43d2 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -147,7 +147,8 @@ dtb-$(CONFIG_ARCH_TEGRA) += tegra20-harmony.dtb \
tegra20-whistler.dtb \
tegra30-cardhu-a02.dtb \
tegra30-cardhu-a04.dtb \
- tegra30-beaver.dtb
+ tegra30-beaver.dtb \
+ tegra114-dalmore.dtb
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
vexpress-v2p-ca9.dtb \
vexpress-v2p-ca15-tc1.dtb \
diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts
new file mode 100644
index 0000000..a30aca6
--- /dev/null
+++ b/arch/arm/boot/dts/tegra114-dalmore.dts
@@ -0,0 +1,21 @@
+/dts-v1/;
+
+/include/ "tegra114.dtsi"
+
+/ {
+ model = "NVIDIA Tegra114 Dalmore evaluation board";
+ compatible = "nvidia,dalmore", "nvidia,tegra114";
+
+ memory {
+ reg = <0x80000000 0x40000000>;
+ };
+
+ serial at 70006300 {
+ status = "okay";
+ clock-frequency = <408000000>;
+ };
+
+ pmc {
+ nvidia,invert-interrupt;
+ };
+};
--
1.7.9.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox