* [patch 0/4] SM501 MFD driver updates for next kernel
@ 2008-06-22 21:12 Ben Dooks
2008-06-22 21:12 ` [patch 1/4] SM501: add power control callback Ben Dooks
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Ben Dooks @ 2008-06-22 21:12 UTC (permalink / raw)
To: linux-kernel; +Cc: arnaud.patard, akpm
This is a set of SM501 MFD driver updates for the next
kernel release.
Note, there is an patch offset of -2 lines in the
include/linux/sm501.h due to changes in patches currently
sent to the fbdevel list. If akpm can merge those first,
then this series should then apply cleanly.
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 6+ messages in thread
* [patch 1/4] SM501: add power control callback
2008-06-22 21:12 [patch 0/4] SM501 MFD driver updates for next kernel Ben Dooks
@ 2008-06-22 21:12 ` Ben Dooks
2008-06-22 21:12 ` [patch 2/4] SM501: Add gpiolib support Ben Dooks
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Ben Dooks @ 2008-06-22 21:12 UTC (permalink / raw)
To: linux-kernel; +Cc: arnaud.patard, akpm, Ben Dooks
[-- Attachment #1: simtec/simtec-drivers-mfd-sm501-power-callback.patch --]
[-- Type: text/plain, Size: 2527 bytes --]
Add callback to get or set the power control if
the device has the sleep connected to some form
of GPIO.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc4-quilt3/include/linux/sm501.h
===================================================================
--- linux-2.6.26-rc4-quilt3.orig/include/linux/sm501.h 2008-05-29 10:54:40.000000000 +0100
+++ linux-2.6.26-rc4-quilt3/include/linux/sm501.h 2008-05-29 10:54:55.000000000 +0100
@@ -157,6 +157,8 @@ struct sm501_init_gpio {
struct sm501_reg_init gpio_ddr_high;
};
+#define SM501_FLAG_SUSPEND_OFF (1<<4)
+
/* sm501_platdata
*
* This is passed with the platform device to allow the board
@@ -170,6 +172,11 @@ struct sm501_platdata {
struct sm501_init_gpio *init_gpiop;
struct sm501_platdata_fb *fb;
+ int flags;
+
+ int (*get_power)(struct device *dev);
+ int (*set_power)(struct device *dev, unsigned int on);
+
struct sm501_platdata_gpio_i2c *gpio_i2c;
unsigned int gpio_i2c_nr;
};
Index: linux-2.6.26-rc4-quilt3/drivers/mfd/sm501.c
===================================================================
--- linux-2.6.26-rc4-quilt3.orig/drivers/mfd/sm501.c 2008-05-29 10:54:40.000000000 +0100
+++ linux-2.6.26-rc4-quilt3/drivers/mfd/sm501.c 2008-05-29 10:54:55.000000000 +0100
@@ -1138,8 +1138,31 @@ static int sm501_plat_probe(struct platf
}
#ifdef CONFIG_PM
+
/* power management support */
+static void sm501_set_power(struct sm501_devdata *sm, int on)
+{
+ struct sm501_platdata *pd = sm->platdata;
+
+ if (pd == NULL)
+ return;
+
+ if (pd->get_power) {
+ if (pd->get_power(sm->dev) == on) {
+ dev_dbg(sm->dev, "is already %d\n", on);
+ return;
+ }
+ }
+
+ if (pd->set_power) {
+ dev_dbg(sm->dev, "setting power to %d\n", on);
+
+ pd->set_power(sm->dev, on);
+ sm501_mdelay(sm, 10);
+ }
+}
+
static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state)
{
struct sm501_devdata *sm = platform_get_drvdata(pdev);
@@ -1148,6 +1171,12 @@ static int sm501_plat_suspend(struct pla
sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL);
sm501_dump_regs(sm);
+
+ if (sm->platdata) {
+ if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF)
+ sm501_set_power(sm, 0);
+ }
+
return 0;
}
@@ -1155,6 +1184,8 @@ static int sm501_plat_resume(struct plat
{
struct sm501_devdata *sm = platform_get_drvdata(pdev);
+ sm501_set_power(sm, 1);
+
sm501_dump_regs(sm);
sm501_dump_gate(sm);
sm501_dump_clk(sm);
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 6+ messages in thread
* [patch 2/4] SM501: Add gpiolib support
2008-06-22 21:12 [patch 0/4] SM501 MFD driver updates for next kernel Ben Dooks
2008-06-22 21:12 ` [patch 1/4] SM501: add power control callback Ben Dooks
@ 2008-06-22 21:12 ` Ben Dooks
2008-06-24 4:04 ` Andrew Morton
2008-06-22 21:12 ` [patch 3/4] SM501: GPIO dynamic registration for PCI devices Ben Dooks
2008-06-22 21:12 ` [patch 4/4] SM501: GPIO I2C support Ben Dooks
3 siblings, 1 reply; 6+ messages in thread
From: Ben Dooks @ 2008-06-22 21:12 UTC (permalink / raw)
To: linux-kernel; +Cc: arnaud.patard, akpm, Ben Dooks
[-- Attachment #1: simtec/simtec-drivers-mfd-sm501-gpiolib.patch --]
[-- Type: text/plain, Size: 11131 bytes --]
Add support for exporting the GPIOs on the SM501
via gpiolib.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-quilt1/include/linux/sm501.h
===================================================================
--- linux-2.6.26-rc5-quilt1.orig/include/linux/sm501.h 2008-06-11 11:29:03.000000000 +0100
+++ linux-2.6.26-rc5-quilt1/include/linux/sm501.h 2008-06-11 11:29:03.000000000 +0100
@@ -46,24 +46,6 @@ extern unsigned long sm501_modify_reg(st
unsigned long set,
unsigned long clear);
-/* sm501_gpio_set
- *
- * set the state of the given GPIO line
-*/
-
-extern void sm501_gpio_set(struct device *dev,
- unsigned long gpio,
- unsigned int to,
- unsigned int dir);
-
-/* sm501_gpio_get
- *
- * get the state of the given GPIO line
-*/
-
-extern unsigned long sm501_gpio_get(struct device *dev,
- unsigned long gpio);
-
/* Platform data definitions */
@@ -131,6 +113,7 @@ struct sm501_reg_init {
#define SM501_USE_FBACCEL (1<<6)
#define SM501_USE_AC97 (1<<7)
#define SM501_USE_I2S (1<<8)
+#define SM501_USE_GPIO (1<<9)
#define SM501_USE_ALL (0xffffffff)
@@ -173,6 +156,7 @@ struct sm501_platdata {
struct sm501_platdata_fb *fb;
int flags;
+ unsigned gpio_base;
int (*get_power)(struct device *dev);
int (*set_power)(struct device *dev, unsigned int on);
Index: linux-2.6.26-rc5-quilt1/drivers/mfd/sm501.c
===================================================================
--- linux-2.6.26-rc5-quilt1.orig/drivers/mfd/sm501.c 2008-06-11 11:29:03.000000000 +0100
+++ linux-2.6.26-rc5-quilt1/drivers/mfd/sm501.c 2008-06-11 11:31:57.000000000 +0100
@@ -19,6 +19,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
+#include <linux/gpio.h>
#include <linux/sm501.h>
#include <linux/sm501-regs.h>
@@ -31,10 +32,29 @@ struct sm501_device {
struct platform_device pdev;
};
+struct sm501_gpio;
+
+struct sm501_gpio_chip {
+ struct gpio_chip gpio;
+ struct sm501_gpio *ourgpio; /* to get back to parent. */
+ void __iomem *regbase;
+};
+
+struct sm501_gpio {
+ struct sm501_gpio_chip low;
+ struct sm501_gpio_chip high;
+ spinlock_t lock;
+
+ unsigned int registered : 1;
+ void __iomem *regs;
+ struct resource *regs_res;
+};
+
struct sm501_devdata {
spinlock_t reg_lock;
struct mutex clock_lock;
struct list_head devices;
+ struct sm501_gpio gpio;
struct device *dev;
struct resource *io_res;
@@ -42,6 +62,7 @@ struct sm501_devdata {
struct resource *regs_claim;
struct sm501_platdata *platdata;
+
unsigned int in_suspend;
unsigned long pm_misc;
@@ -52,6 +73,7 @@ struct sm501_devdata {
unsigned int rev;
};
+
#define MHZ (1000 * 1000)
#ifdef DEBUG
@@ -276,58 +298,6 @@ unsigned long sm501_modify_reg(struct de
EXPORT_SYMBOL_GPL(sm501_modify_reg);
-unsigned long sm501_gpio_get(struct device *dev,
- unsigned long gpio)
-{
- struct sm501_devdata *sm = dev_get_drvdata(dev);
- unsigned long result;
- unsigned long reg;
-
- reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW;
- result = readl(sm->regs + reg);
-
- result >>= (gpio & 31);
- return result & 1UL;
-}
-
-EXPORT_SYMBOL_GPL(sm501_gpio_get);
-
-void sm501_gpio_set(struct device *dev,
- unsigned long gpio,
- unsigned int to,
- unsigned int dir)
-{
- struct sm501_devdata *sm = dev_get_drvdata(dev);
-
- unsigned long bit = 1 << (gpio & 31);
- unsigned long base;
- unsigned long save;
- unsigned long val;
-
- base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW;
- base += SM501_GPIO;
-
- spin_lock_irqsave(&sm->reg_lock, save);
-
- val = readl(sm->regs + base) & ~bit;
- if (to)
- val |= bit;
- writel(val, sm->regs + base);
-
- val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit;
- if (dir)
- val |= bit;
-
- writel(val, sm->regs + SM501_GPIO_DDR_LOW);
- sm501_sync_regs(sm);
-
- spin_unlock_irqrestore(&sm->reg_lock, save);
-
-}
-
-EXPORT_SYMBOL_GPL(sm501_gpio_set);
-
-
/* sm501_unit_power
*
* alters the power active gate to set specific units on or off
@@ -906,6 +876,226 @@ static int sm501_register_display(struct
return sm501_register_device(sm, pdev);
}
+#ifdef CONFIG_MFD_SM501_GPIO
+
+static inline struct sm501_gpio_chip *to_sm501_gpio(struct gpio_chip *gc)
+{
+ return container_of(gc, struct sm501_gpio_chip, gpio);
+}
+
+static inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio)
+{
+ return container_of(gpio, struct sm501_devdata, gpio);
+}
+
+static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset)
+
+{
+ struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip);
+ unsigned long result;
+
+ result = readl(smgpio->regbase + SM501_GPIO_DATA_LOW);
+ result >>= offset;
+
+ return result & 1UL;
+}
+
+static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+
+{
+ struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+ struct sm501_gpio *smgpio = smchip->ourgpio;
+ unsigned long bit = 1 << offset;
+ void __iomem *regs = smchip->regbase;
+ unsigned long save;
+ unsigned long val;
+
+ dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
+ __func__, chip, offset);
+
+ spin_lock_irqsave(&smgpio->lock, save);
+
+ val = readl(regs + SM501_GPIO_DATA_LOW) & ~bit;
+ if (value)
+ val |= bit;
+ writel(val, regs);
+
+ sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+ spin_unlock_irqrestore(&smgpio->lock, save);
+}
+
+static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+ struct sm501_gpio *smgpio = smchip->ourgpio;
+ void __iomem *regs = smchip->regbase;
+ unsigned long bit = 1 << offset;
+ unsigned long save;
+ unsigned long ddr;
+
+ dev_info(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
+ __func__, chip, offset);
+
+ spin_lock_irqsave(&smgpio->lock, save);
+
+ ddr = readl(regs + SM501_GPIO_DDR_LOW);
+ writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW);
+
+ sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+ spin_unlock_irqrestore(&smgpio->lock, save);
+
+ return 0;
+}
+
+static int sm501_gpio_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+ struct sm501_gpio *smgpio = smchip->ourgpio;
+ unsigned long bit = 1 << offset;
+ void __iomem *regs = smchip->regbase;
+ unsigned long save;
+ unsigned long val;
+ unsigned long ddr;
+
+ dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n",
+ __func__, chip, offset, value);
+
+ spin_lock_irqsave(&smgpio->lock, save);
+
+ val = readl(regs + SM501_GPIO_DATA_LOW);
+ if (value)
+ val |= bit;
+ else
+ val &= ~bit;
+ writel(val, regs);
+
+ ddr = readl(regs + SM501_GPIO_DDR_LOW);
+ writel(ddr | bit, regs + SM501_GPIO_DDR_LOW);
+
+ sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+ writel(val, regs + SM501_GPIO_DATA_LOW);
+
+ sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+ spin_unlock_irqrestore(&smgpio->lock, save);
+
+ return 0;
+}
+
+static struct gpio_chip gpio_chip_template = {
+ .ngpio = 32,
+ .direction_input = sm501_gpio_input,
+ .direction_output = sm501_gpio_output,
+ .set = sm501_gpio_set,
+ .get = sm501_gpio_get,
+};
+
+static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm,
+ struct sm501_gpio *gpio,
+ struct sm501_gpio_chip *chip)
+{
+ struct sm501_platdata *pdata = sm->platdata;
+ struct gpio_chip *gchip = &chip->gpio;
+ unsigned base = pdata->gpio_base;
+
+ memcpy(chip, &gpio_chip_template, sizeof(struct gpio_chip));
+
+ if (chip == &gpio->high) {
+ base += 32;
+ chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH;
+ gchip->label = "SM501-HIGH";
+ } else {
+ chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW;
+ gchip->label = "SM501-LOW";
+ }
+
+ gchip->base = base;
+ chip->ourgpio = gpio;
+
+ return gpiochip_add(gchip);
+}
+
+static int sm501_register_gpio(struct sm501_devdata *sm)
+{
+ struct sm501_gpio *gpio = &sm->gpio;
+ resource_size_t iobase = sm->io_res->start + SM501_GPIO;
+ int ret;
+ int tmp;
+
+ dev_dbg(sm->dev, "registering gpio block %08llx\n",
+ (unsigned long long)iobase);
+
+ spin_lock_init(&gpio->lock);
+
+ gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio");
+ if (gpio->regs_res == NULL) {
+ dev_err(sm->dev, "gpio: failed to request region\n");
+ return -ENXIO;
+ }
+
+ gpio->regs = ioremap(iobase, 0x20);
+ if (gpio->regs == NULL) {
+ dev_err(sm->dev, "gpio: failed to remap registers\n");
+ ret = -ENXIO;
+ goto err_mapped;
+ }
+
+ /* Register both our chips. */
+
+ ret = sm501_gpio_register_chip(sm, gpio, &gpio->low);
+ if (ret) {
+ dev_err(sm->dev, "failed to add low chip\n");
+ goto err_mapped;
+ }
+
+ ret = sm501_gpio_register_chip(sm, gpio, &gpio->high);
+ if (ret) {
+ dev_err(sm->dev, "failed to add high chip\n");
+ goto err_low_chip;
+ }
+
+ gpio->registered = 1;
+
+ return 0;
+
+ err_low_chip:
+ tmp = gpiochip_remove(&gpio->low.gpio);
+ if (tmp) {
+ dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
+ return ret;
+ }
+
+ err_mapped:
+ release_resource(gpio->regs_res);
+ kfree(gpio->regs_res);
+
+ return ret;
+}
+
+static void sm501_gpio_remove(struct sm501_devdata *sm)
+{
+ int ret;
+
+ ret = gpiochip_remove(&sm->gpio.low.gpio);
+ if (ret)
+ dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
+
+ ret = gpiochip_remove(&sm->gpio.high.gpio);
+ if (ret)
+ dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n");
+}
+
+#else
+static int sm501_register_gpio(struct sm501_devdata *sm)
+{
+ return 0;
+}
+
+static void sm501_gpio_remove(struct sm501_devdata *sm)
+{
+}
+#endif
+
/* sm501_dbg_regs
*
* Debug attribute to attach to parent device to show core registers
@@ -1059,6 +1249,8 @@ static int sm501_init_dev(struct sm501_d
sm501_register_usbhost(sm, &mem_avail);
if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1))
sm501_register_uart(sm, idata->devices);
+ if (idata->devices & SM501_USE_GPIO)
+ sm501_register_gpio(sm);
}
ret = sm501_check_clocks(sm);
@@ -1366,6 +1558,9 @@ static void sm501_dev_remove(struct sm50
sm501_remove_sub(sm, smdev);
device_remove_file(sm->dev, &dev_attr_dbg_regs);
+
+ if (sm->gpio.registered)
+ sm501_gpio_remove(sm);
}
static void sm501_pci_remove(struct pci_dev *dev)
Index: linux-2.6.26-rc5-quilt1/drivers/mfd/Kconfig
===================================================================
--- linux-2.6.26-rc5-quilt1.orig/drivers/mfd/Kconfig 2008-06-11 11:29:37.000000000 +0100
+++ linux-2.6.26-rc5-quilt1/drivers/mfd/Kconfig 2008-06-11 11:31:57.000000000 +0100
@@ -15,6 +15,14 @@ config MFD_SM501
interface. The device may be connected by PCI or local bus with
varying functions enabled.
+config MFD_SM501_GPIO
+ bool "Export GPIO via GPIO layer"
+ depends on MFD_SM501 && HAVE_GPIO_LIB
+ ---help---
+ This option uses the gpio library layer to export the 64 GPIO
+ lines on the SM501. The platform data is used to supply the
+ base number for the first GPIO line to register.
+
config MFD_ASIC3
bool "Support for Compaq ASIC3"
depends on GENERIC_HARDIRQS && ARM
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 6+ messages in thread
* [patch 3/4] SM501: GPIO dynamic registration for PCI devices
2008-06-22 21:12 [patch 0/4] SM501 MFD driver updates for next kernel Ben Dooks
2008-06-22 21:12 ` [patch 1/4] SM501: add power control callback Ben Dooks
2008-06-22 21:12 ` [patch 2/4] SM501: Add gpiolib support Ben Dooks
@ 2008-06-22 21:12 ` Ben Dooks
2008-06-22 21:12 ` [patch 4/4] SM501: GPIO I2C support Ben Dooks
3 siblings, 0 replies; 6+ messages in thread
From: Ben Dooks @ 2008-06-22 21:12 UTC (permalink / raw)
To: linux-kernel; +Cc: arnaud.patard, akpm, Ben Dooks, Arnaud Patard
[-- Attachment #1: thirdparty/rtp-sm501-pci-gpio.patch --]
[-- Type: text/plain, Size: 1929 bytes --]
From: Arnaud Patard <apatard@mandriva.com>
The SM501 PCI card requires a dyanmic gpio allocation as
the number of cards is not known at compile time. Fixup
the platform data and registration to deal with this.
Acked-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Arnaud Patard <apatard@mandriva.com>
Index: linux-2.6.26-rc6-quilt3/drivers/mfd/sm501.c
===================================================================
--- linux-2.6.26-rc6-quilt3.orig/drivers/mfd/sm501.c 2008-06-20 16:34:02.000000000 +0100
+++ linux-2.6.26-rc6-quilt3/drivers/mfd/sm501.c 2008-06-20 16:35:11.000000000 +0100
@@ -996,12 +996,13 @@ static int __devinit sm501_gpio_register
{
struct sm501_platdata *pdata = sm->platdata;
struct gpio_chip *gchip = &chip->gpio;
- unsigned base = pdata->gpio_base;
+ int base = pdata->gpio_base;
memcpy(chip, &gpio_chip_template, sizeof(struct gpio_chip));
if (chip == &gpio->high) {
- base += 32;
+ if (base > 0)
+ base += 32;
chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH;
gchip->label = "SM501-HIGH";
} else {
@@ -1452,6 +1453,7 @@ static struct sm501_platdata_fb sm501_fb
static struct sm501_platdata sm501_pci_platdata = {
.init = &sm501_pci_initdata,
.fb = &sm501_fb_pdata,
+ .gpio_base = -1,
};
static int sm501_pci_probe(struct pci_dev *dev,
Index: linux-2.6.26-rc6-quilt3/include/linux/sm501.h
===================================================================
--- linux-2.6.26-rc6-quilt3.orig/include/linux/sm501.h 2008-06-20 16:34:02.000000000 +0100
+++ linux-2.6.26-rc6-quilt3/include/linux/sm501.h 2008-06-20 16:35:11.000000000 +0100
@@ -156,7 +156,7 @@ struct sm501_platdata {
struct sm501_platdata_fb *fb;
int flags;
- unsigned gpio_base;
+ int gpio_base;
int (*get_power)(struct device *dev);
int (*set_power)(struct device *dev, unsigned int on);
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 6+ messages in thread
* [patch 4/4] SM501: GPIO I2C support
2008-06-22 21:12 [patch 0/4] SM501 MFD driver updates for next kernel Ben Dooks
` (2 preceding siblings ...)
2008-06-22 21:12 ` [patch 3/4] SM501: GPIO dynamic registration for PCI devices Ben Dooks
@ 2008-06-22 21:12 ` Ben Dooks
3 siblings, 0 replies; 6+ messages in thread
From: Ben Dooks @ 2008-06-22 21:12 UTC (permalink / raw)
To: linux-kernel; +Cc: arnaud.patard, akpm, Ben Dooks
[-- Attachment #1: simtec/simtec-drivers-mfd-sm501-gpio-i2c.patch --]
[-- Type: text/plain, Size: 4610 bytes --]
Add support for adding the GPIO based I2C resources.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc6-quilt3/drivers/mfd/sm501.c
===================================================================
--- linux-2.6.26-rc6-quilt3.orig/drivers/mfd/sm501.c 2008-06-22 20:18:23.000000000 +0100
+++ linux-2.6.26-rc6-quilt3/drivers/mfd/sm501.c 2008-06-22 20:19:07.000000000 +0100
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/gpio.h>
+#include <linux/i2c-gpio.h>
#include <linux/sm501.h>
#include <linux/sm501-regs.h>
@@ -1086,6 +1087,11 @@ static void sm501_gpio_remove(struct sm5
dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n");
}
+static int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin)
+{
+ struct sm501_gpio *gpio = &sm->gpio;
+ return pin + (pin < 32) ? gpio->low.gpio.base : gpio->high.gpio.base;
+}
#else
static int sm501_register_gpio(struct sm501_devdata *sm)
{
@@ -1095,8 +1101,66 @@ static int sm501_register_gpio(struct sm
static void sm501_gpio_remove(struct sm501_devdata *sm)
{
}
+
+static int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin)
+{
+ return -1;
+}
#endif
+static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
+ struct sm501_platdata_gpio_i2c *iic)
+{
+ struct i2c_gpio_platform_data *icd;
+ struct platform_device *pdev;
+
+ pdev = sm501_create_subdev(sm, "i2c-gpio", 0,
+ sizeof(struct i2c_gpio_platform_data));
+ if (!pdev)
+ return -ENOMEM;
+
+ icd = pdev->dev.platform_data;
+
+ /* We keep the pin_sda and pin_scl fields relative in case the
+ * same platform data is passed to >1 SM501.
+ */
+
+ icd->sda_pin = sm501_gpio_pin2nr(sm, iic->pin_sda);
+ icd->scl_pin = sm501_gpio_pin2nr(sm, iic->pin_scl);
+ icd->timeout = iic->timeout;
+ icd->udelay = iic->udelay;
+
+ /* note, we can't use either of the pin numbers, as the i2c-gpio
+ * driver uses the platform.id field to generate the bus number
+ * to register with the i2c core; The i2c core doesn't have enough
+ * entries to deal with anything we currently use.
+ */
+
+ pdev->id = iic->bus_num;
+
+ dev_info(sm->dev, "registering i2c-%d: sda=%d (%d), scl=%d (%d)\n",
+ iic->bus_num,
+ icd->sda_pin, iic->pin_sda, icd->scl_pin, iic->pin_scl);
+
+ return sm501_register_device(sm, pdev);
+}
+
+static int sm501_register_gpio_i2c(struct sm501_devdata *sm,
+ struct sm501_platdata *pdata)
+{
+ struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c;
+ int index;
+ int ret;
+
+ for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) {
+ ret = sm501_register_gpio_i2c_instance(sm, iic);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
/* sm501_dbg_regs
*
* Debug attribute to attach to parent device to show core registers
@@ -1204,6 +1268,7 @@ static unsigned int sm501_mem_local[] =
static int sm501_init_dev(struct sm501_devdata *sm)
{
struct sm501_initdata *idata;
+ struct sm501_platdata *pdata;
resource_size_t mem_avail;
unsigned long dramctrl;
unsigned long devid;
@@ -1242,7 +1307,9 @@ static int sm501_init_dev(struct sm501_d
/* check to see if we have some device initialisation */
- idata = sm->platdata ? sm->platdata->init : NULL;
+ pdata = sm->platdata;
+ idata = pdata ? pdata->init : NULL;
+
if (idata) {
sm501_init_regs(sm, idata);
@@ -1254,6 +1321,13 @@ static int sm501_init_dev(struct sm501_d
sm501_register_gpio(sm);
}
+ if (pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) {
+ if (!sm->gpio.registered)
+ dev_err(sm->dev, "no gpio registered for i2c gpio.\n");
+ else
+ sm501_register_gpio_i2c(sm, pdata);
+ }
+
ret = sm501_check_clocks(sm);
if (ret) {
dev_err(sm->dev, "M1X and M clocks sourced from different "
Index: linux-2.6.26-rc6-quilt3/include/linux/sm501.h
===================================================================
--- linux-2.6.26-rc6-quilt3.orig/include/linux/sm501.h 2008-06-22 20:18:23.000000000 +0100
+++ linux-2.6.26-rc6-quilt3/include/linux/sm501.h 2008-06-22 20:19:01.000000000 +0100
@@ -86,11 +86,19 @@ struct sm501_platdata_fb {
struct sm501_platdata_fbsub *fb_pnl;
};
-/* gpio i2c */
+/* gpio i2c
+ *
+ * Note, we have to pass in the bus number, as the number used will be
+ * passed to the i2c-gpio driver's platform_device.id, subsequently used
+ * to register the i2c bus.
+*/
struct sm501_platdata_gpio_i2c {
+ unsigned int bus_num;
unsigned int pin_sda;
unsigned int pin_scl;
+ int udelay;
+ int timeout;
};
/* sm501_initdata
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [patch 2/4] SM501: Add gpiolib support
2008-06-22 21:12 ` [patch 2/4] SM501: Add gpiolib support Ben Dooks
@ 2008-06-24 4:04 ` Andrew Morton
0 siblings, 0 replies; 6+ messages in thread
From: Andrew Morton @ 2008-06-24 4:04 UTC (permalink / raw)
To: Ben Dooks; +Cc: linux-kernel, arnaud.patard
On Sun, 22 Jun 2008 22:12:49 +0100 Ben Dooks <ben-linux@fluff.org> wrote:
> Add support for exporting the GPIOs on the SM501
> via gpiolib.
>
> ...
>
> +struct sm501_gpio_chip {
> + struct gpio_chip gpio;
> + struct sm501_gpio *ourgpio; /* to get back to parent. */
> + void __iomem *regbase;
> +};
> +
> +struct sm501_gpio {
> + struct sm501_gpio_chip low;
> + struct sm501_gpio_chip high;
> + spinlock_t lock;
> +
> + unsigned int registered : 1;
> + void __iomem *regs;
> + struct resource *regs_res;
> +};
> +
>
> ...
>
> +static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm,
> + struct sm501_gpio *gpio,
> + struct sm501_gpio_chip *chip)
> +{
> + struct sm501_platdata *pdata = sm->platdata;
> + struct gpio_chip *gchip = &chip->gpio;
> + unsigned base = pdata->gpio_base;
> +
> + memcpy(chip, &gpio_chip_template, sizeof(struct gpio_chip));
This memcpy is not particularly readable and the driver will explode if
someone adds a new member to the start of struct sm501_gpio_chip, as
they should be able to do.
Less risky would be:
memcpy(&chip->gpio, &gpio_chip_template, sizeof(struct gpio_chip));
But why not actually be type-correct and do
chip->gpio = gpio_chip_template;
?
> +
> + dev_dbg(sm->dev, "registering gpio block %08llx\n",
> + (unsigned long long)iobase);
> +
> + spin_lock_init(&gpio->lock);
> +
> + gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio");
> + if (gpio->regs_res == NULL) {
> + dev_err(sm->dev, "gpio: failed to request region\n");
> + return -ENXIO;
> + }
> +
> + gpio->regs = ioremap(iobase, 0x20);
> + if (gpio->regs == NULL) {
> + dev_err(sm->dev, "gpio: failed to remap registers\n");
> + ret = -ENXIO;
> + goto err_mapped;
> + }
> +
> + /* Register both our chips. */
> +
> + ret = sm501_gpio_register_chip(sm, gpio, &gpio->low);
> + if (ret) {
> + dev_err(sm->dev, "failed to add low chip\n");
> + goto err_mapped;
> + }
> +
> + ret = sm501_gpio_register_chip(sm, gpio, &gpio->high);
> + if (ret) {
> + dev_err(sm->dev, "failed to add high chip\n");
> + goto err_low_chip;
> + }
> +
> + gpio->registered = 1;
> +
> + return 0;
> +
> + err_low_chip:
> + tmp = gpiochip_remove(&gpio->low.gpio);
> + if (tmp) {
> + dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
> + return ret;
> + }
> +
> + err_mapped:
> + release_resource(gpio->regs_res);
> + kfree(gpio->regs_res);
> +
> + return ret;
> +}
I see an ioremap(), but no iounmap() on the error path.
Would it not be better to match request_mem_region() with
release_mem_region(), rather than the lower-level release_reource()?
> +static void sm501_gpio_remove(struct sm501_devdata *sm)
> +{
> + int ret;
> +
> + ret = gpiochip_remove(&sm->gpio.low.gpio);
> + if (ret)
> + dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
> +
> + ret = gpiochip_remove(&sm->gpio.high.gpio);
> + if (ret)
> + dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n");
> +}
Did we free all the reources here? I see no other
release_resource/release_mem_region/kfrees in the driver?
> +#else
> +static int sm501_register_gpio(struct sm501_devdata *sm)
> +{
> + return 0;
> +}
> +
> +static void sm501_gpio_remove(struct sm501_devdata *sm)
> +{
> +}
Might be better to give these an explicit inline rather than trusting
gcc to not be silly.
> +#endif
> +
> /* sm501_dbg_regs
> *
> * Debug attribute to attach to parent device to show core registers
> @@ -1059,6 +1249,8 @@ static int sm501_init_dev(struct sm501_d
> sm501_register_usbhost(sm, &mem_avail);
> if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1))
> sm501_register_uart(sm, idata->devices);
> + if (idata->devices & SM501_USE_GPIO)
> + sm501_register_gpio(sm);
> }
>
> ret = sm501_check_clocks(sm);
> @@ -1366,6 +1558,9 @@ static void sm501_dev_remove(struct sm50
> sm501_remove_sub(sm, smdev);
>
> device_remove_file(sm->dev, &dev_attr_dbg_regs);
> +
> + if (sm->gpio.registered)
> + sm501_gpio_remove(sm);
> }
>
> static void sm501_pci_remove(struct pci_dev *dev)
> Index: linux-2.6.26-rc5-quilt1/drivers/mfd/Kconfig
> ===================================================================
> --- linux-2.6.26-rc5-quilt1.orig/drivers/mfd/Kconfig 2008-06-11 11:29:37.000000000 +0100
> +++ linux-2.6.26-rc5-quilt1/drivers/mfd/Kconfig 2008-06-11 11:31:57.000000000 +0100
> @@ -15,6 +15,14 @@ config MFD_SM501
> interface. The device may be connected by PCI or local bus with
> varying functions enabled.
>
> +config MFD_SM501_GPIO
> + bool "Export GPIO via GPIO layer"
> + depends on MFD_SM501 && HAVE_GPIO_LIB
> + ---help---
> + This option uses the gpio library layer to export the 64 GPIO
> + lines on the SM501. The platform data is used to supply the
> + base number for the first GPIO line to register.
> +
> config MFD_ASIC3
> bool "Support for Compaq ASIC3"
> depends on GENERIC_HARDIRQS && ARM
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2008-06-24 4:05 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-22 21:12 [patch 0/4] SM501 MFD driver updates for next kernel Ben Dooks
2008-06-22 21:12 ` [patch 1/4] SM501: add power control callback Ben Dooks
2008-06-22 21:12 ` [patch 2/4] SM501: Add gpiolib support Ben Dooks
2008-06-24 4:04 ` Andrew Morton
2008-06-22 21:12 ` [patch 3/4] SM501: GPIO dynamic registration for PCI devices Ben Dooks
2008-06-22 21:12 ` [patch 4/4] SM501: GPIO I2C support Ben Dooks
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.