[PATCH 1/2 v3] powerpc/5200: add mpc5200_psc_ac97_gpio_reset

Eric Millbrandt emillbrandt at dekaresearch.com
Wed Jun 16 02:05:06 EST 2010


Work around a silicon bug in the ac97 reset functionality of the
mpc5200(b).  The implementation of the ac97 "cold" reset is flawed.
If the sync and output lines are high when reset is asserted the
attached ac97 device may go into test mode.  Avoid this by
reconfiguring the psc to gpio mode and generating the reset manually.

>From MPC5200B User's Manual:
"Some AC97 devices goes to a test mode, if the Sync line is high
during the Res line is low (reset phase). To avoid this behavior the
Sync line must be also forced to zero during the reset phase. To do
that, the pin muxing should switch to GPIO mode and the GPIO control
register should be used to control the output lines."

Signed-off-by: Eric Millbrandt <emillbrandt at dekaresearch.com>
---

changes since v1
- Refactored to manipulate port_config and gpio pins internally instead of
  exporting an API.

changes since v2
- Factored out gpiolib calls, write to the gpio registers directly

 arch/powerpc/include/asm/mpc52xx.h           |    1 +
 arch/powerpc/include/asm/mpc52xx_psc.h       |    1 +
 arch/powerpc/platforms/52xx/mpc52xx_common.c |  113 ++++++++++++++++++++++++++
 3 files changed, 115 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/mpc52xx.h
index b664ce7..1f41382 100644
--- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -271,6 +271,7 @@ struct mpc52xx_intr {
 /* mpc52xx_common.c */
 extern void mpc5200_setup_xlb_arbiter(void);
 extern void mpc52xx_declare_of_platform_devices(void);
+extern int mpc5200_psc_ac97_gpio_reset(int psc_number);
 extern void mpc52xx_map_common_devices(void);
 extern int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv);
 extern unsigned int mpc52xx_get_xtal_freq(struct device_node *node);
diff --git a/arch/powerpc/include/asm/mpc52xx_psc.h b/arch/powerpc/include/asm/mpc52xx_psc.h
index ecc4fc6..2966df6 100644
--- a/arch/powerpc/include/asm/mpc52xx_psc.h
+++ b/arch/powerpc/include/asm/mpc52xx_psc.h
@@ -131,6 +131,7 @@
 #define MPC52xx_PSC_SICR_SIM_FIR               (0x6 << 24)
 #define MPC52xx_PSC_SICR_SIM_CODEC_24          (0x7 << 24)
 #define MPC52xx_PSC_SICR_SIM_CODEC_32          (0xf << 24)
+#define MPC52xx_PSC_SICR_ACRB                  (0x8 << 24)
 #define MPC52xx_PSC_SICR_AWR                   (1 << 30)
 #define MPC52xx_PSC_SICR_GENCLK                        (1 << 23)
 #define MPC52xx_PSC_SICR_I2S                   (1 << 22)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c
index a46bad0..116d461 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
@@ -12,9 +12,11 @@

 #undef DEBUG

+#include <linux/gpio.h>
 #include <linux/kernel.h>
 #include <linux/spinlock.h>
 #include <linux/of_platform.h>
+#include <linux/of_gpio.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/mpc52xx.h>
@@ -82,6 +84,14 @@ mpc5200_setup_xlb_arbiter(void)
        iounmap(xlb);
 }

+/*
+ * This variable is mapped in mpc52xx_map_common_devices and
+ * used in mpc5200_psc_ac97_gpio_reset().
+ */
+static DEFINE_SPINLOCK(gpio_lock);
+struct mpc52xx_gpio __iomem *simple_gpio;
+struct mpc52xx_gpio_wkup __iomem *wkup_gpio;
+
 /**
  * mpc52xx_declare_of_platform_devices: register internal devices and children
  *                                     of the localplus bus to the of_platform
@@ -109,6 +119,19 @@ static struct of_device_id mpc52xx_cdm_ids[] __initdata = {
        { .compatible = "mpc5200-cdm", }, /* old */
        {}
 };
+static const struct of_device_id mpc52xx_gpio_simple[] = {
+       {
+               .compatible = "fsl,mpc5200-gpio",
+       },
+       {}
+};
+static const struct of_device_id mpc52xx_gpio_wkup[] = {
+       {
+               .compatible = "fsl,mpc5200-gpio-wkup",
+       },
+       {}
+};
+

 /**
  * mpc52xx_map_common_devices: iomap devices required by common code
@@ -135,6 +158,16 @@ mpc52xx_map_common_devices(void)
        np = of_find_matching_node(NULL, mpc52xx_cdm_ids);
        mpc52xx_cdm = of_iomap(np, 0);
        of_node_put(np);
+
+       /* simple_gpio registers */
+       np = of_find_matching_node(NULL, mpc52xx_gpio_simple);
+       simple_gpio = of_iomap(np, 0);
+       of_node_put(np);
+
+       /* wkup_gpio registers */
+       np = of_find_matching_node(NULL, mpc52xx_gpio_wkup);
+       wkup_gpio = of_iomap(np, 0);
+       of_node_put(np);
 }

 /**
@@ -233,3 +266,83 @@ mpc52xx_restart(char *cmd)

        while (1);
 }
+
+#define PSC1_RESET     0x1
+#define PSC1_SYNC      0x4
+#define PSC1_SDATA_OUT 0x1
+#define PSC2_RESET     0x2
+#define PSC2_SYNC      (0x4<<4)
+#define PSC2_SDATA_OUT (0x1<<4)
+#define MPC52xx_GPIO_PSC1_MASK 0x7
+#define MPC52xx_GPIO_PSC2_MASK (0x7<<4)
+
+/**
+ * mpc5200_psc_ac97_gpio_reset: Use gpio pins to reset the ac97 bus
+ *
+ * @psc: psc number to reset (only psc 1 and 2 support ac97)
+ */
+int mpc5200_psc_ac97_gpio_reset(int psc_number)
+{
+       unsigned long flags;
+       u32 gpio;
+       u32 mux;
+       int out;
+       int reset;
+       int sync;
+
+       if ((!simple_gpio) || (!wkup_gpio))
+               return -ENODEV;
+
+       switch (psc_number) {
+       case 0:
+               reset   = PSC1_RESET;           /* AC97_1_RES */
+               sync    = PSC1_SYNC;            /* AC97_1_SYNC */
+               out     = PSC1_SDATA_OUT;       /* AC97_1_SDATA_OUT */
+               gpio    = MPC52xx_GPIO_PSC1_MASK;
+               break;
+       case 1:
+               reset   = PSC2_RESET;           /* AC97_2_RES */
+               sync    = PSC2_SYNC;            /* AC97_2_SYNC */
+               out     = PSC2_SDATA_OUT;       /* AC97_2_SDATA_OUT */
+               gpio    = MPC52xx_GPIO_PSC2_MASK;
+               break;
+       default:
+               printk(KERN_ERR __FILE__ ": "
+                     "Unable to determine PSC, no ac97 cold-reset will be "
+                     "performed\n");
+               return -ENODEV;
+       }
+
+       spin_lock_irqsave(&gpio_lock, flags);
+
+       mux = in_be32(&simple_gpio->port_config);
+
+       /* Reconfiure pin-muxing to gpio */
+       out_be32(&simple_gpio->port_config, mux & (~gpio));
+
+       /* enable gpio pins for output */
+       setbits8(&wkup_gpio->wkup_gpioe, reset);
+       setbits32(&simple_gpio->simple_gpioe, sync | out);
+
+       setbits8(&wkup_gpio->wkup_ddr, reset);
+       setbits32(&simple_gpio->simple_ddr, sync | out);
+
+       /* Assert cold reset */
+       setbits32(&simple_gpio->simple_dvo, sync | out);
+       setbits8(&wkup_gpio->wkup_dvo, reset);
+
+       /* wait at lease 1 us */
+       udelay(2);
+
+       /* Deassert reset */
+       clrbits8(&wkup_gpio->wkup_dvo, reset);
+       clrbits32(&simple_gpio->simple_dvo, sync | out);
+
+       /* Restore pin-muxing */
+       out_be32(&simple_gpio->port_config, mux);
+
+       spin_unlock_irqrestore(&gpio_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(mpc5200_psc_ac97_gpio_reset);
--
-DISCLAIMER: an automatically appended disclaimer may follow. By posting-
-to a public e-mail mailing list I hereby grant permission to distribute-
-and copy this message.-

1.6.3.1


This e-mail and the information, including any attachments, it contains are intended to be a confidential communication only to the person or entity to whom it is addressed and may contain information that is privileged. If the reader of this message is not the intended recipient, you are hereby notified that any dissemination, distribution or copying of this communication is strictly prohibited. If you have received this communication in error, please immediately notify the sender and destroy the original message.

Thank you.

Please consider the environment before printing this email.


More information about the Linuxppc-dev mailing list