All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 3/5] pxa: clean up the legacy SSP API
@ 2009-04-23  5:06 Eric Miao
  2009-04-23  8:24 ` Mark Brown
  2009-04-28 22:20 ` Russell King - ARM Linux
  0 siblings, 2 replies; 12+ messages in thread
From: Eric Miao @ 2009-04-23  5:06 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: alsa-devel, Paul Shen, Mark Brown, Philipp Zabel

Actually, due to the number of possible SSP configurations, it's quite a
difficult job to maintain an API that can be well re-used. The guideline
here is to keep only a list of SSP devices and their basic information,
and it's up to the user driver (like SPI or Audio SSP) to configure the
SSP in a desired way.

Now that the legacy 'struct ssp_dev' based APIs are removed, and only the
functions below are kept, modified a little bit to make them aware of the
new 'struct ssp_device':

  ssp_enable()
  ssp_disable()
  ssp_save_state()
  ssp_restore_state()

And finally, ssp_{save,restore}_state() can be merged into the suspend()
and resume() interface of the SSP's platform_driver, thus saving the user
driver from doing some similar things in common.

Signed-off-by: Eric Miao <eric.miao@marvell.com>
---
 arch/arm/mach-pxa/include/mach/ssp.h |   32 +----
 arch/arm/mach-pxa/ssp.c              |  240 +---------------------------------
 sound/soc/pxa/pxa-ssp.c              |   65 +++++-----
 3 files changed, 45 insertions(+), 292 deletions(-)

diff --git a/arch/arm/mach-pxa/include/mach/ssp.h
b/arch/arm/mach-pxa/include/mach/ssp.h
index cb5cb76..e431685 100644
--- a/arch/arm/mach-pxa/include/mach/ssp.h
+++ b/arch/arm/mach-pxa/include/mach/ssp.h
@@ -58,26 +58,10 @@ struct ssp_state {
 	u32 psp;
 };

-struct ssp_dev {
-	struct ssp_device *ssp;
-	u32 port;
-	u32 mode;
-	u32 flags;
-	u32 psp_flags;
-	u32 speed;
-	int irq;
-};
-
-int ssp_write_word(struct ssp_dev *dev, u32 data);
-int ssp_read_word(struct ssp_dev *dev, u32 *data);
-int ssp_flush(struct ssp_dev *dev);
-void ssp_enable(struct ssp_dev *dev);
-void ssp_disable(struct ssp_dev *dev);
-void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp);
-void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp);
-int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags);
-int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32
psp_flags, u32 speed);
-void ssp_exit(struct ssp_dev *dev);
+void ssp_enable(struct ssp_device *ssp);
+void ssp_disable(struct ssp_device *ssp);
+void ssp_save_state(struct ssp_device *ssp, struct ssp_state *);
+void ssp_restore_state(struct ssp_device *ssp, struct ssp_state *);

 /**
  * ssp_write_reg - Write to a SSP register
@@ -86,9 +70,9 @@ void ssp_exit(struct ssp_dev *dev);
  * @reg: Register to write to
  * @val: Value to be written.
  */
-static inline void ssp_write_reg(struct ssp_device *dev, u32 reg, u32 val)
+static inline void ssp_write_reg(struct ssp_device *ssp, u32 reg, u32 val)
 {
-	__raw_writel(val, dev->mmio_base + reg);
+	__raw_writel(val, ssp->mmio_base + reg);
 }

 /**
@@ -97,9 +81,9 @@ static inline void ssp_write_reg(struct ssp_device
*dev, u32 reg, u32 val)
  * @dev: SSP device to access
  * @reg: Register to read from
  */
-static inline u32 ssp_read_reg(struct ssp_device *dev, u32 reg)
+static inline u32 ssp_read_reg(struct ssp_device *ssp, u32 reg)
 {
-	return __raw_readl(dev->mmio_base + reg);
+	return __raw_readl(ssp->mmio_base + reg);
 }

 struct ssp_device *ssp_request(int port, const char *label);
diff --git a/arch/arm/mach-pxa/ssp.c b/arch/arm/mach-pxa/ssp.c
index 965e38c..f48807a 100644
--- a/arch/arm/mach-pxa/ssp.c
+++ b/arch/arm/mach-pxa/ssp.c
@@ -35,153 +35,25 @@
 #include <mach/ssp.h>
 #include <mach/regs-ssp.h>

-#define TIMEOUT 100000
-
-static irqreturn_t ssp_interrupt(int irq, void *dev_id)
+void ssp_enable(struct ssp_device *ssp)
 {
-	struct ssp_dev *dev = dev_id;
-	struct ssp_device *ssp = dev->ssp;
-	unsigned int status;
-
-	status = __raw_readl(ssp->mmio_base + SSSR);
-	__raw_writel(status, ssp->mmio_base + SSSR);
-
-	if (status & SSSR_ROR)
-		printk(KERN_WARNING "SSP(%d): receiver overrun\n", dev->port);
-
-	if (status & SSSR_TUR)
-		printk(KERN_WARNING "SSP(%d): transmitter underrun\n", dev->port);
-
-	if (status & SSSR_BCE)
-		printk(KERN_WARNING "SSP(%d): bit count error\n", dev->port);
-
-	return IRQ_HANDLED;
-}
-
-/**
- * ssp_write_word - write a word to the SSP port
- * @data: 32-bit, MSB justified data to write.
- *
- * Wait for a free entry in the SSP transmit FIFO, and write a data
- * word to the SSP port.
- *
- * The caller is expected to perform the necessary locking.
- *
- * Returns:
- *   %-ETIMEDOUT	timeout occurred
- *   0			success
- */
-int ssp_write_word(struct ssp_dev *dev, u32 data)
-{
-	struct ssp_device *ssp = dev->ssp;
-	int timeout = TIMEOUT;
-
-	while (!(__raw_readl(ssp->mmio_base + SSSR) & SSSR_TNF)) {
-	        if (!--timeout)
-	        	return -ETIMEDOUT;
-		cpu_relax();
-	}
-
-	__raw_writel(data, ssp->mmio_base + SSDR);
-
-	return 0;
-}
-
-/**
- * ssp_read_word - read a word from the SSP port
- *
- * Wait for a data word in the SSP receive FIFO, and return the
- * received data.  Data is LSB justified.
- *
- * Note: Currently, if data is not expected to be received, this
- * function will wait for ever.
- *
- * The caller is expected to perform the necessary locking.
- *
- * Returns:
- *   %-ETIMEDOUT	timeout occurred
- *   32-bit data	success
- */
-int ssp_read_word(struct ssp_dev *dev, u32 *data)
-{
-	struct ssp_device *ssp = dev->ssp;
-	int timeout = TIMEOUT;
-
-	while (!(__raw_readl(ssp->mmio_base + SSSR) & SSSR_RNE)) {
-	        if (!--timeout)
-	        	return -ETIMEDOUT;
-		cpu_relax();
-	}
-
-	*data = __raw_readl(ssp->mmio_base + SSDR);
-	return 0;
-}
-
-/**
- * ssp_flush - flush the transmit and receive FIFOs
- *
- * Wait for the SSP to idle, and ensure that the receive FIFO
- * is empty.
- *
- * The caller is expected to perform the necessary locking.
- */
-int ssp_flush(struct ssp_dev *dev)
-{
-	struct ssp_device *ssp = dev->ssp;
-	int timeout = TIMEOUT * 2;
-
-	/* ensure TX FIFO is empty instead of not full */
-	if (cpu_is_pxa3xx()) {
-		while (__raw_readl(ssp->mmio_base + SSSR) & 0xf00) {
-			if (!--timeout)
-				return -ETIMEDOUT;
-			cpu_relax();
-		}
-		timeout = TIMEOUT * 2;
-	}
-
-	do {
-		while (__raw_readl(ssp->mmio_base + SSSR) & SSSR_RNE) {
-		        if (!--timeout)
-		        	return -ETIMEDOUT;
-			(void)__raw_readl(ssp->mmio_base + SSDR);
-		}
-	        if (!--timeout)
-	        	return -ETIMEDOUT;
-	} while (__raw_readl(ssp->mmio_base + SSSR) & SSSR_BSY);
-
-	return 0;
-}
-
-/**
- * ssp_enable - enable the SSP port
- *
- * Turn on the SSP port.
- */
-void ssp_enable(struct ssp_dev *dev)
-{
-	struct ssp_device *ssp = dev->ssp;
 	uint32_t sscr0;

 	sscr0 = __raw_readl(ssp->mmio_base + SSCR0);
 	sscr0 |= SSCR0_SSE;
 	__raw_writel(sscr0, ssp->mmio_base + SSCR0);
 }
+EXPORT_SYMBOL(ssp_enable);

-/**
- * ssp_disable - shut down the SSP port
- *
- * Turn off the SSP port, optionally powering it down.
- */
-void ssp_disable(struct ssp_dev *dev)
+void ssp_disable(struct ssp_device *ssp)
 {
-	struct ssp_device *ssp = dev->ssp;
 	uint32_t sscr0;

 	sscr0 = __raw_readl(ssp->mmio_base + SSCR0);
 	sscr0 &= ~SSCR0_SSE;
 	__raw_writel(sscr0, ssp->mmio_base + SSCR0);
 }
+EXPORT_SYMBOL(ssp_disable);

 /**
  * ssp_save_state - save the SSP configuration
@@ -189,16 +61,12 @@ void ssp_disable(struct ssp_dev *dev)
  *
  * Save the configured SSP state for suspend.
  */
-void ssp_save_state(struct ssp_dev *dev, struct ssp_state *state)
+void ssp_save_state(struct ssp_device *ssp, struct ssp_state *state)
 {
-	struct ssp_device *ssp = dev->ssp;
-
 	state->cr0 = __raw_readl(ssp->mmio_base + SSCR0);
 	state->cr1 = __raw_readl(ssp->mmio_base + SSCR1);
 	state->to  = __raw_readl(ssp->mmio_base + SSTO);
 	state->psp = __raw_readl(ssp->mmio_base + SSPSP);
-
-	ssp_disable(dev);
 }

 /**
@@ -207,9 +75,8 @@ void ssp_save_state(struct ssp_dev *dev, struct
ssp_state *state)
  *
  * Restore the SSP configuration saved previously by ssp_save_state.
  */
-void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *state)
+void ssp_restore_state(struct ssp_device *ssp, struct ssp_state *state)
 {
-	struct ssp_device *ssp = dev->ssp;
 	uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE;

 	__raw_writel(sssr, ssp->mmio_base + SSSR);
@@ -221,89 +88,6 @@ void ssp_restore_state(struct ssp_dev *dev, struct
ssp_state *state)
 	__raw_writel(state->cr0, ssp->mmio_base + SSCR0);
 }

-/**
- * ssp_config - configure SSP port settings
- * @mode: port operating mode
- * @flags: port config flags
- * @psp_flags: port PSP config flags
- * @speed: port speed
- *
- * Port MUST be disabled by ssp_disable before making any config changes.
- */
-int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32
psp_flags, u32 speed)
-{
-	struct ssp_device *ssp = dev->ssp;
-
-	dev->mode = mode;
-	dev->flags = flags;
-	dev->psp_flags = psp_flags;
-	dev->speed = speed;
-
-	/* set up port type, speed, port settings */
-	__raw_writel((dev->speed | dev->mode), ssp->mmio_base + SSCR0);
-	__raw_writel(dev->flags, ssp->mmio_base + SSCR1);
-	__raw_writel(dev->psp_flags, ssp->mmio_base + SSPSP);
-
-	return 0;
-}
-
-/**
- * ssp_init - setup the SSP port
- *
- * initialise and claim resources for the SSP port.
- *
- * Returns:
- *   %-ENODEV	if the SSP port is unavailable
- *   %-EBUSY	if the resources are already in use
- *   %0		on success
- */
-int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags)
-{
-	struct ssp_device *ssp;
-	int ret;
-
-	ssp = ssp_request(port, "SSP");
-	if (ssp == NULL)
-		return -ENODEV;
-
-	dev->ssp = ssp;
-	dev->port = port;
-
-	/* do we need to get irq */
-	if (!(init_flags & SSP_NO_IRQ)) {
-		ret = request_irq(ssp->irq, ssp_interrupt,
-				0, "SSP", dev);
-	    	if (ret)
-			goto out_region;
-		dev->irq = ssp->irq;
-	} else
-		dev->irq = NO_IRQ;
-
-	/* turn on SSP port clock */
-	clk_enable(ssp->clk);
-	return 0;
-
-out_region:
-	ssp_free(ssp);
-	return ret;
-}
-
-/**
- * ssp_exit - undo the effects of ssp_init
- *
- * release and free resources for the SSP port.
- */
-void ssp_exit(struct ssp_dev *dev)
-{
-	struct ssp_device *ssp = dev->ssp;
-
-	ssp_disable(dev);
-	if (dev->irq != NO_IRQ)
-		free_irq(dev->irq, dev);
-	clk_disable(ssp->clk);
-	ssp_free(ssp);
-}
-
 static DEFINE_MUTEX(ssp_lock);
 static LIST_HEAD(ssp_list);

@@ -531,18 +315,6 @@ static void __exit pxa_ssp_exit(void)
 arch_initcall(pxa_ssp_init);
 module_exit(pxa_ssp_exit);

-EXPORT_SYMBOL(ssp_write_word);
-EXPORT_SYMBOL(ssp_read_word);
-EXPORT_SYMBOL(ssp_flush);
-EXPORT_SYMBOL(ssp_enable);
-EXPORT_SYMBOL(ssp_disable);
-EXPORT_SYMBOL(ssp_save_state);
-EXPORT_SYMBOL(ssp_restore_state);
-EXPORT_SYMBOL(ssp_init);
-EXPORT_SYMBOL(ssp_exit);
-EXPORT_SYMBOL(ssp_config);
-
 MODULE_DESCRIPTION("PXA SSP driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 308a657..a65993c 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -42,7 +42,7 @@
  * SSP audio private data
  */
 struct ssp_priv {
-	struct ssp_dev dev;
+	struct ssp_device *ssp;
 	unsigned int sysclk;
 	int dai_fmt;
 #ifdef CONFIG_PM
@@ -222,10 +222,8 @@ static int pxa_ssp_startup(struct
snd_pcm_substream *substream,
 	int ret = 0;

 	if (!cpu_dai->active) {
-		priv->dev.port = cpu_dai->id + 1;
-		priv->dev.irq = NO_IRQ;
-		clk_enable(priv->dev.ssp->clk);
-		ssp_disable(&priv->dev);
+		clk_enable(priv->ssp->clk);
+		ssp_disable(priv->ssp);
 	}
 	return ret;
 }
@@ -238,8 +236,8 @@ static void pxa_ssp_shutdown(struct
snd_pcm_substream *substream,
 	struct ssp_priv *priv = cpu_dai->private_data;

 	if (!cpu_dai->active) {
-		ssp_disable(&priv->dev);
-		clk_disable(priv->dev.ssp->clk);
+		ssp_disable(priv->ssp);
+		clk_disable(priv->ssp->clk);
 	}
 }

@@ -252,8 +250,9 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
 	if (!cpu_dai->active)
 		return 0;

-	ssp_save_state(&priv->dev, &priv->state);
-	clk_disable(priv->dev.ssp->clk);
+	ssp_save_state(priv->ssp, &priv->state);
+	ssp_disable(priv->ssp);
+	clk_disable(priv->ssp->clk);
 	return 0;
 }

@@ -264,10 +263,9 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
 	if (!cpu_dai->active)
 		return 0;

-	clk_enable(priv->dev.ssp->clk);
-	ssp_restore_state(&priv->dev, &priv->state);
-	ssp_enable(&priv->dev);
-
+	clk_enable(priv->ssp->clk);
+	ssp_restore_state(priv->ssp, &priv->state);
+	ssp_enable(priv->ssp);
 	return 0;
 }

@@ -280,10 +278,9 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
  * ssp_set_clkdiv - set SSP clock divider
  * @div: serial clock rate divider
  */
-static void ssp_set_scr(struct ssp_dev *dev, u32 div)
+static void ssp_set_scr(struct ssp_device *ssp, u32 div)
 {
-	struct ssp_device *ssp = dev->ssp;
-	u32 sscr0 = ssp_read_reg(dev->ssp, SSCR0) & ~SSCR0_SCR;
+	u32 sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SCR;

 	ssp_write_reg(ssp, SSCR0, (sscr0 | SSCR0_SerClkDiv(div)));
 }
@@ -295,7 +292,7 @@ static int pxa_ssp_set_dai_sysclk(struct
snd_soc_dai *cpu_dai,
 	int clk_id, unsigned int freq, int dir)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
-	struct ssp_device *ssp = priv->dev.ssp;
+	struct ssp_device *ssp = priv->ssp;
 	int val;

 	u32 sscr0 = ssp_read_reg(ssp, SSCR0) &
@@ -326,7 +323,7 @@ static int pxa_ssp_set_dai_sysclk(struct
snd_soc_dai *cpu_dai,
 		break;
 	case PXA_SSP_CLK_AUDIO:
 		priv->sysclk = 0;
-		ssp_set_scr(&priv->dev, 1);
+		ssp_set_scr(priv->ssp, 1);
 		sscr0 |= SSCR0_ACS;
 		break;
 	default:
@@ -336,11 +333,11 @@ static int pxa_ssp_set_dai_sysclk(struct
snd_soc_dai *cpu_dai,
 	/* The SSP clock must be disabled when changing SSP clock mode
 	 * on PXA2xx.  On PXA3xx it must be enabled when doing so. */
 	if (!cpu_is_pxa3xx())
-		clk_disable(priv->dev.ssp->clk);
+		clk_disable(priv->ssp->clk);
 	val = ssp_read_reg(ssp, SSCR0) | sscr0;
 	ssp_write_reg(ssp, SSCR0, val);
 	if (!cpu_is_pxa3xx())
-		clk_enable(priv->dev.ssp->clk);
+		clk_enable(priv->ssp->clk);

 	return 0;
 }
@@ -352,7 +349,7 @@ static int pxa_ssp_set_dai_clkdiv(struct
snd_soc_dai *cpu_dai,
 	int div_id, int div)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
-	struct ssp_device *ssp = priv->dev.ssp;
+	struct ssp_device *ssp = priv->ssp;
 	int val;

 	switch (div_id) {
@@ -387,7 +384,7 @@ static int pxa_ssp_set_dai_clkdiv(struct
snd_soc_dai *cpu_dai,
 		ssp_write_reg(ssp, SSACD, val);
 		break;
 	case PXA_SSP_DIV_SCR:
-		ssp_set_scr(&priv->dev, div);
+		ssp_set_scr(ssp, div);
 		break;
 	default:
 		return -ENODEV;
@@ -403,7 +400,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
 	int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
-	struct ssp_device *ssp = priv->dev.ssp;
+	struct ssp_device *ssp = priv->ssp;
 	u32 ssacd = ssp_read_reg(ssp, SSACD) & ~0x70;

 #if defined(CONFIG_PXA3xx)
@@ -472,7 +469,7 @@ static int pxa_ssp_set_dai_tdm_slot(struct
snd_soc_dai *cpu_dai,
 	unsigned int mask, int slots)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
-	struct ssp_device *ssp = priv->dev.ssp;
+	struct ssp_device *ssp = priv->ssp;
 	u32 sscr0;

 	sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7);
@@ -494,7 +491,7 @@ static int pxa_ssp_set_dai_tristate(struct
snd_soc_dai *cpu_dai,
 	int tristate)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
-	struct ssp_device *ssp = priv->dev.ssp;
+	struct ssp_device *ssp = priv->ssp;
 	u32 sscr1;

 	sscr1 = ssp_read_reg(ssp, SSCR1);
@@ -516,7 +513,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 		unsigned int fmt)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
-	struct ssp_device *ssp = priv->dev.ssp;
+	struct ssp_device *ssp = priv->ssp;
 	u32 sscr0;
 	u32 sscr1;
 	u32 sspsp;
@@ -622,7 +619,7 @@ static int pxa_ssp_hw_params(struct
snd_pcm_substream *substream,
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct ssp_priv *priv = cpu_dai->private_data;
-	struct ssp_device *ssp = priv->dev.ssp;
+	struct ssp_device *ssp = priv->ssp;
 	int dma = 0, chn = params_channels(params);
 	u32 sscr0;
 	u32 sspsp;
@@ -735,12 +732,12 @@ static int pxa_ssp_trigger(struct
snd_pcm_substream *substream, int cmd,
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int ret = 0;
 	struct ssp_priv *priv = cpu_dai->private_data;
-	struct ssp_device *ssp = priv->dev.ssp;
+	struct ssp_device *ssp = priv->ssp;
 	int val;

 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_RESUME:
-		ssp_enable(&priv->dev);
+		ssp_enable(ssp);
 		break;
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		val = ssp_read_reg(ssp, SSCR1);
@@ -759,7 +756,7 @@ static int pxa_ssp_trigger(struct
snd_pcm_substream *substream, int cmd,
 		else
 			val |= SSCR1_RSRE;
 		ssp_write_reg(ssp, SSCR1, val);
-		ssp_enable(&priv->dev);
+		ssp_enable(ssp);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		val = ssp_read_reg(ssp, SSCR1);
@@ -770,7 +767,7 @@ static int pxa_ssp_trigger(struct
snd_pcm_substream *substream, int cmd,
 		ssp_write_reg(ssp, SSCR1, val);
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
-		ssp_disable(&priv->dev);
+		ssp_disable(ssp);
 		break;
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		val = ssp_read_reg(ssp, SSCR1);
@@ -800,8 +797,8 @@ static int pxa_ssp_probe(struct platform_device *pdev,
 	if (!priv)
 		return -ENOMEM;

-	priv->dev.ssp = ssp_request(dai->id + 1, "SoC audio");
-	if (priv->dev.ssp == NULL) {
+	priv->ssp = ssp_request(dai->id + 1, "SoC audio");
+	if (priv->ssp == NULL) {
 		ret = -ENODEV;
 		goto err_priv;
 	}
@@ -819,7 +816,7 @@ static void pxa_ssp_remove(struct platform_device *pdev,
 			      struct snd_soc_dai *dai)
 {
 	struct ssp_priv *priv = dai->private_data;
-	ssp_free(priv->dev.ssp);
+	ssp_free(priv->ssp);
 }

 #define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-- 
1.6.0.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-23  5:06 [RFC PATCH 3/5] pxa: clean up the legacy SSP API Eric Miao
@ 2009-04-23  8:24 ` Mark Brown
  2009-04-28 22:20 ` Russell King - ARM Linux
  1 sibling, 0 replies; 12+ messages in thread
From: Mark Brown @ 2009-04-23  8:24 UTC (permalink / raw)
  To: Eric Miao; +Cc: alsa-devel, Paul Shen, linux-arm-kernel, Philipp Zabel

On Thu, Apr 23, 2009 at 01:06:12PM +0800, Eric Miao wrote:
> Actually, due to the number of possible SSP configurations, it's quite a
> difficult job to maintain an API that can be well re-used. The guideline
> here is to keep only a list of SSP devices and their basic information,
> and it's up to the user driver (like SPI or Audio SSP) to configure the
> SSP in a desired way.

This looks fine to me

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-23  5:06 [RFC PATCH 3/5] pxa: clean up the legacy SSP API Eric Miao
  2009-04-23  8:24 ` Mark Brown
@ 2009-04-28 22:20 ` Russell King - ARM Linux
  2009-04-28 23:06   ` Daniel Ribeiro
  1 sibling, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-04-28 22:20 UTC (permalink / raw)
  To: Eric Miao
  Cc: alsa-devel, Paul Shen, linux-arm-kernel, Philipp Zabel, Mark Brown

[-- Attachment #1: Type: text/plain, Size: 744 bytes --]

On Thu, Apr 23, 2009 at 01:06:12PM +0800, Eric Miao wrote:
> Actually, due to the number of possible SSP configurations, it's quite a
> difficult job to maintain an API that can be well re-used. The guideline
> here is to keep only a list of SSP devices and their basic information,
> and it's up to the user driver (like SPI or Audio SSP) to configure the
> SSP in a desired way.

Hmm, the LX battery driver uses this API for its rudimentary battery
communication channel with the PCON.

What are you suggesting as the replacement API?  Direct register access?

Attached is the (entire) driver as it stands, which does need more work
prior to submission (well, if the placement of the PCON I2C driver in
initial patch set ever gets resolved.)

[-- Attachment #2: lx-battery-monitoring.diff --]
[-- Type: text/plain, Size: 20940 bytes --]

cb466960004e47212a23e474562e66e4aa48123e
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7871f05..8c9ae6d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_HP_ILO)		+= hpilo.o
 obj-$(CONFIG_ISL29003)		+= isl29003.o
 obj-$(CONFIG_C2PORT)		+= c2port/
 obj-y				+= eeprom/
+obj-$(CONFIG_MACH_NETBOOKPRO)	+= lx/
diff --git a/drivers/misc/lx/Makefile b/drivers/misc/lx/Makefile
new file mode 100644
index 0000000..c53c032
--- /dev/null
+++ b/drivers/misc/lx/Makefile
@@ -0,0 +1,6 @@
+
+lx-battery-y				:= lx-battery-core.o
+lx-battery-$(CONFIG_APM_EMULATION)	+= lx-battery-apm.o
+lx-battery-$(CONFIG_PROC_FS)		+= lx-battery-procfs.o
+
+obj-y		 	:= lx-battery.o
diff --git a/drivers/misc/lx/lx-battery-apm.c b/drivers/misc/lx/lx-battery-apm.c
new file mode 100644
index 0000000..3ab7f91
--- /dev/null
+++ b/drivers/misc/lx/lx-battery-apm.c
@@ -0,0 +1,123 @@
+/*
+ * drivers/misc/lx/lx-battery-apm.c
+ *
+ * (c) 2004 Simtec Electronics
+ * Written by Ben Dooks
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/apm-emulation.h>
+#include <linux/pm.h>
+
+#include <mach/lx-pcon.h>
+
+#include "lx-battery.h"
+
+static void lx_apm_get_powerstatus(struct apm_power_info *pwr)
+{
+	struct pcon_battinfo batt;
+
+	if (lx_battmon_getstate(&batt)) {
+		pr_debug("apm: failed to read power info\n");
+		return;
+	}
+
+	if (batt.flags & NBP_PCON_PWRF_ACPresent)
+		pwr->ac_line_status = APM_AC_ONLINE;
+	else
+		pwr->ac_line_status = APM_AC_OFFLINE;
+
+	if (batt.flags & NBP_PCON_PWRF_BatteryPreset) {
+		if (batt.charge_state == NBP_PCON_CS_Charging) {
+			pwr->battery_status = APM_BATTERY_STATUS_CHARGING;
+			pwr->battery_flag = APM_BATTERY_FLAG_CHARGING;
+		} else {
+			pwr->battery_flag = 0;
+		}
+
+		if (batt.main_batt_capacity_percent >= 20) {
+			pwr->battery_status = APM_BATTERY_STATUS_HIGH;
+			pwr->battery_flag |= APM_BATTERY_FLAG_HIGH;
+		} else if (batt.main_batt_capacity_percent >= 5) {
+			pwr->battery_status = APM_BATTERY_STATUS_LOW;
+			pwr->battery_flag |= APM_BATTERY_FLAG_LOW;
+		} else {
+			pwr->battery_status = APM_BATTERY_STATUS_CRITICAL;
+			pwr->battery_flag |= APM_BATTERY_FLAG_CRITICAL;
+		}
+
+		if (batt.discharge_current) {
+			/*
+			 * max_capacity is in mAh, discharge_current is mA,
+			 * so mAh/mA = hours not minutes. --rmk
+			 */
+			pwr->units = APM_UNITS_SECS;
+			pwr->time = batt.max_capacity *
+				    batt.main_batt_capacity_percent * 36 /
+				      batt.discharge_current;
+
+			if (pwr->time >= 32767) {
+				pwr->time /= 60;
+				pwr->units = APM_UNITS_MINS;
+			}
+		}
+
+		pwr->battery_life = batt.main_batt_capacity_percent;
+	} else {
+		pwr->battery_status = APM_BATTERY_STATUS_NOT_PRESENT;
+		pwr->battery_flag = APM_BATTERY_FLAG_NOT_PRESENT;
+	}
+}
+
+/*
+ * irq 1 is "power failing"
+ * irq 2 is "please suspend"
+ */
+static irqreturn_t power_irq(int irqno, void *arg)
+{
+	apm_queue_event((apm_event_t)(int)arg);
+	return IRQ_HANDLED;
+}
+
+static int __init lx_apm_init(void)
+{
+	int ret;
+
+	ret = request_irq(IRQ_GPIO(0), power_irq,
+			  IRQF_DISABLED|IRQF_TRIGGER_RISING,
+			  "Battery Warning", (void *)APM_CRITICAL_SUSPEND);
+	if (ret) {
+		pr_err("LX: Failed to get irq for battery warning\n");
+		return ret;
+	}
+
+	ret = request_irq(IRQ_GPIO(1), power_irq,
+			  IRQF_DISABLED|IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
+			  "Suspend Request", (void *)APM_SYS_SUSPEND);
+	if (ret) {
+		pr_err("LX: Failed to get irq for suspend request\n");
+		free_irq(IRQ_GPIO(0), (void *)APM_CRITICAL_SUSPEND);
+		return ret;
+	}
+
+	apm_get_power_status = lx_apm_get_powerstatus;
+
+	return 0;
+}
+
+static void __exit lx_apm_exit(void)
+{
+	free_irq(IRQ_GPIO(1), (void *)APM_SYS_SUSPEND);
+	free_irq(IRQ_GPIO(0), (void *)APM_CRITICAL_SUSPEND);
+
+	apm_get_power_status = NULL;
+}
+
+module_init(lx_apm_init);
+module_exit(lx_apm_exit);
+
+MODULE_AUTHOR("Ben Dooks, Simtec Electronics <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LX APM Emulation interface");
diff --git a/drivers/misc/lx/lx-battery-core.c b/drivers/misc/lx/lx-battery-core.c
new file mode 100644
index 0000000..563147a
--- /dev/null
+++ b/drivers/misc/lx/lx-battery-core.c
@@ -0,0 +1,432 @@
+/*
+ * LX battery monitoring
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ *
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * PCON sends 32-byte data frames to the PXA over the SSP bus.  When
+ * enabled, it will attempt a transfer once every 10 seconds by raising
+ * DREQ1.  This causes the SSP tx DMA to start, thereby causing the SSP
+ * clock to run.  This allows the SSP rx DMA to receive the battery state
+ * information.
+ *
+ * If no battery is fitted, PCON will not send battery state.  This
+ * causes us to time out and re-send the request to enable battery state
+ * from PCON.  We could avoid this by checking whether PCON says the
+ * battery is fitted, but for safety we leave the timer running.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include <mach/dma.h>
+#include <mach/hardware.h>
+#include <mach/pxa2xx-regs.h>
+#include <mach/regs-ssp.h>
+#include <mach/ssp.h>
+#include <mach/lx-pcon.h>
+
+#include "lx-battery.h"
+
+#define SPI_DMASIZE     (32)
+#define SPI_RXBUFFERS   (12)
+
+#define SPI_DMA_CMD	\
+	(DCMD_FLOWSRC | DCMD_BURST32 | DCMD_WIDTH2 | SPI_DMASIZE)
+
+#define SPI_RXDMA_CMD	(DCMD_INCTRGADDR | SPI_DMA_CMD | DCMD_ENDIRQEN)
+#define SPI_TXDMA_CMD	(DCMD_INCSRCADDR | SPI_DMA_CMD)
+
+#define BAT_TIMEOUT	(30 * HZ)
+
+/* size of this structure must be aligned to 4 words */
+struct spi_buffer {
+	pxa_dma_desc	dma_desc;
+	u32		data[SPI_DMASIZE / sizeof(u32)];
+};
+
+/*
+ * This describes the DMA data buffer.
+ */
+struct spi_dma_data {
+	struct spi_buffer	rx[SPI_RXBUFFERS] __attribute__((aligned(16)));
+	struct spi_buffer	tx __attribute__((aligned(16)));
+};
+
+struct bat_priv {
+	struct device		*dev;
+	struct ssp_dev		ssp;
+	struct ssp_state	state;
+	struct spi_dma_data	*cpu_ring;
+	dma_addr_t		dma_ring;
+	int			rx_chan;
+	int			tx_chan;
+	unsigned int		rx_last;
+	dma_addr_t 		rx_phys[SPI_RXBUFFERS];
+	struct timer_list	timer;
+	struct work_struct	work;
+
+	struct pcon_battinfo	battinfo;
+	struct power_stats	stats;
+};
+
+/*
+ * This macro returns the DMA address of element 'x' from the above
+ * structure.
+ */
+#define SPI_DMAPTR(bat,x) \
+	(bat->dma_ring + offsetof(struct spi_dma_data, x))
+
+static struct bat_priv *global_bat;
+
+int lx_battmon_getstate(struct pcon_battinfo *info)
+{
+	unsigned long flags;
+	int ret = -ENOENT;
+
+	local_irq_save(flags);
+	if (global_bat && global_bat->battinfo.magic == PCON_BATTINFO_MAGIC) {
+		memcpy(info, &global_bat->battinfo, sizeof(*info));
+		ret = 0;
+	}
+	local_irq_restore(flags);
+	return ret;
+}
+
+int lx_battmon_getstats(struct power_stats *stats)
+{
+	unsigned long flags;
+	int ret = -ENOENT;
+
+	local_irq_save(flags);
+	if (global_bat) {
+		memcpy(stats, &global_bat->stats, sizeof(*stats));
+		ret = 0;
+	}
+	local_irq_restore(flags);
+	return ret;
+}
+
+static struct pcon_battinfo *lx_to_battinfo(u32 *data)
+{
+	u32 *ptr = data;
+	unsigned int i;
+
+	/*
+	 * 16-bit swap the data.
+	 */
+	for (i = 0; i < SPI_DMASIZE / sizeof(u32); i++, ptr++) {
+		u32 t1, t2;
+		t1  = *ptr;
+		t2  = (t1 >> 8) & ~0x0000ff00;
+		t2 |= (t1 << 8) & ~0x00ff0000;
+		*ptr = t2;
+	}
+	return (struct pcon_battinfo *)data;
+}
+
+static void lx_bat_rx(struct bat_priv *bat, u32 *data)
+{
+	struct pcon_battinfo *battinfo = lx_to_battinfo(data);
+
+	if (battinfo->magic == PCON_BATTINFO_MAGIC) {
+		bat->battinfo = *battinfo;
+		bat->stats.last_good = jiffies;
+		bat->stats.num_good++;
+
+		/* If successfully received, restart the timeout */
+		mod_timer(&bat->timer, jiffies + BAT_TIMEOUT);
+	} else {
+		bat->stats.last_bad = jiffies;
+		bat->stats.num_bad++;
+	}
+
+	/* zero the buffer we where given */
+	memset(data, 0, SPI_DMASIZE);
+}
+
+static void lx_bat_rxirq(int irq, void *param)
+{
+	struct bat_priv *bat = param;
+	unsigned int dcsr, dtadr;
+	dma_addr_t ddadr;
+
+	dcsr  = DCSR(bat->rx_chan);
+	ddadr = DDADR(bat->rx_chan);
+	dtadr = DTADR(bat->rx_chan);
+
+	/* clear all pending interrupts */
+	DCSR(bat->rx_chan) = dcsr;
+
+	dev_dbg(bat->dev, "rx irq: dcsr=%08x, ddadr=%08x, dtadr=%08x\n",
+	    dcsr, ddadr, dtadr);
+
+	if (dcsr & DCSR_BUSERR)
+		dev_err(bat->dev, "rx: dma error (%08x)\n", dcsr);
+
+	if (dcsr & DCSR_ENDINTR) {
+		unsigned int buff = bat->rx_last;
+
+		dev_dbg(bat->dev, "rx: dma channel end\n");
+
+		while (1) {
+			struct spi_buffer *rx = &bat->cpu_ring->rx[buff];
+
+			buff = (buff + 1) % SPI_RXBUFFERS;
+
+			/* check: see if this is the last buffer... */
+			if (bat->rx_phys[buff] == (ddadr & ~15))
+				break;
+
+			bat->rx_last = buff;
+			lx_bat_rx(bat, rx->data);
+		}
+	}
+}
+
+static void lx_bat_txirq(int irq, void *param)
+{
+	struct bat_priv *bat = param;
+	unsigned int dcsr = DCSR(bat->tx_chan);
+
+	DCSR(bat->tx_chan) = dcsr | DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR;
+
+	dev_dbg(bat->dev, "tx: dcsr=%08x\n", dcsr);
+}
+
+/*
+ * Disable the SPI engine and its associated DMA channels.
+ */
+static void lx_bat_disable(struct bat_priv *bat)
+{
+	ssp_disable(&bat->ssp);
+
+	DCSR(bat->rx_chan) = 0;
+	DCSR(bat->tx_chan) = 0;
+
+	CKEN &= ~(1 << 3); //pxa_set_cken(CKEN3_SSP, 0);
+}
+
+/*
+ * Enable the SPI engine and its associated DMA channels.
+ */
+static void lx_bat_enable(struct bat_priv *bat)
+{
+	CKEN |= (1 << 3); //pxa_set_cken(CKEN3_SSP, 1);
+
+	DRCMR(1) = bat->tx_chan | DRCMR_MAPVLD;
+	DRCMR(bat->ssp.ssp->drcmr_rx) = bat->rx_chan | DRCMR_MAPVLD;
+
+	bat->rx_last = 0;
+	DDADR(bat->rx_chan) = SPI_DMAPTR(bat, rx[0].dma_desc);
+	DDADR(bat->tx_chan) = SPI_DMAPTR(bat, tx.dma_desc);
+
+	ssp_enable(&bat->ssp);
+
+	DCSR(bat->rx_chan) |= DCSR_RUN;
+	DCSR(bat->tx_chan) |= DCSR_RUN;
+}
+
+static void lx_bat_work(struct work_struct *work)
+{
+	struct bat_priv *bat = container_of(work, struct bat_priv, work);
+	int ret;
+
+	lx_bat_disable(bat);
+	lx_bat_enable(bat);
+
+	ret = pcon_monitorbattery(1, 0);
+	if (ret < 0)
+		dev_err(bat->dev, "LX: battery monitor setstate failed\n");
+
+	mod_timer(&bat->timer, jiffies + BAT_TIMEOUT);
+}
+
+static void lx_bat_timeout(unsigned long data)
+{
+	struct work_struct *work = (struct work_struct *)data;
+	pr_debug("LX: battery monitor timeout, restarting\n");
+	schedule_work(work);
+}
+
+static int __init lx_bat_probe(struct platform_device *dev)
+{
+	struct bat_priv *bat;
+	int ret, i;
+
+	if (global_bat)
+		return -EBUSY;
+
+	bat = kzalloc(sizeof(*bat), GFP_KERNEL);
+	if (!bat) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+
+	bat->dev = &dev->dev;
+	platform_set_drvdata(dev, bat);
+
+	setup_timer(&bat->timer, lx_bat_timeout, (unsigned long)&bat->work);
+	INIT_WORK(&bat->work, lx_bat_work);
+
+	ret = ssp_init(&bat->ssp, PXA25x_SSP, SSP_NO_IRQ);
+	if (ret)
+		goto err_ssp;
+
+	/*
+	 * SSCR0 -> 0xF | (1<<7)  (0x8F)
+	 * SSCR1 -> (11<<6) | (15<<10)  = (0x3EC0)
+	 *
+	 * SSCR0 = SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_SerClkDiv(2);
+	 * SSCR1 = SSCR1_TxTresh(12) | SSCR1_RxTresh(16);
+	 */
+	ssp_config(&bat->ssp, SSCR0_Motorola | SSCR0_DataSize(16),
+		SSCR1_TxTresh(12) | SSCR1_RxTresh(16), 0, SSCR0_SerClkDiv(4));
+
+	bat->cpu_ring = dma_alloc_coherent(bat->dev,
+					   sizeof(struct spi_dma_data),
+					   &bat->dma_ring, GFP_KERNEL);
+
+	if (bat->cpu_ring == NULL) {
+		dev_err(bat->dev, "no memory for DMA buffers\n");
+		ret = -ENOMEM;
+		goto err_dmabuf;
+	}
+
+	memset(bat->cpu_ring, 0, sizeof(struct spi_dma_data));
+
+	for (i = 0; i < SPI_RXBUFFERS; i++) {
+		struct spi_buffer *rx = &bat->cpu_ring->rx[i];
+		dma_addr_t ddadr = SPI_DMAPTR(bat, rx[i + 1].dma_desc);
+
+		if (i == SPI_RXBUFFERS - 1)
+			ddadr = bat->dma_ring;
+
+		rx->dma_desc.ddadr = ddadr;
+		rx->dma_desc.dsadr = bat->ssp.ssp->phys_base + SSDR;
+		rx->dma_desc.dtadr = SPI_DMAPTR(bat, rx[i].data);
+		rx->dma_desc.dcmd  = SPI_RXDMA_CMD;
+
+		bat->rx_phys[i] = SPI_DMAPTR(bat, rx[i].dma_desc);
+	}
+
+	bat->cpu_ring->tx.dma_desc.ddadr = SPI_DMAPTR(bat, tx.dma_desc);
+	bat->cpu_ring->tx.dma_desc.dsadr = SPI_DMAPTR(bat, tx.data);
+	bat->cpu_ring->tx.dma_desc.dtadr = bat->ssp.ssp->phys_base + SSDR;
+	bat->cpu_ring->tx.dma_desc.dcmd  = SPI_TXDMA_CMD;
+
+	bat->rx_chan = pxa_request_dma("SPI RX", DMA_PRIO_LOW,
+				       lx_bat_rxirq, bat);
+	if (bat->rx_chan < 0) {
+		dev_err(bat->dev, "failed to allocate RX DMA channel (%d)\n",
+			bat->rx_chan);
+		ret = bat->rx_chan;
+		goto err_rxdma;
+	}
+
+	bat->tx_chan = pxa_request_dma("SPI TX", DMA_PRIO_LOW,
+				       lx_bat_txirq, bat);
+	if (bat->tx_chan < 0) {
+		dev_err(bat->dev, "failed to allocate TX DMA channel (%d)\n",
+			bat->tx_chan);
+		ret = bat->tx_chan;
+		goto err_txdma;
+	}
+
+	lx_bat_enable(bat);
+	pcon_monitorbattery(1, PCON_FLG_ASYNC);
+	mod_timer(&bat->timer, jiffies + BAT_TIMEOUT);
+
+	global_bat = bat;
+
+	return 0;
+
+ err_txdma:
+	pxa_free_dma(bat->rx_chan);
+ err_rxdma:
+	dma_free_coherent(bat->dev, sizeof(struct spi_dma_data), bat->cpu_ring,
+			  bat->dma_ring);
+ err_dmabuf:
+	ssp_exit(&bat->ssp);
+ err_ssp:
+	kfree(bat);
+ err_out:
+	return ret;
+}
+
+static int lx_bat_remove(struct platform_device *dev)
+{
+	struct bat_priv *bat = platform_get_drvdata(dev);
+
+	global_bat = NULL;
+
+	pcon_monitorbattery(0, 0);
+	lx_bat_disable(bat);
+
+	del_timer_sync(&bat->timer);
+	cancel_work_sync(&bat->work);
+	pxa_free_dma(bat->tx_chan);
+	pxa_free_dma(bat->rx_chan);
+	dma_free_coherent(bat->dev, sizeof(struct spi_dma_data), bat->cpu_ring,
+			  bat->dma_ring);
+	ssp_exit(&bat->ssp);
+	kfree(bat);
+	return 0;
+}
+
+static int lx_bat_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct bat_priv *bat = platform_get_drvdata(dev);
+	pcon_monitorbattery(0, 0);
+	lx_bat_disable(bat);
+	del_timer_sync(&bat->timer);
+	ssp_save_state(&bat->ssp, &bat->state);
+	return 0;
+}
+
+static int lx_bat_resume(struct platform_device *dev)
+{
+	struct bat_priv *bat = platform_get_drvdata(dev);
+	ssp_restore_state(&bat->ssp, &bat->state);
+	lx_bat_enable(bat);
+	pcon_monitorbattery(1, PCON_FLG_ASYNC);
+	mod_timer(&bat->timer, jiffies + BAT_TIMEOUT);
+	return 0;
+}
+
+static struct platform_driver bat_driver = {
+	.probe	 = lx_bat_probe,
+	.remove  = lx_bat_remove,
+	.suspend = lx_bat_suspend,
+	.resume	 = lx_bat_resume,
+	.driver = {
+		.name = "lx-spi",
+	},
+};
+
+static int __init lx_bat_init(void)
+{
+	return platform_driver_register(&bat_driver);
+}
+
+static void __exit lx_bat_exit(void)
+{
+	platform_driver_unregister(&bat_driver);
+}
+
+module_init(lx_bat_init);
+module_exit(lx_bat_exit);
+
+MODULE_AUTHOR("Ben Dooks, Simtec Electronics <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("LX SPI Battery Monitor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/lx/lx-battery-procfs.c b/drivers/misc/lx/lx-battery-procfs.c
new file mode 100644
index 0000000..892ea2a
--- /dev/null
+++ b/drivers/misc/lx/lx-battery-procfs.c
@@ -0,0 +1,140 @@
+/*
+ * drivers/misc/lx/lx-battery-procfs.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Written by Ben Dooks
+ *
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/jiffies.h>
+
+#include "lx-battery.h"
+
+static int lx_batproc_proc_rd_stats(char *page, char **start, off_t off,
+				    int count, int *eof, void *data)
+{
+	struct power_stats stats;
+	unsigned long timediff;
+	int i;
+
+	lx_battmon_getstats(&stats);
+
+	/* work out how long since last management frame received */
+	timediff = (jiffies - stats.last_good) / (HZ / 10);
+	i = sprintf(page, "Good frames: %ld (%ld.%ld secs)\n",
+		    stats.num_good, timediff / 10, timediff % 10);
+
+	if (stats.num_bad) {
+		timediff = (jiffies - stats.last_bad) / (HZ / 10);
+		i += sprintf(page+i, "Bad frames:  %ld (%ld.%ld secs)\n",
+			    stats.num_bad, timediff / 10, timediff % 10);
+	}
+
+	return i;
+}
+
+static const char *charge_state[] = {
+	[NBP_PCON_CS_NotCharging] = "not charging",
+	[NBP_PCON_CS_Charging] = "charging",
+	[NBP_PCON_CS_QuickChargeComplete] = "quick charging",
+	[NBP_PCON_CS_ChargeComplete] = "charging complete",
+};
+
+static int lx_batproc_proc_rd_drv(char *page, char **start, off_t off,
+				  int count, int *eof, void *data)
+{
+	struct pcon_battinfo bi;
+	int i = lx_battmon_getstate(&bi);
+
+	if (i)
+		return sprintf(page, "Invalid battery state\n");
+
+	i += sprintf(page+i, "Main Battery: %spresent\n",
+		     bi.flags & NBP_PCON_PWRF_BatteryPreset ? "" : "not ");
+	i += sprintf(page+i, " Temperature      : %d.%d C\n",
+		     bi.temperature / 10, bi.temperature % 10);
+	i += sprintf(page+i, " Maximum Capacity : %d mAh\n",
+		     bi.max_capacity);
+	i += sprintf(page+i, " Voltage          : %d mV\n",
+		     bi.main_batt_voltage);
+	i += sprintf(page+i, " Capacity         : %d %%\n",
+		     bi.main_batt_capacity_percent);
+	i += sprintf(page+i, " Charge Current   : %d mA\n",
+		     bi.charge_current);
+	i += sprintf(page+i, " Discharge Current: %d mA\n",
+		     bi.discharge_current);
+	i += sprintf(page+i, " Status           : %scalibrated%s\n\n",
+		     bi.flags & NBP_PCON_PWRF_Calibrated ? "" : "not ",
+		     bi.flags & NBP_PCON_PWRF_Calibrating ? ", calibrating" : "");
+
+	i += sprintf(page+i, "Backup Battery: %spresent\n",
+		     bi.flags & NBP_PCON_PWRF_BackupBatteryPreset ? "" : "not ");
+	i += sprintf(page+i, " Battery          : %d mV\n",
+		     bi.backup_batt_voltage);
+	i += sprintf(page+i, " Capacity         : %d %%\n\n",
+		     bi.backup_batt_capacity_percent);
+
+	i += sprintf(page+i, "Charger State:\n");
+	i += sprintf(page+i, " AC %splugged in\n",
+		     bi.flags & NBP_PCON_PWRF_ACPresent ? "" : "not " );
+	i += sprintf(page+i, " Charging %s\n",
+		     bi.flags & NBP_PCON_PWRF_ChargeEnabled ? "enabled" : "disabled");
+	i += sprintf(page+i, " Status: ");
+
+	if (bi.charge_state < ARRAY_SIZE(charge_state) &&
+	    charge_state[bi.charge_state])
+		i += sprintf(page+i, "%s\n", charge_state[bi.charge_state]);
+	else
+		i += sprintf(page+i, "unknown state (%d)\n", bi.charge_state);
+
+	return i;
+}
+
+static int __init lx_batproc_init(void)
+{
+	struct proc_dir_entry *pde;
+	int ret = -ENOMEM;
+
+	pde = create_proc_read_entry("driver/battery", 0, NULL,
+				     lx_batproc_proc_rd_drv, NULL);
+	if (pde == NULL) {
+		pr_err("LX: failed to create /proc/driver/battery entry\n");
+		goto err_out;
+	}
+
+	pde = create_proc_read_entry("driver/battery-stats", 0, NULL,
+				     lx_batproc_proc_rd_stats, NULL);
+	if (pde == NULL) {
+		pr_err("LX: failed to create /proc/driver/battery-stats entry\n");
+		goto err_drv;
+	}
+
+	return 0;
+
+ err_drv:
+	remove_proc_entry("driver/battery", NULL);
+ err_out:
+	return ret;
+}
+
+static void __exit lx_batproc_exit(void)
+{
+	remove_proc_entry("driver/battery-stats", NULL);
+	remove_proc_entry("driver/battery", NULL);
+}
+
+module_init(lx_batproc_init);
+module_exit(lx_batproc_exit);
+
+MODULE_AUTHOR("Ben Dooks, Simtec Electronics <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LX Battery Monitor procfs support");
diff --git a/drivers/misc/lx/lx-battery.h b/drivers/misc/lx/lx-battery.h
new file mode 100644
index 0000000..e57fb9d
--- /dev/null
+++ b/drivers/misc/lx/lx-battery.h
@@ -0,0 +1,37 @@
+/* struct pcon_battinfo should be 32bytes long */
+struct pcon_battinfo {
+	u16	flags;
+#define NBP_PCON_PWRF_BatteryPreset		0x0001
+#define NBP_PCON_PWRF_ACPresent 		0x0002
+#define NBP_PCON_PWRF_ChargeEnabled		0x0004
+#define NBP_PCON_PWRF_BackupBatteryPreset	0x0008
+#define NBP_PCON_PWRF_Calibrated		0x0010
+#define NBP_PCON_PWRF_Calibrating		0x0020
+	u16	unused[4];
+	u16	max_capacity;
+	u16	temperature;
+	u16	charge_state;
+#define NBP_PCON_CS_NotCharging 		0
+#define NBP_PCON_CS_Charging			1
+#define NBP_PCON_CS_QuickChargeComplete		2
+#define NBP_PCON_CS_ChargeComplete		3
+	u16	main_batt_voltage;
+	u16	backup_batt_voltage;
+	u8	main_batt_capacity_percent;
+	u8	backup_batt_capacity_percent;
+	u16	charge_current;
+	u16	discharge_current;
+	u16	pad[2];
+	u16	magic;
+#define PCON_BATTINFO_MAGIC	(0xADB0)
+} __attribute__((__packed__));
+
+struct power_stats {
+	unsigned long last_good;
+	unsigned long num_good;
+	unsigned long last_bad;
+	unsigned long num_bad;
+};
+
+extern int lx_battmon_getstate(struct pcon_battinfo *info);
+extern int lx_battmon_getstats(struct power_stats *stats);

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-28 22:20 ` Russell King - ARM Linux
@ 2009-04-28 23:06   ` Daniel Ribeiro
  2009-04-29  2:18     ` Eric Miao
  2009-04-29  7:27     ` Russell King - ARM Linux
  0 siblings, 2 replies; 12+ messages in thread
From: Daniel Ribeiro @ 2009-04-28 23:06 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: alsa-devel, Eric Miao, Mark Brown, Philipp Zabel, Paul Shen,
	linux-arm-kernel

Em Ter, 2009-04-28 às 23:20 +0100, Russell King - ARM Linux escreveu:
> On Thu, Apr 23, 2009 at 01:06:12PM +0800, Eric Miao wrote:
> > Actually, due to the number of possible SSP configurations, it's quite a
> > difficult job to maintain an API that can be well re-used. The guideline
> > here is to keep only a list of SSP devices and their basic information,
> > and it's up to the user driver (like SPI or Audio SSP) to configure the
> > SSP in a desired way.
> 
> Hmm, the LX battery driver uses this API for its rudimentary battery
> communication channel with the PCON.
> 
> What are you suggesting as the replacement API?  Direct register access?

The SPI subsystem?

-- 
Daniel Ribeiro

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-28 23:06   ` Daniel Ribeiro
@ 2009-04-29  2:18     ` Eric Miao
  2009-04-29  7:27     ` Russell King - ARM Linux
  1 sibling, 0 replies; 12+ messages in thread
From: Eric Miao @ 2009-04-29  2:18 UTC (permalink / raw)
  To: Daniel Ribeiro
  Cc: alsa-devel, Russell King - ARM Linux, Mark Brown, Philipp Zabel,
	Paul Shen, linux-arm-kernel

On Wed, Apr 29, 2009 at 7:06 AM, Daniel Ribeiro <drwyrm@gmail.com> wrote:
> Em Ter, 2009-04-28 às 23:20 +0100, Russell King - ARM Linux escreveu:
>> On Thu, Apr 23, 2009 at 01:06:12PM +0800, Eric Miao wrote:
>> > Actually, due to the number of possible SSP configurations, it's quite a
>> > difficult job to maintain an API that can be well re-used. The guideline
>> > here is to keep only a list of SSP devices and their basic information,
>> > and it's up to the user driver (like SPI or Audio SSP) to configure the
>> > SSP in a desired way.
>>
>> Hmm, the LX battery driver uses this API for its rudimentary battery
>> communication channel with the PCON.
>>
>> What are you suggesting as the replacement API?  Direct register access?
>
> The SPI subsystem?
>

Yeah, I looked into the patch a bit. It looks like the SSP configuration
falls into the SPI subystem quite well.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-28 23:06   ` Daniel Ribeiro
  2009-04-29  2:18     ` Eric Miao
@ 2009-04-29  7:27     ` Russell King - ARM Linux
  2009-04-29  8:21       ` Mark Brown
  1 sibling, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-04-29  7:27 UTC (permalink / raw)
  To: Daniel Ribeiro
  Cc: alsa-devel, Eric Miao, Mark Brown, Philipp Zabel, Paul Shen,
	linux-arm-kernel

On Tue, Apr 28, 2009 at 08:06:08PM -0300, Daniel Ribeiro wrote:
> Em Ter, 2009-04-28 às 23:20 +0100, Russell King - ARM Linux escreveu:
> > On Thu, Apr 23, 2009 at 01:06:12PM +0800, Eric Miao wrote:
> > > Actually, due to the number of possible SSP configurations, it's quite a
> > > difficult job to maintain an API that can be well re-used. The guideline
> > > here is to keep only a list of SSP devices and their basic information,
> > > and it's up to the user driver (like SPI or Audio SSP) to configure the
> > > SSP in a desired way.
> > 
> > Hmm, the LX battery driver uses this API for its rudimentary battery
> > communication channel with the PCON.
> > 
> > What are you suggesting as the replacement API?  Direct register access?
> 
> The SPI subsystem?

Can the SPI system cope with the other chip being the master of the
interface.  In other words, can SPI sit around primed for a transfer,
and notify the user of SPI that new data has arrived?

If not, the SPI subsystem is totally unsuitable for this.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-29  7:27     ` Russell King - ARM Linux
@ 2009-04-29  8:21       ` Mark Brown
  2009-04-29  8:41         ` Russell King - ARM Linux
  0 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2009-04-29  8:21 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: alsa-devel, Eric Miao, Philipp Zabel, Paul Shen,
	linux-arm-kernel, Daniel Ribeiro

On Wed, Apr 29, 2009 at 08:27:06AM +0100, Russell King - ARM Linux wrote:
> On Tue, Apr 28, 2009 at 08:06:08PM -0300, Daniel Ribeiro wrote:

> > The SPI subsystem?

> Can the SPI system cope with the other chip being the master of the
> interface.  In other words, can SPI sit around primed for a transfer,
> and notify the user of SPI that new data has arrived?

> If not, the SPI subsystem is totally unsuitable for this.

It's slave only at the minute - there's holes in the API someone could
fill in but that's not happened yet.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-29  8:21       ` Mark Brown
@ 2009-04-29  8:41         ` Russell King - ARM Linux
  2009-04-29  9:14           ` Eric Miao
  0 siblings, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-04-29  8:41 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, Eric Miao, Philipp Zabel, Paul Shen,
	linux-arm-kernel, Daniel Ribeiro

On Wed, Apr 29, 2009 at 09:21:53AM +0100, Mark Brown wrote:
> On Wed, Apr 29, 2009 at 08:27:06AM +0100, Russell King - ARM Linux wrote:
> > On Tue, Apr 28, 2009 at 08:06:08PM -0300, Daniel Ribeiro wrote:
> 
> > > The SPI subsystem?
> 
> > Can the SPI system cope with the other chip being the master of the
> > interface.  In other words, can SPI sit around primed for a transfer,
> > and notify the user of SPI that new data has arrived?
> 
> > If not, the SPI subsystem is totally unsuitable for this.
> 
> It's slave only at the minute - there's holes in the API someone could
> fill in but that's not happened yet.

Well, I guess someone else will have to do that; I don't spend much time
on the LX stuff, and so if it requires any time consuming work, it won't
be worth trying to get it into mainline.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-29  8:41         ` Russell King - ARM Linux
@ 2009-04-29  9:14           ` Eric Miao
  2009-04-29 18:51             ` Russell King - ARM Linux
  0 siblings, 1 reply; 12+ messages in thread
From: Eric Miao @ 2009-04-29  9:14 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: alsa-devel, Mark Brown, Philipp Zabel, Paul Shen,
	linux-arm-kernel, Daniel Ribeiro

On Wed, Apr 29, 2009 at 4:41 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Apr 29, 2009 at 09:21:53AM +0100, Mark Brown wrote:
>> On Wed, Apr 29, 2009 at 08:27:06AM +0100, Russell King - ARM Linux wrote:
>> > On Tue, Apr 28, 2009 at 08:06:08PM -0300, Daniel Ribeiro wrote:
>>
>> > > The SPI subsystem?
>>
>> > Can the SPI system cope with the other chip being the master of the
>> > interface.  In other words, can SPI sit around primed for a transfer,
>> > and notify the user of SPI that new data has arrived?
>>
>> > If not, the SPI subsystem is totally unsuitable for this.
>>
>> It's slave only at the minute - there's holes in the API someone could
>> fill in but that's not happened yet.
>
> Well, I guess someone else will have to do that; I don't spend much time
> on the LX stuff, and so if it requires any time consuming work, it won't
> be worth trying to get it into mainline.
>

However, the patch itself doesn't looks like to be a SPI slave
(I mean from the view of the processor).

'struct spi_message' does support simultaneous rx/tx transfer
and multiple such transfers being queued.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-29  9:14           ` Eric Miao
@ 2009-04-29 18:51             ` Russell King - ARM Linux
  2009-04-30  2:31               ` Eric Miao
  0 siblings, 1 reply; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-04-29 18:51 UTC (permalink / raw)
  To: Eric Miao
  Cc: alsa-devel, Mark Brown, Philipp Zabel, Paul Shen,
	linux-arm-kernel, Daniel Ribeiro

On Wed, Apr 29, 2009 at 05:14:13PM +0800, Eric Miao wrote:
> On Wed, Apr 29, 2009 at 4:41 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Wed, Apr 29, 2009 at 09:21:53AM +0100, Mark Brown wrote:
> >> On Wed, Apr 29, 2009 at 08:27:06AM +0100, Russell King - ARM Linux wrote:
> >> > On Tue, Apr 28, 2009 at 08:06:08PM -0300, Daniel Ribeiro wrote:
> >>
> >> > > The SPI subsystem?
> >>
> >> > Can the SPI system cope with the other chip being the master of the
> >> > interface.  In other words, can SPI sit around primed for a transfer,
> >> > and notify the user of SPI that new data has arrived?
> >>
> >> > If not, the SPI subsystem is totally unsuitable for this.
> >>
> >> It's slave only at the minute - there's holes in the API someone could
> >> fill in but that's not happened yet.
> >
> > Well, I guess someone else will have to do that; I don't spend much time
> > on the LX stuff, and so if it requires any time consuming work, it won't
> > be worth trying to get it into mainline.
> >
> 
> However, the patch itself doesn't looks like to be a SPI slave
> (I mean from the view of the processor).
> 
> 'struct spi_message' does support simultaneous rx/tx transfer
> and multiple such transfers being queued.

The transfers happen under control of PCON not the PXA; PCON asserts a
GPIO to activate the DMA to control when the transfer happens.

We can not just start the SSP for a 32 byte transfer and hope it happens.
PCON has to be totally in control.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-29 18:51             ` Russell King - ARM Linux
@ 2009-04-30  2:31               ` Eric Miao
  2009-04-30  7:08                 ` Russell King - ARM Linux
  0 siblings, 1 reply; 12+ messages in thread
From: Eric Miao @ 2009-04-30  2:31 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: alsa-devel, Mark Brown, Philipp Zabel, Paul Shen,
	linux-arm-kernel, Daniel Ribeiro

On Thu, Apr 30, 2009 at 2:51 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Apr 29, 2009 at 05:14:13PM +0800, Eric Miao wrote:
>> On Wed, Apr 29, 2009 at 4:41 PM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> > On Wed, Apr 29, 2009 at 09:21:53AM +0100, Mark Brown wrote:
>> >> On Wed, Apr 29, 2009 at 08:27:06AM +0100, Russell King - ARM Linux wrote:
>> >> > On Tue, Apr 28, 2009 at 08:06:08PM -0300, Daniel Ribeiro wrote:
>> >>
>> >> > > The SPI subsystem?
>> >>
>> >> > Can the SPI system cope with the other chip being the master of the
>> >> > interface.  In other words, can SPI sit around primed for a transfer,
>> >> > and notify the user of SPI that new data has arrived?
>> >>
>> >> > If not, the SPI subsystem is totally unsuitable for this.
>> >>
>> >> It's slave only at the minute - there's holes in the API someone could
>> >> fill in but that's not happened yet.
>> >
>> > Well, I guess someone else will have to do that; I don't spend much time
>> > on the LX stuff, and so if it requires any time consuming work, it won't
>> > be worth trying to get it into mainline.
>> >
>>
>> However, the patch itself doesn't looks like to be a SPI slave
>> (I mean from the view of the processor).
>>
>> 'struct spi_message' does support simultaneous rx/tx transfer
>> and multiple such transfers being queued.
>
> The transfers happen under control of PCON not the PXA; PCON asserts a
> GPIO to activate the DMA to control when the transfer happens.
>

That's too bad then. Does that GPIO act like a chip select or it's just a
signal to inform the host to do a transfer? And it also depends on the
SCLK direction.

If the GPIO behaves like an IRQ to inform the host for a transfer, maybe
the driver can arrange a transfer initiated by that event.

> We can not just start the SSP for a 32 byte transfer and hope it happens.
> PCON has to be totally in control.
>
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 3/5] pxa: clean up the legacy SSP API
  2009-04-30  2:31               ` Eric Miao
@ 2009-04-30  7:08                 ` Russell King - ARM Linux
  0 siblings, 0 replies; 12+ messages in thread
From: Russell King - ARM Linux @ 2009-04-30  7:08 UTC (permalink / raw)
  To: Eric Miao
  Cc: alsa-devel, Mark Brown, Philipp Zabel, Paul Shen,
	linux-arm-kernel, Daniel Ribeiro

On Thu, Apr 30, 2009 at 10:31:53AM +0800, Eric Miao wrote:
> On Thu, Apr 30, 2009 at 2:51 AM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > The transfers happen under control of PCON not the PXA; PCON asserts a
> > GPIO to activate the DMA to control when the transfer happens.
> 
> That's too bad then. Does that GPIO act like a chip select or it's just a
> signal to inform the host to do a transfer? And it also depends on the
> SCLK direction.
> 
> If the GPIO behaves like an IRQ to inform the host for a transfer, maybe
> the driver can arrange a transfer initiated by that event.

I don't know what the timing requirement is.  What I do know is what
the current code does, which is to use the signal to control the
transmit DMA when PCON wants to transfer data.

Now, remember that PCON has a habbit of resetting the PXA when things
happen that it doesn't like, I really don't want to go round
experimenting with this thing; as already stated it's really not worth
trying to merge this code if that's what will be required.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2009-04-30  7:09 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-23  5:06 [RFC PATCH 3/5] pxa: clean up the legacy SSP API Eric Miao
2009-04-23  8:24 ` Mark Brown
2009-04-28 22:20 ` Russell King - ARM Linux
2009-04-28 23:06   ` Daniel Ribeiro
2009-04-29  2:18     ` Eric Miao
2009-04-29  7:27     ` Russell King - ARM Linux
2009-04-29  8:21       ` Mark Brown
2009-04-29  8:41         ` Russell King - ARM Linux
2009-04-29  9:14           ` Eric Miao
2009-04-29 18:51             ` Russell King - ARM Linux
2009-04-30  2:31               ` Eric Miao
2009-04-30  7:08                 ` Russell King - ARM Linux

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.