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