TEST mfld pmu: add arch/x86/platform/mfld/pmu.c
authorLen Brown <len.brown@intel.com>
Fri, 12 Aug 2011 21:43:52 +0000 (22:43 +0100)
committermgross <mark.gross@intel.com>
Wed, 9 Nov 2011 20:37:15 +0000 (12:37 -0800)
From UMG 2.6.35 tree: arch/x86/kernel/mid_pmu.c

commit 30365108003d6d71f508b5cf4429e9588a5928a0
Author: Fengwei Yin <fengwei.yin@intel.com>
Date:   Mon Jul 11 11:16:33 2011 +0800

    Revert "mid_pmu:  do not issue set_command during s3 entry"

    BZ: 4883

    This reverts commit a49316f33d6066dd13d85f577dce84dd0278d4d0.

    Change-Id: If9a8c696f626a7d6d8fcd42da8e1563a5c1bf4f8
Signed-off-by: Fengwei Yin <fengwei.yin@intel.com>
commit 9f42b0f20930f74e6840a335db62b9089f7176da
Author: Prabu Renganathan <prabu.renganathan@intel.com>
Date:   Fri Jun 17 18:28:36 2011 -0700

    mid_pmu:  pick D0i1 as USB low power state

    BZ: 3497

    HSD: 3337060

    Fix for the Penwell bug where USB only supports D0i1

Signed-off-by: Prabu Renganathan <prabu.renganathan@intel.com>
    Change-Id: Icb0c7c9ffb9889c2de7743ff326f74a9b2ab8771

commit a49316f33d6066dd13d85f577dce84dd0278d4d0
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Wed Jun 29 13:43:02 2011 +0530

    mid_pmu:  do not issue set_command during s3 entry

    BZ: 4139

    during s3 entry dpm_suspend_noirq will call each of the drivers
    suspend calls and that in turn calls set_power_state() calls,
    we issue an interactive command to SCU and wait for the status
    interrupt this takes a long time until all the devices are suspended.
    This is not required since anyways after the devices are suspended
    we do S3 that puts all the devices in D0i3.

    We just cache the status of all the devices and configure that as
    the wakeup states after S3. This reduces the time to enter S3.

    Change-Id: Ice0f5b326335675890fa60ce6c546693d11d98f2
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 1df781ef9bbc9b0fc453537da2a3b1861f363138
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Thu Jun 23 04:58:43 2011 +0530

    mid_pmu:  sparse warnings fixed

    BZ: 3856

    Fixed the following sparse warnings.

    arch/x86/kernel/mid_pmu.c:302:18: warning: symbol 'pmu_wake_lock' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:1126:15: warning: symbol 'pmu_get_cstate' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:1813:3: warning: symbol 'medfield_lsses' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:1874:5: warning: symbol 'medfield_lsses_num' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:1876:6: warning: symbol 'lss_device_status' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:2368:5: warning: symbol 'mid_suspend' was not declared. Should it be static?

    fixed make namespacecheck issues.

    Change-Id: I7731a0391b1433a7ecfa28bdd3265591d1389495
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit c68b6b1636a4b898773a05a5651839806dd3314c
Author: Laurent Kappel <laurentx.kappel@intel.com>
Date:   Wed Jul 6 20:22:10 2011 +0200

    Revert "mid_pmu:  s0i1 lpmp3 s0i3 s3 automatic selection"

    This reverts commit e899b6438b842047bef1bbdfdf9188bd09fab447.

    Change-Id: I7d51be7532056ff81d89ce0bc2af8480ffc812ea
Signed-off-by: Laurent Kappel <laurentx.kappel@intel.com>
commit e899b6438b842047bef1bbdfdf9188bd09fab447
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Fri Jun 17 10:13:31 2011 +0530

    mid_pmu:  s0i1 lpmp3 s0i3 s3 automatic selection

    BZ: 2457

    Automatically select s0i1, lpmp3, s0i3 or S3 does'nt happen currently,
    it is based on "echo s0i1/3 > /sys/modules/mid_pmu/parameters/s0ix"

    changed this to automatically select s0ix/lpmp3/s3 based on cpuidle
    latency and wakelocks.

    Change-Id: I5f3eef919aaac90d44e0d6ef0502c86f38da3ba2
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 54f32f953f356027c774c3cf46462992eadc73a7
Author: Pierre Tardy <pierre.tardy@intel.com>
Date:   Tue Jun 28 10:51:29 2011 +0200

    REVERTME:mid_pmu: deactivate S3 on PR2

    BZ: 4177

    quite a lot of patches are pending to enable S3 on PR2.
    Waiting for that we disable S3 by default on PR2

    to be reverted as soon as PR2 drivers are S3 capable

    Change-Id: I4de53e978fe850303617f8ace4ab9f073ce64d89
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit 5e73c57d106f8c766a5a06221e2817a7c9a5b393
Author: Rajesh Poornachandran <rajesh.poornachandran@intel.com>
Date:   Thu Jun 16 16:59:39 2011 -0700

    Revert "Revert "mid_pmu: Moving NC power management logic to OSPM""

    BZ: 1957

    Currently the PM for north complex was done independently by Gfx and ISP drivers.
    There was no synchronization between ISP and Gfx while sending the PM cmd.

    OSPM patch to provide an API, pmu_nc_set_power_state. This API will take care
    of talking to P-unit and getting the corresponding power islands to ON/OFF.
    Gfx and ISP drivers need to use this API in their drivers.

    Change-Id: I628dd8b03f5d09607e3c931e29943fcdb73613cb
Signed-off-by: Sujith Thomas <sujith.thomas@intel.com>
Signed-off-by: Rajesh Poornachandran <rajesh.poornachandran@intel.com>
commit 3b40317a7924fd4277b06992ea8755fe4d39094f
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Wed Jun 22 03:58:51 2011 +0530

    mid_pmu:  camera blocks s0ix when camera driver not present

    BZ: 3762

    With Camera driver removed we are not able to enter s0i1/3 S3, this is because
    the camera_off variable will be '0' even if the driver is not configured.

    conditionally set the variable '1' if camera device is not configured.

    Change-Id: Ia582399c8f3f493255e387a1a8ec0612fc6b50d0
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit dd6db50f98b6c0d368b9315124b65ec3b1aae2a1
Author: Erwan Mahe <erwanx.mahe@intel.com>
Date:   Thu Jun 23 18:29:21 2011 +0200

    Revert "Revert "mid_pmu: pick correct D0ix states in choose_state""

    BZ: 2102
    This reverts commit 41ff42ea4b74c89d7ed44b51e46ab7ae45b4b87c.

    Change-Id: I5aa5658cf28696a3c79a939ea8644a3b3c4d73f0

commit 41ff42ea4b74c89d7ed44b51e46ab7ae45b4b87c
Author: Marie-Celine Dubut <marie.celinex.dubut@intel.com>
Date:   Wed Jun 22 12:44:06 2011 +0200

    Revert "mid_pmu: pick correct D0ix states in choose_state"

    BZ 2102
    This reverts commit c60a2ae1f8b33abdabbff9c0de5a57ec6588df9d.

    Change-Id: I7e7aed5071adabc824c303628ba855031892e70a

commit c60a2ae1f8b33abdabbff9c0de5a57ec6588df9d
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Wed Jun 1 13:19:54 2011 +0530

    mid_pmu: pick correct D0ix states in choose_state

    BZ: 2102

    currently pmu_pci_choose_state return PCI_D3hot for all devices.
    For some of devices, it may not be possible to support PCI_D3hot state.
    eg., HSU needs to be put in D0i1, chaabi needs to be put in
    D0i2. It is not appropriate to always choose D0i3 for all devices

    We choose the correct state that is appropriate for each device
    for the  platform.

    This patch should be used only with a FW that is capable of supporting
    Chaabi D0i2.

    Change-Id: Ief7b64bc1ec37d4452ce3222454bdd6102d54c10
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit c4e73f14acb7ffd39a5d965fc23b1f01673363b8
Author: Hari Kanigeri <hari.k.kanigeri@intel.com>
Date:   Fri Jun 3 11:27:13 2011 -0600

    mid_pmu: fix checkpatch violations and some cleanup in exit paths

    BZ: 3066

    Fix the following:

    1. Checkpatch violations and cosmetic changes.
    2. fix correct cleanup in the exit paths of the function mid_pmu_probe

    Below log is related to checkpatch violations.

    1. Checkpatch violations
    =========================

    ERROR: space required before the open parenthesis '('
    + if(pdev->device == 0x81c || pdev->device == 0x81b)

    WARNING: line over 80 characters
    + for (i = 0; i < PMU_MAX_LSS_SHARE && intel_mid_pci_devices[index].dev_driver[i]; i++) {}

    ERROR: trailing statements should be on next line
    + for (i = 0; i < PMU_MAX_LSS_SHARE && intel_mid_pci_devices[index].dev_driver[i]; i++) {}

    WARNING: braces {} are not necessary for single statement blocks
    + for (i = 0; i < PMU_MAX_LSS_SHARE && intel_mid_pci_devices[index].dev_driver[i]; i++) {}

    WARNING: line over 80 characters
    +static pci_power_t  pmu_pci_get_weakest_state_for_lss(int lss_index, struct pci_dev *pdev, pci_power_t state)

    <cut here for brewity, please run checkpatch to see a complete list>

    total: 122 errors, 14 warnings, 2340 lines checked

    Change-Id: I1cdbd6a8b70d303611f0cd426ce63235010d0a07
Signed-off-by: Hari Kanigeri <hari.k.kanigeri@intel.com>
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 3df93729faabfefb971badf3b51f8e652da3f288
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Mon Jun 13 01:52:20 2011 +0530

    mid_pmu: abort s0i3 c6offload write access not set

    BZ: 3356

    OSPM waits (busy loop) for "write access" to C6 Offload area to be granted by
    SCU. After sending S0ix command to SCU via the PM_CMD reg.

    In current OSPM code we clear "enable MSR" bit if we find c6 offload "write
    access" bit is not set, c6 offload wont happen, a c6 will still be issued. This
    will trigger ack_c6, but in this case SCU will check offload signature to
    makesure if offload has occurred, since it won't find the offload signature it
    should abort s0ix entry.

    We have busy loop'd considerable amount of time waiting for c6 "write
    access" bit to be set we may have overshot the c6 latency, hence it is better
    to demote the c6 to a c4.

    Change-Id: Ie0027ff4efb8a418a7377ba30eb6a20d0a74087c
Signed-off-by: Illyas mansoor <illyas.mansoor@intel.com>
commit 4405feeea2eef28c40bda0e29817ee5ea223abe5
Author: MingmingX Zhang <mingmingx.zhang@intel.com>
Date:   Mon Jun 13 10:05:08 2011 +0800

    mid_pmu: delete repeated WARN in function mfld_s0i3_enter function

    BZ: 3346

    In function mfld_s0i3_enter, there are 2 WARN to check the same
    error status. Delete the needless WARN_ON.

    Change-Id: I8ba7cdab1eedf4bebdd3bfa599e3db795c56bdb0
Signed-off-by: MingmingX Zhang <mingmingx.zhang@intel.com>
commit d332d4a59c01e1774cd4fdc28d97fe4d865989ee
Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Date:   Thu Feb 16 22:36:00 2006 +0530

    pmu: adding atomisp as part of entry criterion for S0i3

    BZ: 1339

    In current build we dont look at the isp runtime status before
    we enter S0i3 state. In this patch we check for the camera status
    in addition to all other device states that we check today in S0i3.

    Change-Id: If3f024fa22d0d744160b961e637e2b1c25e5e1c9
Signed-off-by: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
commit 397b53b9679edd28669876fbbb528a4f3a0daa7f
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Wed Jun 1 23:33:33 2011 +0530

    mid_pmu: adding s0ix first_entry time and residency

    BZ: 2709

    Earlier mid_pmu stats did'nt have first entry time and
    residency percentages. Also clear option was not available.

    Implemented the same.

    In order to clear pmu_stats please use the following command
    mount -t debugfs none /d
    echo "clear" > /d/mid_pmu_states

    Change-Id: I4f24272a11b017d0decdb875880e0bc27eb6bf18
Signed-off-by: Sudharshan Ramachandra <sudarshan.n.ramachandra@intel.com>
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 71fd1c6ca28e68e7d3b8279a594bd51296e8be79
Author: Erwan Mahe <erwanx.mahe@intel.com>
Date:   Tue Jun 7 18:22:06 2011 +0200

    mid_pmu: s3/S0i3 automatic selection

    BZ: 2457

    Once the screen saver times out system would do few cycles of S0i3
    untill all the wakelocks are released then system will enter S3.This
    will avoid echo s0i1/3/x > /sys/module/mid_pmu/parameter/s0ix. This
    patch also increases the wakelock timeout for mid_pmu wakelock taken
    at end of s3 to 1 sec.

    Revert "Revert "mid_pmu: s3/S0i3 automatic selection""
    => Reinstate initial commit

    This reverts commit 21b136056afcfe32626a76ce2a3afe653a860c55.

    Change-Id: I19e2580bddc656c6ae809a6e1331bedc8579531b
Signed-off-by: Erwan Mahe <erwanx.mahe@intel.com>
commit 21b136056afcfe32626a76ce2a3afe653a860c55
Author: Erwan Mahe <erwanx.mahe@intel.com>
Date:   Mon Jun 6 21:01:26 2011 +0200

    Revert "mid_pmu: s3/S0i3 automatic selection"

    This reverts commit c589050d2d9a90b0a6a334f57c9008c630fb24f1.

    Change-Id: I7e09f304b2eadffb27e83ab810c8a2edb8044fbd
Signed-off-by: Erwan Mahe <erwanx.mahe@intel.com>
commit c589050d2d9a90b0a6a334f57c9008c630fb24f1
Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Date:   Mon Jun 6 10:38:11 2011 +0530

    mid_pmu: s3/S0i3 automatic selection

    BZ: 2457

    Once the screen saver times out system would do few cycles of S0i3
    untill all the wakelocks are released then system will enter S3.This
    will avoid echo s0i1/3/x > /sys/module/mid_pmu/parameter/s0ix. This
    patch also increases the wakelock timeout for mid_pmu wakelock taken
    at end of s3 to 1 sec.

    Change-Id: I5fc8e13ff0873d6dadd918ad8eabdaa0001e0950
Signed-off-by: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
commit 5e7997d26409e1e0f92c22c71b34fdcc4221e862
Author: Olivier Monnier <olivierx.monnier@intel.com>
Date:   Fri Jun 3 11:28:01 2011 +0200

    Revert "mid_pmu: s3/S0i3 automatic selection"

    This reverts commit f3ae17eb792bab5ab9bd5adcb0c5385cf9722c60.

    Change-Id: I2186fa8813677fcec6bf981c97aaf75dcff5ad5b
Signed-off-by: Olivier Monnier <olivierx.monnier@intel.com>
commit f3ae17eb792bab5ab9bd5adcb0c5385cf9722c60
Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Date:   Thu May 26 15:48:28 2011 +0530

    mid_pmu: s3/S0i3 automatic selection

    BZ 2457

    Once the screen saver times out system would do few cycles of S0i3
    untill all the wakelocks are released then system will enter S3.This
    will avoid echo s0i1/3/x > /sys/module/mid_pmu/parameter/s0ix

    Change-Id: I7cb82c3bb519d769f1905632e7066985676bffef
Signed-off-by: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
commit 53f93b022da79eadc22a2ac5c3adade98380cf84
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Mon May 16 00:26:13 2011 +0530

    mid_pmu: demote c6 to c4 while s0ix is in progress

    BZ: 2120

    While S0ix aborts SCU waits for Ack_C6 timeout, IA comes out of
    mwait and since the SCU is still waiting for Ack_C6 the S0ix
    ACK_C6 timeout error interrupt is not recieved, meanwhile a new
    C6 could be triggered from IA and this could trigger a Ack_C6
    SCU can mistake this Ack_C6 as a ack for the previously aborted
    S0ix and continue S0ix flow. This is not correct.

    We check if the system is in S0ix progress state by checking
    scu_ready_sem if its already acquired that means S0ix is in
    progress and the subsequent C6 should be demoted to C4's

    Change-Id: I1b60824b28f9b6579dd551faa8fe485d2074b712
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 654da78fa8f45bcf4d64d9c0ea115d589f4374c4
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Sun May 15 23:44:23 2011 +0530

    mid_pmu: c6 offload msr bit cleanup

    BZ: 2116

    We were using magic number for wmsr(0x120) for setting/clearing
    offload bit.

    Fixed this by creating #defines

    Change-Id: I5fe4ec0568e48d12fe30074f8eebbc2a6411b4d8
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 516322a23d3e7b1606d83753b121de66e79daa20
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Mon May 16 11:09:48 2011 +0530

    mid_pmu: releasing scu_ready_sem in mfld_s3_enter

    BZ: 2179

    In function mfld_s3_enter we are checking if scu_ready_sem is already acquired.
    The intention is to make sure we enter S3 only if scu_ready_sem is free, but if
    its acquired instead of returning error we release the scu_ready_sem which is
    incorrect.

    removed the release of scu_ready_sem, if already acquired.

    Change-Id: I4ce888ae1702e4cdcb8dd584a87d0d10fb6c84db
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 8118849a0329d0e73761562443ae25c8b1cd3bf9
Author: Fengwei Yin <fengwei.yin@intel.com>
Date:   Sun May 22 11:57:12 2011 +0800

    Revert "mid_pmu:  Moving NC power management logic to OSPM"

    BZ: 2470

    This reverts commit 594c7f0327dd02985fbcde1e0bd83c98e684097f.

commit 594c7f0327dd02985fbcde1e0bd83c98e684097f
Author: Sujith Thomas <sthoma3@umg-lab1.iind.intel.com>
Date:   Thu May 12 10:51:38 2011 +0530

    mid_pmu:  Moving NC power management logic to OSPM

    BZ: 1957

    Currently the PM for north complex was done independently by Gfx and ISP drivers.
    There was no synchronization between ISP and Gfx while sending the PM cmd.

    OSPM patch to provide an API, pmu_nc_set_power_state.
    This API will take care of talking to P-unit and getting the corresponding power
    islands to ON/OFF. Gfx and ISP drivers need to use this API in their drivers.

    Change-Id: I302c9a7a6bb10ae572d340affb2502443d6178fc
Signed-off-by: Sujith Thomas <sujith.thomas@intel.com>
commit abfc3b5f6d6df8a5d5985c73f99a8dc72cd18853
Author: Pierre Tardy <pierre.tardy@intel.com>
Date:   Mon May 16 16:49:50 2011 +0200

    Revert "Revert "Revert "REVERTME:mid_pmu: disable s0i3 by default for HSI IFWIs"""

    BZ: 2177

    This reverts commit eefaf8d45186b6ad465e67fb31cb4293c0a7a5fa.
    and re-enable s0i3 by default on HSI based IFWI

    Change-Id: Ib0289dcb5ef60b7750267aebb53f1bf38ab4ee3d
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit 50154e4edbe93d9d0d72e3b318cbe83b7e21831a
Author: Pierre Tardy <pierre.tardy@intel.com>
Date:   Mon May 16 13:32:44 2011 +0200

    mid_pmu: add more trace_printk

    BZ: 2139

    in order to see wake sources, and errors from s0i3 and s3

    Change-Id: If5d6ea297dde5d47ed79583c188d8457bb978076
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit 47f329c5a5a244636b7805b991d6ba5e1e8c7bd7
Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Date:   Mon May 16 09:29:28 2011 +0530

    pmu: fixing the aont mask fix for S3

    BZ 1756

    aont mask set for s3 was not proper which was not allowing to stay
    in S3. this patch fixes the mask.

    Change-Id: I8dc5a9372054d83a17184d289ea9ae7a621388bc
Signed-off-by: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
commit c52543cfc2fbade1fac03796c2b0615141e46cea
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Tue May 10 12:00:08 2011 +0530

    mid_pmu: fix incorrect residency for abort s0ix

    BZ: 1885

    In case of S0ix/Lpmp3 aborts for any reason we are still counting the
    residency times, in case of failure we should not account this time
    into actual s0ix/lpmp3 residency times.

    corrected this by only accumulating times for successful s0ix exits.

    Change-Id: Icdde3491fe786aec4c65795805bfec43d9c107f4
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit e0c7284d712fdc0715726ad1dc7df4ac6e9b18c9
Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Date:   Tue May 10 16:45:05 2011 +0530

    pmu: adding ignore wkc lss code needed for long s3

    BZ 1756

    This patch we add ignore wkc lss to add all those devices
    which needs to be ignored as wake sources during entry to s3
    state.

    Change-Id: I73a46d23f3bee2ef8d979162a4d1b6a020fe4bae
Signed-off-by: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
commit cba613f2bde66236706b9c0ba7f1fe6b5ff14ad7
Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Date:   Tue May 10 16:22:10 2011 +0530

    pmu:  renaming lss after new wkc implementation in scu

    BZ 1756

    This patch we are re assigning some of the lss to new lss
    based on the WKC implementation in scu.

    Change-Id: Ic7a9adf37194182b424dcdcc0d61cb09bba4a7a5
Signed-off-by: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
commit 43ea3ecf3ba252e6859f33d38d9c2bcd0e2c6f06
Author: Jean-Pierre Stierlin <jean-pierrex.stierlin@intel.com>
Date:   Fri Apr 29 11:01:35 2011 +0200

    mid_pmu: Disable S0i3 when HSI is not in D0i3

    BZ: 1226

    The platform should not enter S0i3 state when the HSI is still active

    Add PMU_HSI_LSS_03 to the s0ix_lss and lpmp3_lss tables

    Change-Id: I8ccddaa12d04b0af813820dad19fa96de7fe5e53
Signed-off-by: Jean-Pierre Stierlin <jean-pierrex.stierlin@intel.com>
commit eefaf8d45186b6ad465e67fb31cb4293c0a7a5fa
Author: Erwan Mahe <erwanx.mahe@intel.com>
Date:   Thu May 5 12:12:04 2011 +0200

    Revert "Revert "REVERTME:mid_pmu: disable s0i3 by default for HSI IFWIs""

    This reverts commit a561e8620d08973fd5f9eea6a91488977fc3b539.

commit a561e8620d08973fd5f9eea6a91488977fc3b539
Author: Erwan Mahe <erwanx.mahe@intel.com>
Date:   Wed May 4 11:24:08 2011 +0200

    Revert "REVERTME:mid_pmu: disable s0i3 by default for HSI IFWIs"

    BZ: 1646

    This reverts commit 18a41ce2756933e6200e05283e3ea172c7aa320d.

commit d7a22f9c70e926ebe0bdb78a79153a5b8050a864
Author: Christophe Fiat <christophex.fiat@intel.com>
Date:   Thu Apr 28 14:55:45 2011 +0200

    mid_pmu: Add HSU in PMU Devices list blocking S0i3

    BZ: 932 - Taskforce: Mtfld-Android No Driver with D3/D0i3 for UART0

    Now that HSU is RT PM compliant, HSU can block s0i3 from happening when
    Bt active.

    put HSU LSS in the list of s0i3 and lpmp3 required lss.

    Change-Id: I0a4be909433b470b0b1dfa021fcc997d200cd835
Signed-off-by: Christophe Fiat <christophex.fiat@intel.com>
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit 2f408985f4aeeb807ff095f0e86669fc6a6b14aa
Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Date:   Wed Apr 20 23:30:00 2011 +0530

    pmu: fixing wake sub system configuration on s3 exit

    wake sub system configuration was being set to D0 state
    on exit from S3. this is being reverted to state of devices
    before entering S3 to follow the same path as S0i3 flow.

    Change-Id: Ia6276b151b303912469511a5d1378e39b63d43b4
Signed-off-by: Vishwesh M. Rudramuni <vishwesh.m.rudramuni@intel.com>
commit 96045de5b1e64969513c05f103d2db0d63c43b83
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Fri Apr 15 22:58:54 2011 +0530

    mid_pmu: change function name read_all_lss_states

    read_all_lss_states reads the current runtime_pm status of
    each SCU mapped pci driver and updates the current_status
    on to the corresponding LSS.

    The name leads to confusion since it implies reading of LSS
    regs which is not the case.

    Hence changing the name to update_all_lss_states

    Change-Id: I4f87b6e62f619db6ae07538a5a88ca70bf47c6aa
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 5d31bc498ade6f0ae46dad8298005724a0927ae2
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Thu Apr 14 00:05:28 2011 +0530

    [HSD:3743029] mid_pmu: hack: fix HSI lss not found in pci_table issue

    HSI drivers D0ix requested were not propogated to SCU
    on examination it was found that IA fw does not have
    the required driver to LSS mapping, hence the requests
    were skipped in PMU driver.

    Added a hack to fix this in PMU driver, revert this
    once IA FW fixes this issue.

    Change-Id: Iff5be2fa6a46dbd94dee787011b79ec347dcbaf3
Signed-off-by: Illyas mansoor <illyas.mansoor@intel.com>
commit 18a41ce2756933e6200e05283e3ea172c7aa320d
Author: Pierre Tardy <pierre.tardy@intel.com>
Date:   Thu Apr 21 17:51:38 2011 +0200

    REVERTME:mid_pmu: disable s0i3 by default for HSI IFWIs

    HSI runtime_pm is not yet ready.
    device will go s0i3 even if there is HSI activity
    This causing lot of fabric errors.

    We disable s0i3 by default if we find HSI device (0x833)
    in the pci table.

    to be reverted as soon as HSI runtime_pm is ready.

    Change-Id: I9877c48ea78f0ea80528869ce978fb575385e621
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit 16a0bab0a9b9ae6cbcc4ec826315ef1d87cdc25d
Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Date:   Thu Apr 7 15:01:02 2011 +0530

    mid_pmu: enable full suspend to RAM (S3) (path#1)

    Enabling full suspend path in the pmu driver.

    use IRQF_NO_SUSPEND to keep irq alive during s3 path
    while we are at it removed unused irq parameter

    Changed the s0i3 wake lock in a timed wake lock for wakeup.
    This wakelocks stays locked until someone writes
    echo "s3s3" >/sys/modules/mid_pmu/parameters/s0ix
    So that system will still default to s0i3 until s3 is
    ready for everybody

    this patch also updates the s3 statistics.

    Change-Id: Ic496f3d96e70e9d37370413d9019e9a451d1b9c6
Signed-off-by: Vishwesh M. Rudramuni <vishwesh.m.rudramuni@intel.com>
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit 1ec1299dd7c6854d9902640592003ec508f53bfc
Author: Pierre Tardy <pierre.tardy@intel.com>
Date:   Tue Apr 12 10:52:43 2011 +0200

    mid_pmu: add more powerfull s0ix statistics

    Current statistics only do s0ix entry count

    This adds timing statistics.
    This is mainly intended for automated tests, in order to make
    automated test have time spend in s0ix, and be able to do regressions
    over daily builds.

    There is also placeholder for s3 statistics that is not yet implemented.

    Change-Id: Ib2eed8e87f66c4efe8ca52767a76021576040333
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit bdb518ae8e9af2a27857cad22b6650b0bd20d9c8
Author: Pierre Tardy <pierre.tardy@intel.com>
Date:   Fri Apr 8 17:26:27 2011 +0200

    mid_pmu: suspend devices not in pci-table

    We suspend devices that are in the target config, and that are
    not in the pci table

    Some devices are indeed not advertised in pci
    table for certain firmwares. This is the case for HSI firmwares,
    SPI3 device is not advertised, and would then prevent s0i3.

    Change-Id: I319fcb8fe6ee3fa4a8272d2f2389aa4804a99699
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit 09bfcd007dd40dcead807525203baffe1371bb41
Author: Pierre Tardy <pierre.tardy@intel.com>
Date:   Wed Mar 2 18:35:03 2011 +0100

    mid_pmu/android: take a wakelock if s0i3 is not possible

    Change-Id: Icc3b1509873d76ad8afb63f04a8e52025c6c5c10

commit 3452915bdf4a946d0b969e646f00f2205a3b71b2
Author: Liu, ShuoX <shuox.liu@intel.com>
Date:   Tue Mar 15 11:37:26 2011 +0800

    modify s0ix default val for s0i3 debug

    Change-Id: I9ca88d1420768702b6550039d8313b61dafc5788

commit cfda66e44bc142f836c2e550211997759aa0d8e2
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Wed Mar 30 16:23:28 2011 +0530

    mid_pmu: cleanup added explicit LSS ids for s0ix entry

    Currently the LSS that need to be checked for S0ix/LPMP3
    entry is hardcoded making it difficult to read the sub
    system values.

    Added #defines for all the sub system LSS id's, using this
    defined the target config values to check against the SSS
    register values, that tells which subsystems need to in D0i3
    to issue S0i1/3 or LPMP3 commands.

    Also added a way to keep always ON LSS to be in D0i0.

    Added check for Keyboard, UART0/1/2 LSS in S0i1/lpmp3/S0i3 SSS
    config registers.

    Removed LSS 40, 41 from ignore_lss list.

    Change-Id: Ifd28b58f11c66232dc2993b3946c672bbe9931a3
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 4e082fe683a0d9b22b9ef0c6675705ba75a7dceb
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Wed Mar 30 16:22:21 2011 +0530

    mid_pmu: update current driver status during first interactive command

    At the time of pmu driver initialization we set all the LSS to be in D0i0
    state, this causes some of the drivers to be in forced D0i0 state in
    SCU, whereas there actual state could be D0i3, hence there could be
    potential power impact until next cycle of D0i0->D0i3 from the driver.

    To avoid this we acquire the lock, allow drivers to get blocked in
    pmu_pci_set_power_state(), then reflect the current driver state in
    SCU for all SCU managed drivers via setting the appropriate LSS values
    by issuing interactive command.

    This will ensure we reflect correct driver state during pmu driver
    initialization.

    Change-Id: I01caf76d6a32b38a556e7309457ab0e829b5dca4
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit a1e3d9fd3db6a395f9f45a233fc4a757e610700c
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Wed Mar 30 16:21:47 2011 +0530

    mid_pmu: added some counters

    Added counter to keep track of number of S0ix
    and error counts.

    Change-Id: I20d55781038a4732e50f98e0163eada394450702
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 37c03045efcd790183acdbd3cb94db59566bdb6d
Author: Pierre Tardy <pierre.tardy@intel.com>
Date:   Mon Mar 21 14:47:53 2011 +0100

    mid_pmu: allow several devices to be on the same lss

    HSU devices are in HW in the same lss.
    There is a workaround in IAFW, that will not advertise the correct lss
    for HSU0 and HSU1. This does not solve anything, as HSU0 driver will start
    to make fabric errors as soon as HSU2 is suspended.

    We generically solve the issue by maintaining a table of devices
    present in the same lss. For such very short list, we do not take the
    overhead of using linked list. but rather use a 4 slots table for each lss.

    This patch contains ss hardcode for HSU0 and HSU1. To be removed when
    IAFW remove the bad workaround.

    Change-Id: I11240c225edc92984cca7e5e176bbebab6df2d5d
Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
commit aa7111008d6f823807dd117b4384ba66415eff40
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Fri Mar 18 12:10:59 2011 +0530

    mid_pmu: hack: replace lss 16 with 9

    IA FW is reporting Audio DMA subsytem id as 16, whereas it should report 9
    fixed this in the code, need to remove this once IA FW fixes this.

    Change-Id: I22b6bc9e425b43dae00233fd43b7bcf756c62c8b
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit feef0283493d0309bcf6c2021807e0150dc079d9
Author: Yanmin Zhang <yanmin.zhang@intel.com>
Date:   Tue Mar 8 16:18:49 2011 +0800

    Add a new interface under debugfs, pmu_sss_states, to output pmu sss registers. We could use it to check every subsystem state. mid_pmu_states saves device (subsystem) states intercepted by pci_set_power_state, while pmu_sss_states gets the states from pmu hardware registers.

    Change-Id: If0d9cb3b4286d1ea89064d9b94df29ed1ae2f7fa

commit fb7d10c9d7ba74245c1e14b739d207c920dab637
Author: mgross <mark.gross@intel.com>
Date:   Thu Mar 3 10:34:48 2011 +0800

    revert me asap block suspend to ram with wakelock

    Change-Id: I89812515397690f6dc99446a0f04c15bf2ac567e
Signed-off-by: mgross <mark.gross@intel.com>
commit 0d15acc903de50173abffbc8e7830749e46c4ef6
Author: mgross <mark.gross@intel.com>
Date:   Mon Feb 28 15:18:46 2011 -0800

    Stub to enable pm_suspend and android early suspend calls to start
    working.
    With this change (and the propper config file) you can enter early
    suspend and start enabling android power managment.

    Change-Id: I80c10a33f8fc80221502761175ed372a379ae779

commit 0c2cb76662387a575c6f41b9f91a85cd76064f55
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Sat Feb 5 14:52:41 2011 +0100

    mid_pmu: cleanup: fix sparse check warnings

    moved all #defines to mid_pmu.h

    fixed warnings after installing sparse tool

    Fixed the following warnings from sparse.
    with make C=2 >& sparse.log command.

    arch/x86/kernel/mid_pmu.c:46:3: warning: symbol 'pci_dev_index' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:1175:70: warning: incorrect type in argument 2 (different address spaces)
    arch/x86/kernel/mid_pmu.c:1175:70:    expected void volatile [noderef] <asn:2>*addr
    arch/x86/kernel/mid_pmu.c:1175:70:    got unsigned int *
    arch/x86/kernel/mid_pmu.c:1180:76: warning: incorrect type in argument 2 (different address spaces)
    arch/x86/kernel/mid_pmu.c:1180:76:    expected void volatile [noderef] <asn:2>*addr
    arch/x86/kernel/mid_pmu.c:1180:76:    got unsigned int *
    arch/x86/kernel/mid_pmu.c:322:5: warning: symbol 'get_target_platform_state' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:691:6: warning: symbol 'mfld_s0i1_enter' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:717:5: warning: symbol 'mfld_s0i3_enter' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:844:5: warning: symbol 'is_msi_enabled' was not declared. Should it be static?
    arch/x86/kernel/mid_pmu.c:1191:59:    got unsigned int *
    arch/x86/kernel/mid_pmu.c:1502:13: warning: incorrect type in assignment (different address spaces)
    arch/x86/kernel/mid_pmu.c:1502:13:    expected struct mrst_pmu_reg *pmu
    arch/x86/kernel/mid_pmu.c:1502:13:    got void [noderef] <asn:2>*
    arch/x86/kernel/mid_pmu.c:1567:27:    expected void [noderef] <asn:2>*<noident>
    arch/x86/kernel/mid_pmu.c:1567:27:    got struct mrst_pmu_reg *static [toplevel] *<noident>
    ...
    and similar warnings.

Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit cf3aa8b17adab4006d49f01aa66bd184f9598eb8
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Fri Feb 4 01:12:34 2011 +0100

    mid_pmu: wait for interactive command complete in pmu_init

    At the time of pmu_init we set all the LSS's into D0i0 mode
    by sending an interactive command. This command was not
    waiting for interactive complete semaphore that is released
    from pmu interrupt handler.

    Also interrupt handler was registered after pmu_init, which
    should be the otherway.

    This will ensure the interrupt handler is registered before
    calling pmu_init and also after interactive command is sent
    in pmu_init() we wait for interactive command complete semaphore.

Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 9d8e0e6094afb88033a3e85cb92e905a8f87cb60
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Fri Feb 4 00:14:00 2011 +0100

    mid_pmu: wake source counter statistics

    We need a way to tell which logical subsystem woke up from S0i3
    Wake status register provides this information.

    We provide a way to capture how many times each LSS
    woke up the system from S0i3, by reading Wake status
    register and incrementing a counter for each LSS.

    This is printed as part of /sys/kernel/debug/mid_pmu_states

Signed-off-by: Pierre Tardy <pierre.tardy@intel.com>
Acked-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 54c94eb1ef0d810458a6d4b3b558a356fce54493
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Mon Jan 31 04:22:59 2011 +0100

    mid_pmu: clear cpu offload cntrl reg in error scenarios

    For S0i3 case we need to set the offload cntrl reg to copy the contents
    of IA C6 SRAM to SC always on SRAM. In case of error if S0i3 fails for
    some reason, or on S0ix successful completion this bit needs to be cleared.

    We ensure it's set during S0i3 entry and is cleared in interrupt handler.

Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 98cb52baaeb1c7d058ec3a12db943f7c60918605
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Fri Jan 28 05:37:37 2011 +0100

    mid_pmu: bug fix: change lpmp3 command value

    For LPMP3, trigger must be based on "core must be idle" (cmbi)
    and not just C6. Hence LPMP3 was breaking.

    After setting cmbi bit as the trigger, LPMP3 usecase works.

Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 4908f77de188bae7b61b39113747eef2f2bd9b83
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Fri Jan 28 05:36:18 2011 +0100

    mid_pmu: disable scu semaphore watchdog by default

    SCU semaphore watchdog is getting triggered with 500ms
    timeout value, because of which we see synchronization
    issues, providing an option to selectively enabling it.

    By default disabled.

Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 8e35a33d7754c82fb89e48d25ff3988e4cc03977
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Thu Jan 27 03:38:19 2011 +0100

    mid_pmu: bug fix: fixed device lss reporting lss0 wrongly

    The MSB of "logical subsystem" byte tells if the device is managed
    by SCU, the MSB was not being checked before getting the LSS
    values.

    This was causing drivers that were not part of SCU getting
    LSS value '0'

    By checking the MSB of "logical sub sytemid" byte from PCI config
    we will get to know if this driver actually belongs to SCU, and
    hence avoid multiple drivers with LSS value 0.

Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
commit 33d19d30ff22685f18f184183b1e675d8fe94fea
Author: Illyas Mansoor <illyas.mansoor@intel.com>
Date:   Mon Jan 17 08:22:32 2011 +0100

    mid_pmu: driver that controls pmus of intel_mid platforms

    Mobile Internet Devices based on the Intel "Medfield" platform have two
    Platform Management Units (PMU). The first PMU (pmu1) comprises of the
    Silverthorne CPU, Graphics, Video encode/decode and Display engines,
    The second PMU (pmu2) is the IO hub.

    The Platform Management Unit (PMU) driver is a Medfield-specific power
    management driver. Intel's  MID provides fine tuned knobs for
    platform level. Intel's Medfield MID provides control for platform
    level power management and the OS Power Management solution guides the
    power states that the subsystems and CPU needs to be in depending on the
    current usage and power policy set by the user.

    The purpose of the Medfield Power Management architecture is to turn
    off subsystems without affecting the end user functionality/usability of
    the system.

    The Power Management scheme uses the concept of operating modes, which
    define the configuration of all the subsystems under each of the usage model
    that the user might put the system in. Based on current platform usage,
    OSPM decides the target power states for the sub systems, and the PMU
    driver implements the OS-HW interface.

    "Intel" Medfield PMU driver interfaces with two power management units
    (PMU) pmu1 & pmu2. On receiving commands PMU driver interfaces
    with the pmu1 & pmu2 via a well defined register interface to drive the
    required power  management flows.

    This patch contains:
    - key definitions for the PMU driver.
    - core logic of the PMU driver as it interfaces with pmu1 & pmu2 for
    different platform power management flows.

Signed-off-by: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>
Signed-off-by: Rajeev D Muralidhar <rajeev.d.muralidhar@intel.com>
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
arch/x86/platform/mfld/pmu.c [new file with mode: 0755]

diff --git a/arch/x86/platform/mfld/pmu.c b/arch/x86/platform/mfld/pmu.c
new file mode 100755 (executable)
index 0000000..0e84973
--- /dev/null
@@ -0,0 +1,2646 @@
+/*
+ * mid_pmu.c - This driver provides interface to configure the 2 pmu's
+ * Copyright (c) 2010, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/jhash.h>
+#include <linux/intel_mid_pm.h>
+#include <linux/suspend.h>
+
+#include "pmu.h"
+
+/* These are the set of LSS's that are checked
+ * to be D0i3 before entering S0i1/3
+ */
+static u8 s0ix_lss[] = {
+       PMU_SDIO0_LSS_00,
+       PMU_EMMC0_LSS_01,
+
+#ifdef CONFIG_HSI_ARASAN
+       PMU_HSI_LSS_03,
+#endif
+
+#ifdef CONFIG_DX_SEP
+       PMU_SECURITY_LSS_04,
+#endif
+       PMU_EMMC1_LSS_05,
+#ifdef CONFIG_USB_PENWELL_OTG
+       PMU_USB_OTG_LSS_06,
+#endif
+
+#ifdef TELEPHONY_ENABLE_S0IX
+       PMU_AUDIO_ENGINE_LSS_08,
+       PMU_AUDIO_DMA_LSS_09,
+       PMU_AUDIO_SSP0_LSS_51,
+       PMU_AUDIO_SSP1_LSS_52,
+       PMU_SPI3_LSS_36,
+#endif
+       PMU_SDIO2_LSS_14,
+#ifdef DWSPI_ENABLE_S0IX
+       PMU_SPI1_LSS_18,
+#endif
+
+#ifdef WLAN_ENABLE_S0IX
+       PMU_SDIO1_LSS_30,
+#endif
+
+#ifdef I2C_ENABLE_S0IX
+       PMU_I2C0_LSS_20,
+       PMU_I2C1_LSS_21,
+       PMU_I2C2_LSS_27,
+       PMU_I2C3_LSS_33,
+       PMU_I2C4_LSS_34,
+       PMU_I2C5_LSS_35,
+#endif
+
+       PMU_UART2_LSS_41,
+
+};
+static u32 TARGET_CFG[4];
+static u32 s0ix_target[4];
+
+/* These are the set of LSS's that are checked
+ * to be D0i3 before entering LPMP3 mode.
+ */
+static u8 lpmp3_lss[] = {
+       PMU_SDIO0_LSS_00,
+       PMU_EMMC0_LSS_01,
+
+#ifdef CONFIG_HSI_ARASAN
+       PMU_HSI_LSS_03,
+#endif
+
+#ifdef CONFIG_DX_SEP
+       PMU_SECURITY_LSS_04,
+#endif
+       PMU_EMMC1_LSS_05,
+#ifdef CONFIG_USB_PENWELL_OTG
+       PMU_USB_OTG_LSS_06,
+#endif
+       PMU_SDIO2_LSS_14,
+#ifdef TELEPHONY_ENABLE_S0IX
+       PMU_SPI3_LSS_36,
+#endif
+
+#ifdef DWSPI_ENABLE_S0IX
+       PMU_SPI1_LSS_18,
+#endif
+
+#ifdef WLAN_ENABLE_S0IX
+       PMU_SDIO1_LSS_30,
+#endif
+
+#ifdef I2C_ENABLE_S0IX
+       PMU_I2C0_LSS_20,
+       PMU_I2C1_LSS_21,
+       PMU_I2C2_LSS_27,
+       PMU_I2C3_LSS_33,
+       PMU_I2C4_LSS_34,
+       PMU_I2C5_LSS_35,
+#endif
+
+       PMU_UART2_LSS_41,
+
+};
+static u32 LPMP3_CFG[4];
+static u32 lpmp3_target[4];
+
+/* These are the LSS that need to be kept
+ * always ON D0i0, you can add any LSS id
+ * that you need to keep always on here.
+ */
+static u8 lss_to_ignore[] = {
+       PMU_USB_HSIC_LSS_07,
+       PMU_SRAM_LSS_10,
+       PMU_SRAM_LSS_11,
+       PMU_SRAM_LSS_12,
+       PMU_SRAM_LSS_13,
+       PMU_PTI_DAFCA_LSS_15,
+       PMU_SC_DMA_LSS_16,
+       PMU_SPIO_LSS_17,
+       PMU_MAIN_FABRIC_LSS_22,
+       PMU_SEC_FABRIC_LSS_23,
+       PMU_SC_FABRIC_LSS_24,
+       PMU_SCU_ROM_LSS_26,
+       PMU_SSC_LSS_28,
+       PMU_SECURITY_LSS_29,
+       PMU_SCU_RAM0_LSS_31,
+       PMU_SCU_RAM1_LSS_32,
+       PMU_GPIO1_LSS_37,
+       PMU_PWR_BUTTON_LSS_38,
+       PMU_GPIO0_LSS_39,
+       PMU_ADC_LSS_42,
+       PMU_CHARGER_LSS_43,
+       PMU_SEC_TAPC_LSS_44,
+       PMU_RTC_LSS_45,
+       PMU_GPI_LSS_46,
+       PMU_HDMI_VREG_LSS_47,
+       PMU_IOSF_OCP_BRG_LSS_53,
+       PMU_SVID_LSS_55,
+       PMU_SOC_FUSE_LSS_56,
+       PMU_RSVD3_LSS_57,
+       PMU_RSVD4_LSS_58,
+       PMU_RSVD5_LSS_59,
+       PMU_RSVD6_LSS_60,
+       PMU_RSVD7_LSS_61,
+       PMU_RSVD8_LSS_62,
+       PMU_RSVD9_LSS_63
+};
+static u32 IGNORE_CFG[4];
+
+/* These are the LSS that need
+ * to be configured in SSS registers
+ * before issuing S0i1 command
+ */
+static u8 s0i1_sss_lss[] = {
+       PMU_SDIO0_LSS_00,
+       PMU_EMMC0_LSS_01,
+       PMU_AONT_LSS_02,
+       PMU_HSI_LSS_03,
+       PMU_SECURITY_LSS_04,
+       PMU_EMMC1_LSS_05,
+       PMU_USB_OTG_LSS_06,
+       PMU_USB_HSIC_LSS_07,
+       PMU_AUDIO_ENGINE_LSS_08,
+       PMU_AUDIO_DMA_LSS_09,
+       PMU_SRAM_LSS_12,
+       PMU_SRAM_LSS_13,
+       PMU_SDIO2_LSS_14,
+       PMU_SPI1_LSS_18,
+       PMU_SPI2_LSS_19,
+       PMU_I2C0_LSS_20,
+       PMU_I2C1_LSS_21,
+       PMU_AUDIO_RAM_LSS_25,
+       PMU_I2C2_LSS_27,
+       PMU_SDIO1_LSS_30,
+       PMU_I2C3_LSS_33,
+       PMU_I2C4_LSS_34,
+       PMU_I2C5_LSS_35,
+       PMU_SPI3_LSS_36,
+       PMU_GPIO1_LSS_37,
+       PMU_PWR_BUTTON_LSS_38,
+       PMU_KEYBRD_LSS_40,
+       PMU_UART2_LSS_41,
+       PMU_AUDIO_SSP2_LSS_48,
+       PMU_AUDIO_SLIM1_LSS_49,
+       PMU_RESET_LSS_50,
+       PMU_AUDIO_SSP0_LSS_51,
+       PMU_AUDIO_SSP1_LSS_52,
+       PMU_GP_DMA_LSS_54
+};
+static u32 s0i1_sss[4];
+
+/* These are the LSS that need
+ * to be configured in SSS registers
+ * before issuing S0i3 command
+ */
+static u8 s0i3_sss_lss[] = {
+       PMU_SDIO0_LSS_00,
+       PMU_EMMC0_LSS_01,
+       PMU_AONT_LSS_02,
+       PMU_HSI_LSS_03,
+       PMU_SECURITY_LSS_04,
+       PMU_EMMC1_LSS_05,
+       PMU_USB_OTG_LSS_06,
+       PMU_USB_HSIC_LSS_07,
+       PMU_AUDIO_ENGINE_LSS_08,
+       PMU_AUDIO_DMA_LSS_09,
+       PMU_SRAM_LSS_12,
+       PMU_SRAM_LSS_13,
+       PMU_SDIO2_LSS_14,
+       PMU_SPI1_LSS_18,
+       PMU_SPI2_LSS_19,
+       PMU_I2C0_LSS_20,
+       PMU_I2C1_LSS_21,
+       PMU_AUDIO_RAM_LSS_25,
+       PMU_I2C2_LSS_27,
+       PMU_SDIO1_LSS_30,
+       PMU_I2C3_LSS_33,
+       PMU_I2C4_LSS_34,
+       PMU_I2C5_LSS_35,
+       PMU_SPI3_LSS_36,
+       PMU_GPIO1_LSS_37,
+       PMU_PWR_BUTTON_LSS_38,
+       PMU_KEYBRD_LSS_40,
+       PMU_UART2_LSS_41,
+       PMU_AUDIO_SSP2_LSS_48,
+       PMU_AUDIO_SLIM1_LSS_49,
+       PMU_RESET_LSS_50,
+       PMU_AUDIO_SSP0_LSS_51,
+       PMU_AUDIO_SSP1_LSS_52,
+       PMU_GP_DMA_LSS_54
+};
+static u32 s0i3_sss[4];
+
+/* These are the LSS that need
+ * to be configured in SSSarch/x86/kernel/mid_pmu.c registers
+ * before issuing LPMP3 command
+ */
+static u8 lpmp3_sss_lss[] = {
+       PMU_SDIO0_LSS_00,
+       PMU_EMMC0_LSS_01,
+       PMU_AONT_LSS_02,
+       PMU_HSI_LSS_03,
+       PMU_SECURITY_LSS_04,
+       PMU_EMMC1_LSS_05,
+       PMU_USB_OTG_LSS_06,
+       PMU_USB_HSIC_LSS_07,
+       PMU_SDIO2_LSS_14,
+       PMU_SPI1_LSS_18,
+       PMU_SPI2_LSS_19,
+       PMU_I2C0_LSS_20,
+       PMU_I2C1_LSS_21,
+       PMU_I2C2_LSS_27,
+       PMU_SDIO1_LSS_30,
+       PMU_I2C3_LSS_33,
+       PMU_I2C4_LSS_34,
+       PMU_I2C5_LSS_35,
+       PMU_SPI3_LSS_36,
+       PMU_GPIO1_LSS_37,
+       PMU_PWR_BUTTON_LSS_38,
+       PMU_KEYBRD_LSS_40,
+       PMU_UART2_LSS_41,
+       PMU_AUDIO_SLIM1_LSS_49,
+       PMU_RESET_LSS_50,
+       PMU_AUDIO_SSP0_LSS_51,
+       PMU_AUDIO_SSP1_LSS_52,
+       PMU_GP_DMA_LSS_54
+};
+static u32 lpmp3_sss[4];
+
+/* These are the LSS that need to be ignored
+ * as wake sources when in S3
+ */
+static u8 ignore_lss_in_wkc[] = {
+       PMU_AONT_LSS_02,
+       PMU_ADC_LSS_42
+};
+static u32 IGNORE_WKC_S3_CFG[2];
+
+static struct pci_dev_index    pci_dev_hash[MID_PCI_INDEX_HASH_SIZE];
+
+
+static int dev_init_state;
+static struct intel_mid_base_addr base_addr;
+static struct mrst_pmu_reg     __iomem *pmu_reg;
+
+#ifdef CONFIG_HAS_WAKELOCK
+static struct wake_lock pmu_wake_lock;
+#endif
+
+static u32 apm_base;
+static u32 ospm_base;
+static spinlock_t nc_ready_lock;
+
+/*
+ * Locking strategy::
+ *
+ * Two semaphores are used to lock the global variables used in
+ * the code. The entry points in pmu driver are pmu_pci_set_power_state()
+ * and PMU interrupt handler contexts, so here is the flow of how
+ * the semaphores are used.
+ *
+ * In D0ix command case::
+ * set_power_state process context:
+ * set_power_state()->acquire_scu_ready_sem()->issue_interactive_cmd->
+ * wait_for_interactive_complete_sem->release scu_ready sem
+ *
+ * PMU Interrupt context:
+ * pmu_interrupt_handler()->release interactive_complete_sem->return
+ *
+ * In Idle handler case::
+ * Idle context:
+ * idle_handler()->try_acquire_scu_ready_sem->if acquired->
+ * issue s0ix command->return
+ *
+ * PMU Interrupt context:
+ * pmu_Interrupt_handler()->release scu_ready_sem->return
+ *
+ */
+static struct semaphore scu_ready_sem =
+               __SEMAPHORE_INITIALIZER(scu_ready_sem, 1);
+static struct semaphore set_mode_complete_sem =
+               __SEMAPHORE_INITIALIZER(set_mode_complete_sem, 0);
+
+static int scu_comms_okay = 1;
+
+#ifdef ENABLE_SCU_WATCHDOG
+/* scu semaphore watchdog timer */
+static void scu_watchdog_func(unsigned long arg)
+{
+       struct semaphore* sem = (struct semaphore *) arg;
+
+       WARN(1, "SCU watchdog triggered, dead lock occured,"
+                                        "diabling SCU comms.\n");
+       scu_comms_okay = 0;
+
+       /* unblock the blocking thread */
+       up(sem);
+}
+#endif
+
+/*
+ * Acquire the said semaphore and activating a watchdog timer
+ * that triggers after timeout, if we are able to acquire then
+ * delete the timer.
+ */
+static void down_scu_timed(struct semaphore *sem)
+{
+#ifdef ENABLE_SCU_WATCHDOG
+       struct timer_list *scu_watchdog_timer =
+                       kmalloc(sizeof(struct timer_list), GFP_KERNEL);
+
+       if (!scu_watchdog_timer) {
+               WARN(1, "Malloc failed.\n");
+               return;
+       }
+
+       init_timer(scu_watchdog_timer);
+       scu_watchdog_timer->function    = scu_watchdog_func;
+       scu_watchdog_timer->data        = (unsigned long) sem;
+
+       /*10secs timeout*/
+       scu_watchdog_timer->expires     =
+                               jiffies + msecs_to_jiffies(10 * 1000);
+       add_timer(scu_watchdog_timer);
+#endif
+
+       down(sem);
+
+#ifdef ENABLE_SCU_WATCHDOG
+       del_timer_sync(scu_watchdog_timer);
+       kfree(scu_watchdog_timer);
+#endif
+}
+
+/* Device information for all the PCI devices present on SC */
+static struct pci_dev_info intel_mid_pci_devices[MAX_DEVICES];
+static int num_wakes[MAX_DEVICES];
+static int cmd_error_int;
+static u64 pmu_init_time;
+static struct mid_pmu_stats {
+       u64 err_count[3];
+       u64 count;
+       u64 time;
+       u64 last_entry;
+       u64 last_try;
+       u64 first_entry;
+} pmu_stats[SYS_STATE_S5];
+static enum sys_state  pmu_current_state;
+
+static void pmu_stat_start(enum sys_state type)
+{
+       pmu_current_state = type;
+       pmu_stats[type].last_try = cpu_clock(smp_processor_id());
+}
+
+static void pmu_stat_end(void)
+{
+       enum sys_state type = pmu_current_state;
+
+       if (type > SYS_STATE_S0I0) {
+               pmu_stats[type].last_entry =
+                       pmu_stats[type].last_try;
+
+               /*if it is the first entry save the time*/
+               if (!pmu_stats[type].count)
+                       pmu_stats[type].first_entry =
+                               pmu_stats[type].last_entry;
+
+               pmu_stats[type].time +=
+                       cpu_clock(smp_processor_id())
+                       - pmu_stats[type].last_entry;
+
+               pmu_stats[type].count++;
+       }
+
+       pmu_current_state = SYS_STATE_S0I0;
+}
+
+static void pmu_stat_clear(void)
+{
+       pmu_current_state = SYS_STATE_S0I0;
+}
+
+static void pmu_stat_error(u8 err_type)
+{
+       enum sys_state type = pmu_current_state;
+       u8 err_index;
+
+       if (type > SYS_STATE_S0I0) {
+               switch (err_type) {
+               case SUBSYS_POW_ERR_INT:
+                       trace_printk("S0ix_POW_ERR_INT\n");
+                       err_index = 0;
+                       break;
+               case S0ix_MISS_INT:
+                       trace_printk("S0ix_MISS_INT\n");
+                       err_index = 1;
+                       break;
+               case NO_ACKC6_INT:
+                       trace_printk("S0ix_ACKC6_INT\n");
+                       err_index = 2;
+                       break;
+               default:
+                       err_index = 3;
+                       break;
+               }
+
+               if (err_index < 3)
+                       pmu_stats[type].err_count[err_index]++;
+       }
+}
+
+static void pmu_stat_seq_printf(struct seq_file *s, int type, char *typestr)
+{
+       unsigned long long t;
+       unsigned long nanosec_rem, remainder;
+       unsigned long time, init_2_now_time;
+
+       seq_printf(s, "%s\t%5llu\t%10llu\t%9llu\t%9llu\t", typestr,
+                pmu_stats[type].count,
+                pmu_stats[type].err_count[0],
+                pmu_stats[type].err_count[1],
+                pmu_stats[type].err_count[2]);
+
+       t = pmu_stats[type].time;
+       nanosec_rem = do_div(t, 1000000000);
+
+       /* convert time in secs */
+       time = (unsigned long)t;
+
+       seq_printf(s, "%5lu.%06lu\t",
+          (unsigned long) t, nanosec_rem / 1000);
+
+       t = pmu_stats[type].last_entry;
+       nanosec_rem = do_div(t, 1000000000);
+       seq_printf(s, "%5lu.%06lu\t",
+          (unsigned long) t, nanosec_rem / 1000);
+
+       t = pmu_stats[type].first_entry;
+       nanosec_rem = do_div(t, 1000000000);
+       seq_printf(s, "%5lu.%06lu\t",
+          (unsigned long) t, nanosec_rem / 1000);
+
+       t =  cpu_clock(raw_smp_processor_id());
+       t -= pmu_init_time;
+       nanosec_rem = do_div(t, 1000000000);
+
+       init_2_now_time =  (unsigned long) t;
+
+       /* for calculating percentage residency */
+       time = time * 100;
+       t = (u64) time;
+       remainder = do_div(t, init_2_now_time);
+       time = (unsigned long) t;
+
+       /* for getting 3 digit precision after
+        * decimal dot */
+       remainder *= 1000;
+       t = (u64) remainder;
+       remainder = do_div(t, init_2_now_time);
+       seq_printf(s, "%5lu.%03lu\n", time, (unsigned long) t);
+}
+
+/*Experimentally enabling S0i3 as extended C-States*/
+static char s0ix[5] = "s0i3";
+static int extended_cstate_mode = MID_S0I3_STATE;
+static int set_extended_cstate_mode(const char *val, struct kernel_param *kp)
+{
+       char       valcp[5];
+       strncpy(valcp, val, 5);
+       valcp[4] = '\0';
+
+#ifdef CONFIG_HAS_WAKELOCK
+       /* will also disable s0ix */
+       if (strcmp(valcp, "s3s3") == 0)
+               wake_unlock(&pmu_wake_lock);
+#endif
+
+       /* Acquire the scu_ready_sem */
+       down_scu_timed(&scu_ready_sem);
+
+       if (strcmp(valcp, "s0i3") == 0)
+               extended_cstate_mode = MID_S0I3_STATE;
+       else if (strcmp(valcp, "s0i1") == 0)
+               extended_cstate_mode = MID_S0I1_STATE;
+       else if (strcmp(valcp, "s0ix") == 0)
+               extended_cstate_mode = MID_S0IX_STATE;
+       else {
+               extended_cstate_mode = -1;
+               strncpy(valcp, "none", 5);
+       }
+       strncpy(s0ix, valcp, 5);
+
+       up(&scu_ready_sem);
+
+       return 0;
+}
+
+/* Maps pci power states to SCU D0ix mask */
+static int pci_2_mfld_state(pci_power_t pci_state)
+{
+       int mfld_state = D0I0_MASK;
+
+       switch (pci_state) {
+       case PCI_D0:
+               mfld_state = D0I0_MASK;
+               break;
+
+       case PCI_D1:
+               mfld_state = D0I1_MASK;
+               break;
+
+       case PCI_D2:
+               mfld_state = D0I2_MASK;
+               break;
+
+       case PCI_D3cold:
+       case PCI_D3hot:
+               mfld_state = D0I3_MASK;
+               break;
+
+       default:
+               WARN(1, "%s: wrong pci_state received.\n", __func__);
+               break;
+       }
+
+       return mfld_state;
+}
+
+static int get_extended_cstate_mode(char *buffer, struct kernel_param *kp)
+{
+       strcpy(buffer, s0ix);
+       return 4;
+}
+
+module_param_call(s0ix, set_extended_cstate_mode,
+               get_extended_cstate_mode, NULL, 0644);
+MODULE_PARM_DESC(s0ix,
+               "setup extended c state s0ix mode [s0i3|s0i1|s0ix|none]");
+
+/* the device object */
+static struct pci_dev *pmu_dev;
+
+/*virtual memory address for PMU base returned by ioremap_nocache().*/
+static struct mid_pmu_dev intel_mid_pmu_base;
+
+#ifdef CONFIG_VIDEO_ATOMISP
+static int camera_off;
+#else
+static int camera_off = 1;
+#endif
+
+#ifdef GFX_ENABLE
+static int display_off;
+#else
+/* If Gfx is disabled
+ * assume s0ix is not blocked
+ * from gfx side
+ */
+static int display_off = 1;
+#endif
+
+static int s0i1_possible;
+static int lpmp3_possible;
+static int s0i3_possible;
+static int c6_demoted;
+static int interactive_cmd_sent;
+static int s0ix_entered;
+static u32 pmu1_max_devs, pmu2_max_devs, ss_per_reg;
+
+static int _pmu_issue_command(struct pmu_ss_states *pm_ssc, int mode, int ioc,
+                      int pmu_num);
+static void pmu_read_sss(struct pmu_ss_states *pm_ssc);
+static int _pmu2_wait_not_busy(void);
+
+/* PCI Device Id structure */
+static DEFINE_PCI_DEVICE_TABLE(mid_pm_ids) = {
+       {PCI_VDEVICE(INTEL, MID_PMU_MRST_DRV_DEV_ID), 0},
+       {PCI_VDEVICE(INTEL, MID_PMU_MFLD_DRV_DEV_ID), 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(pci, mid_pm_ids);
+
+int get_target_platform_state(void)
+{
+       if (likely(scu_comms_okay &&
+               (extended_cstate_mode != -1) && display_off\
+                && camera_off))
+               return extended_cstate_mode;
+
+       return 0;
+}
+EXPORT_SYMBOL(get_target_platform_state);
+
+static int _pmu_read_status(int pmu_num, int type)
+{
+       u32 temp;
+       union pmu_pm_status result;
+
+       temp = readl(&pmu_reg->pm_sts);
+
+       /* extract the busy bit */
+       result.pmu_status_value = temp;
+
+       if (type == PMU_BUSY_STATUS)
+               return result.pmu_status_parts.pmu_busy;
+       else if (type == PMU_MODE_ID)
+               return result.pmu_status_parts.mode_id;
+
+       return 0;
+}
+
+static int _pmu2_wait_not_busy(void)
+{
+       int pmu_busy_retry = 500000;
+
+       /* wait 500ms that the latest pmu command finished */
+       while (--pmu_busy_retry) {
+               if (_pmu_read_status(PMU_NUM_2, PMU_BUSY_STATUS) == 0)
+                       return 0;
+
+               udelay(1);
+       }
+
+       WARN(1, "pmu2 busy!");
+
+       return -EBUSY;
+}
+
+static void pmu_write_subsys_config(struct pmu_ss_states *pm_ssc)
+{
+       /* South complex in Penwell has multiple registers for
+        * PM_SSC, etc.
+        */
+       writel(pm_ssc->pmu2_states[0], &pmu_reg->pm_ssc[0]);
+
+       if (__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) {
+               writel(pm_ssc->pmu2_states[1], &pmu_reg->pm_ssc[1]);
+               writel(pm_ssc->pmu2_states[2], &pmu_reg->pm_ssc[2]);
+               writel(pm_ssc->pmu2_states[3], &pmu_reg->pm_ssc[3]);
+       }
+}
+
+/**
+ * pmu_set_cfg_mode_params - platform specific configuration mode parameters
+ */
+static int pmu_set_cfg_mode_params(struct cfg_mode_params
+        *cfg_mode_parms, union pmu_pm_set_cfg_cmd_t *command, int pmu_num)
+{
+       struct cfg_trig_param_t cmd;
+
+       /* parameter check */
+       if (cfg_mode_parms->cfg_mode >= CM_INVALID) {
+               pr_alert("invalid config mode\n");
+               return PMU_FAILED;
+       }
+
+       if (cfg_mode_parms->cfg_cmbi > 1) {
+               pr_alert("invalid cfg_cmbi bit\n");
+               return PMU_FAILED;
+       }
+
+       if (cfg_mode_parms->cfg_trigger >= INVALID_TRIG) {
+               pr_alert("invalid cfg_trigger mode\n");
+               return PMU_FAILED;
+       }
+
+       if (command == NULL) {
+               pr_alert("invalid pointer\n");
+               return PMU_FAILED;
+       }
+
+       if (pmu_num == PMU_NUM_1) {
+               command->pmu1_params.cfg_mode = cfg_mode_parms->cfg_mode;
+               command->pmu1_params.cfg_delay = cfg_mode_parms->cfg_delay;
+               command->pmu1_params.cfg_trigger = cfg_mode_parms->cfg_trigger;
+       } else if (pmu_num == PMU_NUM_2) {
+               command->pmu2_params.d_param.cfg_mode =
+                   cfg_mode_parms->cfg_mode;
+
+               /* Based on the cfg_mode set these parameters */
+               switch (cfg_mode_parms->cfg_mode) {
+               case CM_NOP:
+               case CM_IMMEDIATE:
+                       command->pmu2_params.d_param.cfg_delay = 0;
+                       command->pmu2_params.d_param.rsvd = 0;
+                       break;
+               case CM_DELAY:
+                       command->pmu2_params.d_param.cfg_delay =
+                           cfg_mode_parms->cfg_delay;
+                       command->pmu2_params.d_param.rsvd = 0;
+                       break;
+               case CM_TRIGGER:
+                       /* Based on the trigger type construct the cfg_trigger
+                        * parameters
+                        */
+                       cmd = command->pmu2_params.t_param;
+                       switch (cfg_mode_parms->cfg_trigger) {
+                       case NO_TRIG:
+                               cmd.cfg_trig_type = cfg_mode_parms->cfg_trigger;
+                               cmd.cmbi = cfg_mode_parms->cfg_cmbi;
+                               cmd.rsvd1 = 0;
+                               cmd.cfg_trig_val = cfg_mode_parms->cfg_trig_val;
+                               break;
+                       case LPM_EVENT_TRIG:
+                               cmd.cfg_trig_type = cfg_mode_parms->cfg_trigger;
+                               cmd.cmbi = cfg_mode_parms->cfg_cmbi;
+                               cmd.rsvd1 = 0;
+                               if (cfg_mode_parms->cfg_trig_val != PME_ID_LPE
+                                   && cfg_mode_parms->cfg_trig_val !=
+                                   PME_ID_USB) {
+                                       pr_alert("pme_Id"
+                                       "not supported in this release\n");
+                                       return PMU_FAILED;
+                               }
+                               cmd.cfg_trig_val = cfg_mode_parms->cfg_trig_val;
+                               break;
+                       case EXT_GPIO_INPUT_TRIG:
+                               cmd.cfg_trig_type = cfg_mode_parms->cfg_trigger;
+                               cmd.cmbi = cfg_mode_parms->cfg_cmbi;
+                               cmd.rsvd1 = 0;
+                               cmd.cfg_trig_val = cfg_mode_parms->cfg_trig_val;
+                               break;
+                       case C_STATE_TRANS_TRIG:
+                               cmd.cfg_trig_type = cfg_mode_parms->cfg_trigger;
+                               cmd.cmbi = cfg_mode_parms->cfg_cmbi;
+                               cmd.rsvd1 = 0;
+                               cmd.cfg_trig_val = cfg_mode_parms->cfg_trig_val;
+                               break;
+                       default:
+                               pr_alert("invalid state\n");
+                               return PMU_FAILED;
+                               break;
+                       };
+                       command->pmu2_params.t_param = cmd;
+                       break;
+               default:
+                       WARN_ON(1);
+                       return PMU_FAILED;
+               };
+       }
+       return 0;
+}
+
+/**
+ * pmu_send_set_config_command - Platform specific function to send
+ * configuration commands to Firmware
+ */
+static int pmu_send_set_config_command(union pmu_pm_set_cfg_cmd_t
+                    *command, u8 ioc, enum sys_state state, int pmu_num)
+{
+       /* parameter check */
+       if ((ioc != 1) && (ioc != 0)) {
+               pr_alert("invalid ioc bit\n");
+               return PMU_FAILED;
+       }
+
+       /* construct the command to send SET_CFG to particular PMU */
+       if (pmu_num == PMU_NUM_1) {
+               command->pmu1_params.cmd = SET_CFG_CMD;
+               command->pmu1_params.ioc = ioc;
+               command->pmu1_params.mode_id = MODE_ID_MAGIC_NUM;
+               command->pmu1_params.rsvd = 0;
+       } else if (pmu_num == PMU_NUM_2) {
+               command->pmu2_params.d_param.cmd =
+                   SET_CFG_CMD;
+               command->pmu2_params.d_param.ioc = ioc;
+               command->pmu2_params.d_param.mode_id =
+                   MODE_ID_MAGIC_NUM;
+
+               /* set the sys state */
+               switch (state) {
+               case SYS_STATE_S0I0:
+               case SYS_STATE_S0I1:
+               case SYS_STATE_S0I3:
+                       command->pmu2_params.
+                           d_param.sys_state = state;
+                       break;
+               default:
+                       pr_alert("invalid state\n");
+                       return PMU_FAILED;
+                       break;
+               }
+       }
+
+       /* write the value of PM_CMD into particular PMU */
+       dev_dbg(&pmu_dev->dev, "command being written %x\n", \
+       command->pmu_pm_set_cfg_cmd_value);
+
+       writel(command->pmu_pm_set_cfg_cmd_value, &pmu_reg->pm_cmd);
+
+       return 0;
+}
+
+/* return the last wake source id, and make statistics about wake sources */
+static int pmu_get_wake_source(void)
+{
+       u32 wake0, wake1;
+       int i;
+       int source = INVALID_WAKE_SRC;
+
+       wake0 = readl(&pmu_reg->pm_wks[0]);
+       wake1 = readl(&pmu_reg->pm_wks[1]);
+
+       while (wake0) {
+               i = fls(wake0) - 1;
+               source = i + pmu1_max_devs;
+               num_wakes[source]++;
+               trace_printk("wake_from_lss%d\n", source - pmu1_max_devs);
+               wake0 &= ~(1<<i);
+       }
+
+       while (wake1) {
+               i = fls(wake1) - 1;
+               source = i + 32 + pmu1_max_devs;
+               trace_printk("wake_from_lss%d\n", source - pmu1_max_devs);
+               num_wakes[source]++;
+               wake1 &= ~(1<<i);
+       }
+
+       return source;
+}
+
+static int pmu_is_interrupt_pending(int pmu_num)
+{
+       u32 temp;
+       union pmu_pm_ics result;
+
+       /* read the pm interrupt status register */
+       temp = readl(&pmu_reg->pm_ics);
+       result.pmu_pm_ics_value = temp;
+
+       /* return the pm interrupt status int pending bit info */
+       return result.pmu_pm_ics_parts.int_pend;
+}
+
+static inline void pmu_clear_interrupt_pending(int pmu_num)
+{
+       u32 temp;
+
+       /* read the pm interrupt status register */
+       temp = readl(&pmu_reg->pm_ics);
+
+       /* write into the PM_ICS register */
+       writel(temp, &pmu_reg->pm_ics);
+}
+
+static inline void pmu_enable_interrupt_from_pmu(int pmu_num)
+{
+       u32 temp;
+       union pmu_pm_ics result;
+
+       /* read the pm interrupt status register */
+       temp = readl(&pmu_reg->pm_ics);
+       result = (union pmu_pm_ics)temp;
+
+       result.pmu_pm_ics_parts.int_enable = 1;
+
+       /* enable the interrupts */
+       writel(result.pmu_pm_ics_value, &pmu_reg->pm_ics);
+}
+
+static inline int pmu_read_interrupt_status(int pmu_num)
+{
+       u32 temp;
+       union pmu_pm_ics result;
+
+       /* read the pm interrupt status register */
+       temp = readl(&pmu_reg->pm_ics);
+
+       result.pmu_pm_ics_value = temp;
+
+       if (result.pmu_pm_ics_parts.int_status == 0)
+               return PMU_FAILED;
+
+       /* return the pm interrupt status int pending bit info */
+       return result.pmu_pm_ics_parts.int_status;
+}
+
+/**
+ * This is a helper function used to program pm registers
+ * in SCU. We configure the wakeable devices & the
+ * wake sub system states on exit from S0ix state
+ */
+static inline int _mfld_s0ix_enter(u32 s0ix_value)
+{
+       struct pmu_ss_states cur_pmsss;
+
+       /* If display status is 0 retry on next c6 */
+       if (unlikely((display_off == 0) || (camera_off == 0))) {
+               up(&scu_ready_sem);
+               goto ret;
+       }
+
+       /* If PMU is busy, we'll retry on next C6 */
+       if (unlikely(_pmu_read_status(PMU_NUM_2, PMU_BUSY_STATUS))) {
+               up(&scu_ready_sem);
+               goto ret;
+       }
+
+       /* setup the wake capable devices */
+       writel(intel_mid_pmu_base.ss_config->wake_state.wake_enable[0],
+                       &pmu_reg->pm_wkc[0]);
+       writel(intel_mid_pmu_base.ss_config->wake_state.wake_enable[1],
+                       &pmu_reg->pm_wkc[1]);
+
+       /* Re-program the sub systems state on wakeup as the current SSS*/
+       pmu_read_sss(&cur_pmsss);
+
+       writel(cur_pmsss.pmu2_states[0], &pmu_reg->pm_wssc[0]);
+       writel(cur_pmsss.pmu2_states[1], &pmu_reg->pm_wssc[1]);
+       writel(cur_pmsss.pmu2_states[2], &pmu_reg->pm_wssc[2]);
+       writel(cur_pmsss.pmu2_states[3], &pmu_reg->pm_wssc[3]);
+
+       switch (s0ix_value) {
+       case S0I1_VALUE:
+               wrmsr(MSR_C6OFFLOAD_CTL_REG,
+                       MSR_C6OFFLOAD_CLEAR_LOW, MSR_C6OFFLOAD_CLEAR_HIGH);
+               writel(s0i1_sss[0], &pmu_reg->pm_ssc[0]);
+               writel(s0i1_sss[1], &pmu_reg->pm_ssc[1]);
+               writel(s0i1_sss[2], &pmu_reg->pm_ssc[2]);
+               writel(s0i1_sss[3], &pmu_reg->pm_ssc[3]);
+               pmu_stat_start(SYS_STATE_S0I1);
+               break;
+
+       case LPMP3_VALUE:
+               wrmsr(MSR_C6OFFLOAD_CTL_REG,
+                       MSR_C6OFFLOAD_CLEAR_LOW, MSR_C6OFFLOAD_CLEAR_HIGH);
+               writel(lpmp3_sss[0], &pmu_reg->pm_ssc[0]);
+               writel(lpmp3_sss[1], &pmu_reg->pm_ssc[1]);
+               writel(lpmp3_sss[2], &pmu_reg->pm_ssc[2]);
+               writel(lpmp3_sss[3], &pmu_reg->pm_ssc[3]);
+               pmu_stat_start(SYS_STATE_S0I2);
+               break;
+
+       case S0I3_VALUE:
+               writel(s0i3_sss[0], &pmu_reg->pm_ssc[0]);
+               writel(s0i3_sss[1], &pmu_reg->pm_ssc[1]);
+               writel(s0i3_sss[2], &pmu_reg->pm_ssc[2]);
+               writel(s0i3_sss[3], &pmu_reg->pm_ssc[3]);
+               pmu_stat_start(SYS_STATE_S0I3);
+               break;
+       }
+
+       /* issue a command to SCU */
+       writel(s0ix_value, &pmu_reg->pm_cmd);
+       return 1;
+
+ret:
+       return 0;
+}
+
+int mfld_s0i1_enter(void)
+{
+       /* check if we can acquire scu_ready_sem
+        * if we are not able to then do a c6 */
+       if (down_trylock(&scu_ready_sem))
+               goto ret;
+
+       if (!s0i1_possible && !lpmp3_possible) {
+               up(&scu_ready_sem);
+               goto ret;
+       }
+
+       s0ix_entered =
+               _mfld_s0ix_enter(s0i1_possible ? S0I1_VALUE : LPMP3_VALUE);
+
+ret:
+       return s0ix_entered;
+}
+EXPORT_SYMBOL(mfld_s0i1_enter);
+
+int mfld_s0i3_enter(void)
+{
+       u32 ssw_val = 0;
+       int num_retry = 15000;
+       int status = 0;
+
+       /* skip S0i3 if SCU is not okay */
+       if (unlikely(!scu_comms_okay))
+               goto ret;
+
+       /* check if we can acquire scu_ready_sem
+        * if we are not able to then do a c6 */
+       if (down_trylock(&scu_ready_sem))
+               goto ret;
+
+       if (!s0i3_possible) {
+               up(&scu_ready_sem);
+               goto ret;
+       }
+
+       s0ix_entered = _mfld_s0ix_enter(S0I3_VALUE);
+
+       /* If s0i3 command is issued
+        * then wait for c6 sram offload
+        * to complete */
+       if (s0ix_entered) {
+               do {
+                       ssw_val = readl(base_addr.offload_reg);
+
+                       if ((ssw_val & C6OFFLOAD_BIT_MASK) ==  C6OFFLOAD_BIT)
+                               break;
+
+               } while (num_retry--);
+
+               /* enable c6Offload only if write access bit is set
+                * if not then we will demote c6 to a c4 */
+               if (likely((ssw_val & C6OFFLOAD_BIT_MASK) ==  C6OFFLOAD_BIT)) {
+                       wrmsr(MSR_C6OFFLOAD_CTL_REG,
+                               MSR_C6OFFLOAD_SET_LOW, MSR_C6OFFLOAD_SET_HIGH);
+                       status = 1;
+               } else {
+                       pmu_stat_clear();
+                       WARN(1, "mid_pmu: error cpu offload bit not set.\n");
+               }
+       }
+
+ret:
+       return status;
+}
+EXPORT_SYMBOL(mfld_s0i3_enter);
+
+/**
+ * pmu_sc_irq - pmu driver interrupt handler
+ * Context: interrupt context
+ *
+ * PMU interrupt handler based on the status of the interrupt
+ * will send net link event to PM Framework.
+ * - During inactivity interrupt it sends the information  of inactivity
+ * counter that caused the interrupt as net link event.
+ * - During wake interrupt it sends the information  of source of wake
+ * sub system that caused the wake interrupt as net link event.
+ */
+static irqreturn_t pmu_sc_irq(int irq, void *ignored)
+{
+       u8 status = IRQ_NONE;
+
+       /* check if interrup pending bit is set, if not ignore interrupt */
+       if (unlikely(!pmu_is_interrupt_pending(PMU_NUM_2)))
+               goto ret_no_clear;
+
+       /* read the interrupt status */
+       status = pmu_read_interrupt_status(PMU_NUM_2);
+       if (unlikely(status == PMU_FAILED))
+               dev_dbg(&pmu_dev->dev, "Invalid interrupt source\n");
+
+       switch (status) {
+       case INVALID_INT:
+               status = IRQ_NONE;
+               goto ret_no_clear;
+
+       case CMD_COMPLETE_INT:
+               break;
+
+       case CMD_ERROR_INT:
+               cmd_error_int++;
+               break;
+
+       case SUBSYS_POW_ERR_INT:
+       case NO_ACKC6_INT:
+       case S0ix_MISS_INT:
+               pmu_stat_error(status);
+               break;
+
+       case WAKE_RECEIVED_INT:
+               (void)pmu_get_wake_source();
+               break;
+       }
+
+       if (status == WAKE_RECEIVED_INT)
+               pmu_stat_end();
+       else
+               pmu_stat_clear();
+
+       wrmsr(MSR_C6OFFLOAD_CTL_REG,
+               MSR_C6OFFLOAD_CLEAR_LOW, MSR_C6OFFLOAD_CLEAR_HIGH);
+
+       /* clear the interrupt pending bit */
+       pmu_clear_interrupt_pending(PMU_NUM_2);
+
+       /*
+        * In case of interactive command
+        * let the waiting set_power_state()
+        * release scu_ready_sem
+        */
+       if (interactive_cmd_sent) {
+               interactive_cmd_sent = 0;
+
+               /* unblock set_power_state() */
+               up(&set_mode_complete_sem);
+       } else {
+               s0ix_entered = 0;
+
+               /* S0ix case release it */
+               up(&scu_ready_sem);
+       }
+
+       status = IRQ_HANDLED;
+ret_no_clear:
+       return status;
+}
+
+void pmu_enable_forward_msi(void)
+{
+       writel(0, &pmu_reg->pm_msic);
+}
+EXPORT_SYMBOL(pmu_enable_forward_msi);
+
+unsigned long pmu_get_cstate(unsigned long eax)
+{
+       unsigned long state = eax;
+
+       /* If we get C6 in between s0ix, it should
+        * be demoted to C4 */
+       if ((s0ix_entered) && (eax == C6_HINT)) {
+               state = C4_HINT;
+               c6_demoted++;
+       } else if ((eax == C7_HINT) || (eax == C8_HINT)) {
+               /* for S0ix choose C6 */
+               state = C6_HINT;
+       }
+
+       return state;
+}
+EXPORT_SYMBOL(pmu_get_cstate);
+
+static inline u32 find_index_in_hash(struct pci_dev *pdev, int *found)
+{
+       u32 h_index;
+       int i;
+
+       /* assuming pdev is not null */
+       WARN_ON(pdev == NULL);
+
+       h_index = jhash_1word(((pdev->vendor<<16)|pdev->device),
+                MID_PCI_INDEX_HASH_INITVALUE) & MID_PCI_INDEX_HASH_MASK;
+
+       /* assume not found */
+       *found = 0;
+
+       for (i = 0; i < MID_PCI_INDEX_HASH_SIZE; i++) {
+               if (likely(pci_dev_hash[h_index].pdev == pdev)) {
+                       *found = 1;
+                       break;
+               }
+
+               /* assume no deletions, hence there shouldn't be any
+                * gaps ie., NULL's */
+               if (unlikely(pci_dev_hash[h_index].pdev == NULL)) {
+                       /* found NULL, that means we wont have
+                        * it in hash */
+                       break;
+               }
+
+               h_index = (h_index+1)%MID_PCI_INDEX_HASH_SIZE;
+       }
+
+       /* Assume hash table wont be full */
+       WARN_ON(i == MID_PCI_INDEX_HASH_SIZE);
+
+       return h_index;
+}
+
+static int get_pci_to_pmu_index(struct pci_dev *pdev)
+{
+       int pm, type;
+       unsigned int base_class;
+       unsigned int sub_class;
+       u8 ss;
+       int index = PMU_FAILED;
+       u32 h_index;
+       int found;
+
+       h_index = find_index_in_hash(pdev, &found);
+
+       if (found)
+               return (int)pci_dev_hash[h_index].index;
+
+       /* if not found, h_index would be where
+        * we can insert this */
+
+       base_class = pdev->class >> 16;
+       sub_class  = (pdev->class & SUB_CLASS_MASK) >> 8;
+       pm = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+
+       /* read the logical sub system id & cap if present */
+       pci_read_config_byte(pdev, pm + 4, &ss);
+
+       /*FIXME:: pci_table is wrong and put bad ss for HSU0 and HSU1 */
+       if (pdev->device == 0x81c || pdev->device == 0x81b)
+               ss = (LOG_SS_MASK | PMU_UART2_LSS_41);
+
+       /*FIXME:: HSI does not have an LSS listed in pci_table */
+       if (pdev->device == 0x833) {
+               dev_warn(&pdev->dev, "HSI pci pm config = 0x%x\n", ss);
+               ss = (LOG_SS_MASK | PMU_HSI_LSS_03);
+       }
+
+       type = ss & LOG_SS_MASK;
+       ss = ss & LOG_ID_MASK;
+
+       /*FIXME:: remove this once IFWI fixes Audio DMA LSS to 9 */
+       if (ss == PMU_SC_DMA_LSS_16)
+               ss = PMU_AUDIO_DMA_LSS_09;
+
+       if ((base_class == PCI_BASE_CLASS_DISPLAY) && !sub_class)
+               index = 1;
+       else if ((__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) &&
+                       (base_class == PCI_BASE_CLASS_MULTIMEDIA) &&
+                       (sub_class == ISP_SUB_CLASS))
+                               index = MFLD_ISP_POS;
+       else if (type)
+               index = pmu1_max_devs + ss;
+
+       if (index != PMU_FAILED) {
+               /* insert into hash table */
+               pci_dev_hash[h_index].pdev = pdev;
+
+               /* assume index never exceeds 0xff */
+               WARN_ON(index > 0xFF);
+
+               pci_dev_hash[h_index].index = (u8)index;
+
+               if (index < pmu1_max_devs) {
+                       intel_mid_pci_devices[index].ss_idx = 0;
+                       intel_mid_pci_devices[index].ss_pos = index;
+                       intel_mid_pci_devices[index].pmu_num = PMU_NUM_1;
+               } else if (index >= pmu1_max_devs &&
+                          index < (pmu1_max_devs + pmu2_max_devs)) {
+                       intel_mid_pci_devices[index].ss_idx = ss / ss_per_reg;
+                       intel_mid_pci_devices[index].ss_pos = ss % ss_per_reg;
+                       intel_mid_pci_devices[index].pmu_num = PMU_NUM_2;
+               } else {
+                       index = PMU_FAILED;
+               }
+
+               WARN_ON(index == PMU_FAILED);
+       }
+
+       return index;
+}
+
+static void mid_pci_find_info(struct pci_dev *pdev)
+{
+       int index, pm;
+       unsigned int base_class;
+       unsigned int sub_class;
+       u8 ss, cap;
+       int i;
+       base_class = pdev->class >> 16;
+       sub_class  = (pdev->class & SUB_CLASS_MASK) >> 8;
+
+       pm = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+
+       /* read the logical sub system id & cap if present */
+       pci_read_config_byte(pdev, pm + 4, &ss);
+       pci_read_config_byte(pdev, pm + 5, &cap);
+
+       /* get the index for the copying of ss info */
+       index = get_pci_to_pmu_index(pdev);
+
+       if (index == PMU_FAILED)
+               return;
+
+       /* initialize gfx subsystem info */
+       if ((base_class == PCI_BASE_CLASS_DISPLAY) && !sub_class) {
+               intel_mid_pci_devices[index].log_id = index;
+               intel_mid_pci_devices[index].cap = PM_SUPPORT;
+       } else if ((base_class == PCI_BASE_CLASS_MULTIMEDIA) &&
+               (sub_class == ISP_SUB_CLASS)) {
+               if (__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) {
+                       intel_mid_pci_devices[index].log_id = index;
+                       intel_mid_pci_devices[index].cap = PM_SUPPORT;
+               } else if (ss && cap) {
+                       intel_mid_pci_devices[index].log_id = ss & LOG_ID_MASK;
+                       intel_mid_pci_devices[index].cap    = cap;
+               }
+       } else if (ss && cap) {
+               intel_mid_pci_devices[index].log_id = ss & LOG_ID_MASK;
+               intel_mid_pci_devices[index].cap    = cap;
+       }
+
+       for (i = 0; i < PMU_MAX_LSS_SHARE &&
+               intel_mid_pci_devices[index].dev_driver[i]; i++) {
+               /* do nothing */
+       }
+
+       WARN_ON(i >= PMU_MAX_LSS_SHARE);
+
+       if (i < PMU_MAX_LSS_SHARE) {
+               intel_mid_pci_devices[index].dev_driver[i] = pdev;
+               intel_mid_pci_devices[index].dev_power_state[i] = PCI_D3hot;
+       }
+}
+
+static void pmu_enumerate(void)
+{
+       struct pci_dev *pdev = NULL;
+       unsigned int base_class;
+
+       while ((pdev = pci_get_device(PCI_ID_ANY, PCI_ID_ANY, pdev)) != NULL) {
+
+               /* find the base class info */
+               base_class = pdev->class >> 16;
+
+               if (base_class == PCI_BASE_CLASS_BRIDGE)
+                       continue;
+
+               mid_pci_find_info(pdev);
+       }
+}
+
+static void pmu_read_sss(struct pmu_ss_states *pm_ssc)
+{
+       pm_ssc->pmu2_states[0] = readl(&pmu_reg->pm_sss[0]);
+
+       if (__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) {
+               pm_ssc->pmu2_states[1] = readl(&pmu_reg->pm_sss[1]);
+               pm_ssc->pmu2_states[2] = readl(&pmu_reg->pm_sss[2]);
+               pm_ssc->pmu2_states[3] = readl(&pmu_reg->pm_sss[3]);
+       } else {
+               pm_ssc->pmu2_states[1] = 0;
+               pm_ssc->pmu2_states[2] = 0;
+               pm_ssc->pmu2_states[3] = 0;
+       }
+}
+
+static bool check_s0ix_possible(struct pmu_ss_states *pmsss)
+{
+       if (((pmsss->pmu2_states[0] & TARGET_CFG[0]) ==
+                                               s0ix_target[0]) &&
+               ((pmsss->pmu2_states[1] & TARGET_CFG[1]) ==
+                                               s0ix_target[1]) &&
+               ((pmsss->pmu2_states[2] & TARGET_CFG[2]) ==
+                                               s0ix_target[2]) &&
+               ((pmsss->pmu2_states[3] & TARGET_CFG[3]) ==
+                                               s0ix_target[3]))
+               return true;
+
+       return false;
+}
+
+static bool check_lpmp3_possible(struct pmu_ss_states *pmsss)
+{
+       if (((pmsss->pmu2_states[0] & LPMP3_CFG[0]) ==
+                                               lpmp3_target[0]) &&
+               ((pmsss->pmu2_states[1] & LPMP3_CFG[1]) ==
+                                               lpmp3_target[1]) &&
+               ((pmsss->pmu2_states[2] & LPMP3_CFG[2]) ==
+                                               lpmp3_target[2]) &&
+               ((pmsss->pmu2_states[3] & LPMP3_CFG[3]) ==
+                                               lpmp3_target[3]))
+               return true;
+
+       return false;
+}
+
+static void pmu_set_s0ix_possible(int state)
+{
+       /* assume S0ix not possible */
+       s0i1_possible = lpmp3_possible = s0i3_possible = 0;
+
+       if (state != PCI_D0) {
+               struct pmu_ss_states cur_pmsss;
+
+               pmu_read_sss(&cur_pmsss);
+
+               if (likely(check_s0ix_possible(&cur_pmsss)))
+                       s0i1_possible = s0i3_possible = 1;
+               else if (check_lpmp3_possible(&cur_pmsss))
+                       lpmp3_possible = 1;
+       }
+
+#ifdef CONFIG_HAS_WAKELOCK
+#ifdef S0I3_POSSIBLE_WAKELOCK
+       if (s0i3_possible && wake_lock_active(&pmu_wake_lock))
+               wake_unlock(&pmu_wake_lock);
+       if (!s0i3_possible && !wake_lock_active(&pmu_wake_lock))
+               wake_lock(&pmu_wake_lock);
+#endif
+#endif
+}
+/*
+ * For all devices in this lss, we check what is the weakest power state
+ *
+ * Thus we dont power down if another device needs more power
+ */
+
+static pci_power_t  pmu_pci_get_weakest_state_for_lss(int lss_index,
+                               struct pci_dev *pdev, pci_power_t state)
+{
+       int i;
+       pci_power_t weakest = state;
+
+       for (i = 0; i < PMU_MAX_LSS_SHARE; i++) {
+               if (intel_mid_pci_devices[lss_index].dev_driver[i] == pdev)
+                       intel_mid_pci_devices[lss_index].dev_power_state[i] =
+                                                               state;
+
+               if (intel_mid_pci_devices[lss_index].dev_driver[i] &&
+                       (intel_mid_pci_devices[lss_index].dev_power_state[i]
+                       < weakest)) {
+                       dev_warn(&pdev->dev, "%s:%s prevented me to suspend...\n",
+                               dev_name(&intel_mid_pci_devices[lss_index].dev_driver[i]->dev),
+                               dev_driver_string(&intel_mid_pci_devices[lss_index].dev_driver[i]->dev));
+
+                       weakest = intel_mid_pci_devices[lss_index].
+                                               dev_power_state[i];
+               }
+       }
+       return weakest;
+}
+
+static int pmu_pci_to_indexes(struct pci_dev *pdev, int *index,
+                               int *pmu_num, int *ss_idx, int *ss_pos)
+{
+       int i;
+
+       i = get_pci_to_pmu_index(pdev);
+       if (i == PMU_FAILED)
+               return PMU_FAILED;
+
+       *index          = i;
+       *ss_pos         = intel_mid_pci_devices[i].ss_pos;
+       *ss_idx         = intel_mid_pci_devices[i].ss_idx;
+       *pmu_num        = intel_mid_pci_devices[i].pmu_num;
+
+       return PMU_SUCCESS;
+}
+
+static int wait_for_nc_pmcmd_complete(int verify_mask, int state_type
+                                       , int reg_type)
+{
+       int pwr_sts;
+       int count = 0;
+       u32 addr;
+
+       switch (reg_type) {
+       case APM_REG_TYPE:
+               addr = apm_base + APM_STS;
+               break;
+       case OSPM_REG_TYPE:
+               addr = ospm_base + OSPM_PM_SSS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       while (true) {
+               pwr_sts = inl(addr);
+               if (state_type == OSPM_ISLAND_DOWN) {
+                       if ((pwr_sts & verify_mask) == verify_mask)
+                               break;
+                       else
+                               udelay(10);
+               } else if (state_type == OSPM_ISLAND_UP) {
+                       if (pwr_sts  == verify_mask)
+                               break;
+                       else
+                               udelay(10);
+               }
+               count++;
+               if (WARN_ONCE(count > 500000, "Timed out waiting for P-Unit"))
+                       return -EBUSY;
+       }
+       return 0;
+}
+
+/**
+ * pmu_nc_set_power_state - Callback function is used by all the devices
+ * in north complex for a platform  specific device power on/shutdown.
+ * Following assumptions are made by this function
+ *
+ * Every new request starts from scratch with no assumptions
+ * on previous/pending request to Punit.
+ * Caller is responsible to retry if request fails.
+ * Avoids multiple requests to Punit if target state is
+ * already in the expected state.
+ * spin_locks guarantee serialized access to these registers
+ * and avoid concurrent access from 2d/3d, VED, VEC, ISP & IPH.
+ *
+ */
+int pmu_nc_set_power_state(int islands, int state_type, int reg_type)
+{
+       u32 pwr_cnt = 0;
+       u32 pwr_mask = 0;
+       unsigned long flags;
+       int i, lss, mask;
+       int ret = 0;
+
+       spin_lock_irqsave(&nc_ready_lock, flags);
+
+       switch (reg_type) {
+       case APM_REG_TYPE:
+               pwr_cnt = inl(apm_base + APM_STS);
+               break;
+       case OSPM_REG_TYPE:
+               pwr_cnt = inl(ospm_base + OSPM_PM_SSS);
+               break;
+       default:
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       pwr_mask = pwr_cnt;
+       for (i = 0; i < OSPM_MAX_POWER_ISLANDS; i++) {
+               lss = islands & (0x1 << i);
+               if (lss) {
+                       mask = 0x3 << (2 * i);
+                       if (state_type == OSPM_ISLAND_DOWN)
+                               pwr_mask |= mask;
+                       else if (state_type == OSPM_ISLAND_UP)
+                               pwr_mask &= ~mask;
+               }
+       }
+
+       if (pwr_mask != pwr_cnt) {
+               switch (reg_type) {
+               case APM_REG_TYPE:
+                       outl(pwr_mask, apm_base + APM_CMD);
+                       break;
+               case OSPM_REG_TYPE:
+                       outl(pwr_mask, (ospm_base + OSPM_PM_SSC));
+                       break;
+               }
+
+               ret =
+               wait_for_nc_pmcmd_complete(pwr_mask, state_type, reg_type);
+       }
+
+unlock:
+       spin_unlock_irqrestore(&nc_ready_lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL(pmu_nc_set_power_state);
+
+/**
+ * pmu_pci_set_power_state - Callback function is used by all the PCI devices
+ *                     for a platform  specific device power on/shutdown.
+ *
+ */
+int __ref pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t state)
+{
+       int i;
+       u32 pm_cmd_val;
+       u32 new_value;
+       int sub_sys_pos, sub_sys_index;
+       int pmu_num;
+       struct pmu_ss_states cur_pmssc;
+       int status = 0;
+
+       /* while booting just ignore this callback from devices */
+       /* If SCU is not okay, nothing to do */
+       if (unlikely((dev_init_state == 0) || !scu_comms_okay))
+               return status;
+
+       /* Try to acquire the scu_ready_sem, if not
+        * get blocked, until pmu_sc_irq() releases */
+       down_scu_timed(&scu_ready_sem);
+       interactive_cmd_sent = 1;
+
+       status =
+       pmu_pci_to_indexes(pdev, &i, &pmu_num, &sub_sys_index,  &sub_sys_pos);
+
+       if (status)
+               goto unlock;
+
+       state = pmu_pci_get_weakest_state_for_lss(i, pdev, state);
+
+       /* store the display status */
+       if (i == GFX_LSS_INDEX)
+               display_off = (int)(state != PCI_D0);
+
+       /*Update the Camera status per ISP Driver Suspended/Resumed */
+       if (i == MFLD_ISP_POS)
+               camera_off = (int)(state != PCI_D0);
+
+       if (pmu_num == PMU_NUM_2) {
+
+               /* initialize the current pmssc states */
+               memset(&cur_pmssc, 0, sizeof(cur_pmssc));
+
+               status = _pmu2_wait_not_busy();
+
+               if (status)
+                       goto unlock;
+
+               pmu_read_sss(&cur_pmssc);
+
+               /* Read the pm_cmd val & update the value */
+               pm_cmd_val =
+                       (D0I3_MASK << (sub_sys_pos * BITS_PER_LSS));
+
+               /* First clear the LSS bits */
+               new_value = cur_pmssc.pmu2_states[sub_sys_index] &
+                                                       (~pm_cmd_val);
+
+               if (state != PCI_D0) {
+                       pm_cmd_val =
+               (pci_2_mfld_state(state) << (sub_sys_pos * BITS_PER_LSS));
+
+                       new_value |= pm_cmd_val;
+               }
+
+               /* nothing to do, so dont do it... */
+               if (new_value == cur_pmssc.pmu2_states[sub_sys_index])
+                       goto unlock;
+
+               cur_pmssc.pmu2_states[sub_sys_index] = new_value;
+
+               /* set the lss positions that need
+                * to be ignored to D0i0 */
+               for (i = 0; i < 4; i++)
+                       cur_pmssc.pmu2_states[i] &= IGNORE_CFG[i];
+
+               /* Issue the pmu command to PMU 2
+                * flag is needed to distinguish between
+                * S0ix vs interactive command in pmu_sc_irq()
+                */
+               status = _pmu_issue_command(&cur_pmssc, SET_MODE, 1, PMU_NUM_2);
+
+               if (unlikely(status != PMU_SUCCESS)) {
+                       dev_dbg(&pmu_dev->dev,
+                                "Failed to Issue a PM command to PMU2\n");
+                       goto unlock;
+               }
+
+               /*
+                * Wait for interactive command to complete.
+                * If we dont wait, there is a possibility that
+                * the driver may access the device before its
+                * powered on in SCU.
+                *
+                */
+               down_scu_timed(&set_mode_complete_sem);
+
+               pmu_set_s0ix_possible(state);
+       }
+
+unlock:
+       interactive_cmd_sent = 0;
+       up(&scu_ready_sem);
+       return status;
+}
+
+static int _pmu_issue_command(struct pmu_ss_states *pm_ssc, int mode, int ioc,
+                        int pmu_num)
+{
+       union pmu_pm_set_cfg_cmd_t command;
+       int sys_state = SYS_STATE_S0I0;
+       struct cfg_mode_params cfg_mode_param;
+       int status;
+       u32 tmp;
+
+       /* pmu_issue_command for PMU1 is an NOP */
+       if (pmu_num == PMU_NUM_1)
+               return 0;
+
+       if (_pmu_read_status(PMU_NUM_2, PMU_BUSY_STATUS)) {
+               dev_dbg(&pmu_dev->dev, "PMU2 is busy, Operation not"
+               "permitted\n");
+               return PMU_FAILED;
+       }
+
+
+       /* enable interrupts in PMU2 so that interrupts are
+        * propagated when ioc bit for a particular set
+        * command is set
+        */
+       /* Enable the hardware interrupt */
+       tmp = readl(&pmu_reg->pm_ics);
+       tmp |= 0x100;/* Enable interrupts */
+       writel(tmp, &pmu_reg->pm_ics);
+
+       switch (mode) {
+       case SET_MODE:
+               cfg_mode_param.cfg_mode = CM_IMMEDIATE;
+               cfg_mode_param.cfg_trigger = NO_TRIG;
+               cfg_mode_param.cfg_delay = 0;
+               cfg_mode_param.cfg_trig_val = 0;
+               cfg_mode_param.cfg_cmbi = 0;
+               sys_state = SYS_STATE_S0I0;
+               break;
+       case SET_AOAC_S0i1:
+               cfg_mode_param.cfg_mode = CM_TRIGGER;
+               cfg_mode_param.cfg_trigger = C_STATE_TRANS_TRIG;
+               cfg_mode_param.cfg_trig_val = ACK_C6_DMI_MSG;
+               cfg_mode_param.cfg_delay = 0;
+               cfg_mode_param.cfg_cmbi = 0;
+               sys_state = SYS_STATE_S0I1;
+               break;
+       case SET_AOAC_S0i2:
+               cfg_mode_param.cfg_mode = CM_TRIGGER;
+               cfg_mode_param.cfg_trigger = C_STATE_TRANS_TRIG;
+               cfg_mode_param.cfg_trig_val = ACK_C6_DMI_MSG;
+               cfg_mode_param.cfg_delay = 0;
+               cfg_mode_param.cfg_cmbi = 0;
+               sys_state = SYS_STATE_S0I2;
+               break;
+       case SET_AOAC_S0i3:
+               cfg_mode_param.cfg_mode = CM_TRIGGER;
+               cfg_mode_param.cfg_trigger = C_STATE_TRANS_TRIG;
+               cfg_mode_param.cfg_trig_val = ACK_C6_DMI_MSG;
+               cfg_mode_param.cfg_delay = 0;
+               cfg_mode_param.cfg_cmbi = 0;
+               sys_state = SYS_STATE_S0I3;
+               break;
+       case SET_LPAUDIO:
+               cfg_mode_param.cfg_mode = CM_TRIGGER;
+               cfg_mode_param.cfg_trigger = LPM_EVENT_TRIG;
+               cfg_mode_param.cfg_trig_val = PME_ID_USB;
+               cfg_mode_param.cfg_delay = 0;
+               cfg_mode_param.cfg_cmbi = 1;
+               sys_state = SYS_STATE_S0I3;
+               break;
+       default:
+               WARN_ON(1);
+               status = PMU_FAILED;
+               goto ret;
+       }
+
+       /* Configure the sub systems for pmu2 */
+       pmu_write_subsys_config(pm_ssc);
+
+       /* Configre the parameters required for config mode */
+       status =
+               pmu_set_cfg_mode_params(&cfg_mode_param, &command, PMU_NUM_2);
+       if (status != PMU_SUCCESS)
+               goto ret;
+
+       /* Send the set config command for pmu1 its configured
+        *  for mode CM_IMMEDIATE & hence with No Trigger
+        */
+       status =
+       pmu_send_set_config_command(&command, ioc, sys_state, PMU_NUM_2);
+
+ret:
+       return status;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int pmu_devices_state_show(struct seq_file *s, void *unused)
+{
+       struct pci_dev *pdev = NULL;
+       int index, i, pmu_num, ss_idx, ss_pos;
+       unsigned int base_class;
+       u32 mask, val, needed;
+       struct pmu_ss_states cur_pmsss;
+       char *dstates[] = {"D0", "D0i1", "D0i2", "D0i3"};
+
+       /* Acquire the scu_ready_sem */
+       down_scu_timed(&scu_ready_sem);
+
+       if (_pmu2_wait_not_busy())
+               goto unlock;
+
+       pmu_read_sss(&cur_pmsss);
+
+       seq_printf(s, "TARGET_CFG: ");
+       for (i = 0; i < 4; i++)
+               seq_printf(s, "%08X ", TARGET_CFG[i]);
+
+       seq_printf(s, "\n");
+       seq_printf(s, "SSS: ");
+
+       for (i = 0; i < 4; i++)
+               seq_printf(s, "%08lX ", cur_pmsss.pmu2_states[i]);
+
+       if (!display_off)
+               seq_printf(s, "display not suspended: blocking s0ix\n");
+       else if (!camera_off)
+               seq_printf(s, "camera not suspended: blocking s0ix\n");
+       else if (s0i3_possible)
+               seq_printf(s, "can enter s0i1 or s0i3\n");
+       else if (lpmp3_possible)
+               seq_printf(s, "can enter lpmp3\n");
+       else
+               seq_printf(s, "blocking s0ix\n");
+
+       seq_printf(s, "cmd_error_int count: %d\n", cmd_error_int);
+       seq_printf(s, "c6_demotion count: %d\n", c6_demoted);
+
+       seq_printf(s,
+       "\tcount\tsybsys_pow\ts0ix_miss\tno_ack_c6\ttime (secs)\tlast_entry");
+       seq_printf(s, "\tfirst_entry\tresidency(%%)\n");
+
+       pmu_stat_seq_printf(s, SYS_STATE_S0I1, "s0i1");
+       pmu_stat_seq_printf(s, SYS_STATE_S0I2, "lpmp3");
+       pmu_stat_seq_printf(s, SYS_STATE_S0I3, "s0i3");
+       pmu_stat_seq_printf(s, SYS_STATE_S3, "s3");
+
+       while ((pdev = pci_get_device(PCI_ID_ANY, PCI_ID_ANY, pdev)) != NULL) {
+
+               /* find the base class info */
+               base_class = pdev->class >> 16;
+
+               if (base_class == PCI_BASE_CLASS_BRIDGE)
+                       continue;
+
+               if (pmu_pci_to_indexes(pdev, &index, &pmu_num, &ss_idx,
+                                                                 &ss_pos))
+                       continue;
+
+               if (pmu_num == PMU_NUM_1)
+                       continue;
+
+               mask    = (D0I3_MASK << (ss_pos * BITS_PER_LSS));
+               val     = (cur_pmsss.pmu2_states[ss_idx] & mask) >>
+                                               (ss_pos * BITS_PER_LSS);
+               needed  = ((TARGET_CFG[ss_idx] & mask) != 0);
+
+               seq_printf(s, "pci %04x %04X %s %20s: lss:%02d reg:%d"
+                       "mask:%08X wk:% 8d %s  %s\n",
+                       pdev->vendor, pdev->device, dev_name(&pdev->dev),
+                       dev_driver_string(&pdev->dev),
+                       index-pmu1_max_devs, ss_idx, mask, num_wakes[index],
+                       dstates[val&3],
+                       (needed && !val) ? "blocking s0ix" : "");
+
+       }
+
+unlock:
+       up(&scu_ready_sem);
+       return 0;
+}
+
+static int devices_state_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, pmu_devices_state_show, NULL);
+}
+
+static ssize_t devices_state_write(struct file *file,
+                    const char __user *userbuf, size_t count, loff_t *ppos)
+{
+       char buf[32];
+       int buf_size = min(count, sizeof(buf)-1);
+
+       if (copy_from_user(buf, userbuf, buf_size))
+               return -EFAULT;
+
+       /* Acquire the scu_ready_sem */
+       down_scu_timed(&scu_ready_sem);
+
+       buf[buf_size] = 0;
+
+       if (((strlen("clear")+1) == buf_size) &&
+               !strncmp(buf, "clear", strlen("clear"))) {
+               memset(pmu_stats, 0,
+                       (sizeof(struct mid_pmu_stats)*SYS_STATE_S5));
+               memset(num_wakes, 0, sizeof(int)*MAX_DEVICES);
+               cmd_error_int = c6_demoted = 0;
+               pmu_current_state = SYS_STATE_S0I0;
+               pmu_init_time =
+                       cpu_clock(raw_smp_processor_id());
+       }
+
+       up(&scu_ready_sem);
+
+       return buf_size;
+}
+
+static const struct file_operations devices_state_operations = {
+       .open           = devices_state_open,
+       .read           = seq_read,
+       .write          = devices_state_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct lss_definition {
+       char *lss_name;
+       char *block;
+       char *subsystem;
+} medfield_lsses[] = {
+       {"Lss00", "Storage", "SDIO0 (HC2)"},
+       {"Lss01", "Storage", "eMMC0 (HC0a)"},
+       {"NA", "Storage", "ND_CTL (Note 5)"},
+       {"Lss03", "H S I", "H S I DMA"},
+       {"Lss04", "Security", "RNG"},
+       {"Lss05", "Storage", "eMMC1 (HC0b)"},
+       {"Lss06", "USB", "USB OTG (ULPI)"},
+       {"NA", "USB", "USB HSIC (Host) (Note 5)"},
+       {"Lss08", "Audio", ""},
+       {"Lss09", "Audio", ""},
+       {"Lss10", "SRAM", " SRAM CTL+SRAM_16KB"},
+       {"Lss11", "SRAM", " SRAM CTL+SRAM_16KB"},
+       {"Lss12", "SRAM", "SRAM BANK (16KB+3x32KBKB)"},
+       {"Lss13", "SRAM", "SRAM BANK(4x32KB)"},
+       {"Lss14", "SDIO COMMS", "SDIO2 (HC1b)"},
+       {"Lss15", "PTI, DAFCA", " DFX Blocks"},
+       {"Lss16", "SC", " DMA"},
+       {"NA", "SC", "SPI0/MSIC"},
+       {"Lss18", "GP", "SPI1"},
+       {"Lss19", "GP", " SPI2"},
+       {"Lss20", "GP", " I2C0"},
+       {"Lss21", "GP", " I2C1"},
+       {"NA", "Fabrics", " Main Fabric"},
+       {"NA", "Fabrics", " Secondary Fabric"},
+       {"NA", "SC", "SC Fabric"},
+       {"Lss25", "Audio", " I-RAM BANK1 (32 + 256KB)"},
+       {"NA", "SCU", " ROM BANK1 (18KB+18KB+18KB)"},
+       {"Lss27", "GP", "I2C2"},
+       {"NA", "SSC", "SSC (serial bus controller to FLIS)"},
+       {"Lss29", "Security", "Chaabi AON Registers"},
+       {"Lss30", "SDIO COMMS", "SDIO1 (HC1a)"},
+       {"NA", "SCU", "I-RAM BANK0 (32KB)"},
+       {"NA", "SCU", "I-RAM BANK1 (32KB)"},
+       {"Lss33", "GP", "I2C3 (HDMI)"},
+       {"Lss34", "GP", "I2C4"},
+       {"Lss35", "GP", "I2C5"},
+       {"Lss36", "GP", "SSP (SPI3)"},
+       {"Lss37", "GP", "GPIO1"},
+       {"NA", "GP", "GP Fabric"},
+       {"Lss39", "SC", "GPIO0"},
+       {"Lss40", "SC", "KBD"},
+       {"Lss41", "SC", "UART2:0"},
+       {"NA", "NA", "NA"},
+       {"NA", "NA", "NA"},
+       {"Lss44", "Security", " Security TAPC"},
+       {"NA", "MISC", "AON Timers"},
+       {"NA", "PLL", "LFHPLL and Spread Spectrum"},
+       {"NA", "PLL", "USB PLL"},
+       {"Lss48", "Audio", " SSP2 (I2S2)"},
+       {"NA", "Audio", "SLIMBUS CTL 1 (note 5)"},
+       {"NA", "Audio", "SLIMBUS CTL 2 (note 5)"},
+       {"Lss51", "Audio", "SSP0"},
+       {"Lss52", "Audio", "SSP1"},
+       {"NA", "Bridge", "IOSF to OCP Bridge"},
+       {"Lss54", "GP", "DMA"},
+       {"NA", "SC", "SVID (Serial Voltage ID)"},
+       {"NA", "SOC Fuse", "SoC Fuse Block (note 3)"},
+       {"NA", "NA", "NA"},
+};
+
+static int medfield_lsses_num =
+                       sizeof(medfield_lsses)/sizeof(medfield_lsses[0]);
+
+static char *lss_device_status[4] = { "D0i0", "D0i1", "D0i2", "D0i3" };
+
+static int show_pmu_lss_status(struct seq_file *s, void *unused)
+{
+       int sss_reg_index;
+       int offset;
+       int lss;
+       unsigned long status;
+       unsigned long sub_status;
+       unsigned long lss_status[4];
+       struct lss_definition *entry;
+
+       /* Acquire the scu_ready_sem */
+       down_scu_timed(&scu_ready_sem);
+
+       lss_status[0] = readl(&pmu_reg->pm_sss[0]);
+       lss_status[1] = readl(&pmu_reg->pm_sss[1]);
+       lss_status[2] = readl(&pmu_reg->pm_sss[2]);
+       lss_status[3] = readl(&pmu_reg->pm_sss[3]);
+       lss = 0;
+       seq_printf(s, "%5s\t%12s %35s %4s\n",
+                       "lss", "block", "subsystem", "state");
+       seq_printf(s, "==========================================================\n");
+       for (sss_reg_index = 0; sss_reg_index < 4; sss_reg_index++) {
+               status = lss_status[sss_reg_index];
+               for (offset = 0; offset < sizeof(unsigned long) * 8 / 2;
+                                                               offset++) {
+                       sub_status = status & 3;
+                       if (lss >= medfield_lsses_num)
+                               entry = &medfield_lsses[medfield_lsses_num - 1];
+                       else
+                               entry = &medfield_lsses[lss];
+                       seq_printf(s, "%5s\t%12s %35s %4s\n",
+                                       entry->lss_name, entry->block,
+                                       entry->subsystem,
+                                       lss_device_status[sub_status]);
+                       status >>= 2;
+                       lss++;
+               }
+       }
+
+       up(&scu_ready_sem);
+
+       return 0;
+}
+
+static int pmu_sss_state_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, show_pmu_lss_status, NULL);
+}
+
+static const struct file_operations pmu_sss_state_operations = {
+       .open           = pmu_sss_state_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+#endif /* DEBUG_FS */
+
+/* Reads the status of each driver and updates the LSS values.
+ * To be called with scu_ready_sem mutex held, and pmu_config
+ * initialized with '0's
+ */
+static void update_all_lss_states(struct pmu_ss_states *pmu_config)
+{
+       int i;
+       u32 PCIALLDEV_CFG[4] = {0, 0, 0, 0};
+
+       for (i = 0; i < MAX_DEVICES; i++) {
+               int pmu_num =
+                       intel_mid_pci_devices[i].pmu_num;
+               struct pci_dev *pdev =
+                       intel_mid_pci_devices[i].dev_driver[0];
+
+               if ((pmu_num == PMU_NUM_2) && pdev) {
+                       int ss_idx, ss_pos;
+                       pci_power_t state;
+
+                       ss_idx = intel_mid_pci_devices[i].ss_idx;
+                       ss_pos = intel_mid_pci_devices[i].ss_pos;
+                       state = pdev->current_state;
+                       /* the case of device not probed yet: Force D0i3 */
+                       if (state == PCI_UNKNOWN) {
+                               state = pmu_pci_choose_state(pdev);
+                               /* for those weird devices that does not do
+                                  D0i3, we keep them alive
+                                  (or we get F503's
+                                  @todo: need to figure out why)
+                               */
+                               if (state != PCI_D3hot)
+                                       state = PCI_D0;
+
+                       }
+                       /* By default its set to '0' hence
+                        * no need to update PCI_D0 state
+                        */
+                       state = pmu_pci_get_weakest_state_for_lss(i, pdev,
+                                                                 state);
+
+                       pmu_config->pmu2_states[ss_idx] |=
+                        (pci_2_mfld_state(state) << (ss_pos * BITS_PER_LSS));
+
+                       PCIALLDEV_CFG[ss_idx] |=
+                               (D0I3_MASK << (ss_pos * BITS_PER_LSS));
+               }
+       }
+
+       /* We shutdown devices that are in the target config, and that are
+          not in the pci table, some devices are indeed not advertised in pci
+          table for certain firmwares. This is the case for HSI firmwares,
+          SPI3 device is not advertised, and would then prevent s0i3. */
+       for (i = 0; i < 4; i++) {
+               pmu_config->pmu2_states[i] |=
+                       TARGET_CFG[i] & (~PCIALLDEV_CFG[i]);
+               /* also take IGNORE_CFG in account (for e.g. GPIO1)*/
+               pmu_config->pmu2_states[i] &= IGNORE_CFG[i];
+       }
+}
+
+static pci_power_t _pmu_choose_state(int device_lss)
+{
+       pci_power_t state;
+
+       switch (device_lss) {
+       case PMU_SECURITY_LSS_04:
+               state = PCI_D2;
+               break;
+
+       case PMU_USB_OTG_LSS_06:
+       case PMU_USB_HSIC_LSS_07:
+       case PMU_UART2_LSS_41:
+               state = PCI_D1;
+               break;
+
+       default:
+               state = PCI_D3hot;
+               break;
+       }
+
+       return state;
+}
+
+static int pmu_init(void)
+{
+       int i, status;
+       struct pmu_ss_states pmu_config;
+       pci_power_t state;
+       struct pmu_suspend_config *ss_config;
+
+       dev_dbg(&pmu_dev->dev, "PMU Driver loaded\n");
+       spin_lock_init(&nc_ready_lock);
+
+       /* initialise s0ix lss list */
+       for (i = 0; i < (sizeof(s0ix_lss)/sizeof(u8)); i++) {
+               TARGET_CFG[s0ix_lss[i]/ss_per_reg] |=
+               (D0I3_MASK << (s0ix_lss[i]%ss_per_reg * BITS_PER_LSS));
+
+               state = _pmu_choose_state(s0ix_lss[i]);
+               s0ix_target[s0ix_lss[i]/ss_per_reg] |=
+                       (pci_2_mfld_state(state)
+                               << (s0ix_lss[i]%ss_per_reg * BITS_PER_LSS));
+       }
+
+       /* initialise lpmp3 lss list */
+       for (i = 0; i < (sizeof(lpmp3_lss)/sizeof(u8)); i++) {
+               LPMP3_CFG[lpmp3_lss[i]/ss_per_reg] |=
+               (D0I3_MASK << (lpmp3_lss[i]%ss_per_reg * BITS_PER_LSS));
+
+               state = _pmu_choose_state(lpmp3_lss[i]);
+               lpmp3_target[lpmp3_lss[i]/ss_per_reg] |=
+                       (pci_2_mfld_state(state)
+                               << (lpmp3_lss[i]%ss_per_reg * BITS_PER_LSS));
+       }
+
+       /* initialise ignore lss list */
+       for (i = 0; i < (sizeof(lss_to_ignore)/sizeof(u8)); i++)
+               IGNORE_CFG[lss_to_ignore[i]/ss_per_reg] |=
+               (D0I3_MASK << (lss_to_ignore[i]%ss_per_reg * BITS_PER_LSS));
+
+       /* All ignored lss should be in D0i0 */
+       for (i = 0; i < 4; i++)
+               IGNORE_CFG[i] = ~IGNORE_CFG[i];
+
+       /* initialise ignore wkc lss list */
+       for (i = 0; i < (sizeof(ignore_lss_in_wkc)/sizeof(u8)); i++)
+               IGNORE_WKC_S3_CFG[ignore_lss_in_wkc[i]/32] |=
+               (1 << (ignore_lss_in_wkc[i]%32));
+
+       /* All ignored lss should be in non wakable state */
+       for (i = 0; i < 2; i++)
+               IGNORE_WKC_S3_CFG[i] = ~IGNORE_WKC_S3_CFG[i];
+
+       /* initialise s0i1 sss list */
+       for (i = 0; i < (sizeof(s0i1_sss_lss)/sizeof(u8)); i++) {
+               state = _pmu_choose_state(s0i1_sss_lss[i]);
+               s0i1_sss[s0i1_sss_lss[i]/ss_per_reg] |= (pci_2_mfld_state(state)
+                               << (s0i1_sss_lss[i]%ss_per_reg * BITS_PER_LSS));
+       }
+
+       /* initialise s0i3 sss list */
+       for (i = 0; i < (sizeof(s0i3_sss_lss)/sizeof(u8)); i++) {
+               state = _pmu_choose_state(s0i3_sss_lss[i]);
+               s0i3_sss[s0i3_sss_lss[i]/ss_per_reg] |= (pci_2_mfld_state(state)
+                               << (s0i3_sss_lss[i]%ss_per_reg * BITS_PER_LSS));
+       }
+
+       /* initialise lpmp3 sss list */
+       for (i = 0; i < (sizeof(lpmp3_sss_lss)/sizeof(u8)); i++) {
+               state = _pmu_choose_state(lpmp3_sss_lss[i]);
+               lpmp3_sss[lpmp3_sss_lss[i]/ss_per_reg] |=
+                       (pci_2_mfld_state(state) <<
+                       (lpmp3_sss_lss[i]%ss_per_reg * BITS_PER_LSS));
+       }
+
+       /* enumerate the PCI configuration space */
+       pmu_enumerate();
+
+#ifdef CONFIG_DEBUG_FS
+       /* /sys/kernel/debug/mid_pmu_states */
+       (void) debugfs_create_file("mid_pmu_states", S_IFREG | S_IRUGO,
+                               NULL, NULL, &devices_state_operations);
+
+       /* /sys/kernel/debug/pmu_sss_states */
+       (void) debugfs_create_file("pmu_sss_states", S_IFREG | S_IRUGO,
+                               NULL, NULL, &pmu_sss_state_operations);
+#endif
+
+       /* initialize the state variables here */
+       intel_mid_pmu_base.disable_cpu1 = false;
+
+       intel_mid_pmu_base.ss_config =
+       kzalloc(sizeof(struct pmu_suspend_config), GFP_KERNEL);
+
+       if (intel_mid_pmu_base.ss_config == NULL) {
+               dev_dbg(&pmu_dev->dev, "Allocation of memory"
+               "for ss_config has failed\n");
+               status = PMU_FAILED;
+               goto out_err1;
+       }
+
+       memset(&pmu_config, 0, sizeof(pmu_config));
+
+       intel_mid_pmu_base.ss_config->ss_state = pmu_config;
+
+       /* initialize for the autonomous S0i3 */
+       ss_config = intel_mid_pmu_base.ss_config;
+
+       /* setup the wake capable devices */
+       intel_mid_pmu_base.ss_config->wake_state.wake_enable[0] =
+                                                       WAKE_ENABLE_0;
+       intel_mid_pmu_base.ss_config->wake_state.wake_enable[1] =
+                                                       WAKE_ENABLE_1;
+
+       /* Acquire the scu_ready_sem */
+       down_scu_timed(&scu_ready_sem);
+
+       /* Now we have initialized the driver
+        * Allow drivers to get blocked in
+        * pmu_pci_set_power_state(), until we finish
+        * first interactive command.
+        */
+       dev_init_state = 1;
+
+       /* get the current status of each of the driver
+        * and update it in SCU
+        */
+       update_all_lss_states(&pmu_config);
+
+       /* send a interactive command to fw */
+       interactive_cmd_sent = 1;
+       status = _pmu_issue_command(&pmu_config, SET_MODE, 1, PMU_NUM_2);
+       if (status != PMU_SUCCESS) {
+               interactive_cmd_sent = 0;
+               dev_dbg(&pmu_dev->dev,\
+                "Failure from pmu mode change to interactive."
+               " = %d\n", status);
+               status = PMU_FAILED;
+               up(&scu_ready_sem);
+               goto out_err1;
+       }
+
+       /*
+        * Wait for interactive command to complete.
+        * If we dont wait, there is a possibility that
+        * the driver may access the device before its
+        * powered on in SCU.
+        *
+        */
+       down_scu_timed(&set_mode_complete_sem);
+
+       /* In cases were gfx is not enabled
+        * this will enable s0ix immediately
+        */
+       pmu_set_s0ix_possible(PCI_D3hot);
+
+       up(&scu_ready_sem);
+
+out_err1:
+       return status;
+}
+
+/**
+ * mid_pmu_probe - This is the function where most of the PMU driver
+ *                initialization happens.
+ */
+static int __devinit mid_pmu_probe(struct pci_dev *dev,
+                                  const struct pci_device_id *pci_id)
+{
+       int ret;
+       struct mrst_pmu_reg __iomem *pmu;
+
+#ifdef CONFIG_HAS_WAKELOCK
+       wake_lock_init(&pmu_wake_lock, WAKE_LOCK_SUSPEND, "mid_pmu");
+#endif
+
+       /* Init the device */
+       ret = pci_enable_device(dev);
+       if (ret) {
+               pr_err("Mid PM device cant be enabled\n");
+               goto out_err0;
+       }
+
+       /* store the dev */
+       pmu_dev = dev;
+
+       ret = pci_request_regions(dev, PMU_DRV_NAME);
+       if (ret < 0) {
+               pr_err("pci request region has failed\n");
+               goto out_err1;
+       }
+
+       if (__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) {
+               pmu1_max_devs = PMU1_MAX_PENWELL_DEVS;
+               pmu2_max_devs = PMU2_MAX_PENWELL_DEVS;
+               ss_per_reg = 16;
+       } else {
+               pmu1_max_devs = PMU1_MAX_MRST_DEVS;
+               pmu2_max_devs = PMU2_MAX_MRST_DEVS;
+               ss_per_reg = 8;
+       }
+
+       /* Map the NC PM registers */
+       apm_base =  MDFLD_MSG_READ32(OSPM_PUNIT_PORT, OSPM_APMBA) & 0xffff;
+       ospm_base =  MDFLD_MSG_READ32(OSPM_PUNIT_PORT, OSPM_OSPMBA) & 0xffff;
+
+       /* Map the memory of pmu1 PMU reg base */
+       pmu = pci_iomap(dev, 0, 0);
+       if (pmu == NULL) {
+               dev_dbg(&pmu_dev->dev, "Unable to map the PMU2 address"
+                        "space\n");
+               ret = PMU_FAILED;
+               goto out_err2;
+       }
+
+       pmu_reg = pmu;
+
+       if (__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) {
+               /* Map the memory of offload_reg */
+               base_addr.offload_reg = ioremap_nocache(0xffd01ffc, 4);
+               if (base_addr.offload_reg == NULL) {
+                       dev_dbg(&pmu_dev->dev,
+                       "Unable to map the offload_reg address space\n");
+                       ret = PMU_FAILED;
+                       goto out_err3;
+               }
+       }
+
+       if (request_irq(dev->irq, pmu_sc_irq, IRQF_NO_SUSPEND, PMU_DRV_NAME,
+                       NULL)) {
+               dev_dbg(&pmu_dev->dev, "Registering isr has failed\n");
+               ret = PMU_FAILED;
+               goto out_err3;
+       }
+
+       /* call pmu init() for initialization of pmu interface */
+       ret = pmu_init();
+       if (ret != PMU_SUCCESS) {
+               dev_dbg(&pmu_dev->dev, "PMU initialization has failed\n");
+               goto out_err4;
+       }
+
+       pmu_init_time =
+               cpu_clock(raw_smp_processor_id());
+
+       return 0;
+
+out_err4:
+       free_irq(dev->irq, &pmu_sc_irq);
+out_err3:
+       pci_iounmap(dev, base_addr.pmu2_base);
+       base_addr.pmu2_base = NULL;
+out_err2:
+       base_addr.pmu1_base = NULL;
+out_err1:
+       pci_release_region(dev, 0);
+       pci_disable_device(dev);
+out_err0:
+#ifdef CONFIG_HAS_WAKELOCK
+       wake_unlock(&pmu_wake_lock);
+#endif
+       return ret;
+}
+
+static void __devexit mid_pmu_remove(struct pci_dev *dev)
+{
+       dev_dbg(&pmu_dev->dev, "Mid PM mid_pmu_remove called\n");
+
+       /* Freeing up the irq */
+       free_irq(dev->irq, &pmu_sc_irq);
+
+       /* Freeing up memory allocated for PMU1 & PMU2 */
+       if (__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) {
+               iounmap(base_addr.offload_reg);
+               base_addr.offload_reg = NULL;
+       }
+       pci_iounmap(dev, pmu_reg);
+       base_addr.pmu1_base = NULL;
+       base_addr.pmu2_base = NULL;
+
+       /* disable the current PCI device */
+       pci_release_region(dev, 0);
+       pci_disable_device(dev);
+}
+
+static struct pci_driver driver = {
+       .name = PMU_DRV_NAME,
+       .id_table = mid_pm_ids,
+       .probe = mid_pmu_probe,
+       .remove = __devexit_p(mid_pmu_remove),
+};
+
+/* return platform specific deepest states that the device can enter */
+pci_power_t pmu_pci_choose_state(struct pci_dev *pdev)
+{
+       int i;
+       int sub_sys_pos, sub_sys_index;
+       int status;
+       int device_lss;
+       int pmu_num;
+
+       pci_power_t state = PCI_D3hot;
+
+       if (dev_init_state) {
+               status =
+               pmu_pci_to_indexes(pdev, &i, &pmu_num,
+                                       &sub_sys_index,  &sub_sys_pos);
+
+               if ((status == PMU_SUCCESS) &&
+                       (pmu_num == PMU_NUM_2)) {
+
+                       device_lss = (sub_sys_index * ss_per_reg) + sub_sys_pos;
+
+                       state = _pmu_choose_state(device_lss);
+               }
+       }
+
+       return state;
+}
+EXPORT_SYMBOL(pmu_pci_choose_state);
+
+bool pmu_pci_power_manageable(struct pci_dev *dev)
+{
+       return true;
+}
+EXPORT_SYMBOL(pmu_pci_power_manageable);
+
+/* At this point of time we expect all devices to be
+ * wake capable will be modified in future
+ */
+bool pmu_pci_can_wakeup(struct pci_dev *dev)
+{
+       return true;
+}
+EXPORT_SYMBOL(pmu_pci_can_wakeup);
+
+int pmu_pci_sleep_wake(struct pci_dev *dev, bool enable)
+{
+       return 0;
+}
+EXPORT_SYMBOL(pmu_pci_sleep_wake);
+
+static int mfld_s3_enter(void)
+{
+       u32 ssw_val = 0;
+       int num_retry = 15000;
+       int status = 0;
+       struct pmu_ss_states cur_pmsss;
+
+       /* check if we can acquire scu_ready_sem
+        * if we are not able to then do a c6 */
+       if (down_trylock(&scu_ready_sem)) {
+               status = -EINVAL;
+               goto ret;
+       }
+
+       /* If PMU is busy, we'll retry on next C6 */
+       if (unlikely(_pmu_read_status(PMU_NUM_2, PMU_BUSY_STATUS))) {
+               up(&scu_ready_sem);
+               status = -EINVAL;
+               goto ret;
+       }
+
+       /* setup the wake capable devices */
+       writel(IGNORE_WKC_S3_CFG[0], &pmu_reg->pm_wkc[0]);
+       writel(IGNORE_WKC_S3_CFG[1], &pmu_reg->pm_wkc[1]);
+
+       /* Re-program the sub systems state on wakeup as the current SSS*/
+       pmu_read_sss(&cur_pmsss);
+
+       writel(cur_pmsss.pmu2_states[0], &pmu_reg->pm_wssc[0]);
+       writel(cur_pmsss.pmu2_states[1], &pmu_reg->pm_wssc[1]);
+       writel(cur_pmsss.pmu2_states[2], &pmu_reg->pm_wssc[2]);
+       writel(cur_pmsss.pmu2_states[3], &pmu_reg->pm_wssc[3]);
+
+       /* program pm ssc registers */
+       writel(s0i3_sss[0], &pmu_reg->pm_ssc[0]);
+       writel(s0i3_sss[1], &pmu_reg->pm_ssc[1]);
+       writel(s0i3_sss[2], &pmu_reg->pm_ssc[2]);
+       writel(s0i3_sss[3], &pmu_reg->pm_ssc[3]);
+
+       /* issue a command to SCU */
+       writel(S0I3_VALUE, &pmu_reg->pm_cmd);
+
+       /* If s0i3 command is issued
+        * then wait for c6 sram offload
+        * to complete */
+       do {
+               ssw_val = readl(base_addr.offload_reg);
+
+               if ((ssw_val & C6OFFLOAD_BIT_MASK) ==  C6OFFLOAD_BIT)
+                       break;
+
+       } while (num_retry--);
+
+       if (unlikely((ssw_val & C6OFFLOAD_BIT_MASK) !=  C6OFFLOAD_BIT)) {
+               status = -EINVAL;
+               pmu_stat_clear();
+               WARN(1, "mid_pmu: error cpu offload bit not set.\n");
+       } else {
+               unsigned long ecx = 1; /* break on interrupt flag */
+               unsigned long eax = C6_HINT;
+               u32 temp = 0;
+               pmu_stat_start(SYS_STATE_S3);
+
+               /* issue c6 offload */
+               wrmsr(MSR_C6OFFLOAD_CTL_REG,
+                       MSR_C6OFFLOAD_SET_LOW, MSR_C6OFFLOAD_SET_HIGH);
+
+               __monitor((void *) &temp, 0, 0);
+               smp_mb();
+
+               s0ix_entered = 1;
+               __mwait(eax, ecx);
+               pmu_enable_forward_msi();
+       }
+
+ret:
+       return status;
+}
+
+static int mid_valid(suspend_state_t state)
+{
+       switch (state) {
+       case PM_SUSPEND_ON:
+       case PM_SUSPEND_MEM:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int mid_prepare(void)
+{
+       return 0;
+}
+
+static int mid_prepare_late(void)
+{
+       return 0;
+}
+
+static int mid_suspend(suspend_state_t state)
+{
+       int ret;
+
+       if (state != PM_SUSPEND_MEM)
+               ret = -EINVAL;
+
+       trace_printk("s3_entry\n");
+       ret = mfld_s3_enter();
+       trace_printk("s3_exit %d\n", ret);
+       if (ret != 0)
+               dev_dbg(&pmu_dev->dev, "Failed to enter S3 status: %d\n", ret);
+
+       return ret;
+}
+
+static void mid_end(void)
+{
+#ifdef CONFIG_HAS_WAKELOCK
+       /* Prime the wakelock system to re-suspend after giving other
+        * threads a chance to wake up and acquire wake lock
+        * this avoids to put wake lock in other things like pwrbutton
+        */
+       long timeout = HZ;
+       wake_lock_timeout(&pmu_wake_lock, timeout);
+#endif
+}
+
+static struct platform_suspend_ops mid_suspend_ops = {
+       .valid = mid_valid,
+       .prepare = mid_prepare,
+       .prepare_late = mid_prepare_late,
+       .enter = mid_suspend,
+       .end = mid_end,
+};
+
+/**
+ * mid_pci_register_init - register the PMU driver as PCI device
+ */
+static int __init mid_pci_register_init(void)
+{
+       int ret;
+
+       /* registering PCI device */
+       ret = pci_register_driver(&driver);
+       suspend_set_ops(&mid_suspend_ops);
+
+       return ret;
+}
+fs_initcall(mid_pci_register_init);
+
+static void __exit mid_pci_cleanup(void)
+{
+       suspend_set_ops(NULL);
+       /* registering PCI device */
+       pci_unregister_driver(&driver);
+}
+module_exit(mid_pci_cleanup);