Merge remote-tracking branch 'origin/pm/imx' into pm/next
* origin/pm/imx: (49 commits) MLK-22992 firmware: imx: scu-pd: fix wu_num MLK-22986-1: soc: imx: gpc: add ldo-bypass firmware: imx: scu-pd: correct edma0 channel number imx: scu-pd: add vpu-enc1 power domains for imx8qm cpufreq: imx-cpufreq-dt: Correct i.MX8MN's default speed grade value ...5.4-rM2-2.2.x-imx-squashed
commit
d80b3c114b
|
@ -0,0 +1,46 @@
|
|||
Device Tree Bindings for Freescale i.MX8M Generic Power Domain
|
||||
==============================================================
|
||||
The binding for the i.MX8M Generic power Domain[1].
|
||||
|
||||
[1] Documentation/devicetree/bindings/power/power_domain.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be of:
|
||||
- "fsl,imx8m-power-domain"
|
||||
- #power-domain-cells: Number of cells in a PM domain Specifier, must be 0
|
||||
- domain-index: should be the domain index number need to pass to TF-A
|
||||
- domain-name: the name of this pm domain
|
||||
|
||||
Optional properties:
|
||||
- clocks: a number of phandles to clocks that need to be enabled during
|
||||
domain power-up sequence to ensure reset propagation into devices
|
||||
located inside this power domain
|
||||
- power-supply: Power supply used to power the domain
|
||||
- parent-domains: the phandle to the parent power domain
|
||||
|
||||
example:
|
||||
vpu_g1_pd: vpug1-pd {
|
||||
compatible = "fsl,imx8mm-pm-domain";
|
||||
#power-domain-cells = <0>;
|
||||
domain-index = <6>;
|
||||
domain-name = "vpu_g1";
|
||||
parent-domains = <&vpumix_pd>;
|
||||
clocks = <&clk IMX8MM_CLK_VPU_G1_ROOT>;
|
||||
};
|
||||
|
||||
|
||||
Specifying Power domain for IP modules
|
||||
======================================
|
||||
|
||||
IP cores belonging to a power domain should contain a 'power-domains'
|
||||
property that is a phandle for PGC node representing the domain.
|
||||
|
||||
Example of a device that is part of the vpu_g1 power domain:
|
||||
vpu_g1: vpu_g1@38300000 {
|
||||
/* ... */
|
||||
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "irq_hantro";
|
||||
/* ... */
|
||||
power-domains = <&vpu_g1_pd>;
|
||||
};
|
|
@ -102,14 +102,6 @@ static void deferred_probe_work_func(struct work_struct *work)
|
|||
*/
|
||||
mutex_unlock(&deferred_probe_mutex);
|
||||
|
||||
/*
|
||||
* Force the device to the end of the dpm_list since
|
||||
* the PM code assumes that the order we add things to
|
||||
* the list is a good order for suspend but deferred
|
||||
* probe makes that very unsafe.
|
||||
*/
|
||||
device_pm_move_to_tail(dev);
|
||||
|
||||
dev_dbg(dev, "Retrying from deferred list\n");
|
||||
bus_probe_device(dev);
|
||||
mutex_lock(&deferred_probe_mutex);
|
||||
|
@ -380,6 +372,14 @@ static void driver_bound(struct device *dev)
|
|||
|
||||
device_pm_check_callbacks(dev);
|
||||
|
||||
/*
|
||||
* Force the device to the end of the dpm_list since
|
||||
* the PM code assumes that the order we add things to
|
||||
* the list is a good order for suspend but deferred
|
||||
* probe makes that very unsafe.
|
||||
*/
|
||||
device_pm_move_to_tail(dev);
|
||||
|
||||
/*
|
||||
* Make sure the device is no longer in one of the deferred lists and
|
||||
* kick off retrying all pending devices
|
||||
|
|
|
@ -448,6 +448,9 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
|
|||
if (!genpd->power_off)
|
||||
return 0;
|
||||
|
||||
if (atomic_read(&genpd->sd_count) > 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (!timed)
|
||||
return genpd->power_off(genpd);
|
||||
|
||||
|
@ -497,6 +500,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
|
|||
struct pm_domain_data *pdd;
|
||||
struct gpd_link *link;
|
||||
unsigned int not_suspended = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Do not try to power off the domain in the following situations:
|
||||
|
@ -544,24 +548,21 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
|
|||
if (!genpd->gov)
|
||||
genpd->state_idx = 0;
|
||||
|
||||
if (genpd->power_off) {
|
||||
int ret;
|
||||
/* Choose the deepest state if no devices using this domain */
|
||||
if (!genpd->device_count)
|
||||
genpd->state_idx = genpd->state_count - 1;
|
||||
|
||||
if (atomic_read(&genpd->sd_count) > 0)
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* If sd_count > 0 at this point, one of the subdomains hasn't
|
||||
* managed to call genpd_power_on() for the master yet after
|
||||
* incrementing it. In that case genpd_power_on() will wait
|
||||
* for us to drop the lock, so we can call .power_off() and let
|
||||
* the genpd_power_on() restore power for us (this shouldn't
|
||||
* happen very often).
|
||||
*/
|
||||
ret = _genpd_power_off(genpd, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* If sd_count > 0 at this point, one of the subdomains hasn't
|
||||
* managed to call genpd_power_on() for the master yet after
|
||||
* incrementing it. In that case genpd_power_on() will wait
|
||||
* for us to drop the lock, so we can call .power_off() and let
|
||||
* the genpd_power_on() restore power for us (this shouldn't
|
||||
* happen very often).
|
||||
*/
|
||||
ret = _genpd_power_off(genpd, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
genpd->status = GPD_STATE_POWER_OFF;
|
||||
genpd_update_accounting(genpd);
|
||||
|
@ -960,11 +961,20 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
|
|||
{
|
||||
struct gpd_link *link;
|
||||
|
||||
if (!genpd_status_on(genpd) || genpd_is_always_on(genpd))
|
||||
/*
|
||||
* Give the power domain a chance to switch to the deepest state in
|
||||
* case it's already off but in an intermediate low power state.
|
||||
*/
|
||||
genpd->state_idx_saved = genpd->state_idx;
|
||||
|
||||
if (genpd_is_always_on(genpd))
|
||||
return;
|
||||
|
||||
if (genpd->suspended_count != genpd->device_count
|
||||
|| atomic_read(&genpd->sd_count) > 0)
|
||||
if (!genpd_status_on(genpd) &&
|
||||
genpd->state_idx == (genpd->state_count - 1))
|
||||
return;
|
||||
|
||||
if (genpd->suspended_count != genpd->device_count)
|
||||
return;
|
||||
|
||||
/* Choose the deepest state when suspending */
|
||||
|
@ -972,6 +982,9 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
|
|||
if (_genpd_power_off(genpd, false))
|
||||
return;
|
||||
|
||||
if (genpd->status == GPD_STATE_POWER_OFF)
|
||||
return;
|
||||
|
||||
genpd->status = GPD_STATE_POWER_OFF;
|
||||
|
||||
list_for_each_entry(link, &genpd->slave_links, slave_node) {
|
||||
|
@ -1019,6 +1032,9 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
|
|||
|
||||
_genpd_power_on(genpd, false);
|
||||
|
||||
/* restore save power domain state after resume */
|
||||
genpd->state_idx = genpd->state_idx_saved;
|
||||
|
||||
genpd->status = GPD_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
|
@ -1829,7 +1845,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
|
|||
return ret;
|
||||
}
|
||||
} else if (!gov && genpd->state_count > 1) {
|
||||
pr_warn("%s: no governor for states\n", genpd->name);
|
||||
pr_debug("%s: no governor for states\n", genpd->name);
|
||||
}
|
||||
|
||||
device_initialize(&genpd->dev);
|
||||
|
|
|
@ -535,6 +535,7 @@ config ADI
|
|||
and SSM (Silicon Secured Memory). Intended consumers of this
|
||||
driver include crash and makedumpfile.
|
||||
|
||||
source "drivers/char/imx_amp/Kconfig"
|
||||
endmenu
|
||||
|
||||
config RANDOM_TRUST_CPU
|
||||
|
|
|
@ -52,3 +52,4 @@ js-rtc-y = rtc.o
|
|||
obj-$(CONFIG_XILLYBUS) += xillybus/
|
||||
obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
|
||||
obj-$(CONFIG_ADI) += adi.o
|
||||
obj-$(CONFIG_HAVE_IMX_AMP) += imx_amp/
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# imx mcc
|
||||
#
|
||||
|
||||
config IMX_SEMA4
|
||||
bool "IMX SEMA4 driver"
|
||||
depends on SOC_IMX6SX
|
||||
help
|
||||
Support for IMX SEMA4 driver, most people should say N here.
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for imx mcc
|
||||
#
|
||||
#
|
||||
obj-$(CONFIG_IMX_SEMA4) += imx_sema4.o
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/imx_sema4.h>
|
||||
|
||||
static struct imx_sema4_mutex_device *imx6_sema4;
|
||||
|
||||
/*!
|
||||
* \brief mutex create function.
|
||||
*
|
||||
* This function allocates imx_sema4_mutex structure and returns a handle
|
||||
* to it. The mutex to be created is identified by SEMA4 device number and mutex
|
||||
* (gate) number. The handle is used to reference the created mutex in calls to
|
||||
* other imx_sema4_mutex API functions. This function is to be called only
|
||||
* once for each mutex.
|
||||
*
|
||||
* \param[in] dev_num SEMA4 device (module) number.
|
||||
* \param[in] mutex_num Mutex (gate) number.
|
||||
*
|
||||
* \return NULL (Failure.)
|
||||
* \return imx_sema4_mutex (Success.)
|
||||
*/
|
||||
struct imx_sema4_mutex *
|
||||
imx_sema4_mutex_create(u32 dev_num, u32 mutex_num)
|
||||
{
|
||||
struct imx_sema4_mutex *mutex_ptr = NULL;
|
||||
|
||||
if (mutex_num >= SEMA4_NUM_GATES || dev_num >= SEMA4_NUM_DEVICES)
|
||||
goto out;
|
||||
|
||||
if (imx6_sema4->cpine_val & (1 < mutex_num)) {
|
||||
pr_err("Error: requiring a allocated sema4.\n");
|
||||
pr_err("mutex_num %d cpine_val 0x%08x.\n",
|
||||
mutex_num, imx6_sema4->cpine_val);
|
||||
}
|
||||
mutex_ptr = kzalloc(sizeof(*mutex_ptr), GFP_KERNEL);
|
||||
if (!mutex_ptr)
|
||||
goto out;
|
||||
imx6_sema4->mutex_ptr[mutex_num] = mutex_ptr;
|
||||
imx6_sema4->alloced |= 1 < mutex_num;
|
||||
imx6_sema4->cpine_val |= idx_sema4[mutex_num];
|
||||
writew(imx6_sema4->cpine_val, imx6_sema4->ioaddr + SEMA4_CP0INE);
|
||||
|
||||
mutex_ptr->valid = CORE_MUTEX_VALID;
|
||||
mutex_ptr->gate_num = mutex_num;
|
||||
init_waitqueue_head(&mutex_ptr->wait_q);
|
||||
|
||||
out:
|
||||
return mutex_ptr;
|
||||
}
|
||||
EXPORT_SYMBOL(imx_sema4_mutex_create);
|
||||
|
||||
/*!
|
||||
* \brief mutex destroy function.
|
||||
*
|
||||
* This function destroys a mutex.
|
||||
*
|
||||
* \param[in] mutex_ptr Pointer to mutex structure.
|
||||
*
|
||||
* \return MQX_COMPONENT_DOES_NOT_EXIST (mutex component not installed.)
|
||||
* \return MQX_INVALID_PARAMETER (Wrong input parameter.)
|
||||
* \return COREMUTEX_OK (Success.)
|
||||
*
|
||||
*/
|
||||
int imx_sema4_mutex_destroy(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
u32 mutex_num;
|
||||
|
||||
if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_num = mutex_ptr->gate_num;
|
||||
if ((imx6_sema4->cpine_val & idx_sema4[mutex_num]) == 0) {
|
||||
pr_err("Error: trying to destroy a un-allocated sema4.\n");
|
||||
pr_err("mutex_num %d cpine_val 0x%08x.\n",
|
||||
mutex_num, imx6_sema4->cpine_val);
|
||||
}
|
||||
imx6_sema4->mutex_ptr[mutex_num] = NULL;
|
||||
imx6_sema4->alloced &= ~(1 << mutex_num);
|
||||
imx6_sema4->cpine_val &= ~(idx_sema4[mutex_num]);
|
||||
writew(imx6_sema4->cpine_val, imx6_sema4->ioaddr + SEMA4_CP0INE);
|
||||
|
||||
kfree(mutex_ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(imx_sema4_mutex_destroy);
|
||||
|
||||
/*!
|
||||
* \brief Lock the mutex, shouldn't be interruted by INT.
|
||||
*
|
||||
* This function attempts to lock a mutex. If the mutex is already locked
|
||||
* by another task the function return -EBUSY, and tell invoker wait until
|
||||
* it is possible to lock the mutex.
|
||||
*
|
||||
* \param[in] mutex_ptr Pointer to mutex structure.
|
||||
*
|
||||
* \return MQX_INVALID_POINTER (Wrong pointer to the mutex structure provided.)
|
||||
* \return COREMUTEX_OK (mutex successfully locked.)
|
||||
*
|
||||
* \see imx_sema4_mutex_unlock
|
||||
*/
|
||||
int _imx_sema4_mutex_lock(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
int ret = 0, i = 0;
|
||||
|
||||
if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID))
|
||||
return -EINVAL;
|
||||
|
||||
i = mutex_ptr->gate_num;
|
||||
mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= SEMA4_GATE_MASK;
|
||||
/* Check to see if this core already own it */
|
||||
if (mutex_ptr->gate_val == SEMA4_A9_LOCK) {
|
||||
/* return -EBUSY, invoker should be in sleep, and re-lock ag */
|
||||
pr_err("%s -> %s %d already locked, wait! num %d val %d.\n",
|
||||
__FILE__, __func__, __LINE__,
|
||||
i, mutex_ptr->gate_val);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
} else {
|
||||
/* try to lock the mutex */
|
||||
mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= (~SEMA4_GATE_MASK);
|
||||
mutex_ptr->gate_val |= SEMA4_A9_LOCK;
|
||||
writeb(mutex_ptr->gate_val, imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= SEMA4_GATE_MASK;
|
||||
/* double check the mutex is locked, otherwise, return -EBUSY */
|
||||
if (mutex_ptr->gate_val != SEMA4_A9_LOCK) {
|
||||
pr_debug("wait-locked num %d val %d.\n",
|
||||
i, mutex_ptr->gate_val);
|
||||
ret = -EBUSY;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* !
|
||||
* \brief Try to lock the core mutex.
|
||||
*
|
||||
* This function attempts to lock a mutex. If the mutex is successfully locked
|
||||
* for the calling task, SEMA4_A9_LOCK is returned. If the mutex is already
|
||||
* locked by another task, the function does not block but rather returns
|
||||
* negative immediately.
|
||||
*
|
||||
* \param[in] mutex_ptr Pointer to core_mutex structure.
|
||||
*
|
||||
* \return SEMA4_A9_LOCK (mutex successfully locked.)
|
||||
* \return negative (mutex not locked.)
|
||||
*
|
||||
*/
|
||||
int imx_sema4_mutex_trylock(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = _imx_sema4_mutex_lock(mutex_ptr);
|
||||
if (ret == 0)
|
||||
return SEMA4_A9_LOCK;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(imx_sema4_mutex_trylock);
|
||||
|
||||
/*!
|
||||
* \brief Invoke _imx_sema4_mutex_lock to lock the mutex.
|
||||
*
|
||||
* This function attempts to lock a mutex. If the mutex is already locked
|
||||
* by another task the function, sleep itself and schedule out.
|
||||
* Wait until it is possible to lock the mutex.
|
||||
*
|
||||
* Invoker should add its own wait queue into the wait queue header of the
|
||||
* required semaphore, set TASK_INTERRUPTIBLE and sleep on itself by
|
||||
* schedule() when the lock is failed. Re-try to lock the semaphore when
|
||||
* it is woke up by the sema4 isr.
|
||||
*
|
||||
* \param[in] mutex_ptr Pointer to mutex structure.
|
||||
*
|
||||
* \return SEMA4_A9_LOCK (mutex successfully locked.)
|
||||
*
|
||||
* \see imx_sema4_mutex_unlock
|
||||
*/
|
||||
int imx_sema4_mutex_lock(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&imx6_sema4->lock, flags);
|
||||
ret = _imx_sema4_mutex_lock(mutex_ptr);
|
||||
spin_unlock_irqrestore(&imx6_sema4->lock, flags);
|
||||
while (-EBUSY == ret) {
|
||||
spin_lock_irqsave(&imx6_sema4->lock, flags);
|
||||
ret = _imx_sema4_mutex_lock(mutex_ptr);
|
||||
spin_unlock_irqrestore(&imx6_sema4->lock, flags);
|
||||
if (ret == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(imx_sema4_mutex_lock);
|
||||
|
||||
/*!
|
||||
* \brief Unlock the mutex.
|
||||
*
|
||||
* This function unlocks the specified mutex.
|
||||
*
|
||||
* \param[in] mutex_ptr Pointer to mutex structure.
|
||||
*
|
||||
* \return -EINVAL (Wrong pointer to the mutex structure provided.)
|
||||
* \return -EINVAL (This mutex has not been locked by this core.)
|
||||
* \return 0 (mutex successfully unlocked.)
|
||||
*
|
||||
* \see imx_sema4_mutex_lock
|
||||
*/
|
||||
int imx_sema4_mutex_unlock(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
int ret = 0, i = 0;
|
||||
|
||||
if ((mutex_ptr == NULL) || (mutex_ptr->valid != CORE_MUTEX_VALID))
|
||||
return -EINVAL;
|
||||
|
||||
i = mutex_ptr->gate_num;
|
||||
mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= SEMA4_GATE_MASK;
|
||||
/* make sure it is locked by this core */
|
||||
if (mutex_ptr->gate_val != SEMA4_A9_LOCK) {
|
||||
pr_err("%d Trying to unlock an unlock mutex.\n", __LINE__);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/* unlock it */
|
||||
mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= (~SEMA4_GATE_MASK);
|
||||
writeb(mutex_ptr->gate_val | SEMA4_UNLOCK, imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= SEMA4_GATE_MASK;
|
||||
/* make sure it is locked by this core */
|
||||
if (mutex_ptr->gate_val == SEMA4_A9_LOCK)
|
||||
pr_err("%d ERROR, failed to unlock the mutex.\n", __LINE__);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(imx_sema4_mutex_unlock);
|
||||
|
||||
/*
|
||||
* isr used by SEMA4, wake up the sleep tasks if there are the tasks waiting
|
||||
* for locking semaphore.
|
||||
* FIXME the bits order of the gatn, cpnie, cpnntf are not exact identified yet!
|
||||
*/
|
||||
static irqreturn_t imx_sema4_isr(int irq, void *dev_id)
|
||||
{
|
||||
int i;
|
||||
struct imx_sema4_mutex *mutex_ptr;
|
||||
unsigned int mask;
|
||||
struct imx_sema4_mutex_device *imx6_sema4 = dev_id;
|
||||
|
||||
imx6_sema4->cpntf_val = readw(imx6_sema4->ioaddr + SEMA4_CP0NTF);
|
||||
for (i = 0; i < SEMA4_NUM_GATES; i++) {
|
||||
mask = idx_sema4[i];
|
||||
if ((imx6_sema4->cpntf_val) & mask) {
|
||||
mutex_ptr = imx6_sema4->mutex_ptr[i];
|
||||
/*
|
||||
* An interrupt is pending on this mutex, the only way
|
||||
* to clear it is to lock it (either by this core or
|
||||
* another).
|
||||
*/
|
||||
mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= (~SEMA4_GATE_MASK);
|
||||
mutex_ptr->gate_val |= SEMA4_A9_LOCK;
|
||||
writeb(mutex_ptr->gate_val, imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val = readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= SEMA4_GATE_MASK;
|
||||
if (mutex_ptr->gate_val == SEMA4_A9_LOCK) {
|
||||
/*
|
||||
* wake up the wait queue, whatever there
|
||||
* are wait task or not.
|
||||
* NOTE: check gate is locted or not in
|
||||
* sema4_lock func by wait task.
|
||||
*/
|
||||
mutex_ptr->gate_val =
|
||||
readb(imx6_sema4->ioaddr + i);
|
||||
mutex_ptr->gate_val &= (~SEMA4_GATE_MASK);
|
||||
mutex_ptr->gate_val |= SEMA4_UNLOCK;
|
||||
|
||||
writeb(mutex_ptr->gate_val,
|
||||
imx6_sema4->ioaddr + i);
|
||||
wake_up(&mutex_ptr->wait_q);
|
||||
} else {
|
||||
pr_debug("can't lock gate%d %s retry!\n", i,
|
||||
mutex_ptr->gate_val ?
|
||||
"locked by m4" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_sema4_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6sx-sema4", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_sema4_dt_ids);
|
||||
|
||||
static int imx_sema4_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
imx6_sema4 = devm_kzalloc(&pdev->dev, sizeof(*imx6_sema4), GFP_KERNEL);
|
||||
if (!imx6_sema4)
|
||||
return -ENOMEM;
|
||||
|
||||
imx6_sema4->dev = &pdev->dev;
|
||||
imx6_sema4->cpine_val = 0;
|
||||
spin_lock_init(&imx6_sema4->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (IS_ERR(res)) {
|
||||
dev_err(&pdev->dev, "unable to get imx sema4 resource 0\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
imx6_sema4->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(imx6_sema4->ioaddr)) {
|
||||
ret = PTR_ERR(imx6_sema4->ioaddr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
imx6_sema4->irq = platform_get_irq(pdev, 0);
|
||||
if (!imx6_sema4->irq) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, imx6_sema4->irq, imx_sema4_isr,
|
||||
IRQF_SHARED, "imx6sx-sema4", imx6_sema4);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request imx sema4 irq\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, imx6_sema4);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_sema4_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver imx_sema4_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "imx-sema4",
|
||||
.of_match_table = imx_sema4_dt_ids,
|
||||
},
|
||||
.probe = imx_sema4_probe,
|
||||
.remove = imx_sema4_remove,
|
||||
};
|
||||
|
||||
static int __init imx_sema4_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&imx_sema4_driver);
|
||||
if (ret)
|
||||
pr_err("Unable to initialize sema4 driver\n");
|
||||
else
|
||||
pr_info("imx sema4 driver is registered.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit imx_sema4_exit(void)
|
||||
{
|
||||
pr_info("imx sema4 driver is unregistered.\n");
|
||||
platform_driver_unregister(&imx_sema4_driver);
|
||||
}
|
||||
|
||||
module_exit(imx_sema4_exit);
|
||||
module_init(imx_sema4_init);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IMX SEMA4 driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -114,6 +114,14 @@ config ARM_IMX_CPUFREQ_DT
|
|||
|
||||
If in doubt, say N.
|
||||
|
||||
config ARM_IMX7ULP_CPUFREQ
|
||||
tristate "NXP i.MX7ULP cpufreq support"
|
||||
depends on ARCH_MXC
|
||||
help
|
||||
This adds cpufreq driver support for NXP i.MX7ULP series SoCs.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
config ARM_KIRKWOOD_CPUFREQ
|
||||
def_bool MACH_KIRKWOOD
|
||||
help
|
||||
|
|
|
@ -57,6 +57,7 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
|
|||
obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o
|
||||
obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
|
||||
obj-$(CONFIG_ARM_IMX_CPUFREQ_DT) += imx-cpufreq-dt.o
|
||||
obj-$(CONFIG_ARM_IMX7ULP_CPUFREQ) += imx7ulp-cpufreq.o
|
||||
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
|
||||
obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o
|
||||
obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
|
||||
|
|
|
@ -44,19 +44,19 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev)
|
|||
mkt_segment = (cell_value & OCOTP_CFG3_MKT_SEGMENT_MASK) >> OCOTP_CFG3_MKT_SEGMENT_SHIFT;
|
||||
|
||||
/*
|
||||
* Early samples without fuses written report "0 0" which means
|
||||
* consumer segment and minimum speed grading.
|
||||
*
|
||||
* According to datasheet minimum speed grading is not supported for
|
||||
* consumer parts so clamp to 1 to avoid warning for "no OPPs"
|
||||
* Early samples without fuses written report "0 0" which may NOT
|
||||
* match any OPP defined in DT. So clamp to minimum OPP defined in
|
||||
* DT to avoid warning for "no OPPs".
|
||||
*
|
||||
* Applies to i.MX8M series SoCs.
|
||||
*/
|
||||
if (mkt_segment == 0 && speed_grade == 0 && (
|
||||
of_machine_is_compatible("fsl,imx8mm") ||
|
||||
of_machine_is_compatible("fsl,imx8mn") ||
|
||||
of_machine_is_compatible("fsl,imx8mq")))
|
||||
speed_grade = 1;
|
||||
if (mkt_segment == 0 && speed_grade == 0) {
|
||||
if (of_machine_is_compatible("fsl,imx8mm") ||
|
||||
of_machine_is_compatible("fsl,imx8mq"))
|
||||
speed_grade = 1;
|
||||
if (of_machine_is_compatible("fsl,imx8mn"))
|
||||
speed_grade = 0xb;
|
||||
}
|
||||
|
||||
supported_hw[0] = BIT(speed_grade);
|
||||
supported_hw[1] = BIT(mkt_segment);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (C) 2013 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/busfreq-imx.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpufreq.h>
|
||||
|
@ -14,14 +15,27 @@
|
|||
#include <linux/pm_opp.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#define PU_SOC_VOLTAGE_NORMAL 1250000
|
||||
#define PU_SOC_VOLTAGE_HIGH 1275000
|
||||
#define FREQ_1P2_GHZ 1200000000
|
||||
#define FREQ_396_MHZ 396000
|
||||
|
||||
static struct regulator *arm_reg;
|
||||
static struct regulator *pu_reg;
|
||||
static struct regulator *soc_reg;
|
||||
#define PU_SOC_VOLTAGE_NORMAL 1250000
|
||||
#define PU_SOC_VOLTAGE_HIGH 1275000
|
||||
#define DC_VOLTAGE_MIN 1300000
|
||||
#define DC_VOLTAGE_MAX 1400000
|
||||
#define FREQ_1P2_GHZ 1200000000
|
||||
#define FREQ_396_MHZ 396000
|
||||
#define FREQ_528_MHZ 528000
|
||||
#define FREQ_198_MHZ 198000
|
||||
#define FREQ_24_MHZ 24000
|
||||
|
||||
struct regulator *arm_reg;
|
||||
struct regulator *pu_reg;
|
||||
struct regulator *soc_reg;
|
||||
struct regulator *dc_reg;
|
||||
|
||||
enum IMX6_CPUFREQ_CLKS {
|
||||
ARM,
|
||||
|
@ -29,12 +43,15 @@ enum IMX6_CPUFREQ_CLKS {
|
|||
STEP,
|
||||
PLL1_SW,
|
||||
PLL2_PFD2_396M,
|
||||
PLL1,
|
||||
PLL1_BYPASS,
|
||||
PLL1_BYPASS_SRC,
|
||||
/* MX6UL requires two more clks */
|
||||
PLL2_BUS,
|
||||
SECONDARY_SEL,
|
||||
};
|
||||
#define IMX6Q_CPUFREQ_CLK_NUM 5
|
||||
#define IMX6UL_CPUFREQ_CLK_NUM 7
|
||||
#define IMX6Q_CPUFREQ_CLK_NUM 8
|
||||
#define IMX6UL_CPUFREQ_CLK_NUM 10
|
||||
|
||||
static int num_clks;
|
||||
static struct clk_bulk_data clks[] = {
|
||||
|
@ -43,6 +60,9 @@ static struct clk_bulk_data clks[] = {
|
|||
{ .id = "step" },
|
||||
{ .id = "pll1_sw" },
|
||||
{ .id = "pll2_pfd2_396m" },
|
||||
{ .id = "pll1" },
|
||||
{ .id = "pll1_bypass" },
|
||||
{ .id = "pll1_bypass_src" },
|
||||
{ .id = "pll2_bus" },
|
||||
{ .id = "secondary_sel" },
|
||||
};
|
||||
|
@ -56,6 +76,9 @@ static unsigned int transition_latency;
|
|||
static u32 *imx6_soc_volt;
|
||||
static u32 soc_opp_count;
|
||||
|
||||
static bool ignore_dc_reg;
|
||||
static bool low_power_run_support;
|
||||
|
||||
static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
struct dev_pm_opp *opp;
|
||||
|
@ -66,7 +89,16 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
|
|||
|
||||
new_freq = freq_table[index].frequency;
|
||||
freq_hz = new_freq * 1000;
|
||||
old_freq = clk_get_rate(clks[ARM].clk) / 1000;
|
||||
old_freq = policy->cur;
|
||||
|
||||
/*
|
||||
* ON i.MX6ULL, the 24MHz setpoint is not seen by cpufreq
|
||||
* so we neet to prevent the cpufreq change frequency
|
||||
* from 24MHz to 198Mhz directly. busfreq will handle this
|
||||
* when exit from low bus mode.
|
||||
*/
|
||||
if (old_freq == FREQ_24_MHZ && new_freq == FREQ_198_MHZ)
|
||||
return 0;
|
||||
|
||||
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
|
||||
if (IS_ERR(opp)) {
|
||||
|
@ -83,6 +115,13 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
|
|||
old_freq / 1000, volt_old / 1000,
|
||||
new_freq / 1000, volt / 1000);
|
||||
|
||||
if (low_power_run_support) {
|
||||
if (old_freq == freq_table[0].frequency)
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
} else if (old_freq <= FREQ_396_MHZ && new_freq > FREQ_396_MHZ) {
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
}
|
||||
|
||||
/* scaling up? scale voltage before frequency */
|
||||
if (new_freq > old_freq) {
|
||||
if (!IS_ERR(pu_reg)) {
|
||||
|
@ -143,11 +182,18 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
|
|||
clk_set_parent(clks[STEP].clk, clks[PLL2_PFD2_396M].clk);
|
||||
clk_set_parent(clks[PLL1_SW].clk, clks[STEP].clk);
|
||||
if (freq_hz > clk_get_rate(clks[PLL2_PFD2_396M].clk)) {
|
||||
/* Ensure that pll1_bypass is set back to
|
||||
* pll1. We have to do this first so that the
|
||||
* change rate done to pll1_sys_clk done below
|
||||
* can propagate up to pll1.
|
||||
*/
|
||||
clk_set_parent(clks[PLL1_BYPASS].clk, clks[PLL1].clk);
|
||||
clk_set_rate(clks[PLL1_SYS].clk, new_freq * 1000);
|
||||
clk_set_parent(clks[PLL1_SW].clk, clks[PLL1_SYS].clk);
|
||||
} else {
|
||||
/* pll1_sys needs to be enabled for divider rate change to work. */
|
||||
pll1_sys_temp_enabled = true;
|
||||
clk_set_parent(clks[PLL1_BYPASS].clk, clks[PLL1_BYPASS_SRC].clk);
|
||||
clk_prepare_enable(clks[PLL1_SYS].clk);
|
||||
}
|
||||
}
|
||||
|
@ -185,16 +231,34 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If CPU is dropped to the lowest level, release the need
|
||||
* for a high bus frequency.
|
||||
*/
|
||||
if (low_power_run_support) {
|
||||
if (new_freq == freq_table[0].frequency)
|
||||
release_bus_freq(BUS_FREQ_HIGH);
|
||||
} else if (old_freq > FREQ_396_MHZ && new_freq <= FREQ_396_MHZ) {
|
||||
release_bus_freq(BUS_FREQ_HIGH);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx6q_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
policy->clk = clks[ARM].clk;
|
||||
policy->cur = clk_get_rate(policy->clk) / 1000;
|
||||
cpufreq_generic_init(policy, freq_table, transition_latency);
|
||||
policy->suspend_freq = max_freq;
|
||||
dev_pm_opp_of_register_em(policy->cpus);
|
||||
|
||||
if (low_power_run_support && policy->cur > freq_table[0].frequency) {
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
} else if (policy->cur > FREQ_396_MHZ) {
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -324,6 +388,43 @@ static int imx6ul_opp_check_speed_grading(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int imx6_cpufreq_pm_notify(struct notifier_block *nb,
|
||||
unsigned long event, void *dummy)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (event) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
if (!IS_ERR(dc_reg) && !ignore_dc_reg) {
|
||||
ret = regulator_set_voltage_tol(dc_reg, DC_VOLTAGE_MAX, 0);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev,
|
||||
"failed to scale dc_reg to max: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PM_POST_SUSPEND:
|
||||
if (!IS_ERR(dc_reg) && !ignore_dc_reg) {
|
||||
ret = regulator_set_voltage_tol(dc_reg, DC_VOLTAGE_MIN, 0);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev,
|
||||
"failed to scale dc_reg to min: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block imx6_cpufreq_pm_notifier = {
|
||||
.notifier_call = imx6_cpufreq_pm_notify,
|
||||
};
|
||||
|
||||
static int imx6q_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -372,6 +473,11 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
|
|||
goto put_reg;
|
||||
}
|
||||
|
||||
dc_reg = devm_regulator_get_optional(cpu_dev, "dc");
|
||||
|
||||
/* On i.MX6ULL, check the 24MHz low power run mode support */
|
||||
low_power_run_support = of_property_read_bool(np, "fsl,low-power-run");
|
||||
|
||||
ret = dev_pm_opp_of_add_table(cpu_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(cpu_dev, "failed to init OPP table: %d\n", ret);
|
||||
|
@ -382,12 +488,14 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
|
|||
of_machine_is_compatible("fsl,imx6ull")) {
|
||||
ret = imx6ul_opp_check_speed_grading(cpu_dev);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto put_node;
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
dev_err(cpu_dev, "defer the cpufreq\n\n");
|
||||
goto out_free_opp;
|
||||
}
|
||||
|
||||
dev_err(cpu_dev, "failed to read ocotp: %d\n",
|
||||
ret);
|
||||
goto put_node;
|
||||
goto out_free_opp;
|
||||
}
|
||||
} else {
|
||||
imx6q_opp_check_speed_grading(cpu_dev);
|
||||
|
@ -408,6 +516,21 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
|
|||
goto out_free_opp;
|
||||
}
|
||||
|
||||
/*
|
||||
* On i.MX6UL/ULL EVK board, if the SOC is run in overide frequency,
|
||||
* the dc_regulator voltage should not be touched.
|
||||
*/
|
||||
if (freq_table[num - 1].frequency > FREQ_528_MHZ)
|
||||
ignore_dc_reg = true;
|
||||
if (!IS_ERR(dc_reg) && !ignore_dc_reg) {
|
||||
ret = regulator_set_voltage_tol(dc_reg, DC_VOLTAGE_MIN, 0);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev,
|
||||
"failed to scale dc_reg to min: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make imx6_soc_volt array's size same as arm opp number */
|
||||
imx6_soc_volt = devm_kcalloc(cpu_dev, num, sizeof(*imx6_soc_volt),
|
||||
GFP_KERNEL);
|
||||
|
@ -490,6 +613,8 @@ soc_opp_out:
|
|||
goto free_freq_table;
|
||||
}
|
||||
|
||||
register_pm_notifier(&imx6_cpufreq_pm_notifier);
|
||||
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* Copyright 2017 NXP.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#define MAX_RUN_FREQ 528000
|
||||
|
||||
static struct clk *arm_clk;
|
||||
static struct clk *core_div;
|
||||
static struct clk *sys_sel;
|
||||
static struct clk *hsrun_sys_sel;
|
||||
static struct clk *hsrun_core;
|
||||
static struct clk *spll_pfd0;
|
||||
static struct clk *spll_sel;
|
||||
static struct clk *firc_clk;
|
||||
static struct clk *spll;
|
||||
|
||||
static struct pm_qos_request pm_qos_hsrun;
|
||||
static struct regulator *arm_reg;
|
||||
static struct device *cpu_dev;
|
||||
static struct cpufreq_frequency_table *freq_table;
|
||||
static unsigned int transition_latency;
|
||||
|
||||
static int imx7ulp_set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long freq_hz, volt, volt_old;
|
||||
unsigned int old_freq, new_freq;
|
||||
int ret;
|
||||
|
||||
new_freq = freq_table[index].frequency;
|
||||
freq_hz = new_freq * 1000;
|
||||
old_freq = clk_get_rate(arm_clk) / 1000;
|
||||
if (new_freq == 0 || old_freq == 0)
|
||||
return -EINVAL;
|
||||
|
||||
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
|
||||
if (IS_ERR(opp)) {
|
||||
dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz);
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
volt = dev_pm_opp_get_voltage(opp);
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
volt_old = regulator_get_voltage(arm_reg);
|
||||
|
||||
dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n",
|
||||
old_freq / 1000, volt_old / 1000,
|
||||
new_freq / 1000, volt / 1000);
|
||||
|
||||
/* Scaling up? scale voltage before frequency */
|
||||
if (new_freq > old_freq) {
|
||||
ret = regulator_set_voltage_tol(arm_reg, volt, 0);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "failed to scale vddarm up: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* before changing pll_arm rate, change the arm_src's soure
|
||||
* to firc clk first.
|
||||
*/
|
||||
if (new_freq > MAX_RUN_FREQ) {
|
||||
pm_qos_add_request(&pm_qos_hsrun, PM_QOS_CPU_DMA_LATENCY, 0);
|
||||
/* change the RUN clock to firc */
|
||||
clk_set_parent(sys_sel, firc_clk);
|
||||
/* change the clock rate in HSRUN */
|
||||
clk_set_rate(spll, 480000000);
|
||||
clk_set_rate(spll_pfd0, new_freq * 1000);
|
||||
clk_set_parent(hsrun_sys_sel, spll_sel);
|
||||
clk_set_parent(arm_clk, hsrun_core);
|
||||
} else {
|
||||
/* change the HSRUN clock to firc */
|
||||
clk_set_parent(hsrun_sys_sel, firc_clk);
|
||||
/* change the clock rate in RUN */
|
||||
clk_set_rate(spll, 528000000);
|
||||
clk_set_rate(spll_pfd0, new_freq * 1000);
|
||||
clk_set_parent(sys_sel, spll_sel);
|
||||
clk_set_parent(arm_clk, core_div);
|
||||
if (old_freq > MAX_RUN_FREQ)
|
||||
pm_qos_remove_request(&pm_qos_hsrun);
|
||||
}
|
||||
|
||||
/* scaling down? scaling voltage after frequency */
|
||||
if (new_freq < old_freq) {
|
||||
ret = regulator_set_voltage_tol(arm_reg, volt, 0);
|
||||
if (ret) {
|
||||
dev_warn(cpu_dev, "failed to scale vddarm down: %d\n", ret);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx7ulp_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
policy->clk = arm_clk;
|
||||
policy->cur = clk_get_rate(arm_clk) / 1000;
|
||||
policy->suspend_freq = freq_table[0].frequency;
|
||||
|
||||
cpufreq_generic_init(policy, freq_table, transition_latency);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cpufreq_driver imx7ulp_cpufreq_driver = {
|
||||
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
|
||||
.verify = cpufreq_generic_frequency_table_verify,
|
||||
.target_index = imx7ulp_set_target,
|
||||
.get = cpufreq_generic_get,
|
||||
.init = imx7ulp_cpufreq_init,
|
||||
.name = "imx7ulp-cpufreq",
|
||||
.attr = cpufreq_generic_attr,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = cpufreq_generic_suspend,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int imx7ulp_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
cpu_dev = get_cpu_device(0);
|
||||
if (!cpu_dev) {
|
||||
pr_err("failed to get cpu0 device\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
np = of_node_get(cpu_dev->of_node);
|
||||
if (!np) {
|
||||
dev_err(cpu_dev, "failed to find the cpu0 node\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
arm_clk = clk_get(cpu_dev, "arm");
|
||||
sys_sel = clk_get(cpu_dev, "sys_sel");
|
||||
core_div = clk_get(cpu_dev, "core_div");
|
||||
hsrun_sys_sel = clk_get(cpu_dev, "hsrun_sys_sel");
|
||||
hsrun_core = clk_get(cpu_dev, "hsrun_core");
|
||||
spll_pfd0 = clk_get(cpu_dev, "spll_pfd0");
|
||||
spll_sel = clk_get(cpu_dev, "spll_sel");
|
||||
firc_clk = clk_get(cpu_dev, "firc");
|
||||
spll = clk_get(cpu_dev, "spll");
|
||||
|
||||
if (IS_ERR(arm_clk) || IS_ERR(sys_sel) || IS_ERR(spll_sel) ||
|
||||
IS_ERR(spll_sel) || IS_ERR(firc_clk) || IS_ERR(hsrun_sys_sel) ||
|
||||
IS_ERR(hsrun_core) || IS_ERR(spll)) {
|
||||
dev_err(cpu_dev, "failed to get cpu clock\n");
|
||||
ret = -ENOENT;
|
||||
goto put_clk;
|
||||
}
|
||||
|
||||
arm_reg = regulator_get(cpu_dev, "arm");
|
||||
if (IS_ERR(arm_reg)) {
|
||||
dev_err(cpu_dev, "failed to get regulator\n");
|
||||
ret = -ENOENT;
|
||||
goto put_reg;
|
||||
}
|
||||
|
||||
ret = dev_pm_opp_of_add_table(cpu_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(cpu_dev, "failed to init OPP table: %d\n", ret);
|
||||
goto put_reg;
|
||||
}
|
||||
|
||||
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "failed to init cpufreq table\n");
|
||||
goto put_reg;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "clock-latency", &transition_latency))
|
||||
transition_latency = CPUFREQ_ETERNAL;
|
||||
|
||||
ret = cpufreq_register_driver(&imx7ulp_cpufreq_driver);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "failed to register driver\n");
|
||||
goto free_opp_table;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_opp_table:
|
||||
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
||||
put_reg:
|
||||
regulator_put(arm_reg);
|
||||
put_clk:
|
||||
if (!IS_ERR(arm_clk))
|
||||
clk_put(arm_clk);
|
||||
if (!IS_ERR(sys_sel))
|
||||
clk_put(sys_sel);
|
||||
if (!IS_ERR(core_div))
|
||||
clk_put(core_div);
|
||||
if (!IS_ERR(hsrun_sys_sel))
|
||||
clk_put(hsrun_sys_sel);
|
||||
if (!IS_ERR(hsrun_core))
|
||||
clk_put(hsrun_core);
|
||||
if (!IS_ERR(spll_pfd0))
|
||||
clk_put(spll_pfd0);
|
||||
if (!IS_ERR(spll_sel))
|
||||
clk_put(spll_sel);
|
||||
if (!IS_ERR(firc_clk))
|
||||
clk_put(firc_clk);
|
||||
if (!IS_ERR(spll))
|
||||
clk_put(spll);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx7ulp_cpufreq_remove(struct platform_device *pdev)
|
||||
{
|
||||
cpufreq_unregister_driver(&imx7ulp_cpufreq_driver);
|
||||
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
||||
|
||||
regulator_put(arm_reg);
|
||||
clk_put(arm_clk);
|
||||
clk_put(sys_sel);
|
||||
clk_put(core_div);
|
||||
clk_put(hsrun_sys_sel);
|
||||
clk_put(hsrun_core);
|
||||
clk_put(spll_pfd0);
|
||||
clk_put(spll_sel);
|
||||
clk_put(firc_clk);
|
||||
clk_put(spll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver imx7ulp_cpufreq_platdrv = {
|
||||
.driver = {
|
||||
.name = "imx7ulp-cpufreq",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = imx7ulp_cpufreq_probe,
|
||||
.remove = imx7ulp_cpufreq_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(imx7ulp_cpufreq_platdrv);
|
||||
|
||||
MODULE_DESCRIPTION("NXP i.MX7ULP cpufreq driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -44,9 +44,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <dt-bindings/firmware/imx/rsrc.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
@ -55,6 +58,17 @@
|
|||
#include <linux/pm.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#define IMX_WU_MAX_IRQS (((IMX_SC_R_LAST + 31) / 32 ) * 32 )
|
||||
|
||||
#define IMX_SIP_WAKEUP_SRC 0xc2000009
|
||||
#define IMX_SIP_WAKEUP_SRC_SCU 0x1
|
||||
#define IMX_SIP_WAKEUP_SRC_IRQSTEER 0x2
|
||||
|
||||
static u32 wu[IMX_WU_MAX_IRQS];
|
||||
static int wu_num;
|
||||
static void __iomem *gic_dist_base;
|
||||
|
||||
/* SCU Power Mode Protocol definition */
|
||||
struct imx_sc_msg_req_set_resource_power_mode {
|
||||
|
@ -85,6 +99,8 @@ struct imx_sc_pd_soc {
|
|||
u8 num_ranges;
|
||||
};
|
||||
|
||||
int imx_con_rsrc;
|
||||
|
||||
static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
||||
/* LSIO SS */
|
||||
{ "pwm", IMX_SC_R_PWM_0, 8, true, 0 },
|
||||
|
@ -109,14 +125,26 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
|||
{ "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 },
|
||||
{ "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 },
|
||||
{ "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 },
|
||||
{ "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 },
|
||||
{ "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 },
|
||||
{ "mclk-out-0", IMX_SC_R_MCLK_OUT_0, 1, false, 0 },
|
||||
{ "mclk-out-1", IMX_SC_R_MCLK_OUT_1, 1, false, 0 },
|
||||
{ "dma0-ch", IMX_SC_R_DMA_0_CH0, 32, true, 0 },
|
||||
{ "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 },
|
||||
{ "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 },
|
||||
{ "dma2-ch-0", IMX_SC_R_DMA_2_CH0, 5, true, 0 },
|
||||
{ "dma2-ch-1", IMX_SC_R_DMA_2_CH5, 27, true, 0 },
|
||||
{ "dma3-ch", IMX_SC_R_DMA_3_CH0, 32, true, 0 },
|
||||
{ "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 },
|
||||
{ "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 },
|
||||
{ "esai0", IMX_SC_R_ESAI_0, 1, false, 0 },
|
||||
{ "esai1", IMX_SC_R_ESAI_1, 1, false, 0 },
|
||||
{ "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 },
|
||||
{ "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 },
|
||||
{ "sai", IMX_SC_R_SAI_0, 3, true, 0 },
|
||||
{ "sai3", IMX_SC_R_SAI_3, 1, false, 0 },
|
||||
{ "sai4", IMX_SC_R_SAI_4, 1, false, 0 },
|
||||
{ "sai5", IMX_SC_R_SAI_5, 1, false, 0 },
|
||||
{ "sai6", IMX_SC_R_SAI_6, 1, false, 0 },
|
||||
{ "sai7", IMX_SC_R_SAI_7, 1, false, 0 },
|
||||
{ "amix", IMX_SC_R_AMIX, 1, false, 0 },
|
||||
{ "mqs0", IMX_SC_R_MQS_0, 1, false, 0 },
|
||||
{ "dsp", IMX_SC_R_DSP, 1, false, 0 },
|
||||
|
@ -130,6 +158,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
|||
{ "lcd", IMX_SC_R_LCD_0, 1, true, 0 },
|
||||
{ "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 },
|
||||
{ "lpuart", IMX_SC_R_UART_0, 4, true, 0 },
|
||||
{ "sim", IMX_SC_R_EMVSIM_0, 2, true, 0 },
|
||||
{ "lpspi", IMX_SC_R_SPI_0, 4, true, 0 },
|
||||
{ "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 },
|
||||
|
||||
|
@ -138,13 +167,22 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
|||
{ "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 },
|
||||
{ "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 },
|
||||
{ "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 },
|
||||
{ "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 },
|
||||
{ "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 },
|
||||
{ "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 },
|
||||
{ "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 },
|
||||
|
||||
/* GPU SS */
|
||||
{ "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 },
|
||||
{ "gpu1-pid", IMX_SC_R_GPU_1_PID0, 4, true, 0 },
|
||||
|
||||
|
||||
/* HSIO SS */
|
||||
{ "pcie-a", IMX_SC_R_PCIE_A, 1, false, 0 },
|
||||
{ "serdes-0", IMX_SC_R_SERDES_0, 1, false, 0 },
|
||||
{ "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 },
|
||||
{ "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 },
|
||||
{ "sata-0", IMX_SC_R_SATA_0, 1, false, 0 },
|
||||
{ "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 },
|
||||
|
||||
/* MIPI SS */
|
||||
|
@ -152,12 +190,65 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
|||
{ "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 },
|
||||
{ "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 },
|
||||
|
||||
{ "mipi1", IMX_SC_R_MIPI_1, 1, 0 },
|
||||
{ "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, 0 },
|
||||
{ "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, 1 },
|
||||
|
||||
/* LVDS SS */
|
||||
{ "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 },
|
||||
{ "lvds0-i2c0", IMX_SC_R_LVDS_0_I2C_0, 1, false, 0 },
|
||||
{ "lvds0-pwm0", IMX_SC_R_LVDS_0_PWM_0, 1, false, 0 },
|
||||
|
||||
{ "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 },
|
||||
{ "lvds1-i2c0", IMX_SC_R_LVDS_1_I2C_0, 1, false, 0 },
|
||||
{ "lvds1-pwm0", IMX_SC_R_LVDS_1_PWM_0, 1, false, 0 },
|
||||
|
||||
/* DC SS */
|
||||
{ "dc0", IMX_SC_R_DC_0, 1, false, 0 },
|
||||
{ "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 },
|
||||
{ "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 },
|
||||
|
||||
{ "dc1", IMX_SC_R_DC_1, 1, false, 0 },
|
||||
{ "dc1-pll", IMX_SC_R_DC_1_PLL_0, 2, true, 0 },
|
||||
{ "dc1-video", IMX_SC_R_DC_1_VIDEO0, 2, true, 0 },
|
||||
|
||||
/* CM40 SS */
|
||||
{ "cm40_i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 },
|
||||
{ "cm40_intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 },
|
||||
|
||||
/* CM41 SS */
|
||||
{ "cm41_i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 },
|
||||
{ "cm41_intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 },
|
||||
|
||||
/* IMAGE SS */
|
||||
{ "img-pdma", IMX_SC_R_ISI_CH0, 8, true, 0 },
|
||||
{ "img-csi0", IMX_SC_R_CSI_0, 1, false, 0 },
|
||||
{ "img-csi0-i2c0", IMX_SC_R_CSI_0_I2C_0, 1, false, 0 },
|
||||
{ "img-csi0-pwm0", IMX_SC_R_CSI_0_PWM_0, 1, false, 0 },
|
||||
{ "img-csi1", IMX_SC_R_CSI_1, 1, false, 0 },
|
||||
{ "img-csi1-i2c0", IMX_SC_R_CSI_1_I2C_0, 1, false, 0 },
|
||||
{ "img-csi1-pwm0", IMX_SC_R_CSI_1_PWM_0, 1, false, 0 },
|
||||
{ "img-parallel", IMX_SC_R_PI_0, 1, false, 0 },
|
||||
{ "img-parallel-i2c0", IMX_SC_R_PI_0_I2C_0, 1, false, 0 },
|
||||
{ "img-parallel-pwm0", IMX_SC_R_PI_0_PWM_0, 2, true, 0 },
|
||||
{ "img-parallel-pll", IMX_SC_R_PI_0_PLL, 1, false, 0 },
|
||||
{ "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 },
|
||||
{ "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 },
|
||||
{ "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 },
|
||||
{ "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 },
|
||||
|
||||
/* HDMI TX SS */
|
||||
{ "hdmi-tx", IMX_SC_R_HDMI, 1, false, 0},
|
||||
{ "hdmi-tx-i2s", IMX_SC_R_HDMI_I2S, 1, false, 0},
|
||||
{ "hdmi-tx-i2c0", IMX_SC_R_HDMI_I2C_0, 1, false, 0},
|
||||
{ "hdmi-tx-pll0", IMX_SC_R_HDMI_PLL_0, 1, false, 0},
|
||||
{ "hdmi-tx-pll1", IMX_SC_R_HDMI_PLL_1, 1, false, 0},
|
||||
|
||||
/* SECURITY SS */
|
||||
{ "sec-jr", IMX_SC_R_CAAM_JR2, 2, true, 2},
|
||||
|
||||
/* BOARD SS */
|
||||
{ "board", IMX_SC_R_BOARD_R0, 8, true, 0},
|
||||
};
|
||||
|
||||
static const struct imx_sc_pd_soc imx8qxp_scu_pd = {
|
||||
|
@ -173,6 +264,73 @@ to_imx_sc_pd(struct generic_pm_domain *genpd)
|
|||
return container_of(genpd, struct imx_sc_pm_domain, pd);
|
||||
}
|
||||
|
||||
static int imx_pm_domains_suspend(void)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
u32 offset;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < wu_num; i++) {
|
||||
offset = GICD_ISENABLER + ((wu[i] + 32) / 32) * 4;
|
||||
if (BIT(wu[i] % 32) & readl_relaxed(gic_dist_base + offset)) {
|
||||
arm_smccc_smc(IMX_SIP_WAKEUP_SRC,
|
||||
IMX_SIP_WAKEUP_SRC_IRQSTEER,
|
||||
0, 0, 0, 0, 0, 0, &res);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
arm_smccc_smc(IMX_SIP_WAKEUP_SRC,
|
||||
IMX_SIP_WAKEUP_SRC_SCU,
|
||||
0, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct syscore_ops imx_pm_domains_syscore_ops = {
|
||||
.suspend = imx_pm_domains_suspend,
|
||||
};
|
||||
|
||||
static void imx_sc_pd_enable_irqsteer_wakeup(struct device_node *np)
|
||||
{
|
||||
struct device_node *gic_node;
|
||||
unsigned int i;
|
||||
|
||||
wu_num = of_property_count_u32_elems(np, "wakeup-irq");
|
||||
if (wu_num <= 0) {
|
||||
pr_warn("no irqsteer wakeup source supported!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gic_node = of_find_compatible_node(NULL, NULL, "arm,gic-v3");
|
||||
WARN_ON(!gic_node);
|
||||
|
||||
gic_dist_base = of_iomap(gic_node, 0);
|
||||
WARN_ON(!gic_dist_base);
|
||||
|
||||
for (i = 0; i < wu_num; i++)
|
||||
WARN_ON(of_property_read_u32_index(np, "wakeup-irq", i, &wu[i]));
|
||||
|
||||
register_syscore_ops(&imx_pm_domains_syscore_ops);
|
||||
}
|
||||
|
||||
static void imx_sc_pd_get_console_rsrc(void)
|
||||
{
|
||||
struct of_phandle_args specs;
|
||||
int ret;
|
||||
|
||||
if (!of_stdout)
|
||||
return;
|
||||
|
||||
ret = of_parse_phandle_with_args(of_stdout, "power-domains",
|
||||
"#power-domain-cells",
|
||||
0, &specs);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
imx_con_rsrc = specs.args[0];
|
||||
}
|
||||
|
||||
static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on)
|
||||
{
|
||||
struct imx_sc_msg_req_set_resource_power_mode msg;
|
||||
|
@ -188,7 +346,12 @@ static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on)
|
|||
hdr->size = 2;
|
||||
|
||||
msg.resource = pd->rsrc;
|
||||
msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP;
|
||||
msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : pd->pd.state_idx ?
|
||||
IMX_SC_PM_PW_MODE_OFF : IMX_SC_PM_PW_MODE_LP;
|
||||
|
||||
/* keep uart console power on for no_console_suspend */
|
||||
if (imx_con_rsrc == pd->rsrc && !console_suspend_enabled && !power_on)
|
||||
return 0;
|
||||
|
||||
ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true);
|
||||
if (ret)
|
||||
|
@ -233,15 +396,31 @@ imx_scu_add_pm_domain(struct device *dev, int idx,
|
|||
const struct imx_sc_pd_range *pd_ranges)
|
||||
{
|
||||
struct imx_sc_pm_domain *sc_pd;
|
||||
struct genpd_power_state *states;
|
||||
bool is_off = true;
|
||||
int ret;
|
||||
|
||||
sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL);
|
||||
if (!sc_pd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
states = devm_kcalloc(dev, 2, sizeof(*states), GFP_KERNEL);
|
||||
if (!states) {
|
||||
devm_kfree(dev, sc_pd);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
sc_pd->rsrc = pd_ranges->rsrc + idx;
|
||||
sc_pd->pd.power_off = imx_sc_pd_power_off;
|
||||
sc_pd->pd.power_on = imx_sc_pd_power_on;
|
||||
sc_pd->pd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
|
||||
states[0].power_off_latency_ns = 25000;
|
||||
states[0].power_on_latency_ns = 25000;
|
||||
states[1].power_off_latency_ns = 2500000;
|
||||
states[1].power_on_latency_ns = 2500000;
|
||||
|
||||
sc_pd->pd.states = states;
|
||||
sc_pd->pd.state_count = 2;
|
||||
|
||||
if (pd_ranges->postfix)
|
||||
snprintf(sc_pd->name, sizeof(sc_pd->name),
|
||||
|
@ -251,20 +430,26 @@ imx_scu_add_pm_domain(struct device *dev, int idx,
|
|||
"%s", pd_ranges->name);
|
||||
|
||||
sc_pd->pd.name = sc_pd->name;
|
||||
if (imx_con_rsrc == sc_pd->rsrc) {
|
||||
sc_pd->pd.flags |= GENPD_FLAG_RPM_ALWAYS_ON;
|
||||
is_off = false;
|
||||
}
|
||||
|
||||
if (sc_pd->rsrc >= IMX_SC_R_LAST) {
|
||||
dev_warn(dev, "invalid pd %s rsrc id %d found",
|
||||
sc_pd->name, sc_pd->rsrc);
|
||||
|
||||
devm_kfree(dev, sc_pd);
|
||||
devm_kfree(dev, states);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = pm_genpd_init(&sc_pd->pd, NULL, true);
|
||||
ret = pm_genpd_init(&sc_pd->pd, NULL, is_off);
|
||||
if (ret) {
|
||||
dev_warn(dev, "failed to init pd %s rsrc id %d",
|
||||
sc_pd->name, sc_pd->rsrc);
|
||||
devm_kfree(dev, sc_pd);
|
||||
devm_kfree(dev, states);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -326,6 +511,9 @@ static int imx_sc_pd_probe(struct platform_device *pdev)
|
|||
if (!pd_soc)
|
||||
return -ENODEV;
|
||||
|
||||
imx_sc_pd_get_console_rsrc();
|
||||
imx_sc_pd_enable_irqsteer_wakeup(pdev->dev.of_node);
|
||||
|
||||
return imx_scu_init_pm_domains(&pdev->dev, pd_soc);
|
||||
}
|
||||
|
||||
|
@ -342,7 +530,12 @@ static struct platform_driver imx_sc_pd_driver = {
|
|||
},
|
||||
.probe = imx_sc_pd_probe,
|
||||
};
|
||||
builtin_platform_driver(imx_sc_pd_driver);
|
||||
|
||||
static int __init imx_sc_pd_driver_init(void)
|
||||
{
|
||||
return platform_driver_register(&imx_sc_pd_driver);
|
||||
}
|
||||
subsys_initcall(imx_sc_pd_driver_init);
|
||||
|
||||
MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
|
||||
MODULE_DESCRIPTION("IMX SCU Power Domain driver");
|
||||
|
|
|
@ -441,6 +441,14 @@ config CHARGER_ISP1704
|
|||
Say Y to enable support for USB Charger Detection with
|
||||
ISP1707/ISP1704 USB transceivers.
|
||||
|
||||
config SABRESD_MAX8903
|
||||
tristate "Sabresd Board Battery DC-DC Charger for USB and Adapter Power"
|
||||
depends on TOUCHSCREEN_MAX11801
|
||||
help
|
||||
Say Y to enable support for the MAX8903 DC-DC charger and sysfs on
|
||||
sabresd board.The driver supports controlling charger and battery
|
||||
based on the status of charger connections with interrupt handlers.
|
||||
|
||||
config CHARGER_MAX8903
|
||||
tristate "MAX8903 Battery DC-DC Charger for USB and Adapter Power"
|
||||
help
|
||||
|
|
|
@ -62,6 +62,7 @@ obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
|
|||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o
|
||||
obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o
|
||||
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
|
||||
obj-$(CONFIG_SABRESD_MAX8903) += sabresd_battery.o
|
||||
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
|
||||
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
|
||||
obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,4 +17,9 @@ config IMX_SCU_SOC
|
|||
Controller Unit SoC info module, it will provide the SoC info
|
||||
like SoC family, ID and revision etc.
|
||||
|
||||
config IMX8M_PM_DOMAINS
|
||||
bool "i.MX8M PM domains"
|
||||
depends on ARCH_MXC || (COMPILE_TEST && OF)
|
||||
depends on PM
|
||||
select PM_GENERIC_DOMAINS
|
||||
endmenu
|
||||
|
|
|
@ -3,3 +3,4 @@ obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
|
|||
obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
|
||||
obj-$(CONFIG_ARCH_MXC) += soc-imx8.o
|
||||
obj-$(CONFIG_IMX_SCU_SOC) += soc-imx-scu.o
|
||||
obj-$(CONFIG_IMX8M_PM_DOMAINS) += imx8m_pm_domains.o
|
||||
|
|
|
@ -39,6 +39,11 @@
|
|||
|
||||
#define PGC_DOMAIN_FLAG_NO_PD BIT(0)
|
||||
|
||||
#define GPC_PGC_DOMAIN_ARM 0
|
||||
#define GPC_PGC_DOMAIN_PU 1
|
||||
#define GPC_PGC_DOMAIN_DISPLAY 2
|
||||
#define GPC_PGC_DOMAIN_PCI 3
|
||||
|
||||
struct imx_pm_domain {
|
||||
struct generic_pm_domain base;
|
||||
struct regmap *regmap;
|
||||
|
@ -175,6 +180,8 @@ static int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain)
|
|||
return imx_pgc_get_clocks(dev, domain);
|
||||
}
|
||||
|
||||
static void imx_gpc_handle_ldobypass(struct platform_device *pdev);
|
||||
|
||||
static int imx_pgc_power_domain_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_pm_domain *domain = pdev->dev.platform_data;
|
||||
|
@ -201,6 +208,10 @@ static int imx_pgc_power_domain_probe(struct platform_device *pdev)
|
|||
|
||||
device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE_CONSUMER);
|
||||
|
||||
/* Mark PU regulator as bypass */
|
||||
if (pdev->id == GPC_PGC_DOMAIN_PU)
|
||||
imx_gpc_handle_ldobypass(pdev);
|
||||
|
||||
return 0;
|
||||
|
||||
genpd_err:
|
||||
|
@ -238,11 +249,6 @@ static struct platform_driver imx_pgc_power_domain_driver = {
|
|||
};
|
||||
builtin_platform_driver(imx_pgc_power_domain_driver)
|
||||
|
||||
#define GPC_PGC_DOMAIN_ARM 0
|
||||
#define GPC_PGC_DOMAIN_PU 1
|
||||
#define GPC_PGC_DOMAIN_DISPLAY 2
|
||||
#define GPC_PGC_DOMAIN_PCI 3
|
||||
|
||||
static struct genpd_power_state imx6_pm_domain_pu_state = {
|
||||
.power_off_latency_ns = 25000,
|
||||
.power_on_latency_ns = 2000000,
|
||||
|
@ -399,6 +405,22 @@ clk_err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void imx_gpc_handle_ldobypass(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_pm_domain *domain = pdev->dev.platform_data;
|
||||
struct regulator *pu_reg = domain->supply;
|
||||
u32 bypass = 0;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.parent->of_node, "fsl,ldo-bypass", &bypass);
|
||||
if (ret && ret != -EINVAL)
|
||||
dev_warn(pdev->dev.parent, "failed to read fsl,ldo-bypass property: %d\n", ret);
|
||||
|
||||
/* We only bypass pu since arm and soc has been set in u-boot */
|
||||
if (pu_reg && bypass)
|
||||
regulator_allow_bypass(pu_reg, true);
|
||||
}
|
||||
|
||||
static int imx_gpc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id =
|
||||
|
@ -453,6 +475,8 @@ static int imx_gpc_probe(struct platform_device *pdev)
|
|||
of_id_data->num_domains);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
imx_gpc_handle_ldobypass(pdev);
|
||||
} else {
|
||||
struct imx_pm_domain *domain;
|
||||
struct platform_device *pd_pdev;
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2019 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <soc/imx/imx_sip.h>
|
||||
|
||||
#define MAX_CLK_NUM 6
|
||||
#define to_imx8m_pm_domain(_genpd) container_of(_genpd, struct imx8m_pm_domain, pd)
|
||||
|
||||
|
||||
struct imx8m_pm_domain {
|
||||
struct device *dev;
|
||||
struct generic_pm_domain pd;
|
||||
u32 domain_index;
|
||||
struct clk *clk[MAX_CLK_NUM];
|
||||
unsigned int num_clks;
|
||||
struct regulator *reg;
|
||||
};
|
||||
|
||||
enum imx8m_pm_domain_state {
|
||||
PD_STATE_OFF,
|
||||
PD_STATE_ON,
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(gpc_pd_mutex);
|
||||
|
||||
static int imx8m_pd_power_on(struct generic_pm_domain *genpd)
|
||||
{
|
||||
struct imx8m_pm_domain *domain = to_imx8m_pm_domain(genpd);
|
||||
struct arm_smccc_res res;
|
||||
int index, ret = 0;
|
||||
|
||||
/* power on the external supply */
|
||||
if (!IS_ERR(domain->reg)) {
|
||||
ret = regulator_enable(domain->reg);
|
||||
if (ret) {
|
||||
dev_warn(domain->dev, "failed to power up the reg%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* enable the necessary clks needed by the power domain */
|
||||
if (domain->num_clks) {
|
||||
for (index = 0; index < domain->num_clks; index++)
|
||||
clk_prepare_enable(domain->clk[index]);
|
||||
}
|
||||
|
||||
mutex_lock(&gpc_pd_mutex);
|
||||
arm_smccc_smc(IMX_SIP_GPC, IMX_SIP_CONFIG_GPC_PM_DOMAIN, domain->domain_index,
|
||||
PD_STATE_ON, 0, 0, 0, 0, &res);
|
||||
mutex_unlock(&gpc_pd_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8m_pd_power_off(struct generic_pm_domain *genpd)
|
||||
{
|
||||
struct imx8m_pm_domain *domain = to_imx8m_pm_domain(genpd);
|
||||
struct arm_smccc_res res;
|
||||
int index, ret = 0;
|
||||
|
||||
mutex_lock(&gpc_pd_mutex);
|
||||
arm_smccc_smc(IMX_SIP_GPC, IMX_SIP_CONFIG_GPC_PM_DOMAIN, domain->domain_index,
|
||||
PD_STATE_OFF, 0, 0, 0, 0, &res);
|
||||
mutex_unlock(&gpc_pd_mutex);
|
||||
|
||||
/* power off the external supply */
|
||||
if (!IS_ERR(domain->reg)) {
|
||||
ret = regulator_disable(domain->reg);
|
||||
if (ret) {
|
||||
dev_warn(domain->dev, "failed to power off the reg%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* disable clks when power domain is off */
|
||||
if (domain->num_clks) {
|
||||
for (index = 0; index < domain->num_clks; index++)
|
||||
clk_disable_unprepare(domain->clk[index]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static int imx8m_pd_get_clocks(struct imx8m_pm_domain *domain)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
struct clk *clk = of_clk_get(domain->dev->of_node, i);
|
||||
if (IS_ERR(clk))
|
||||
break;
|
||||
if (i >= MAX_CLK_NUM) {
|
||||
dev_err(domain->dev, "more than %d clocks\n",
|
||||
MAX_CLK_NUM);
|
||||
ret = -EINVAL;
|
||||
goto clk_err;
|
||||
}
|
||||
domain->clk[i] = clk;
|
||||
}
|
||||
domain->num_clks = i;
|
||||
|
||||
return 0;
|
||||
|
||||
clk_err:
|
||||
while (i--)
|
||||
clk_put(domain->clk[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void imx8m_pd_put_clocks(struct imx8m_pm_domain *domain)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = domain->num_clks - 1; i >= 0; i--)
|
||||
clk_put(domain->clk[i]);
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8m_pm_domain_ids[] = {
|
||||
{.compatible = "fsl,imx8m-pm-domain"},
|
||||
{},
|
||||
};
|
||||
|
||||
static int imx8m_pm_domain_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct imx8m_pm_domain *domain;
|
||||
struct of_phandle_args parent, child;
|
||||
int ret;
|
||||
|
||||
domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL);
|
||||
if (!domain)
|
||||
return -ENOMEM;
|
||||
|
||||
child.np = np;
|
||||
domain->dev = dev;
|
||||
|
||||
ret = of_property_read_string(np, "domain-name", &domain->pd.name);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get the domain name\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "domain-index", &domain->domain_index);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get the domain index\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
domain->reg = devm_regulator_get_optional(dev, "power");
|
||||
if (IS_ERR(domain->reg)) {
|
||||
if (PTR_ERR(domain->reg) != -ENODEV) {
|
||||
if (PTR_ERR(domain->reg) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get domain's regulator\n");
|
||||
return PTR_ERR(domain->reg);
|
||||
}
|
||||
}
|
||||
|
||||
ret = imx8m_pd_get_clocks(domain);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get domain's clocks\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
domain->pd.power_off = imx8m_pd_power_off;
|
||||
domain->pd.power_on = imx8m_pd_power_on;
|
||||
|
||||
pm_genpd_init(&domain->pd, NULL, true);
|
||||
|
||||
ret = of_genpd_add_provider_simple(np, &domain->pd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add the domain provider\n");
|
||||
pm_genpd_remove(&domain->pd);
|
||||
imx8m_pd_put_clocks(domain);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* add it as subdomain if necessary */
|
||||
if (!of_parse_phandle_with_args(np, "parent-domains",
|
||||
"#power-domain-cells", 0, &parent)) {
|
||||
ret = of_genpd_add_subdomain(&parent, &child);
|
||||
of_node_put(parent.np);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev, "failed to add the subdomain: %s: %d",
|
||||
domain->pd.name, ret);
|
||||
of_genpd_del_provider(np);
|
||||
pm_genpd_remove(&domain->pd);
|
||||
imx8m_pd_put_clocks(domain);
|
||||
return driver_deferred_probe_check_state(dev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver imx8m_pm_domain_driver = {
|
||||
.driver = {
|
||||
.name = "imx8m_pm_domain",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = imx8m_pm_domain_ids,
|
||||
},
|
||||
.probe = imx8m_pm_domain_probe,
|
||||
};
|
||||
module_platform_driver(imx8m_pm_domain_driver);
|
||||
|
||||
MODULE_AUTHOR("NXP");
|
||||
MODULE_DESCRIPTION("NXP i.MX8M power domain driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_IMX_SEMA4_H__
|
||||
#define __LINUX_IMX_SEMA4_H__
|
||||
|
||||
#define SEMA4_NUM_DEVICES 1
|
||||
#define SEMA4_NUM_GATES 16
|
||||
|
||||
#define SEMA4_UNLOCK 0x00
|
||||
#define SEMA4_A9_LOCK 0x01
|
||||
#define SEMA4_GATE_MASK 0x03
|
||||
|
||||
#define CORE_MUTEX_VALID (('c'<<24)|('m'<<24)|('t'<<24)|'x')
|
||||
|
||||
/*
|
||||
* The enumerates
|
||||
*/
|
||||
enum {
|
||||
/* sema4 registers offset */
|
||||
SEMA4_CP0INE = 0x40,
|
||||
SEMA4_CP1INE = 0x48,
|
||||
SEMA4_CP0NTF = 0x80,
|
||||
SEMA4_CP1NTF = 0x88,
|
||||
};
|
||||
|
||||
static const unsigned int idx_sema4[SEMA4_NUM_GATES] = {
|
||||
1 << 7, 1 << 6, 1 << 5, 1 << 4,
|
||||
1 << 3, 1 << 2, 1 << 1, 1 << 0,
|
||||
1 << 15, 1 << 14, 1 << 13, 1 << 12,
|
||||
1 << 11, 1 << 10, 1 << 9, 1 << 8,
|
||||
};
|
||||
|
||||
struct imx_sema4_mutex {
|
||||
u32 valid;
|
||||
u32 gate_num;
|
||||
unsigned char gate_val;
|
||||
wait_queue_head_t wait_q;
|
||||
};
|
||||
|
||||
struct imx_sema4_mutex_device {
|
||||
struct device *dev;
|
||||
u16 cpntf_val;
|
||||
u16 cpine_val;
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
spinlock_t lock; /* Mutex */
|
||||
int irq;
|
||||
|
||||
u16 alloced;
|
||||
struct imx_sema4_mutex *mutex_ptr[SEMA4_NUM_GATES];
|
||||
};
|
||||
|
||||
struct imx_sema4_mutex *
|
||||
imx_sema4_mutex_create(u32 dev_num, u32 mutex_num);
|
||||
#ifdef CONFIG_IMX_SEMA4
|
||||
int imx_sema4_mutex_destroy(struct imx_sema4_mutex *mutex_ptr);
|
||||
int imx_sema4_mutex_trylock(struct imx_sema4_mutex *mutex_ptr);
|
||||
int imx_sema4_mutex_lock(struct imx_sema4_mutex *mutex_ptr);
|
||||
int imx_sema4_mutex_unlock(struct imx_sema4_mutex *mutex_ptr);
|
||||
#else
|
||||
static inline int imx_sema4_mutex_destroy(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx_sema4_mutex_trylock(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx_sema4_mutex_lock(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx_sema4_mutex_unlock(struct imx_sema4_mutex *mutex_ptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* __LINUX_IMX_SEMA4_H__ */
|
|
@ -143,6 +143,7 @@ struct generic_pm_domain {
|
|||
};
|
||||
};
|
||||
|
||||
unsigned int state_idx_saved; /* saved power state for recovery after system suspend/resume */
|
||||
};
|
||||
|
||||
static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* sabresd_battery.h - Maxim 8903 USB/Adapter Charger Driver
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
|
||||
* Based on max8903_charger.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MAX8903_SABRESD_H__
|
||||
#define __MAX8903_SABRESD_H__
|
||||
|
||||
struct max8903_pdata {
|
||||
/*
|
||||
* GPIOs
|
||||
* cen, chg, flt, and usus are optional.
|
||||
* dok, dcm, and uok are not optional depending on the status of
|
||||
* dc_valid and usb_valid.
|
||||
*/
|
||||
int cen; /* Charger Enable input */
|
||||
int dok; /* DC(Adapter) Power OK output */
|
||||
int uok; /* USB Power OK output */
|
||||
int chg; /* Charger status output */
|
||||
int flt; /* Fault output */
|
||||
int dcm; /* Current-Limit Mode input (1: DC, 2: USB) */
|
||||
int usus; /* USB Suspend Input (1: suspended) */
|
||||
int feature_flag;/* battery capacity feature(0:enable, 1:disable) */
|
||||
|
||||
/*
|
||||
* DCM wired to Logic High Set this true when DCM pin connect to
|
||||
* Logic high.
|
||||
*/
|
||||
bool dcm_always_high;
|
||||
|
||||
/*
|
||||
* DC(Adapter/TA) is wired
|
||||
* When dc_valid is true,
|
||||
* dok and dcm should be valid.
|
||||
*
|
||||
* At least one of dc_valid or usb_valid should be true.
|
||||
*/
|
||||
bool dc_valid;
|
||||
/*
|
||||
* USB is wired
|
||||
* When usb_valid is true,
|
||||
* uok should be valid.
|
||||
*/
|
||||
bool usb_valid;
|
||||
};
|
||||
|
||||
#endif /* __SABRESD_BATTERY_H__ */
|
|
@ -0,0 +1,12 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
#ifndef __IMX_SIP_H__
|
||||
#define __IMX_SIP_H__
|
||||
|
||||
#define IMX_SIP_GPC 0xC2000000
|
||||
#define IMX_SIP_CONFIG_GPC_PM_DOMAIN 0x03
|
||||
|
||||
#endif /* __IMX_SIP_H__ */
|
Loading…
Reference in New Issue