linux/drivers/staging/tidspbridge/core/dsp-clock.c
<<
>>
Prefs
   1/*
   2 * clk.c
   3 *
   4 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
   5 *
   6 * Clock and Timer services.
   7 *
   8 * Copyright (C) 2005-2006 Texas Instruments, Inc.
   9 *
  10 * This package is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 *
  14 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  17 */
  18
  19#define L4_34XX_BASE            0x48000000
  20
  21#include <linux/types.h>
  22
  23/*  ----------------------------------- Host OS */
  24#include <dspbridge/host_os.h>
  25#include <plat/dmtimer.h>
  26#include <linux/platform_data/asoc-ti-mcbsp.h>
  27
  28/*  ----------------------------------- DSP/BIOS Bridge */
  29#include <dspbridge/dbdefs.h>
  30#include <dspbridge/drv.h>
  31#include <dspbridge/dev.h>
  32#include "_tiomap.h"
  33
  34/*  ----------------------------------- This */
  35#include <dspbridge/clk.h>
  36
  37/*  ----------------------------------- Defines, Data Structures, Typedefs */
  38
  39#define OMAP_SSI_OFFSET                 0x58000
  40#define OMAP_SSI_SIZE                   0x1000
  41#define OMAP_SSI_SYSCONFIG_OFFSET       0x10
  42
  43#define SSI_AUTOIDLE                    (1 << 0)
  44#define SSI_SIDLE_SMARTIDLE             (2 << 3)
  45#define SSI_MIDLE_NOIDLE                (1 << 12)
  46
  47/* Clk types requested by the dsp */
  48#define IVA2_CLK        0
  49#define GPT_CLK         1
  50#define WDT_CLK         2
  51#define MCBSP_CLK       3
  52#define SSI_CLK         4
  53
  54/* Bridge GPT id (1 - 4), DM Timer id (5 - 8) */
  55#define DMT_ID(id) ((id) + 4)
  56#define DM_TIMER_CLOCKS         4
  57
  58/* Bridge MCBSP id (6 - 10), OMAP Mcbsp id (0 - 4) */
  59#define MCBSP_ID(id) ((id) - 6)
  60
  61static struct omap_dm_timer *timer[4];
  62
  63struct clk *iva2_clk;
  64
  65struct dsp_ssi {
  66        struct clk *sst_fck;
  67        struct clk *ssr_fck;
  68        struct clk *ick;
  69};
  70
  71static struct dsp_ssi ssi;
  72
  73static u32 dsp_clocks;
  74
  75static inline u32 is_dsp_clk_active(u32 clk, u8 id)
  76{
  77        return clk & (1 << id);
  78}
  79
  80static inline void set_dsp_clk_active(u32 *clk, u8 id)
  81{
  82        *clk |= (1 << id);
  83}
  84
  85static inline void set_dsp_clk_inactive(u32 *clk, u8 id)
  86{
  87        *clk &= ~(1 << id);
  88}
  89
  90static s8 get_clk_type(u8 id)
  91{
  92        s8 type;
  93
  94        if (id == DSP_CLK_IVA2)
  95                type = IVA2_CLK;
  96        else if (id <= DSP_CLK_GPT8)
  97                type = GPT_CLK;
  98        else if (id == DSP_CLK_WDT3)
  99                type = WDT_CLK;
 100        else if (id <= DSP_CLK_MCBSP5)
 101                type = MCBSP_CLK;
 102        else if (id == DSP_CLK_SSI)
 103                type = SSI_CLK;
 104        else
 105                type = -1;
 106
 107        return type;
 108}
 109
 110/*
 111 *  ======== dsp_clk_exit ========
 112 *  Purpose:
 113 *      Cleanup CLK module.
 114 */
 115void dsp_clk_exit(void)
 116{
 117        int i;
 118
 119        dsp_clock_disable_all(dsp_clocks);
 120
 121        for (i = 0; i < DM_TIMER_CLOCKS; i++)
 122                omap_dm_timer_free(timer[i]);
 123
 124        clk_unprepare(iva2_clk);
 125        clk_put(iva2_clk);
 126        clk_unprepare(ssi.sst_fck);
 127        clk_put(ssi.sst_fck);
 128        clk_unprepare(ssi.ssr_fck);
 129        clk_put(ssi.ssr_fck);
 130        clk_unprepare(ssi.ick);
 131        clk_put(ssi.ick);
 132}
 133
 134/*
 135 *  ======== dsp_clk_init ========
 136 *  Purpose:
 137 *      Initialize CLK module.
 138 */
 139void dsp_clk_init(void)
 140{
 141        static struct platform_device dspbridge_device;
 142        int i, id;
 143
 144        dspbridge_device.dev.bus = &platform_bus_type;
 145
 146        for (i = 0, id = 5; i < DM_TIMER_CLOCKS; i++, id++)
 147                timer[i] = omap_dm_timer_request_specific(id);
 148
 149        iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck");
 150        if (IS_ERR(iva2_clk))
 151                dev_err(bridge, "failed to get iva2 clock %p\n", iva2_clk);
 152        else
 153                clk_prepare(iva2_clk);
 154
 155        ssi.sst_fck = clk_get(&dspbridge_device.dev, "ssi_sst_fck");
 156        ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck");
 157        ssi.ick = clk_get(&dspbridge_device.dev, "ssi_ick");
 158
 159        if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick)) {
 160                dev_err(bridge, "failed to get ssi: sst %p, ssr %p, ick %p\n",
 161                                        ssi.sst_fck, ssi.ssr_fck, ssi.ick);
 162        } else {
 163                clk_prepare(ssi.sst_fck);
 164                clk_prepare(ssi.ssr_fck);
 165                clk_prepare(ssi.ick);
 166        }
 167}
 168
 169/**
 170 * dsp_gpt_wait_overflow - set gpt overflow and wait for fixed timeout
 171 * @clk_id:      GP Timer clock id.
 172 * @load:        Overflow value.
 173 *
 174 * Sets an overflow interrupt for the desired GPT waiting for a timeout
 175 * of 5 msecs for the interrupt to occur.
 176 */
 177void dsp_gpt_wait_overflow(short int clk_id, unsigned int load)
 178{
 179        struct omap_dm_timer *gpt = timer[clk_id - 1];
 180        unsigned long timeout;
 181
 182        if (!gpt)
 183                return;
 184
 185        /* Enable overflow interrupt */
 186        omap_dm_timer_set_int_enable(gpt, OMAP_TIMER_INT_OVERFLOW);
 187
 188        /*
 189         * Set counter value to overflow counter after
 190         * one tick and start timer.
 191         */
 192        omap_dm_timer_set_load_start(gpt, 0, load);
 193
 194        /* Wait 80us for timer to overflow */
 195        udelay(80);
 196
 197        timeout = msecs_to_jiffies(5);
 198        /* Check interrupt status and wait for interrupt */
 199        while (!(omap_dm_timer_read_status(gpt) & OMAP_TIMER_INT_OVERFLOW)) {
 200                if (time_is_after_jiffies(timeout)) {
 201                        pr_err("%s: GPTimer interrupt failed\n", __func__);
 202                        break;
 203                }
 204        }
 205}
 206
 207/*
 208 *  ======== dsp_clk_enable ========
 209 *  Purpose:
 210 *      Enable Clock .
 211 *
 212 */
 213int dsp_clk_enable(enum dsp_clk_id clk_id)
 214{
 215        int status = 0;
 216
 217        if (is_dsp_clk_active(dsp_clocks, clk_id)) {
 218                dev_err(bridge, "WARN: clock id %d already enabled\n", clk_id);
 219                goto out;
 220        }
 221
 222        switch (get_clk_type(clk_id)) {
 223        case IVA2_CLK:
 224                clk_enable(iva2_clk);
 225                break;
 226        case GPT_CLK:
 227                status = omap_dm_timer_start(timer[clk_id - 1]);
 228                break;
 229#ifdef CONFIG_OMAP_MCBSP
 230        case MCBSP_CLK:
 231                omap_mcbsp_request(MCBSP_ID(clk_id));
 232                omap2_mcbsp_set_clks_src(MCBSP_ID(clk_id), MCBSP_CLKS_PAD_SRC);
 233                break;
 234#endif
 235        case WDT_CLK:
 236                dev_err(bridge, "ERROR: DSP requested to enable WDT3 clk\n");
 237                break;
 238        case SSI_CLK:
 239                clk_enable(ssi.sst_fck);
 240                clk_enable(ssi.ssr_fck);
 241                clk_enable(ssi.ick);
 242
 243                /*
 244                 * The SSI module need to configured not to have the Forced
 245                 * idle for master interface. If it is set to forced idle,
 246                 * the SSI module is transitioning to standby thereby causing
 247                 * the client in the DSP hang waiting for the SSI module to
 248                 * be active after enabling the clocks
 249                 */
 250                ssi_clk_prepare(true);
 251                break;
 252        default:
 253                dev_err(bridge, "Invalid clock id for enable\n");
 254                status = -EPERM;
 255        }
 256
 257        if (!status)
 258                set_dsp_clk_active(&dsp_clocks, clk_id);
 259
 260out:
 261        return status;
 262}
 263
 264/**
 265 * dsp_clock_enable_all - Enable clocks used by the DSP
 266 * @dev_context         Driver's device context strucure
 267 *
 268 * This function enables all the peripheral clocks that were requested by DSP.
 269 */
 270u32 dsp_clock_enable_all(u32 dsp_per_clocks)
 271{
 272        u32 clk_id;
 273        u32 status = -EPERM;
 274
 275        for (clk_id = 0; clk_id < DSP_CLK_NOT_DEFINED; clk_id++) {
 276                if (is_dsp_clk_active(dsp_per_clocks, clk_id))
 277                        status = dsp_clk_enable(clk_id);
 278        }
 279
 280        return status;
 281}
 282
 283/*
 284 *  ======== dsp_clk_disable ========
 285 *  Purpose:
 286 *      Disable the clock.
 287 *
 288 */
 289int dsp_clk_disable(enum dsp_clk_id clk_id)
 290{
 291        int status = 0;
 292
 293        if (!is_dsp_clk_active(dsp_clocks, clk_id)) {
 294                dev_err(bridge, "ERR: clock id %d already disabled\n", clk_id);
 295                goto out;
 296        }
 297
 298        switch (get_clk_type(clk_id)) {
 299        case IVA2_CLK:
 300                clk_disable(iva2_clk);
 301                break;
 302        case GPT_CLK:
 303                status = omap_dm_timer_stop(timer[clk_id - 1]);
 304                break;
 305#ifdef CONFIG_OMAP_MCBSP
 306        case MCBSP_CLK:
 307                omap2_mcbsp_set_clks_src(MCBSP_ID(clk_id), MCBSP_CLKS_PRCM_SRC);
 308                omap_mcbsp_free(MCBSP_ID(clk_id));
 309                break;
 310#endif
 311        case WDT_CLK:
 312                dev_err(bridge, "ERROR: DSP requested to disable WDT3 clk\n");
 313                break;
 314        case SSI_CLK:
 315                ssi_clk_prepare(false);
 316                ssi_clk_prepare(false);
 317                clk_disable(ssi.sst_fck);
 318                clk_disable(ssi.ssr_fck);
 319                clk_disable(ssi.ick);
 320                break;
 321        default:
 322                dev_err(bridge, "Invalid clock id for disable\n");
 323                status = -EPERM;
 324        }
 325
 326        if (!status)
 327                set_dsp_clk_inactive(&dsp_clocks, clk_id);
 328
 329out:
 330        return status;
 331}
 332
 333/**
 334 * dsp_clock_disable_all - Disable all active clocks
 335 * @dev_context         Driver's device context structure
 336 *
 337 * This function disables all the peripheral clocks that were enabled by DSP.
 338 * It is meant to be called only when DSP is entering hibernation or when DSP
 339 * is in error state.
 340 */
 341u32 dsp_clock_disable_all(u32 dsp_per_clocks)
 342{
 343        u32 clk_id;
 344        u32 status = -EPERM;
 345
 346        for (clk_id = 0; clk_id < DSP_CLK_NOT_DEFINED; clk_id++) {
 347                if (is_dsp_clk_active(dsp_per_clocks, clk_id))
 348                        status = dsp_clk_disable(clk_id);
 349        }
 350
 351        return status;
 352}
 353
 354u32 dsp_clk_get_iva2_rate(void)
 355{
 356        u32 clk_speed_khz;
 357
 358        clk_speed_khz = clk_get_rate(iva2_clk);
 359        clk_speed_khz /= 1000;
 360        dev_dbg(bridge, "%s: clk speed Khz = %d\n", __func__, clk_speed_khz);
 361
 362        return clk_speed_khz;
 363}
 364
 365void ssi_clk_prepare(bool FLAG)
 366{
 367        void __iomem *ssi_base;
 368        unsigned int value;
 369
 370        ssi_base = ioremap(L4_34XX_BASE + OMAP_SSI_OFFSET, OMAP_SSI_SIZE);
 371        if (!ssi_base) {
 372                pr_err("%s: error, SSI not configured\n", __func__);
 373                return;
 374        }
 375
 376        if (FLAG) {
 377                /* Set Autoidle, SIDLEMode to smart idle, and MIDLEmode to
 378                 * no idle
 379                 */
 380                value = SSI_AUTOIDLE | SSI_SIDLE_SMARTIDLE | SSI_MIDLE_NOIDLE;
 381        } else {
 382                /* Set Autoidle, SIDLEMode to forced idle, and MIDLEmode to
 383                 * forced idle
 384                 */
 385                value = SSI_AUTOIDLE;
 386        }
 387
 388        __raw_writel(value, ssi_base + OMAP_SSI_SYSCONFIG_OFFSET);
 389        iounmap(ssi_base);
 390}
 391
 392