Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tiomap3430_pwr.c
Go to the documentation of this file.
1 /*
2  * tiomap_pwr.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * Implementation of DSP wake/sleep routines.
7  *
8  * Copyright (C) 2007-2008 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 /* ----------------------------------- Host OS */
20 #include <dspbridge/host_os.h>
21 
23 
24 /* ----------------------------------- DSP/BIOS Bridge */
25 #include <dspbridge/dbdefs.h>
26 #include <dspbridge/drv.h>
27 #include <dspbridge/io_sm.h>
28 
29 /* ----------------------------------- Platform Manager */
30 #include <dspbridge/brddefs.h>
31 #include <dspbridge/dev.h>
32 #include <dspbridge/io.h>
33 
34 /* ------------------------------------ Hardware Abstraction Layer */
35 #include <hw_defs.h>
36 #include <hw_mmu.h>
37 
38 #include <dspbridge/pwr.h>
39 
40 /* ----------------------------------- Bridge Driver */
41 #include <dspbridge/dspdeh.h>
42 #include <dspbridge/wdt.h>
43 
44 /* ----------------------------------- specific to this file */
45 #include "_tiomap.h"
46 #include "_tiomap_pwr.h"
49 
50 #define PWRSTST_TIMEOUT 200
51 
52 /*
53  * ======== handle_constraints_set ========
54  * Sets new DSP constraint
55  */
57  void *pargs)
58 {
59 #ifdef CONFIG_TIDSPBRIDGE_DVFS
60  u32 *constraint_val;
62  omap_dspbridge_dev->dev.platform_data;
63 
64  constraint_val = (u32 *) (pargs);
65  /* Read the target value requested by DSP */
66  dev_dbg(bridge, "OPP: %s opp requested = 0x%x\n", __func__,
67  (u32) *(constraint_val + 1));
68 
69  /* Set the new opp value */
70  if (pdata->dsp_set_min_opp)
71  (*pdata->dsp_set_min_opp) ((u32) *(constraint_val + 1));
72 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
73  return 0;
74 }
75 
76 /*
77  * ======== handle_hibernation_from_dsp ========
78  * Handle Hibernation requested from DSP
79  */
81 {
82  int status = 0;
83 #ifdef CONFIG_PM
85  u32 pwr_state;
86 #ifdef CONFIG_TIDSPBRIDGE_DVFS
87  u32 opplevel;
88  struct io_mgr *hio_mgr;
89 #endif
91  omap_dspbridge_dev->dev.platform_data;
92 
93  pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) &
95  /* Wait for DSP to move into OFF state */
96  while ((pwr_state != PWRDM_POWER_OFF) && --timeout) {
97  if (msleep_interruptible(10)) {
98  pr_err("Waiting for DSP OFF mode interrupted\n");
99  return -EPERM;
100  }
101  pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD,
103  }
104  if (timeout == 0) {
105  pr_err("%s: Timed out waiting for DSP off mode\n", __func__);
106  status = -ETIMEDOUT;
107  return status;
108  } else {
109 
110  /* Save mailbox settings */
111  omap_mbox_save_ctx(dev_context->mbox);
112 
113  /* Turn off DSP Peripheral clocks and DSP Load monitor timer */
114  status = dsp_clock_disable_all(dev_context->dsp_per_clks);
115 
116  /* Disable wdt on hibernation. */
117  dsp_wdt_enable(false);
118 
119  if (!status) {
120  /* Update the Bridger Driver state */
121  dev_context->brd_state = BRD_DSP_HIBERNATION;
122 #ifdef CONFIG_TIDSPBRIDGE_DVFS
123  status =
124  dev_get_io_mgr(dev_context->dev_obj, &hio_mgr);
125  if (!hio_mgr) {
126  status = DSP_EHANDLE;
127  return status;
128  }
129  io_sh_msetting(hio_mgr, SHM_GETOPP, &opplevel);
130 
131  /*
132  * Set the OPP to low level before moving to OFF
133  * mode
134  */
135  if (pdata->dsp_set_min_opp)
136  (*pdata->dsp_set_min_opp) (VDD1_OPP1);
137  status = 0;
138 #endif /* CONFIG_TIDSPBRIDGE_DVFS */
139  }
140  }
141 #endif
142  return status;
143 }
144 
145 /*
146  * ======== sleep_dsp ========
147  * Put DSP in low power consuming state.
148  */
149 int sleep_dsp(struct bridge_dev_context *dev_context, u32 dw_cmd,
150  void *pargs)
151 {
152  int status = 0;
153 #ifdef CONFIG_PM
154 #ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR
155  struct deh_mgr *hdeh_mgr;
156 #endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */
157  u16 timeout = PWRSTST_TIMEOUT / 10;
158  u32 pwr_state, target_pwr_state;
159  struct omap_dsp_platform_data *pdata =
160  omap_dspbridge_dev->dev.platform_data;
161 
162  /* Check if sleep code is valid */
163  if ((dw_cmd != PWR_DEEPSLEEP) && (dw_cmd != PWR_EMERGENCYDEEPSLEEP))
164  return -EINVAL;
165 
166  switch (dev_context->brd_state) {
167  case BRD_RUNNING:
168  omap_mbox_save_ctx(dev_context->mbox);
171  dev_dbg(bridge, "PM: %s - sent hibernate cmd to DSP\n",
172  __func__);
173  target_pwr_state = PWRDM_POWER_OFF;
174  } else {
176  target_pwr_state = PWRDM_POWER_RET;
177  }
178  break;
179  case BRD_RETENTION:
180  omap_mbox_save_ctx(dev_context->mbox);
183  target_pwr_state = PWRDM_POWER_OFF;
184  } else
185  return 0;
186  break;
187  case BRD_HIBERNATION:
188  case BRD_DSP_HIBERNATION:
189  /* Already in Hibernation, so just return */
190  dev_dbg(bridge, "PM: %s - DSP already in hibernation\n",
191  __func__);
192  return 0;
193  case BRD_STOPPED:
194  dev_dbg(bridge, "PM: %s - Board in STOP state\n", __func__);
195  return 0;
196  default:
197  dev_dbg(bridge, "PM: %s - Bridge in Illegal state\n", __func__);
198  return -EPERM;
199  }
200 
201  /* Get the PRCM DSP power domain status */
202  pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) &
204 
205  /* Wait for DSP to move into target power state */
206  while ((pwr_state != target_pwr_state) && --timeout) {
207  if (msleep_interruptible(10)) {
208  pr_err("Waiting for DSP to Suspend interrupted\n");
209  return -EPERM;
210  }
211  pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD,
213  }
214 
215  if (!timeout) {
216  pr_err("%s: Timed out waiting for DSP off mode, state %x\n",
217  __func__, pwr_state);
218 #ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR
219  dev_get_deh_mgr(dev_context->dev_obj, &hdeh_mgr);
220  bridge_deh_notify(hdeh_mgr, DSP_PWRERROR, 0);
221 #endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */
222  return -ETIMEDOUT;
223  } else {
224  /* Update the Bridger Driver state */
226  dev_context->brd_state = BRD_HIBERNATION;
227  else
228  dev_context->brd_state = BRD_RETENTION;
229 
230  /* Disable wdt on hibernation. */
231  dsp_wdt_enable(false);
232 
233  /* Turn off DSP Peripheral clocks */
234  status = dsp_clock_disable_all(dev_context->dsp_per_clks);
235  if (status)
236  return status;
237 #ifdef CONFIG_TIDSPBRIDGE_DVFS
238  else if (target_pwr_state == PWRDM_POWER_OFF) {
239  /*
240  * Set the OPP to low level before moving to OFF mode
241  */
242  if (pdata->dsp_set_min_opp)
243  (*pdata->dsp_set_min_opp) (VDD1_OPP1);
244  }
245 #endif /* CONFIG_TIDSPBRIDGE_DVFS */
246  }
247 #endif /* CONFIG_PM */
248  return status;
249 }
250 
251 /*
252  * ======== wake_dsp ========
253  * Wake up DSP from sleep.
254  */
255 int wake_dsp(struct bridge_dev_context *dev_context, void *pargs)
256 {
257  int status = 0;
258 #ifdef CONFIG_PM
259 
260  /* Check the board state, if it is not 'SLEEP' then return */
261  if (dev_context->brd_state == BRD_RUNNING ||
262  dev_context->brd_state == BRD_STOPPED) {
263  /* The Device is in 'RET' or 'OFF' state and Bridge state is not
264  * 'SLEEP', this means state inconsistency, so return */
265  return 0;
266  }
267 
268  /* Send a wakeup message to DSP */
269  sm_interrupt_dsp(dev_context, MBX_PM_DSPWAKEUP);
270 
271  /* Set the device state to RUNNIG */
272  dev_context->brd_state = BRD_RUNNING;
273 #endif /* CONFIG_PM */
274  return status;
275 }
276 
277 /*
278  * ======== dsp_peripheral_clk_ctrl ========
279  * Enable/Disable the DSP peripheral clocks as needed..
280  */
282  void *pargs)
283 {
284  u32 ext_clk = 0;
285  u32 ext_clk_id = 0;
286  u32 ext_clk_cmd = 0;
287  u32 clk_id_index = MBX_PM_MAX_RESOURCES;
288  u32 tmp_index;
289  u32 dsp_per_clks_before;
290  int status = 0;
291 
292  dsp_per_clks_before = dev_context->dsp_per_clks;
293 
294  ext_clk = (u32) *((u32 *) pargs);
295  ext_clk_id = ext_clk & MBX_PM_CLK_IDMASK;
296 
297  /* process the power message -- TODO, keep it in a separate function */
298  for (tmp_index = 0; tmp_index < MBX_PM_MAX_RESOURCES; tmp_index++) {
299  if (ext_clk_id == bpwr_clkid[tmp_index]) {
300  clk_id_index = tmp_index;
301  break;
302  }
303  }
304  /* TODO -- Assert may be a too hard restriction here.. May be we should
305  * just return with failure when the CLK ID does not match */
306  if (clk_id_index == MBX_PM_MAX_RESOURCES) {
307  /* return with a more meaningfull error code */
308  return -EPERM;
309  }
310  ext_clk_cmd = (ext_clk >> MBX_PM_CLK_CMDSHIFT) & MBX_PM_CLK_CMDMASK;
311  switch (ext_clk_cmd) {
312  case BPWR_DISABLE_CLOCK:
313  status = dsp_clk_disable(bpwr_clks[clk_id_index].clk);
314  dsp_clk_wakeup_event_ctrl(bpwr_clks[clk_id_index].clk_id,
315  false);
316  if (!status) {
317  (dev_context->dsp_per_clks) &=
318  (~((u32) (1 << bpwr_clks[clk_id_index].clk)));
319  }
320  break;
321  case BPWR_ENABLE_CLOCK:
322  status = dsp_clk_enable(bpwr_clks[clk_id_index].clk);
323  dsp_clk_wakeup_event_ctrl(bpwr_clks[clk_id_index].clk_id, true);
324  if (!status)
325  (dev_context->dsp_per_clks) |=
326  (1 << bpwr_clks[clk_id_index].clk);
327  break;
328  default:
329  dev_dbg(bridge, "%s: Unsupported CMD\n", __func__);
330  /* unsupported cmd */
331  /* TODO -- provide support for AUTOIDLE Enable/Disable
332  * commands */
333  }
334  return status;
335 }
336 
337 /*
338  * ========pre_scale_dsp========
339  * Sends prescale notification to DSP
340  *
341  */
342 int pre_scale_dsp(struct bridge_dev_context *dev_context, void *pargs)
343 {
344 #ifdef CONFIG_TIDSPBRIDGE_DVFS
345  u32 level;
346  u32 voltage_domain;
347 
348  voltage_domain = *((u32 *) pargs);
349  level = *((u32 *) pargs + 1);
350 
351  dev_dbg(bridge, "OPP: %s voltage_domain = %x, level = 0x%x\n",
352  __func__, voltage_domain, level);
353  if ((dev_context->brd_state == BRD_HIBERNATION) ||
354  (dev_context->brd_state == BRD_RETENTION) ||
355  (dev_context->brd_state == BRD_DSP_HIBERNATION)) {
356  dev_dbg(bridge, "OPP: %s IVA in sleep. No message to DSP\n");
357  return 0;
358  } else if ((dev_context->brd_state == BRD_RUNNING)) {
359  /* Send a prenotification to DSP */
360  dev_dbg(bridge, "OPP: %s sent notification to DSP\n", __func__);
362  return 0;
363  } else {
364  return -EPERM;
365  }
366 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
367  return 0;
368 }
369 
370 /*
371  * ========post_scale_dsp========
372  * Sends postscale notification to DSP
373  *
374  */
375 int post_scale_dsp(struct bridge_dev_context *dev_context,
376  void *pargs)
377 {
378  int status = 0;
379 #ifdef CONFIG_TIDSPBRIDGE_DVFS
380  u32 level;
381  u32 voltage_domain;
382  struct io_mgr *hio_mgr;
383 
384  status = dev_get_io_mgr(dev_context->dev_obj, &hio_mgr);
385  if (!hio_mgr)
386  return -EFAULT;
387 
388  voltage_domain = *((u32 *) pargs);
389  level = *((u32 *) pargs + 1);
390  dev_dbg(bridge, "OPP: %s voltage_domain = %x, level = 0x%x\n",
391  __func__, voltage_domain, level);
392  if ((dev_context->brd_state == BRD_HIBERNATION) ||
393  (dev_context->brd_state == BRD_RETENTION) ||
394  (dev_context->brd_state == BRD_DSP_HIBERNATION)) {
395  /* Update the OPP value in shared memory */
396  io_sh_msetting(hio_mgr, SHM_CURROPP, &level);
397  dev_dbg(bridge, "OPP: %s IVA in sleep. Wrote to shm\n",
398  __func__);
399  } else if ((dev_context->brd_state == BRD_RUNNING)) {
400  /* Update the OPP value in shared memory */
401  io_sh_msetting(hio_mgr, SHM_CURROPP, &level);
402  /* Send a post notification to DSP */
404  dev_dbg(bridge, "OPP: %s wrote to shm. Sent post notification "
405  "to DSP\n", __func__);
406  } else {
407  status = -EPERM;
408  }
409 #endif /* #ifdef CONFIG_TIDSPBRIDGE_DVFS */
410  return status;
411 }
412 
414 {
415  struct cfg_hostres *resources;
416  int status = 0;
417  u32 iva2_grpsel;
418  u32 mpu_grpsel;
419  struct dev_object *hdev_object = NULL;
420  struct bridge_dev_context *bridge_context = NULL;
421 
422  hdev_object = (struct dev_object *)drv_get_first_dev_object();
423  if (!hdev_object)
424  return;
425 
426  status = dev_get_bridge_context(hdev_object, &bridge_context);
427  if (!bridge_context)
428  return;
429 
430  resources = bridge_context->resources;
431  if (!resources)
432  return;
433 
434  switch (clock_id) {
435  case BPWR_GP_TIMER5:
436  iva2_grpsel = readl(resources->per_pm_base + 0xA8);
437  mpu_grpsel = readl(resources->per_pm_base + 0xA4);
438  if (enable) {
439  iva2_grpsel |= OMAP3430_GRPSEL_GPT5_MASK;
440  mpu_grpsel &= ~OMAP3430_GRPSEL_GPT5_MASK;
441  } else {
442  mpu_grpsel |= OMAP3430_GRPSEL_GPT5_MASK;
443  iva2_grpsel &= ~OMAP3430_GRPSEL_GPT5_MASK;
444  }
445  writel(iva2_grpsel, resources->per_pm_base + 0xA8);
446  writel(mpu_grpsel, resources->per_pm_base + 0xA4);
447  break;
448  case BPWR_GP_TIMER6:
449  iva2_grpsel = readl(resources->per_pm_base + 0xA8);
450  mpu_grpsel = readl(resources->per_pm_base + 0xA4);
451  if (enable) {
452  iva2_grpsel |= OMAP3430_GRPSEL_GPT6_MASK;
453  mpu_grpsel &= ~OMAP3430_GRPSEL_GPT6_MASK;
454  } else {
455  mpu_grpsel |= OMAP3430_GRPSEL_GPT6_MASK;
456  iva2_grpsel &= ~OMAP3430_GRPSEL_GPT6_MASK;
457  }
458  writel(iva2_grpsel, resources->per_pm_base + 0xA8);
459  writel(mpu_grpsel, resources->per_pm_base + 0xA4);
460  break;
461  case BPWR_GP_TIMER7:
462  iva2_grpsel = readl(resources->per_pm_base + 0xA8);
463  mpu_grpsel = readl(resources->per_pm_base + 0xA4);
464  if (enable) {
465  iva2_grpsel |= OMAP3430_GRPSEL_GPT7_MASK;
466  mpu_grpsel &= ~OMAP3430_GRPSEL_GPT7_MASK;
467  } else {
468  mpu_grpsel |= OMAP3430_GRPSEL_GPT7_MASK;
469  iva2_grpsel &= ~OMAP3430_GRPSEL_GPT7_MASK;
470  }
471  writel(iva2_grpsel, resources->per_pm_base + 0xA8);
472  writel(mpu_grpsel, resources->per_pm_base + 0xA4);
473  break;
474  case BPWR_GP_TIMER8:
475  iva2_grpsel = readl(resources->per_pm_base + 0xA8);
476  mpu_grpsel = readl(resources->per_pm_base + 0xA4);
477  if (enable) {
478  iva2_grpsel |= OMAP3430_GRPSEL_GPT8_MASK;
479  mpu_grpsel &= ~OMAP3430_GRPSEL_GPT8_MASK;
480  } else {
481  mpu_grpsel |= OMAP3430_GRPSEL_GPT8_MASK;
482  iva2_grpsel &= ~OMAP3430_GRPSEL_GPT8_MASK;
483  }
484  writel(iva2_grpsel, resources->per_pm_base + 0xA8);
485  writel(mpu_grpsel, resources->per_pm_base + 0xA4);
486  break;
487  case BPWR_MCBSP1:
488  iva2_grpsel = readl(resources->core_pm_base + 0xA8);
489  mpu_grpsel = readl(resources->core_pm_base + 0xA4);
490  if (enable) {
491  iva2_grpsel |= OMAP3430_GRPSEL_MCBSP1_MASK;
492  mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP1_MASK;
493  } else {
494  mpu_grpsel |= OMAP3430_GRPSEL_MCBSP1_MASK;
495  iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP1_MASK;
496  }
497  writel(iva2_grpsel, resources->core_pm_base + 0xA8);
498  writel(mpu_grpsel, resources->core_pm_base + 0xA4);
499  break;
500  case BPWR_MCBSP2:
501  iva2_grpsel = readl(resources->per_pm_base + 0xA8);
502  mpu_grpsel = readl(resources->per_pm_base + 0xA4);
503  if (enable) {
504  iva2_grpsel |= OMAP3430_GRPSEL_MCBSP2_MASK;
505  mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP2_MASK;
506  } else {
507  mpu_grpsel |= OMAP3430_GRPSEL_MCBSP2_MASK;
508  iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP2_MASK;
509  }
510  writel(iva2_grpsel, resources->per_pm_base + 0xA8);
511  writel(mpu_grpsel, resources->per_pm_base + 0xA4);
512  break;
513  case BPWR_MCBSP3:
514  iva2_grpsel = readl(resources->per_pm_base + 0xA8);
515  mpu_grpsel = readl(resources->per_pm_base + 0xA4);
516  if (enable) {
517  iva2_grpsel |= OMAP3430_GRPSEL_MCBSP3_MASK;
518  mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP3_MASK;
519  } else {
520  mpu_grpsel |= OMAP3430_GRPSEL_MCBSP3_MASK;
521  iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP3_MASK;
522  }
523  writel(iva2_grpsel, resources->per_pm_base + 0xA8);
524  writel(mpu_grpsel, resources->per_pm_base + 0xA4);
525  break;
526  case BPWR_MCBSP4:
527  iva2_grpsel = readl(resources->per_pm_base + 0xA8);
528  mpu_grpsel = readl(resources->per_pm_base + 0xA4);
529  if (enable) {
530  iva2_grpsel |= OMAP3430_GRPSEL_MCBSP4_MASK;
531  mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP4_MASK;
532  } else {
533  mpu_grpsel |= OMAP3430_GRPSEL_MCBSP4_MASK;
534  iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP4_MASK;
535  }
536  writel(iva2_grpsel, resources->per_pm_base + 0xA8);
537  writel(mpu_grpsel, resources->per_pm_base + 0xA4);
538  break;
539  case BPWR_MCBSP5:
540  iva2_grpsel = readl(resources->per_pm_base + 0xA8);
541  mpu_grpsel = readl(resources->per_pm_base + 0xA4);
542  if (enable) {
543  iva2_grpsel |= OMAP3430_GRPSEL_MCBSP5_MASK;
544  mpu_grpsel &= ~OMAP3430_GRPSEL_MCBSP5_MASK;
545  } else {
546  mpu_grpsel |= OMAP3430_GRPSEL_MCBSP5_MASK;
547  iva2_grpsel &= ~OMAP3430_GRPSEL_MCBSP5_MASK;
548  }
549  writel(iva2_grpsel, resources->per_pm_base + 0xA8);
550  writel(mpu_grpsel, resources->per_pm_base + 0xA4);
551  break;
552  }
553 }