-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml --- r: 193560 b: refs/heads/master c: 7e0de02 h: refs/heads/master v: v3
- Loading branch information
Jonathan Corbet
committed
May 7, 2010
1 parent
5d08daf
commit 316e23d
Showing
6 changed files
with
297 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: 24b4d82e4715841848a499534ed5cb7db3d6bca3 | ||
refs/heads/master: 7e0de022680f7899d33141f3ab5724a704f5669a |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
/* | ||
* Support for viafb GPIO ports. | ||
* | ||
* Copyright 2009 Jonathan Corbet <corbet@lwn.net> | ||
* Distributable under version 2 of the GNU General Public License. | ||
*/ | ||
|
||
#include <linux/spinlock.h> | ||
#include <linux/gpio.h> | ||
#include "via-core.h" | ||
#include "via-gpio.h" | ||
#include "global.h" | ||
|
||
/* | ||
* The ports we know about. Note that the port-25 gpios are not | ||
* mentioned in the datasheet. | ||
*/ | ||
|
||
struct viafb_gpio { | ||
char *vg_name; /* Data sheet name */ | ||
u16 vg_io_port; | ||
u8 vg_port_index; | ||
int vg_mask_shift; | ||
}; | ||
|
||
static struct viafb_gpio viafb_all_gpios[] = { | ||
{ | ||
.vg_name = "VGPIO0", /* Guess - not in datasheet */ | ||
.vg_io_port = VIASR, | ||
.vg_port_index = 0x25, | ||
.vg_mask_shift = 1 | ||
}, | ||
{ | ||
.vg_name = "VGPIO1", | ||
.vg_io_port = VIASR, | ||
.vg_port_index = 0x25, | ||
.vg_mask_shift = 0 | ||
}, | ||
{ | ||
.vg_name = "VGPIO2", /* aka DISPCLKI0 */ | ||
.vg_io_port = VIASR, | ||
.vg_port_index = 0x2c, | ||
.vg_mask_shift = 1 | ||
}, | ||
{ | ||
.vg_name = "VGPIO3", /* aka DISPCLKO0 */ | ||
.vg_io_port = VIASR, | ||
.vg_port_index = 0x2c, | ||
.vg_mask_shift = 0 | ||
}, | ||
{ | ||
.vg_name = "VGPIO4", /* DISPCLKI1 */ | ||
.vg_io_port = VIASR, | ||
.vg_port_index = 0x3d, | ||
.vg_mask_shift = 1 | ||
}, | ||
{ | ||
.vg_name = "VGPIO5", /* DISPCLKO1 */ | ||
.vg_io_port = VIASR, | ||
.vg_port_index = 0x3d, | ||
.vg_mask_shift = 0 | ||
}, | ||
}; | ||
|
||
#define VIAFB_NUM_GPIOS ARRAY_SIZE(viafb_all_gpios) | ||
|
||
/* | ||
* This structure controls the active GPIOs, which may be a subset | ||
* of those which are known. | ||
*/ | ||
|
||
struct viafb_gpio_cfg { | ||
struct gpio_chip gpio_chip; | ||
struct viafb_dev *vdev; | ||
struct viafb_gpio *active_gpios[VIAFB_NUM_GPIOS]; | ||
char *gpio_names[VIAFB_NUM_GPIOS]; | ||
}; | ||
|
||
/* | ||
* GPIO access functions | ||
*/ | ||
static void via_gpio_set(struct gpio_chip *chip, unsigned int nr, | ||
int value) | ||
{ | ||
struct viafb_gpio_cfg *cfg = container_of(chip, | ||
struct viafb_gpio_cfg, | ||
gpio_chip); | ||
u8 reg; | ||
struct viafb_gpio *gpio; | ||
unsigned long flags; | ||
|
||
spin_lock_irqsave(&cfg->vdev->reg_lock, flags); | ||
gpio = cfg->active_gpios[nr]; | ||
reg = viafb_read_reg(VIASR, gpio->vg_port_index); | ||
reg |= 0x40 << gpio->vg_mask_shift; /* output enable */ | ||
if (value) | ||
reg |= 0x10 << gpio->vg_mask_shift; | ||
else | ||
reg &= ~(0x10 << gpio->vg_mask_shift); | ||
viafb_write_reg(gpio->vg_port_index, VIASR, reg); | ||
spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
} | ||
|
||
static int via_gpio_dir_out(struct gpio_chip *chip, unsigned int nr, | ||
int value) | ||
{ | ||
via_gpio_set(chip, nr, value); | ||
return 0; | ||
} | ||
|
||
/* | ||
* Set the input direction. I'm not sure this is right; we should | ||
* be able to do input without disabling output. | ||
*/ | ||
static int via_gpio_dir_input(struct gpio_chip *chip, unsigned int nr) | ||
{ | ||
struct viafb_gpio_cfg *cfg = container_of(chip, | ||
struct viafb_gpio_cfg, | ||
gpio_chip); | ||
struct viafb_gpio *gpio; | ||
unsigned long flags; | ||
|
||
spin_lock_irqsave(&cfg->vdev->reg_lock, flags); | ||
gpio = cfg->active_gpios[nr]; | ||
viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0, | ||
0x40 << gpio->vg_mask_shift); | ||
spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
return 0; | ||
} | ||
|
||
static int via_gpio_get(struct gpio_chip *chip, unsigned int nr) | ||
{ | ||
struct viafb_gpio_cfg *cfg = container_of(chip, | ||
struct viafb_gpio_cfg, | ||
gpio_chip); | ||
u8 reg; | ||
struct viafb_gpio *gpio; | ||
unsigned long flags; | ||
|
||
spin_lock_irqsave(&cfg->vdev->reg_lock, flags); | ||
gpio = cfg->active_gpios[nr]; | ||
reg = viafb_read_reg(VIASR, gpio->vg_port_index); | ||
spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
return reg & (0x04 << gpio->vg_mask_shift); | ||
} | ||
|
||
|
||
static struct viafb_gpio_cfg gpio_config = { | ||
.gpio_chip = { | ||
.label = "VIAFB onboard GPIO", | ||
.owner = THIS_MODULE, | ||
.direction_output = via_gpio_dir_out, | ||
.set = via_gpio_set, | ||
.direction_input = via_gpio_dir_input, | ||
.get = via_gpio_get, | ||
.base = -1, | ||
.ngpio = 0, | ||
.can_sleep = 0 | ||
} | ||
}; | ||
|
||
/* | ||
* Manage the software enable bit. | ||
*/ | ||
static void viafb_gpio_enable(struct viafb_gpio *gpio) | ||
{ | ||
viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0x02, 0x02); | ||
} | ||
|
||
static void viafb_gpio_disable(struct viafb_gpio *gpio) | ||
{ | ||
viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0, 0x02); | ||
} | ||
|
||
|
||
|
||
|
||
int viafb_create_gpios(struct viafb_dev *vdev, | ||
const struct via_port_cfg *port_cfg) | ||
{ | ||
int i, ngpio = 0, ret; | ||
struct viafb_gpio *gpio; | ||
unsigned long flags; | ||
|
||
/* | ||
* Set up entries for all GPIOs which have been configured to | ||
* operate as such (as opposed to as i2c ports). | ||
*/ | ||
for (i = 0; i < VIAFB_NUM_PORTS; i++) { | ||
if (port_cfg[i].mode != VIA_MODE_GPIO) | ||
continue; | ||
for (gpio = viafb_all_gpios; | ||
gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++) | ||
if (gpio->vg_port_index == port_cfg[i].ioport_index) { | ||
gpio_config.active_gpios[ngpio] = gpio; | ||
gpio_config.gpio_names[ngpio] = gpio->vg_name; | ||
ngpio++; | ||
} | ||
} | ||
gpio_config.gpio_chip.ngpio = ngpio; | ||
gpio_config.gpio_chip.names = gpio_config.gpio_names; | ||
gpio_config.vdev = vdev; | ||
if (ngpio == 0) { | ||
printk(KERN_INFO "viafb: no GPIOs configured\n"); | ||
return 0; | ||
} | ||
/* | ||
* Enable the ports. They come in pairs, with a single | ||
* enable bit for both. | ||
*/ | ||
spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); | ||
for (i = 0; i < ngpio; i += 2) | ||
viafb_gpio_enable(gpio_config.active_gpios[i]); | ||
spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); | ||
/* | ||
* Get registered. | ||
*/ | ||
gpio_config.gpio_chip.base = -1; /* Dynamic */ | ||
ret = gpiochip_add(&gpio_config.gpio_chip); | ||
if (ret) { | ||
printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); | ||
gpio_config.gpio_chip.ngpio = 0; | ||
} | ||
return ret; | ||
/* Port enable ? */ | ||
} | ||
|
||
|
||
int viafb_destroy_gpios(void) | ||
{ | ||
unsigned long flags; | ||
int ret = 0, i; | ||
|
||
spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); | ||
/* | ||
* Get unregistered. | ||
*/ | ||
if (gpio_config.gpio_chip.ngpio > 0) { | ||
ret = gpiochip_remove(&gpio_config.gpio_chip); | ||
if (ret) { /* Somebody still using it? */ | ||
printk(KERN_ERR "Viafb: GPIO remove failed\n"); | ||
goto out; | ||
} | ||
} | ||
/* | ||
* Disable the ports. | ||
*/ | ||
for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2) | ||
viafb_gpio_disable(gpio_config.active_gpios[i]); | ||
gpio_config.gpio_chip.ngpio = 0; | ||
out: | ||
spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); | ||
return ret; | ||
} | ||
|
||
/* | ||
* Look up a specific gpio and return the number it was assigned. | ||
*/ | ||
int viafb_gpio_lookup(const char *name) | ||
{ | ||
int i; | ||
|
||
for (i = 0; i < gpio_config.gpio_chip.ngpio; i++) | ||
if (!strcmp(name, gpio_config.active_gpios[i]->vg_name)) | ||
return gpio_config.gpio_chip.base + i; | ||
return -1; | ||
} | ||
EXPORT_SYMBOL_GPL(viafb_gpio_lookup); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* Support for viafb GPIO ports. | ||
* | ||
* Copyright 2009 Jonathan Corbet <corbet@lwn.net> | ||
* Distributable under version 2 of the GNU General Public License. | ||
*/ | ||
|
||
#ifndef __VIA_GPIO_H__ | ||
#define __VIA_GPIO_H__ | ||
|
||
extern int viafb_create_gpios(struct viafb_dev *vdev, | ||
const struct via_port_cfg *port_cfg); | ||
extern int viafb_destroy_gpios(void); | ||
extern int viafb_gpio_lookup(const char *name); | ||
#endif |