* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox