public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [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