Blackfin: bf538: add support for extended GPIO banks

The GPIOs on ports C/D/E on the BF538/BF539 do not behave the same way as
the other ports on the part and the same way as all other Blackfin parts.
The MMRs are programmed slightly different and they cannot be used to
generate interrupts or wakeup a sleeping system.  Since these guys don't
fit into the existing code, create a simple gpiolib driver for them.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
Michael Hennerich 2009-09-28 12:23:41 +00:00 committed by Mike Frysinger
parent 46fe23ac39
commit 621dd24743
8 changed files with 280 additions and 27 deletions

View File

@ -159,6 +159,11 @@ struct gpio_port_t {
};
#endif
#ifdef BFIN_SPECIAL_GPIO_BANKS
void bfin_special_gpio_free(unsigned gpio);
int bfin_special_gpio_request(unsigned gpio, const char *label);
#endif
#ifdef CONFIG_PM
unsigned int bfin_pm_standby_setup(void);

View File

@ -100,6 +100,12 @@ u8 pmux_offset[][16] = {
};
# endif
#elif defined(BF538_FAMILY)
static unsigned short * const port_fer[] = {
(unsigned short *) PORTCIO_FER,
(unsigned short *) PORTDIO_FER,
(unsigned short *) PORTEIO_FER,
};
#endif
static unsigned short reserved_gpio_map[GPIO_BANK_NUM];
@ -163,6 +169,27 @@ static int cmp_label(unsigned short ident, const char *label)
static void port_setup(unsigned gpio, unsigned short usage)
{
#if defined(BF538_FAMILY)
/*
* BF538/9 Port C,D and E are special.
* Inverted PORT_FER polarity on CDE and no PORF_FER on F
* Regular PORT F GPIOs are handled here, CDE are exclusively
* managed by GPIOLIB
*/
if (gpio < MAX_BLACKFIN_GPIOS || gpio >= MAX_RESOURCES)
return;
gpio -= MAX_BLACKFIN_GPIOS;
if (usage == GPIO_USAGE)
*port_fer[gpio_bank(gpio)] |= gpio_bit(gpio);
else
*port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio);
SSYNC();
return;
#endif
if (check_gpio(gpio))
return;
@ -981,6 +1008,76 @@ void bfin_gpio_free(unsigned gpio)
}
EXPORT_SYMBOL(bfin_gpio_free);
#ifdef BFIN_SPECIAL_GPIO_BANKS
static unsigned short reserved_special_gpio_map[gpio_bank(MAX_RESOURCES)];
int bfin_special_gpio_request(unsigned gpio, const char *label)
{
unsigned long flags;
local_irq_save_hw(flags);
/*
* Allow that the identical GPIO can
* be requested from the same driver twice
* Do nothing and return -
*/
if (cmp_label(gpio, label) == 0) {
local_irq_restore_hw(flags);
return 0;
}
if (unlikely(reserved_special_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
local_irq_restore_hw(flags);
printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved by %s !\n",
gpio, get_label(gpio));
return -EBUSY;
}
if (unlikely(reserved_peri_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
local_irq_restore_hw(flags);
printk(KERN_ERR
"bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n",
gpio, get_label(gpio));
return -EBUSY;
}
reserved_special_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio);
reserved_peri_map[gpio_bank(gpio)] |= gpio_bit(gpio);
set_label(gpio, label);
local_irq_restore_hw(flags);
port_setup(gpio, GPIO_USAGE);
return 0;
}
EXPORT_SYMBOL(bfin_special_gpio_request);
void bfin_special_gpio_free(unsigned gpio)
{
unsigned long flags;
might_sleep();
local_irq_save_hw(flags);
if (unlikely(!(reserved_special_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)))) {
gpio_error(gpio);
local_irq_restore_hw(flags);
return;
}
reserved_special_gpio_map[gpio_bank(gpio)] &= ~gpio_bit(gpio);
reserved_peri_map[gpio_bank(gpio)] &= ~gpio_bit(gpio);
set_label(gpio, "free");
local_irq_restore_hw(flags);
}
EXPORT_SYMBOL(bfin_special_gpio_free);
#endif
int bfin_gpio_irq_request(unsigned gpio, const char *label)
{
unsigned long flags;

View File

@ -3,3 +3,4 @@
#
obj-y := ints-priority.o dma.o
obj-$(CONFIG_GPIOLIB) += ext-gpio.o

View File

@ -0,0 +1,123 @@
/*
* GPIOLIB interface for BF538/9 PORT C, D, and E GPIOs
*
* Copyright 2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/err.h>
#include <asm/blackfin.h>
#include <asm/gpio.h>
#include <asm/portmux.h>
#define DEFINE_REG(reg, off) \
static inline u16 read_##reg(void __iomem *port) \
{ return bfin_read16(port + off); } \
static inline void write_##reg(void __iomem *port, u16 v) \
{ bfin_write16(port + off, v); }
DEFINE_REG(PORTIO, 0x00)
DEFINE_REG(PORTIO_CLEAR, 0x10)
DEFINE_REG(PORTIO_SET, 0x20)
DEFINE_REG(PORTIO_DIR, 0x40)
DEFINE_REG(PORTIO_INEN, 0x50)
static void __iomem *gpio_chip_to_mmr(struct gpio_chip *chip)
{
switch (chip->base) {
default: /* not really needed, but keeps gcc happy */
case GPIO_PC0: return (void __iomem *)PORTCIO;
case GPIO_PD0: return (void __iomem *)PORTDIO;
case GPIO_PE0: return (void __iomem *)PORTEIO;
}
}
static int bf538_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
{
void __iomem *port = gpio_chip_to_mmr(chip);
return !!(read_PORTIO(port) & (1u << gpio));
}
static void bf538_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value)
{
void __iomem *port = gpio_chip_to_mmr(chip);
if (value)
write_PORTIO_SET(port, (1u << gpio));
else
write_PORTIO_CLEAR(port, (1u << gpio));
}
static int bf538_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
{
void __iomem *port = gpio_chip_to_mmr(chip);
write_PORTIO_DIR(port, read_PORTIO_DIR(port) & ~(1u << gpio));
write_PORTIO_INEN(port, read_PORTIO_INEN(port) | (1u << gpio));
return 0;
}
static int bf538_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value)
{
void __iomem *port = gpio_chip_to_mmr(chip);
write_PORTIO_INEN(port, read_PORTIO_INEN(port) & ~(1u << gpio));
bf538_gpio_set_value(port, gpio, value);
write_PORTIO_DIR(port, read_PORTIO_DIR(port) | (1u << gpio));
return 0;
}
static int bf538_gpio_request(struct gpio_chip *chip, unsigned gpio)
{
return bfin_special_gpio_request(chip->base + gpio, chip->label);
}
static void bf538_gpio_free(struct gpio_chip *chip, unsigned gpio)
{
return bfin_special_gpio_free(chip->base + gpio);
}
/* We don't set the irq fields as these banks cannot generate interrupts */
static struct gpio_chip bf538_portc_chip = {
.label = "GPIO-PC",
.direction_input = bf538_gpio_direction_input,
.get = bf538_gpio_get_value,
.direction_output = bf538_gpio_direction_output,
.set = bf538_gpio_set_value,
.request = bf538_gpio_request,
.free = bf538_gpio_free,
.base = GPIO_PC0,
.ngpio = GPIO_PC9 - GPIO_PC0 + 1,
};
static struct gpio_chip bf538_portd_chip = {
.label = "GPIO-PD",
.direction_input = bf538_gpio_direction_input,
.get = bf538_gpio_get_value,
.direction_output = bf538_gpio_direction_output,
.set = bf538_gpio_set_value,
.request = bf538_gpio_request,
.free = bf538_gpio_free,
.base = GPIO_PD0,
.ngpio = GPIO_PD13 - GPIO_PD0 + 1,
};
static struct gpio_chip bf538_porte_chip = {
.label = "GPIO-PE",
.direction_input = bf538_gpio_direction_input,
.get = bf538_gpio_get_value,
.direction_output = bf538_gpio_direction_output,
.set = bf538_gpio_set_value,
.request = bf538_gpio_request,
.free = bf538_gpio_free,
.base = GPIO_PE0,
.ngpio = GPIO_PE15 - GPIO_PE0 + 1,
};
static int __init bf538_extgpio_setup(void)
{
return gpiochip_add(&bf538_portc_chip) |
gpiochip_add(&bf538_portd_chip) |
gpiochip_add(&bf538_porte_chip);
}
arch_initcall(bf538_extgpio_setup);

View File

@ -468,31 +468,31 @@
/* General-Purpose Ports (0xFFC01500 - 0xFFC015FF) */
/* GPIO Port C Register Names */
#define GPIO_C_CNFG 0xFFC01500 /* GPIO Pin Port C Configuration Register */
#define GPIO_C_D 0xFFC01510 /* GPIO Pin Port C Data Register */
#define GPIO_C_C 0xFFC01520 /* Clear GPIO Pin Port C Register */
#define GPIO_C_S 0xFFC01530 /* Set GPIO Pin Port C Register */
#define GPIO_C_T 0xFFC01540 /* Toggle GPIO Pin Port C Register */
#define GPIO_C_DIR 0xFFC01550 /* GPIO Pin Port C Direction Register */
#define GPIO_C_INEN 0xFFC01560 /* GPIO Pin Port C Input Enable Register */
#define PORTCIO_FER 0xFFC01500 /* GPIO Pin Port C Configuration Register */
#define PORTCIO 0xFFC01510 /* GPIO Pin Port C Data Register */
#define PORTCIO_CLEAR 0xFFC01520 /* Clear GPIO Pin Port C Register */
#define PORTCIO_SET 0xFFC01530 /* Set GPIO Pin Port C Register */
#define PORTCIO_TOGGLE 0xFFC01540 /* Toggle GPIO Pin Port C Register */
#define PORTCIO_DIR 0xFFC01550 /* GPIO Pin Port C Direction Register */
#define PORTCIO_INEN 0xFFC01560 /* GPIO Pin Port C Input Enable Register */
/* GPIO Port D Register Names */
#define GPIO_D_CNFG 0xFFC01504 /* GPIO Pin Port D Configuration Register */
#define GPIO_D_D 0xFFC01514 /* GPIO Pin Port D Data Register */
#define GPIO_D_C 0xFFC01524 /* Clear GPIO Pin Port D Register */
#define GPIO_D_S 0xFFC01534 /* Set GPIO Pin Port D Register */
#define GPIO_D_T 0xFFC01544 /* Toggle GPIO Pin Port D Register */
#define GPIO_D_DIR 0xFFC01554 /* GPIO Pin Port D Direction Register */
#define GPIO_D_INEN 0xFFC01564 /* GPIO Pin Port D Input Enable Register */
#define PORTDIO_FER 0xFFC01504 /* GPIO Pin Port D Configuration Register */
#define PORTDIO 0xFFC01514 /* GPIO Pin Port D Data Register */
#define PORTDIO_CLEAR 0xFFC01524 /* Clear GPIO Pin Port D Register */
#define PORTDIO_SET 0xFFC01534 /* Set GPIO Pin Port D Register */
#define PORTDIO_TOGGLE 0xFFC01544 /* Toggle GPIO Pin Port D Register */
#define PORTDIO_DIR 0xFFC01554 /* GPIO Pin Port D Direction Register */
#define PORTDIO_INEN 0xFFC01564 /* GPIO Pin Port D Input Enable Register */
/* GPIO Port E Register Names */
#define GPIO_E_CNFG 0xFFC01508 /* GPIO Pin Port E Configuration Register */
#define GPIO_E_D 0xFFC01518 /* GPIO Pin Port E Data Register */
#define GPIO_E_C 0xFFC01528 /* Clear GPIO Pin Port E Register */
#define GPIO_E_S 0xFFC01538 /* Set GPIO Pin Port E Register */
#define GPIO_E_T 0xFFC01548 /* Toggle GPIO Pin Port E Register */
#define GPIO_E_DIR 0xFFC01558 /* GPIO Pin Port E Direction Register */
#define GPIO_E_INEN 0xFFC01568 /* GPIO Pin Port E Input Enable Register */
#define PORTEIO_FER 0xFFC01508 /* GPIO Pin Port E Configuration Register */
#define PORTEIO 0xFFC01518 /* GPIO Pin Port E Data Register */
#define PORTEIO_CLEAR 0xFFC01528 /* Clear GPIO Pin Port E Register */
#define PORTEIO_SET 0xFFC01538 /* Set GPIO Pin Port E Register */
#define PORTEIO_TOGGLE 0xFFC01548 /* Toggle GPIO Pin Port E Register */
#define PORTEIO_DIR 0xFFC01558 /* GPIO Pin Port E Direction Register */
#define PORTEIO_INEN 0xFFC01568 /* GPIO Pin Port E Input Enable Register */
/* DMA Controller 1 Traffic Control Registers (0xFFC01B00 - 0xFFC01BFF) */

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008 Analog Devices Inc.
* Copyright (C) 2008-2009 Analog Devices Inc.
* Licensed under the GPL-2 or later.
*/
@ -7,11 +7,8 @@
#ifndef _MACH_GPIO_H_
#define _MACH_GPIO_H_
/* FIXME:
* For now only support PORTF GPIOs.
* PORT C,D and E are for peripheral usage only
*/
#define MAX_BLACKFIN_GPIOS 16
#define BFIN_SPECIAL_GPIO_BANKS 3
#define GPIO_PF0 0 /* PF */
#define GPIO_PF1 1

View File

@ -7,7 +7,7 @@
#ifndef _MACH_PORTMUX_H_
#define _MACH_PORTMUX_H_
#define MAX_RESOURCES MAX_BLACKFIN_GPIOS
#define MAX_RESOURCES 64
#define P_TMR2 (P_DONTCARE)
#define P_TMR1 (P_DONTCARE)

View File

@ -404,6 +404,21 @@ ENTRY(_do_hibernate)
PM_SYS_PUSH(EBIU_FCTL)
#endif
#ifdef PORTCIO_FER
PM_SYS_PUSH16(PORTCIO_DIR)
PM_SYS_PUSH16(PORTCIO_INEN)
PM_SYS_PUSH16(PORTCIO)
PM_SYS_PUSH16(PORTCIO_FER)
PM_SYS_PUSH16(PORTDIO_DIR)
PM_SYS_PUSH16(PORTDIO_INEN)
PM_SYS_PUSH16(PORTDIO)
PM_SYS_PUSH16(PORTDIO_FER)
PM_SYS_PUSH16(PORTEIO_DIR)
PM_SYS_PUSH16(PORTEIO_INEN)
PM_SYS_PUSH16(PORTEIO)
PM_SYS_PUSH16(PORTEIO_FER)
#endif
PM_SYS_PUSH16(SYSCR)
/* Save Core MMRs */
@ -716,6 +731,21 @@ ENTRY(_do_hibernate)
P0.L = lo(PLL_CTL);
PM_SYS_POP16(SYSCR)
#ifdef PORTCIO_FER
PM_SYS_POP16(PORTEIO_FER)
PM_SYS_POP16(PORTEIO)
PM_SYS_POP16(PORTEIO_INEN)
PM_SYS_POP16(PORTEIO_DIR)
PM_SYS_POP16(PORTDIO_FER)
PM_SYS_POP16(PORTDIO)
PM_SYS_POP16(PORTDIO_INEN)
PM_SYS_POP16(PORTDIO_DIR)
PM_SYS_POP16(PORTCIO_FER)
PM_SYS_POP16(PORTCIO)
PM_SYS_POP16(PORTCIO_INEN)
PM_SYS_POP16(PORTCIO_DIR)
#endif
#ifdef EBIU_FCTL
PM_SYS_POP(EBIU_FCTL)
PM_SYS_POP(EBIU_MODE)