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
commit
08dcfadc9a
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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,11 +548,9 @@ 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;
|
||||
|
||||
if (atomic_read(&genpd->sd_count) > 0)
|
||||
return -EBUSY;
|
||||
/* Choose the deepest state if no devices using this domain */
|
||||
if (!genpd->device_count)
|
||||
genpd->state_idx = genpd->state_count - 1;
|
||||
|
||||
/*
|
||||
* If sd_count > 0 at this point, one of the subdomains hasn't
|
||||
|
@ -561,7 +563,6 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
|
|||
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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")))
|
||||
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,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");
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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