linux/arch/powerpc/platforms/52xx/mpc52xx_common.c
<<
>>
Prefs
   1/*
   2 *
   3 * Utility functions for the Freescale MPC52xx.
   4 *
   5 * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
   6 *
   7 * This file is licensed under the terms of the GNU General Public License
   8 * version 2. This program is licensed "as is" without any warranty of any
   9 * kind, whether express or implied.
  10 *
  11 */
  12
  13#undef DEBUG
  14
  15#include <linux/kernel.h>
  16#include <linux/spinlock.h>
  17#include <linux/of_platform.h>
  18#include <asm/io.h>
  19#include <asm/prom.h>
  20#include <asm/mpc52xx.h>
  21
  22/* MPC5200 device tree match tables */
  23static struct of_device_id mpc52xx_xlb_ids[] __initdata = {
  24        { .compatible = "fsl,mpc5200-xlb", },
  25        { .compatible = "mpc5200-xlb", },
  26        {}
  27};
  28static struct of_device_id mpc52xx_bus_ids[] __initdata = {
  29        { .compatible = "fsl,mpc5200-immr", },
  30        { .compatible = "fsl,mpc5200b-immr", },
  31        { .compatible = "fsl,lpb", },
  32
  33        /* depreciated matches; shouldn't be used in new device trees */
  34        { .type = "builtin", .compatible = "mpc5200", }, /* efika */
  35        { .type = "soc", .compatible = "mpc5200", }, /* lite5200 */
  36        {}
  37};
  38
  39/*
  40 * This variable is mapped in mpc52xx_map_wdt() and used in mpc52xx_restart().
  41 * Permanent mapping is required because mpc52xx_restart() can be called
  42 * from interrupt context while node mapping (which calls ioremap())
  43 * cannot be used at such point.
  44 */
  45static spinlock_t mpc52xx_lock = SPIN_LOCK_UNLOCKED;
  46static struct mpc52xx_gpt __iomem *mpc52xx_wdt;
  47static struct mpc52xx_cdm __iomem *mpc52xx_cdm;
  48
  49/**
  50 *      mpc52xx_find_ipb_freq - Find the IPB bus frequency for a device
  51 *      @node:  device node
  52 *
  53 *      Returns IPB bus frequency, or 0 if the bus frequency cannot be found.
  54 */
  55unsigned int
  56mpc52xx_find_ipb_freq(struct device_node *node)
  57{
  58        struct device_node *np;
  59        const unsigned int *p_ipb_freq = NULL;
  60
  61        of_node_get(node);
  62        while (node) {
  63                p_ipb_freq = of_get_property(node, "bus-frequency", NULL);
  64                if (p_ipb_freq)
  65                        break;
  66
  67                np = of_get_parent(node);
  68                of_node_put(node);
  69                node = np;
  70        }
  71        if (node)
  72                of_node_put(node);
  73
  74        return p_ipb_freq ? *p_ipb_freq : 0;
  75}
  76EXPORT_SYMBOL(mpc52xx_find_ipb_freq);
  77
  78
  79/*
  80 * Configure the XLB arbiter settings to match what Linux expects.
  81 */
  82void __init
  83mpc5200_setup_xlb_arbiter(void)
  84{
  85        struct device_node *np;
  86        struct mpc52xx_xlb  __iomem *xlb;
  87
  88        np = of_find_matching_node(NULL, mpc52xx_xlb_ids);
  89        xlb = of_iomap(np, 0);
  90        of_node_put(np);
  91        if (!xlb) {
  92                printk(KERN_ERR __FILE__ ": "
  93                        "Error mapping XLB in mpc52xx_setup_cpu(). "
  94                        "Expect some abnormal behavior\n");
  95                return;
  96        }
  97
  98        /* Configure the XLB Arbiter priorities */
  99        out_be32(&xlb->master_pri_enable, 0xff);
 100        out_be32(&xlb->master_priority, 0x11111111);
 101
 102        /*
 103         * Disable XLB pipelining
 104         * (cfr errate 292. We could do this only just before ATA PIO
 105         *  transaction and re-enable it afterwards ...)
 106         * Not needed on MPC5200B.
 107         */
 108        if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR)
 109                out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_PLDIS);
 110
 111        iounmap(xlb);
 112}
 113
 114/**
 115 * mpc52xx_declare_of_platform_devices: register internal devices and children
 116 *                                      of the localplus bus to the of_platform
 117 *                                      bus.
 118 */
 119void __init
 120mpc52xx_declare_of_platform_devices(void)
 121{
 122        /* Find every child of the SOC node and add it to of_platform */
 123        if (of_platform_bus_probe(NULL, mpc52xx_bus_ids, NULL))
 124                printk(KERN_ERR __FILE__ ": "
 125                        "Error while probing of_platform bus\n");
 126}
 127
 128/*
 129 * match tables used by mpc52xx_map_common_devices()
 130 */
 131static struct of_device_id mpc52xx_gpt_ids[] __initdata = {
 132        { .compatible = "fsl,mpc5200-gpt", },
 133        { .compatible = "mpc5200-gpt", }, /* old */
 134        {}
 135};
 136static struct of_device_id mpc52xx_cdm_ids[] __initdata = {
 137        { .compatible = "fsl,mpc5200-cdm", },
 138        { .compatible = "mpc5200-cdm", }, /* old */
 139        {}
 140};
 141
 142/**
 143 * mpc52xx_map_common_devices: iomap devices required by common code
 144 */
 145void __init
 146mpc52xx_map_common_devices(void)
 147{
 148        struct device_node *np;
 149
 150        /* mpc52xx_wdt is mapped here and used in mpc52xx_restart,
 151         * possibly from a interrupt context. wdt is only implement
 152         * on a gpt0, so check has-wdt property before mapping.
 153         */
 154        for_each_matching_node(np, mpc52xx_gpt_ids) {
 155                if (of_get_property(np, "fsl,has-wdt", NULL) ||
 156                    of_get_property(np, "has-wdt", NULL)) {
 157                        mpc52xx_wdt = of_iomap(np, 0);
 158                        of_node_put(np);
 159                        break;
 160                }
 161        }
 162
 163        /* Clock Distribution Module, used by PSC clock setting function */
 164        np = of_find_matching_node(NULL, mpc52xx_cdm_ids);
 165        mpc52xx_cdm = of_iomap(np, 0);
 166        of_node_put(np);
 167}
 168
 169/**
 170 * mpc52xx_set_psc_clkdiv: Set clock divider in the CDM for PSC ports
 171 *
 172 * @psc_id: id of psc port; must be 1,2,3 or 6
 173 * @clkdiv: clock divider value to put into CDM PSC register.
 174 */
 175int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv)
 176{
 177        unsigned long flags;
 178        u16 __iomem *reg;
 179        u32 val;
 180        u32 mask;
 181        u32 mclken_div;
 182
 183        if (!mpc52xx_cdm)
 184                return -ENODEV;
 185
 186        mclken_div = 0x8000 | (clkdiv & 0x1FF);
 187        switch (psc_id) {
 188        case 1: reg = &mpc52xx_cdm->mclken_div_psc1; mask = 0x20; break;
 189        case 2: reg = &mpc52xx_cdm->mclken_div_psc2; mask = 0x40; break;
 190        case 3: reg = &mpc52xx_cdm->mclken_div_psc3; mask = 0x80; break;
 191        case 6: reg = &mpc52xx_cdm->mclken_div_psc6; mask = 0x10; break;
 192        default:
 193                return -ENODEV;
 194        }
 195
 196        /* Set the rate and enable the clock */
 197        spin_lock_irqsave(&mpc52xx_lock, flags);
 198        out_be16(reg, mclken_div);
 199        val = in_be32(&mpc52xx_cdm->clk_enables);
 200        out_be32(&mpc52xx_cdm->clk_enables, val | mask);
 201        spin_unlock_irqrestore(&mpc52xx_lock, flags);
 202
 203        return 0;
 204}
 205EXPORT_SYMBOL(mpc52xx_set_psc_clkdiv);
 206
 207/**
 208 * mpc52xx_restart: ppc_md->restart hook for mpc5200 using the watchdog timer
 209 */
 210void
 211mpc52xx_restart(char *cmd)
 212{
 213        local_irq_disable();
 214
 215        /* Turn on the watchdog and wait for it to expire.
 216         * It effectively does a reset. */
 217        if (mpc52xx_wdt) {
 218                out_be32(&mpc52xx_wdt->mode, 0x00000000);
 219                out_be32(&mpc52xx_wdt->count, 0x000000ff);
 220                out_be32(&mpc52xx_wdt->mode, 0x00009004);
 221        } else
 222                printk(KERN_ERR __FILE__ ": "
 223                        "mpc52xx_restart: Can't access wdt. "
 224                        "Restart impossible, system halted.\n");
 225
 226        while (1);
 227}
 228
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.