1
0
Fork 0

Merge branch 'pm/next' into next

* pm/next: (54 commits)
  drivers/soc/fsl: add EPU FSM configuration for deep sleep
  fsl_pmc: update device bindings
  soc: fsl: add RCPM driver
  Documentation: dt: binding: fsl: Add 'little-endian' and update Chassis define
  MLK-22992 firmware: imx: scu-pd: fix wu_num
  ...
5.4-rM2-2.2.x-imx-squashed
Dong Aisheng 2019-12-02 18:05:21 +08:00
commit 08dcfadc9a
34 changed files with 3260 additions and 87 deletions

View File

@ -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>;
};

View File

@ -9,15 +9,20 @@ Properties:
"fsl,mpc8548-pmc" should be listed for any chip whose PMC is
compatible. "fsl,mpc8536-pmc" should also be listed for any chip
whose PMC is compatible, and implies deep-sleep capability.
whose PMC is compatible, and implies deep-sleep capability and
wake on user defined packet(wakeup on ARP).
"fsl,p1022-pmc" should be listed for any chip whose PMC is
compatible, and implies lossless Ethernet capability during sleep.
"fsl,mpc8641d-pmc" should be listed for any chip whose PMC is
compatible; all statements below that apply to "fsl,mpc8548-pmc" also
apply to "fsl,mpc8641d-pmc".
Compatibility does not include bit assignments in SCCR/PMCDR/DEVDISR; these
bit assignments are indicated via the sleep specifier in each device's
sleep property.
bit assignments are indicated via the clock nodes. Device which has a
controllable clock source should have a "fsl,pmc-handle" property pointing
to the clock node.
- reg: For devices compatible with "fsl,mpc8349-pmc", the first resource
is the PMC block, and the second resource is the Clock Configuration
@ -33,31 +38,35 @@ Properties:
this is a phandle to an "fsl,gtm" node on which timer 4 can be used as
a wakeup source from deep sleep.
Sleep specifiers:
Clock nodes:
The clock nodes are to describe the masks in PM controller registers for each
soc clock.
- fsl,pmcdr-mask: For "fsl,mpc8548-pmc"-compatible devices, the mask will be
ORed into PMCDR before suspend if the device using this clock is the wake-up
source and need to be running during low power mode; clear the mask if
otherwise.
fsl,mpc8349-pmc: Sleep specifiers consist of one cell. For each bit
that is set in the cell, the corresponding bit in SCCR will be saved
and cleared on suspend, and restored on resume. This sleep controller
supports disabling and resuming devices at any time.
- fsl,sccr-mask: For "fsl,mpc8349-pmc"-compatible devices, the corresponding
bit specified by the mask in SCCR will be saved and cleared on suspend, and
restored on resume.
fsl,mpc8536-pmc: Sleep specifiers consist of three cells, the third of
which will be ORed into PMCDR upon suspend, and cleared from PMCDR
upon resume. The first two cells are as described for fsl,mpc8578-pmc.
This sleep controller only supports disabling devices during system
sleep, or permanently.
fsl,mpc8548-pmc: Sleep specifiers consist of one or two cells, the
first of which will be ORed into DEVDISR (and the second into
DEVDISR2, if present -- this cell should be zero or absent if the
hardware does not have DEVDISR2) upon a request for permanent device
disabling. This sleep controller does not support configuring devices
to disable during system sleep (unless supported by another compatible
match), or dynamically.
- fsl,devdisr-mask: Contain one or two cells, depending on the availability of
DEVDISR2 register. For compatible devices, the mask will be ORed into DEVDISR
or DEVDISR2 when the clock should be permenently disabled.
Example:
power@b00 {
compatible = "fsl,mpc8313-pmc", "fsl,mpc8349-pmc";
reg = <0xb00 0x100 0xa00 0x100>;
interrupts = <80 8>;
power@e0070 {
compatible = "fsl,mpc8536-pmc", "fsl,mpc8548-pmc";
reg = <0xe0070 0x20>;
etsec1_clk: soc-clk@24 {
fsl,pmcdr-mask = <0x00000080>;
};
etsec2_clk: soc-clk@25 {
fsl,pmcdr-mask = <0x00000040>;
};
etsec3_clk: soc-clk@26 {
fsl,pmcdr-mask = <0x00000020>;
};
};

View File

@ -5,7 +5,7 @@ and power management.
Required properites:
- reg : Offset and length of the register set of the RCPM block.
- fsl,#rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the
- #fsl,rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the
fsl,rcpm-wakeup property.
- compatible : Must contain a chip-specific RCPM block compatible string
and (if applicable) may contain a chassis-version RCPM compatible
@ -20,6 +20,7 @@ Required properites:
* "fsl,qoriq-rcpm-1.0": for chassis 1.0 rcpm
* "fsl,qoriq-rcpm-2.0": for chassis 2.0 rcpm
* "fsl,qoriq-rcpm-2.1": for chassis 2.1 rcpm
* "fsl,qoriq-rcpm-2.1+": for chassis 2.1+ rcpm
All references to "1.0" and "2.0" refer to the QorIQ chassis version to
which the chip complies.
@ -27,14 +28,19 @@ Chassis Version Example Chips
--------------- -------------------------------
1.0 p4080, p5020, p5040, p2041, p3041
2.0 t4240, b4860, b4420
2.1 t1040, ls1021
2.1 t1040,
2.1+ ls1021a, ls1012a, ls1043a, ls1046a
Optional properties:
- little-endian : RCPM register block is Little Endian. Without it RCPM
will be Big Endian (default case).
Example:
The RCPM node for T4240:
rcpm: global-utilities@e2000 {
compatible = "fsl,t4240-rcpm", "fsl,qoriq-rcpm-2.0";
reg = <0xe2000 0x1000>;
fsl,#rcpm-wakeup-cells = <2>;
#fsl,rcpm-wakeup-cells = <2>;
};
* Freescale RCPM Wakeup Source Device Tree Bindings
@ -44,7 +50,7 @@ can be used as a wakeup source.
- fsl,rcpm-wakeup: Consists of a phandle to the rcpm node and the IPPDEXPCR
register cells. The number of IPPDEXPCR register cells is defined in
"fsl,#rcpm-wakeup-cells" in the rcpm node. The first register cell is
"#fsl,rcpm-wakeup-cells" in the rcpm node. The first register cell is
the bit mask that should be set in IPPDEXPCR0, and the second register
cell is for IPPDEXPCR1, and so on.

View File

@ -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

View File

@ -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);

View File

@ -247,6 +247,60 @@ void wakeup_source_unregister(struct wakeup_source *ws)
}
EXPORT_SYMBOL_GPL(wakeup_source_unregister);
/**
* wakeup_sources_read_lock - Lock wakeup source list for read.
*
* Returns an index of srcu lock for struct wakeup_srcu.
* This index must be passed to the matching wakeup_sources_read_unlock().
*/
int wakeup_sources_read_lock(void)
{
return srcu_read_lock(&wakeup_srcu);
}
EXPORT_SYMBOL_GPL(wakeup_sources_read_lock);
/**
* wakeup_sources_read_unlock - Unlock wakeup source list.
* @idx: return value from corresponding wakeup_sources_read_lock()
*/
void wakeup_sources_read_unlock(int idx)
{
srcu_read_unlock(&wakeup_srcu, idx);
}
EXPORT_SYMBOL_GPL(wakeup_sources_read_unlock);
/**
* wakeup_sources_walk_start - Begin a walk on wakeup source list
*
* Returns first object of the list of wakeup sources.
*
* Note that to be safe, wakeup sources list needs to be locked by calling
* wakeup_source_read_lock() for this.
*/
struct wakeup_source *wakeup_sources_walk_start(void)
{
struct list_head *ws_head = &wakeup_sources;
return list_entry_rcu(ws_head->next, struct wakeup_source, entry);
}
EXPORT_SYMBOL_GPL(wakeup_sources_walk_start);
/**
* wakeup_sources_walk_next - Get next wakeup source from the list
* @ws: Previous wakeup source object
*
* Note that to be safe, wakeup sources list needs to be locked by calling
* wakeup_source_read_lock() for this.
*/
struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws)
{
struct list_head *ws_head = &wakeup_sources;
return list_next_or_null_rcu(ws_head, &ws->entry,
struct wakeup_source, entry);
}
EXPORT_SYMBOL_GPL(wakeup_sources_walk_next);
/**
* device_wakeup_attach - Attach a wakeup source object to a device object.
* @dev: Device to handle.

View File

@ -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

View File

@ -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/

View File

@ -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.

View File

@ -0,0 +1,5 @@
#
# Makefile for imx mcc
#
#
obj-$(CONFIG_IMX_SEMA4) += imx_sema4.o

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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");

203
drivers/firmware/imx/scu-pd.c 100644 → 100755
View File

@ -44,10 +44,13 @@
*
*/
#include <linux/arm-smccc.h>
#include <dt-bindings/firmware/imx/rsrc.h>
#include <linux/console.h>
#include <linux/firmware/imx/sci.h>
#include <linux/firmware/imx/svc/rm.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>
@ -56,6 +59,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 {
@ -86,6 +100,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 },
@ -110,14 +126,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 },
@ -131,6 +159,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 },
@ -139,13 +168,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 */
@ -153,8 +191,18 @@ 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 },
{ "mipi1", IMX_SC_R_MIPI_1, 1, 0 },
{ "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, 0 },
@ -164,6 +212,49 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
/* 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 = {
@ -179,6 +270,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;
@ -194,7 +352,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)
@ -247,6 +410,8 @@ 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;
if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx))
@ -256,9 +421,23 @@ imx_scu_add_pm_domain(struct device *dev, int idx,
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),
@ -268,20 +447,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;
}
@ -343,6 +528,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);
}
@ -359,7 +547,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");

View File

@ -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

View File

@ -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

View File

@ -31,6 +31,13 @@ config FSL_MC_DPIO
objects individually, but groups them under a service layer
API.
config FSL_SLEEP_FSM
bool
help
This driver configures a hardware FSM (Finite State Machine) for deep sleep.
The FSM is used to finish clean-ups at the last stage of system entering deep
sleep, and also wakes up system when a wake up event happens.
config DPAA2_CONSOLE
tristate "QorIQ DPAA2 console driver"
depends on OF && (ARCH_LAYERSCAPE || COMPILE_TEST)
@ -51,4 +58,13 @@ config FSL_QIXIS
Say y here to enable QIXIS system controller api. The qixis driver
provides FPGA functions to control system.
config FSL_RCPM
bool "Freescale RCPM support"
depends on PM_SLEEP && (ARM || ARM64)
help
The NXP QorIQ Processors based on ARM Core have RCPM module
(Run Control and Power Management), which performs all device-level
tasks associated with power management, such as wakeup source control.
Note that currently this driver will not support PowerPC based
QorIQ processor.
endmenu

View File

@ -10,3 +10,5 @@ obj-$(CONFIG_FSL_QIXIS) += qixis_ctrl.o
obj-$(CONFIG_FSL_GUTS) += guts.o
obj-$(CONFIG_FSL_MC_DPIO) += dpio/
obj-$(CONFIG_DPAA2_CONSOLE) += dpaa2-console.o
obj-$(CONFIG_FSL_RCPM) += rcpm.o
obj-$(CONFIG_FSL_SLEEP_FSM) += sleep_fsm.o

View File

@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0
//
// rcpm.c - Freescale QorIQ RCPM driver
//
// Copyright 2019 NXP
//
// Author: Ran Wang <ran.wang_1@nxp.com>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/kernel.h>
#define RCPM_WAKEUP_CELL_MAX_SIZE 7
struct rcpm {
unsigned int wakeup_cells;
void __iomem *ippdexpcr_base;
bool little_endian;
};
/**
* rcpm_pm_prepare - performs device-level tasks associated with power
* management, such as programming related to the wakeup source control.
* @dev: Device to handle.
*
*/
static int rcpm_pm_prepare(struct device *dev)
{
int i, ret, idx;
void __iomem *base;
struct wakeup_source *ws;
struct rcpm *rcpm;
struct device_node *np = dev->of_node;
u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};
rcpm = dev_get_drvdata(dev);
if (!rcpm)
return -EINVAL;
base = rcpm->ippdexpcr_base;
idx = wakeup_sources_read_lock();
/* Begin with first registered wakeup source */
for_each_wakeup_source(ws) {
/* skip object which is not attached to device */
if (!ws->dev || !ws->dev->parent)
continue;
ret = device_property_read_u32_array(ws->dev->parent,
"fsl,rcpm-wakeup", value,
rcpm->wakeup_cells + 1);
/* Wakeup source should refer to current rcpm device */
if (ret || (np->phandle != value[0]))
continue;
/* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
* number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
* of wakeup source IP contains an integer array: <phandle to
* RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
* IPPDEXPCR2 setting, etc>.
*
* So we will go thought them to collect setting data.
*/
for (i = 0; i < rcpm->wakeup_cells; i++)
setting[i] |= value[i + 1];
}
wakeup_sources_read_unlock(idx);
/* Program all IPPDEXPCRn once */
for (i = 0; i < rcpm->wakeup_cells; i++) {
u32 tmp = setting[i];
void __iomem *address = base + i * 4;
if (!tmp)
continue;
/* We can only OR related bits */
if (rcpm->little_endian) {
tmp |= ioread32(address);
iowrite32(tmp, address);
} else {
tmp |= ioread32be(address);
iowrite32be(tmp, address);
}
}
return 0;
}
static const struct dev_pm_ops rcpm_pm_ops = {
.prepare = rcpm_pm_prepare,
};
static int rcpm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *r;
struct rcpm *rcpm;
int ret;
rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
if (!rcpm)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENODEV;
rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(rcpm->ippdexpcr_base)) {
ret = PTR_ERR(rcpm->ippdexpcr_base);
return ret;
}
rcpm->little_endian = device_property_read_bool(
&pdev->dev, "little-endian");
ret = device_property_read_u32(&pdev->dev,
"#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
if (ret)
return ret;
dev_set_drvdata(&pdev->dev, rcpm);
return 0;
}
static const struct of_device_id rcpm_of_match[] = {
{ .compatible = "fsl,qoriq-rcpm-2.1+", },
{}
};
MODULE_DEVICE_TABLE(of, rcpm_of_match);
static struct platform_driver rcpm_driver = {
.driver = {
.name = "rcpm",
.of_match_table = rcpm_of_match,
.pm = &rcpm_pm_ops,
},
.probe = rcpm_probe,
};
module_platform_driver(rcpm_driver);

View File

@ -0,0 +1,279 @@
/*
* deep sleep FSM (finite-state machine) configuration
*
* Copyright 2018 NXP
*
* Author: Hongbo Zhang <hongbo.zhang@freescale.com>
* Chenhui Zhao <chenhui.zhao@freescale.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the above-listed copyright holders nor the
* names of any contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/types.h>
#include "sleep_fsm.h"
/*
* These values are from chip's reference manual. For example,
* the values for T1040 can be found in "8.4.3.8 Programming
* supporting deep sleep mode" of Chapter 8 "Run Control and
* Power Management (RCPM)".
* The default value can be applied to T104x, LS1021.
*/
struct fsm_reg_vals epu_default_val[] = {
/* EPGCR (Event Processor Global Control Register) */
{EPGCR, 0},
/* EPECR (Event Processor Event Control Registers) */
{EPECR0 + EPECR_STRIDE * 0, 0},
{EPECR0 + EPECR_STRIDE * 1, 0},
{EPECR0 + EPECR_STRIDE * 2, 0xF0004004},
{EPECR0 + EPECR_STRIDE * 3, 0x80000084},
{EPECR0 + EPECR_STRIDE * 4, 0x20000084},
{EPECR0 + EPECR_STRIDE * 5, 0x08000004},
{EPECR0 + EPECR_STRIDE * 6, 0x80000084},
{EPECR0 + EPECR_STRIDE * 7, 0x80000084},
{EPECR0 + EPECR_STRIDE * 8, 0x60000084},
{EPECR0 + EPECR_STRIDE * 9, 0x08000084},
{EPECR0 + EPECR_STRIDE * 10, 0x42000084},
{EPECR0 + EPECR_STRIDE * 11, 0x90000084},
{EPECR0 + EPECR_STRIDE * 12, 0x80000084},
{EPECR0 + EPECR_STRIDE * 13, 0x08000084},
{EPECR0 + EPECR_STRIDE * 14, 0x02000084},
{EPECR0 + EPECR_STRIDE * 15, 0x00000004},
/*
* EPEVTCR (Event Processor EVT Pin Control Registers)
* SCU8 triger EVT2, and SCU11 triger EVT9
*/
{EPEVTCR0 + EPEVTCR_STRIDE * 0, 0},
{EPEVTCR0 + EPEVTCR_STRIDE * 1, 0},
{EPEVTCR0 + EPEVTCR_STRIDE * 2, 0x80000001},
{EPEVTCR0 + EPEVTCR_STRIDE * 3, 0},
{EPEVTCR0 + EPEVTCR_STRIDE * 4, 0},
{EPEVTCR0 + EPEVTCR_STRIDE * 5, 0},
{EPEVTCR0 + EPEVTCR_STRIDE * 6, 0},
{EPEVTCR0 + EPEVTCR_STRIDE * 7, 0},
{EPEVTCR0 + EPEVTCR_STRIDE * 8, 0},
{EPEVTCR0 + EPEVTCR_STRIDE * 9, 0xB0000001},
/* EPCMPR (Event Processor Counter Compare Registers) */
{EPCMPR0 + EPCMPR_STRIDE * 0, 0},
{EPCMPR0 + EPCMPR_STRIDE * 1, 0},
{EPCMPR0 + EPCMPR_STRIDE * 2, 0x000000FF},
{EPCMPR0 + EPCMPR_STRIDE * 3, 0},
{EPCMPR0 + EPCMPR_STRIDE * 4, 0x000000FF},
{EPCMPR0 + EPCMPR_STRIDE * 5, 0x00000020},
{EPCMPR0 + EPCMPR_STRIDE * 6, 0},
{EPCMPR0 + EPCMPR_STRIDE * 7, 0},
{EPCMPR0 + EPCMPR_STRIDE * 8, 0x000000FF},
{EPCMPR0 + EPCMPR_STRIDE * 9, 0x000000FF},
{EPCMPR0 + EPCMPR_STRIDE * 10, 0x000000FF},
{EPCMPR0 + EPCMPR_STRIDE * 11, 0x000000FF},
{EPCMPR0 + EPCMPR_STRIDE * 12, 0x000000FF},
{EPCMPR0 + EPCMPR_STRIDE * 13, 0},
{EPCMPR0 + EPCMPR_STRIDE * 14, 0x000000FF},
{EPCMPR0 + EPCMPR_STRIDE * 15, 0x000000FF},
/* EPCCR (Event Processor Counter Control Registers) */
{EPCCR0 + EPCCR_STRIDE * 0, 0},
{EPCCR0 + EPCCR_STRIDE * 1, 0},
{EPCCR0 + EPCCR_STRIDE * 2, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 3, 0},
{EPCCR0 + EPCCR_STRIDE * 4, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 5, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 6, 0},
{EPCCR0 + EPCCR_STRIDE * 7, 0},
{EPCCR0 + EPCCR_STRIDE * 8, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 9, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 10, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 11, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 12, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 13, 0},
{EPCCR0 + EPCCR_STRIDE * 14, 0x92840000},
{EPCCR0 + EPCCR_STRIDE * 15, 0x92840000},
/* EPSMCR (Event Processor SCU Mux Control Registers) */
{EPSMCR0 + EPSMCR_STRIDE * 0, 0},
{EPSMCR0 + EPSMCR_STRIDE * 1, 0},
{EPSMCR0 + EPSMCR_STRIDE * 2, 0x6C700000},
{EPSMCR0 + EPSMCR_STRIDE * 3, 0x2F000000},
{EPSMCR0 + EPSMCR_STRIDE * 4, 0x002F0000},
{EPSMCR0 + EPSMCR_STRIDE * 5, 0x00002E00},
{EPSMCR0 + EPSMCR_STRIDE * 6, 0x7C000000},
{EPSMCR0 + EPSMCR_STRIDE * 7, 0x30000000},
{EPSMCR0 + EPSMCR_STRIDE * 8, 0x64300000},
{EPSMCR0 + EPSMCR_STRIDE * 9, 0x00003000},
{EPSMCR0 + EPSMCR_STRIDE * 10, 0x65000030},
{EPSMCR0 + EPSMCR_STRIDE * 11, 0x31740000},
{EPSMCR0 + EPSMCR_STRIDE * 12, 0x7F000000},
{EPSMCR0 + EPSMCR_STRIDE * 13, 0x00003100},
{EPSMCR0 + EPSMCR_STRIDE * 14, 0x00000031},
{EPSMCR0 + EPSMCR_STRIDE * 15, 0x76000000},
/* EPACR (Event Processor Action Control Registers) */
{EPACR0 + EPACR_STRIDE * 0, 0},
{EPACR0 + EPACR_STRIDE * 1, 0},
{EPACR0 + EPACR_STRIDE * 2, 0},
{EPACR0 + EPACR_STRIDE * 3, 0x00000080},
{EPACR0 + EPACR_STRIDE * 4, 0},
{EPACR0 + EPACR_STRIDE * 5, 0x00000040},
{EPACR0 + EPACR_STRIDE * 6, 0},
{EPACR0 + EPACR_STRIDE * 7, 0},
{EPACR0 + EPACR_STRIDE * 8, 0},
{EPACR0 + EPACR_STRIDE * 9, 0x0000001C},
{EPACR0 + EPACR_STRIDE * 10, 0x00000020},
{EPACR0 + EPACR_STRIDE * 11, 0},
{EPACR0 + EPACR_STRIDE * 12, 0x00000003},
{EPACR0 + EPACR_STRIDE * 13, 0x06000000},
{EPACR0 + EPACR_STRIDE * 14, 0x04000000},
{EPACR0 + EPACR_STRIDE * 15, 0x02000000},
/* EPIMCR (Event Processor Input Mux Control Registers) */
{EPIMCR0 + EPIMCR_STRIDE * 0, 0},
{EPIMCR0 + EPIMCR_STRIDE * 1, 0},
{EPIMCR0 + EPIMCR_STRIDE * 2, 0},
{EPIMCR0 + EPIMCR_STRIDE * 3, 0},
{EPIMCR0 + EPIMCR_STRIDE * 4, 0x44000000},
{EPIMCR0 + EPIMCR_STRIDE * 5, 0x40000000},
{EPIMCR0 + EPIMCR_STRIDE * 6, 0},
{EPIMCR0 + EPIMCR_STRIDE * 7, 0},
{EPIMCR0 + EPIMCR_STRIDE * 8, 0},
{EPIMCR0 + EPIMCR_STRIDE * 9, 0},
{EPIMCR0 + EPIMCR_STRIDE * 10, 0},
{EPIMCR0 + EPIMCR_STRIDE * 11, 0},
{EPIMCR0 + EPIMCR_STRIDE * 12, 0x44000000},
{EPIMCR0 + EPIMCR_STRIDE * 13, 0},
{EPIMCR0 + EPIMCR_STRIDE * 14, 0},
{EPIMCR0 + EPIMCR_STRIDE * 15, 0},
{EPIMCR0 + EPIMCR_STRIDE * 16, 0x6A000000},
{EPIMCR0 + EPIMCR_STRIDE * 17, 0},
{EPIMCR0 + EPIMCR_STRIDE * 18, 0},
{EPIMCR0 + EPIMCR_STRIDE * 19, 0},
{EPIMCR0 + EPIMCR_STRIDE * 20, 0x48000000},
{EPIMCR0 + EPIMCR_STRIDE * 21, 0},
{EPIMCR0 + EPIMCR_STRIDE * 22, 0x6C000000},
{EPIMCR0 + EPIMCR_STRIDE * 23, 0},
{EPIMCR0 + EPIMCR_STRIDE * 24, 0},
{EPIMCR0 + EPIMCR_STRIDE * 25, 0},
{EPIMCR0 + EPIMCR_STRIDE * 26, 0},
{EPIMCR0 + EPIMCR_STRIDE * 27, 0},
{EPIMCR0 + EPIMCR_STRIDE * 28, 0x76000000},
{EPIMCR0 + EPIMCR_STRIDE * 29, 0},
{EPIMCR0 + EPIMCR_STRIDE * 30, 0},
{EPIMCR0 + EPIMCR_STRIDE * 31, 0x76000000},
/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
{EPXTRIGCR, 0x0000FFDF},
/* end */
{FSM_END_FLAG, 0},
};
struct fsm_reg_vals npc_default_val[] = {
/* NPC triggered Memory-Mapped Access Registers */
{NCR, 0x80000000},
{MCCR1, 0},
{MCSR1, 0},
{MMAR1LO, 0},
{MMAR1HI, 0},
{MMDR1, 0},
{MCSR2, 0},
{MMAR2LO, 0},
{MMAR2HI, 0},
{MMDR2, 0},
{MCSR3, 0x80000000},
{MMAR3LO, 0x000E2130},
{MMAR3HI, 0x00030000},
{MMDR3, 0x00020000},
/* end */
{FSM_END_FLAG, 0},
};
/**
* fsl_fsm_setup - Configure EPU's FSM registers
* @base: the base address of registers
* @val: Pointer to address-value pairs for FSM registers
*/
void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val)
{
struct fsm_reg_vals *data = val;
WARN_ON(!base || !data);
while (data->offset != FSM_END_FLAG) {
iowrite32be(data->value, base + data->offset);
data++;
}
}
void fsl_epu_setup_default(void __iomem *epu_base)
{
fsl_fsm_setup(epu_base, epu_default_val);
}
void fsl_npc_setup_default(void __iomem *npc_base)
{
fsl_fsm_setup(npc_base, npc_default_val);
}
void fsl_epu_clean_default(void __iomem *epu_base)
{
u32 offset;
/* follow the exact sequence to clear the registers */
/* Clear EPACRn */
for (offset = EPACR0; offset <= EPACR15; offset += EPACR_STRIDE)
iowrite32be(0, epu_base + offset);
/* Clear EPEVTCRn */
for (offset = EPEVTCR0; offset <= EPEVTCR9; offset += EPEVTCR_STRIDE)
iowrite32be(0, epu_base + offset);
/* Clear EPGCR */
iowrite32be(0, epu_base + EPGCR);
/* Clear EPSMCRn */
for (offset = EPSMCR0; offset <= EPSMCR15; offset += EPSMCR_STRIDE)
iowrite32be(0, epu_base + offset);
/* Clear EPCCRn */
for (offset = EPCCR0; offset <= EPCCR31; offset += EPCCR_STRIDE)
iowrite32be(0, epu_base + offset);
/* Clear EPCMPRn */
for (offset = EPCMPR0; offset <= EPCMPR31; offset += EPCMPR_STRIDE)
iowrite32be(0, epu_base + offset);
/* Clear EPCTRn */
for (offset = EPCTR0; offset <= EPCTR31; offset += EPCTR_STRIDE)
iowrite32be(0, epu_base + offset);
/* Clear EPIMCRn */
for (offset = EPIMCR0; offset <= EPIMCR31; offset += EPIMCR_STRIDE)
iowrite32be(0, epu_base + offset);
/* Clear EPXTRIGCRn */
iowrite32be(0, epu_base + EPXTRIGCR);
/* Clear EPECRn */
for (offset = EPECR0; offset <= EPECR15; offset += EPECR_STRIDE)
iowrite32be(0, epu_base + offset);
}

View File

@ -0,0 +1,130 @@
/*
* deep sleep FSM (finite-state machine) configuration
*
* Copyright 2018 NXP
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the above-listed copyright holders nor the
* names of any contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _FSL_SLEEP_FSM_H
#define _FSL_SLEEP_FSM_H
#define FSL_STRIDE_4B 4
#define FSL_STRIDE_8B 8
/* End flag */
#define FSM_END_FLAG 0xFFFFFFFFUL
/* Block offsets */
#define RCPM_BLOCK_OFFSET 0x00022000
#define EPU_BLOCK_OFFSET 0x00000000
#define NPC_BLOCK_OFFSET 0x00001000
/* EPGCR (Event Processor Global Control Register) */
#define EPGCR 0x000
/* EPEVTCR0-9 (Event Processor EVT Pin Control Registers) */
#define EPEVTCR0 0x050
#define EPEVTCR9 0x074
#define EPEVTCR_STRIDE FSL_STRIDE_4B
/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
#define EPXTRIGCR 0x090
/* EPIMCR0-31 (Event Processor Input Mux Control Registers) */
#define EPIMCR0 0x100
#define EPIMCR31 0x17C
#define EPIMCR_STRIDE FSL_STRIDE_4B
/* EPSMCR0-15 (Event Processor SCU Mux Control Registers) */
#define EPSMCR0 0x200
#define EPSMCR15 0x278
#define EPSMCR_STRIDE FSL_STRIDE_8B
/* EPECR0-15 (Event Processor Event Control Registers) */
#define EPECR0 0x300
#define EPECR15 0x33C
#define EPECR_STRIDE FSL_STRIDE_4B
/* EPACR0-15 (Event Processor Action Control Registers) */
#define EPACR0 0x400
#define EPACR15 0x43C
#define EPACR_STRIDE FSL_STRIDE_4B
/* EPCCRi0-15 (Event Processor Counter Control Registers) */
#define EPCCR0 0x800
#define EPCCR15 0x83C
#define EPCCR31 0x87C
#define EPCCR_STRIDE FSL_STRIDE_4B
/* EPCMPR0-15 (Event Processor Counter Compare Registers) */
#define EPCMPR0 0x900
#define EPCMPR15 0x93C
#define EPCMPR31 0x97C
#define EPCMPR_STRIDE FSL_STRIDE_4B
/* EPCTR0-31 (Event Processor Counter Register) */
#define EPCTR0 0xA00
#define EPCTR31 0xA7C
#define EPCTR_STRIDE FSL_STRIDE_4B
/* NPC triggered Memory-Mapped Access Registers */
#define NCR 0x000
#define MCCR1 0x0CC
#define MCSR1 0x0D0
#define MMAR1LO 0x0D4
#define MMAR1HI 0x0D8
#define MMDR1 0x0DC
#define MCSR2 0x0E0
#define MMAR2LO 0x0E4
#define MMAR2HI 0x0E8
#define MMDR2 0x0EC
#define MCSR3 0x0F0
#define MMAR3LO 0x0F4
#define MMAR3HI 0x0F8
#define MMDR3 0x0FC
/* RCPM Core State Action Control Register 0 */
#define CSTTACR0 0xB00
/* RCPM Core Group 1 Configuration Register 0 */
#define CG1CR0 0x31C
struct fsm_reg_vals {
u32 offset;
u32 value;
};
void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val);
void fsl_epu_setup_default(void __iomem *epu_base);
void fsl_npc_setup_default(void __iomem *npc_base);
void fsl_epu_clean_default(void __iomem *epu_base);
#endif /* _FSL_SLEEP_FSM_H */

View File

@ -17,6 +17,12 @@ 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
config IMX8M_BUSFREQ
bool "i.MX8M busfreq"
depends on ARCH_MXC && ARM64

View File

@ -4,4 +4,5 @@ obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
obj-$(CONFIG_IMX8M_BUSFREQ) += busfreq-imx8mq.o
obj-$(CONFIG_ARM64) += soc-imx8.o
obj-$(CONFIG_IMX_SCU_SOC) += soc-imx-scu.o
obj-$(CONFIG_IMX8M_PM_DOMAINS) += imx8m_pm_domains.o
obj-y += mu/

View File

@ -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;

View File

@ -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");

View File

@ -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__ */

View File

@ -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)

View File

@ -63,6 +63,11 @@ struct wakeup_source {
bool autosleep_enabled:1;
};
#define for_each_wakeup_source(ws) \
for ((ws) = wakeup_sources_walk_start(); \
(ws); \
(ws) = wakeup_sources_walk_next((ws)))
#ifdef CONFIG_PM_SLEEP
/*
@ -92,6 +97,10 @@ extern void wakeup_source_remove(struct wakeup_source *ws);
extern struct wakeup_source *wakeup_source_register(struct device *dev,
const char *name);
extern void wakeup_source_unregister(struct wakeup_source *ws);
extern int wakeup_sources_read_lock(void);
extern void wakeup_sources_read_unlock(int idx);
extern struct wakeup_source *wakeup_sources_walk_start(void);
extern struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws);
extern int device_wakeup_enable(struct device *dev);
extern int device_wakeup_disable(struct device *dev);
extern void device_set_wakeup_capable(struct device *dev, bool capable);

View File

@ -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__ */

View File

@ -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__ */