summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/s5p_gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/s5p_gpio.c')
-rw-r--r--drivers/gpio/s5p_gpio.c204
1 files changed, 171 insertions, 33 deletions
diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c
index 11a0472c69..db7b673731 100644
--- a/drivers/gpio/s5p_gpio.c
+++ b/drivers/gpio/s5p_gpio.c
@@ -8,11 +8,9 @@
#include <common.h>
#include <asm/io.h>
#include <asm/gpio.h>
+#include <asm/arch/gpio.h>
-#define S5P_GPIO_GET_BANK(x) ((x >> S5P_GPIO_BANK_SHIFT) \
- & S5P_GPIO_BANK_MASK)
-
-#define S5P_GPIO_GET_PIN(x) (x & S5P_GPIO_PIN_MASK)
+#define S5P_GPIO_GET_PIN(x) (x % GPIO_PER_BANK)
#define CON_MASK(x) (0xf << ((x) << 2))
#define CON_SFR(x, v) ((v) << ((x) << 2))
@@ -28,7 +26,103 @@
#define RATE_MASK(x) (0x1 << (x + 16))
#define RATE_SET(x) (0x1 << (x + 16))
-void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg)
+#define name_to_gpio(n) s5p_name_to_gpio(n)
+static inline int s5p_name_to_gpio(const char *name)
+{
+ unsigned num, irregular_set_number, irregular_bank_base;
+ const struct gpio_name_num_table *tabp;
+ char this_bank, bank_name, irregular_bank_name;
+ char *endp;
+
+ /*
+ * The gpio name starts with either 'g' or 'gp' followed by the bank
+ * name character. Skip one or two characters depending on the prefix.
+ */
+ if (name[0] == 'g' && name[1] == 'p')
+ name += 2;
+ else if (name[0] == 'g')
+ name++;
+ else
+ return -1; /* Name must start with 'g' */
+
+ bank_name = *name++;
+ if (!*name)
+ return -1; /* At least one digit is required/expected. */
+
+ /*
+ * On both exynos5 and exynos5420 architectures there is a bank of
+ * GPIOs which does not fall into the regular address pattern. Those
+ * banks are c4 on Exynos5 and y7 on Exynos5420. The rest of the below
+ * assignments help to handle these irregularities.
+ */
+#if defined(CONFIG_EXYNOS4) || defined(CONFIG_EXYNOS5)
+ if (cpu_is_exynos5()) {
+ if (proid_is_exynos5420()) {
+ tabp = exynos5420_gpio_table;
+ irregular_bank_name = 'y';
+ irregular_set_number = '7';
+ irregular_bank_base = EXYNOS5420_GPIO_Y70;
+ } else {
+ tabp = exynos5_gpio_table;
+ irregular_bank_name = 'c';
+ irregular_set_number = '4';
+ irregular_bank_base = EXYNOS5_GPIO_C40;
+ }
+ } else {
+ if (proid_is_exynos4412())
+ tabp = exynos4x12_gpio_table;
+ else
+ tabp = exynos4_gpio_table;
+ irregular_bank_name = 0;
+ irregular_set_number = 0;
+ irregular_bank_base = 0;
+ }
+#else
+ if (cpu_is_s5pc110())
+ tabp = s5pc110_gpio_table;
+ else
+ tabp = s5pc100_gpio_table;
+ irregular_bank_name = 0;
+ irregular_set_number = 0;
+ irregular_bank_base = 0;
+#endif
+
+ this_bank = tabp->bank;
+ do {
+ if (bank_name == this_bank) {
+ unsigned pin_index; /* pin number within the bank */
+ if ((bank_name == irregular_bank_name) &&
+ (name[0] == irregular_set_number)) {
+ pin_index = name[1] - '0';
+ /* Irregular sets have 8 pins. */
+ if (pin_index >= GPIO_PER_BANK)
+ return -1;
+ num = irregular_bank_base + pin_index;
+ } else {
+ pin_index = simple_strtoul(name, &endp, 8);
+ pin_index -= tabp->bank_offset;
+ /*
+ * Sanity check: bunk 'z' has no set number,
+ * for all other banks there must be exactly
+ * two octal digits, and the resulting number
+ * should not exceed the number of pins in the
+ * bank.
+ */
+ if (((bank_name != 'z') && !name[1]) ||
+ *endp ||
+ (pin_index >= tabp->bank_size))
+ return -1;
+ num = tabp->base + pin_index;
+ }
+ return num;
+ }
+ this_bank = (++tabp)->bank;
+ } while (this_bank);
+
+ return -1;
+}
+
+static void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg)
{
unsigned int value;
@@ -38,18 +132,7 @@ void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg)
writel(value, &bank->con);
}
-void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en)
-{
- s5p_gpio_cfg_pin(bank, gpio, GPIO_OUTPUT);
- s5p_gpio_set_value(bank, gpio, en);
-}
-
-void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio)
-{
- s5p_gpio_cfg_pin(bank, gpio, GPIO_INPUT);
-}
-
-void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en)
+static void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en)
{
unsigned int value;
@@ -60,7 +143,19 @@ void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en)
writel(value, &bank->dat);
}
-unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio)
+static void s5p_gpio_direction_output(struct s5p_gpio_bank *bank,
+ int gpio, int en)
+{
+ s5p_gpio_cfg_pin(bank, gpio, S5P_GPIO_OUTPUT);
+ s5p_gpio_set_value(bank, gpio, en);
+}
+
+static void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio)
+{
+ s5p_gpio_cfg_pin(bank, gpio, S5P_GPIO_INPUT);
+}
+
+static unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio)
{
unsigned int value;
@@ -68,7 +163,7 @@ unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio)
return !!(value & DAT_MASK(gpio));
}
-void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
+static void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
@@ -76,8 +171,8 @@ void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
value &= ~PULL_MASK(gpio);
switch (mode) {
- case GPIO_PULL_DOWN:
- case GPIO_PULL_UP:
+ case S5P_GPIO_PULL_DOWN:
+ case S5P_GPIO_PULL_UP:
value |= PULL_MODE(gpio, mode);
break;
default:
@@ -87,7 +182,7 @@ void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
writel(value, &bank->pull);
}
-void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
+static void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
@@ -95,10 +190,10 @@ void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
value &= ~DRV_MASK(gpio);
switch (mode) {
- case GPIO_DRV_1X:
- case GPIO_DRV_2X:
- case GPIO_DRV_3X:
- case GPIO_DRV_4X:
+ case S5P_GPIO_DRV_1X:
+ case S5P_GPIO_DRV_2X:
+ case S5P_GPIO_DRV_3X:
+ case S5P_GPIO_DRV_4X:
value |= DRV_SET(gpio, mode);
break;
default:
@@ -108,7 +203,7 @@ void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
writel(value, &bank->drv);
}
-void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
+static void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
@@ -116,8 +211,8 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
value &= ~RATE_MASK(gpio);
switch (mode) {
- case GPIO_DRV_FAST:
- case GPIO_DRV_SLOW:
+ case S5P_GPIO_DRV_FAST:
+ case S5P_GPIO_DRV_SLOW:
value |= RATE_SET(gpio);
break;
default:
@@ -127,12 +222,31 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
writel(value, &bank->drv);
}
-struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio)
+struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned int gpio)
{
- unsigned bank = S5P_GPIO_GET_BANK(gpio);
- unsigned base = s5p_gpio_base(gpio);
+ const struct gpio_info *data;
+ unsigned int upto;
+ int i, count;
+
+ data = get_gpio_data();
+ count = get_bank_num();
+ upto = 0;
+
+ for (i = 0; i < count; i++) {
+ debug("i=%d, upto=%d\n", i, upto);
+ if (gpio < data->max_gpio) {
+ struct s5p_gpio_bank *bank;
+ bank = (struct s5p_gpio_bank *)data->reg_addr;
+ bank += (gpio - upto) / GPIO_PER_BANK;
+ debug("gpio=%d, bank=%p\n", gpio, bank);
+ return bank;
+ }
+
+ upto = data->max_gpio;
+ data++;
+ }
- return (struct s5p_gpio_bank *)(base + bank);
+ return NULL;
}
int s5p_gpio_get_pin(unsigned gpio)
@@ -179,3 +293,27 @@ int gpio_set_value(unsigned gpio, int value)
return 0;
}
+
+void gpio_set_pull(int gpio, int mode)
+{
+ s5p_gpio_set_pull(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), mode);
+}
+
+void gpio_set_drv(int gpio, int mode)
+{
+ s5p_gpio_set_drv(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), mode);
+}
+
+void gpio_cfg_pin(int gpio, int cfg)
+{
+ s5p_gpio_cfg_pin(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), cfg);
+}
+
+void gpio_set_rate(int gpio, int mode)
+{
+ s5p_gpio_set_rate(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), mode);
+}
OpenPOWER on IntegriCloud