summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/DocBook/prcmu-fw-api.tmpl109
-rw-r--r--arch/arm/mach-ux500/Makefile1
-rw-r--r--arch/arm/mach-ux500/board-mop500-regulators.c349
-rw-r--r--arch/arm/mach-ux500/board-mop500-regulators.h8
-rw-r--r--arch/arm/mach-ux500/board-u5500-regulators.c221
-rw-r--r--arch/arm/mach-ux500/clock-db5500.c745
-rw-r--r--arch/arm/mach-ux500/clock-db8500.c1171
-rw-r--r--arch/arm/mach-ux500/clock-debug.c237
-rw-r--r--arch/arm/mach-ux500/clock.c888
-rw-r--r--arch/arm/mach-ux500/clock.h265
-rw-r--r--arch/arm/mach-ux500/cpuidle.c171
-rw-r--r--arch/arm/mach-ux500/hotplug.c32
-rw-r--r--arch/arm/mach-ux500/include/mach/context.h92
-rw-r--r--arch/arm/mach-ux500/include/mach/pm-timer.h30
-rw-r--r--arch/arm/mach-ux500/include/mach/pm.h109
-rw-r--r--arch/arm/mach-ux500/include/mach/prcmu-debug.h31
-rw-r--r--arch/arm/mach-ux500/include/mach/suspend.h20
-rw-r--r--arch/arm/mach-ux500/pm/Kconfig70
-rw-r--r--arch/arm/mach-ux500/pm/Makefile12
-rw-r--r--arch/arm/mach-ux500/pm/context-db5500.c407
-rw-r--r--arch/arm/mach-ux500/pm/context-db8500.c456
-rw-r--r--arch/arm/mach-ux500/pm/context-db9540.c651
-rw-r--r--arch/arm/mach-ux500/pm/context.c1002
-rw-r--r--arch/arm/mach-ux500/pm/context_arm.S409
-rw-r--r--arch/arm/mach-ux500/pm/performance.c224
-rw-r--r--arch/arm/mach-ux500/pm/pm.c218
-rw-r--r--arch/arm/mach-ux500/pm/prcmu-qos-power.c711
-rw-r--r--arch/arm/mach-ux500/pm/runtime.c514
-rw-r--r--arch/arm/mach-ux500/pm/scu.h25
-rw-r--r--arch/arm/mach-ux500/pm/suspend.c281
-rw-r--r--arch/arm/mach-ux500/pm/suspend_dbg.c165
-rw-r--r--arch/arm/mach-ux500/pm/suspend_dbg.h63
-rw-r--r--arch/arm/mach-ux500/pm/timer.c193
-rw-r--r--arch/arm/mach-ux500/pm/usecase_gov.c962
-rw-r--r--arch/arm/mach-ux500/prcc.h20
-rw-r--r--arch/arm/mach-ux500/prcmu-debug.c1041
-rw-r--r--arch/arm/mach-ux500/product.c134
-rw-r--r--arch/arm/mach-ux500/product.h26
-rw-r--r--arch/arm/mach-ux500/regulator-u5500.h20
-rw-r--r--arch/arm/mach-ux500/test/Kconfig6
-rw-r--r--arch/arm/mach-ux500/test/Makefile1
-rw-r--r--arch/arm/mach-ux500/test/pwr.c828
-rw-r--r--arch/arm/mach-ux500/timer.c11
-rw-r--r--arch/arm/mach-ux500/uart-db8500.c225
-rw-r--r--arch/arm/plat-nomadik/include/plat/mtu.h2
-rw-r--r--drivers/clocksource/Kconfig20
-rw-r--r--drivers/clocksource/Makefile3
-rw-r--r--drivers/clocksource/clksrc-dbx500-prcmu.c23
-rw-r--r--drivers/clocksource/db5500-mtimer.c67
-rw-r--r--drivers/cpufreq/Makefile3
-rw-r--r--drivers/cpufreq/cpufreq.c21
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c167
-rw-r--r--drivers/cpufreq/db8500-cpufreq.c170
-rw-r--r--drivers/cpufreq/dbx500-cpufreq.c340
-rw-r--r--drivers/mfd/db5500-prcmu-regs.h141
-rw-r--r--drivers/mfd/db5500-prcmu.c2055
-rw-r--r--drivers/mfd/db8500-prcmu.c141
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h17
-rw-r--r--drivers/regulator/Kconfig8
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/ab5500.c650
-rw-r--r--drivers/regulator/ab8500-debug.c2083
-rw-r--r--drivers/regulator/ab8500-debug.h80
-rw-r--r--drivers/regulator/ab8500-ext.c451
-rw-r--r--drivers/regulator/ab8500.c1052
-rw-r--r--drivers/regulator/core.c56
-rw-r--r--drivers/regulator/db5500-prcmu.c334
-rw-r--r--drivers/regulator/dbx500-prcmu.c167
-rw-r--r--include/linux/clksrc-db5500-mtimer.h17
-rw-r--r--include/linux/cpufreq-dbx500.h16
-rw-r--r--include/linux/cpufreq.h2
-rw-r--r--include/linux/mfd/db5500-prcmu.h207
-rw-r--r--include/linux/mfd/db8500-prcmu.h93
-rw-r--r--include/linux/mfd/dbx500-prcmu.h71
-rw-r--r--include/linux/regulator/ab5500.h34
-rw-r--r--include/linux/regulator/ab8500-debug.h21
-rw-r--r--include/linux/regulator/ab8500.h47
-rw-r--r--include/linux/regulator/db5500-prcmu.h27
-rw-r--r--include/linux/regulator/dbx500-prcmu.h92
-rw-r--r--kernel/power/suspend.c37
80 files changed, 20690 insertions, 1180 deletions
diff --git a/Documentation/DocBook/prcmu-fw-api.tmpl b/Documentation/DocBook/prcmu-fw-api.tmpl
new file mode 100644
index 00000000000..445a277933c
--- /dev/null
+++ b/Documentation/DocBook/prcmu-fw-api.tmpl
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="STw4500">
+ <bookinfo>
+ <title>PRCMU Driver</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Sudeep Karkada</firstname>
+ <surname>Nagesha</surname>
+ <affiliation>
+ <address>
+ <email>sudeepkarkada.nagesha@stericsson.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2009-2010</year>
+ <holder>ST-Ericsson</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Linux standard functions</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+ <!-- Do NOT remove the legal notice below -->
+
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This documentation describes the API provided by the PRCMU firmware interface driver.
+ </para>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>None</term>
+ <listitem>
+ <para>
+ None.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </chapter>
+
+ <chapter id="enum">
+ <title>Enumerations</title>
+ <para>
+ This chapter contains the autogenerated documentation of the structures
+ and enumerations which are used in the PRCMU firmware interface driver.
+ It is also required by the client drivers.
+ </para>
+!Iinclude/linux/mfd/dbx500-prcmu.h
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+ <para>
+ This chapter contains the autogenerated documentation of the kernel
+ API functions which are exported to the client drivers.
+ </para>
+!Edrivers/mfd/db8500-prcmu.c
+ </chapter>
+
+
+ </book>
diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile
index 465b9ec9510..2ec2e16b080 100644
--- a/arch/arm/mach-ux500/Makefile
+++ b/arch/arm/mach-ux500/Makefile
@@ -5,6 +5,7 @@
obj-y := clock.o cpu.o devices.o devices-common.o \
id.o usb.o timer.o
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
+obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_UX500_SOC_DB5500) += cpu-db5500.o dma-db5500.o
obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o
obj-$(CONFIG_MACH_MOP500) += board-mop500.o board-mop500-sdi.o \
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c
index 52426a42578..ffa0d4f9cdc 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.c
+++ b/arch/arm/mach-ux500/board-mop500-regulators.c
@@ -11,8 +11,52 @@
#include <linux/kernel.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h>
+#include <mach/id.h> /* to identify older boards for fixes */
+#include <asm/mach-types.h>
#include "board-mop500-regulators.h"
+#ifdef CONFIG_REGULATOR_FIXED_VOLTAGE
+/*
+ * GPIO regulator controlled by the ab8500 GPIO16
+ */
+static struct regulator_consumer_supply gpio_wlan_vbat_consumers[] = {
+ /* for cg2900 chip */
+ REGULATOR_SUPPLY("vdd", "cg2900-uart.0"),
+ /* for cw1200 chip */
+ REGULATOR_SUPPLY("vdd", "cw1200_wlan"),
+};
+
+struct regulator_init_data gpio_wlan_vbat_regulator = {
+ .constraints = {
+ .name = "WLAN-VBAT",
+ .min_uV = 3600000,
+ .max_uV = 3600000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(gpio_wlan_vbat_consumers),
+ .consumer_supplies = gpio_wlan_vbat_consumers,
+};
+
+/*
+ * GPIO regulator controlled by the ab8500 GPIO26
+ */
+static struct regulator_consumer_supply gpio_en_3v3_consumers[] = {
+ /* for LAN chip */
+ REGULATOR_SUPPLY("vdd33a", "smsc911x.0"),
+};
+
+struct regulator_init_data gpio_en_3v3_regulator = {
+ .constraints = {
+ .name = "EN-3V3",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(gpio_en_3v3_consumers),
+ .consumer_supplies = gpio_en_3v3_consumers,
+};
+#endif
+
/*
* TPS61052 regulator
*/
@@ -38,21 +82,37 @@ struct regulator_init_data tps61052_regulator = {
};
static struct regulator_consumer_supply ab8500_vaux1_consumers[] = {
- /* External displays, connector on board 2v5 power supply */
- REGULATOR_SUPPLY("vaux12v5", "mcde.0"),
+ /* Main display, u8500 R3 uib */
+ REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
+ /* Main display, u8500 uib and ST uib */
+ REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"),
+ /* Secondary display, ST uib */
+ REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"),
/* SFH7741 proximity sensor */
REGULATOR_SUPPLY("vcc", "gpio-keys.0"),
/* BH1780GLS ambient light sensor */
REGULATOR_SUPPLY("vcc", "2-0029"),
/* lsm303dlh accelerometer */
- REGULATOR_SUPPLY("vdd", "3-0018"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.0"),
/* lsm303dlh magnetometer */
- REGULATOR_SUPPLY("vdd", "3-001e"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.1"),
/* Rohm BU21013 Touchscreen devices */
REGULATOR_SUPPLY("avdd", "3-005c"),
REGULATOR_SUPPLY("avdd", "3-005d"),
/* Synaptics RMI4 Touchscreen device */
REGULATOR_SUPPLY("vdd", "3-004b"),
+ /* L3G4200D Gyroscope device */
+ REGULATOR_SUPPLY("vdd", "l3g4200d"),
+ /* Proximity and Hal sensor device */
+ REGULATOR_SUPPLY("vdd", "sensor1p.0"),
+ /* Ambient light sensor device */
+ REGULATOR_SUPPLY("vdd", "3-0029"),
+ /* Pressure sensor device */
+ REGULATOR_SUPPLY("vdd", "2-005c"),
+ /* Cypress TrueTouch Touchscreen device */
+ REGULATOR_SUPPLY("vcpin", "spi8.0"),
+ /* Camera device */
+ REGULATOR_SUPPLY("vaux12v5", "mmio_camera"),
};
static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
@@ -60,6 +120,12 @@ static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
REGULATOR_SUPPLY("vmmc", "sdi4"),
/* AB8500 audio codec */
REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"),
+ /* AB8500 accessory detect 1 */
+ REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"),
+ /* AB8500 Tv-out device */
+ REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"),
+ /* AV8100 HDMI device */
+ REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"),
};
static struct regulator_consumer_supply ab8500_vaux3_consumers[] = {
@@ -72,26 +138,30 @@ static struct regulator_consumer_supply ab8500_vtvout_consumers[] = {
REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"),
/* Internal general-purpose ADC */
REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"),
+ /* ADC for charger */
+ REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"),
+ /* AB8500 Tv-out device */
+ REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"),
};
static struct regulator_consumer_supply ab8500_vaud_consumers[] = {
/* AB8500 audio-codec main supply */
- REGULATOR_SUPPLY("vaud", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("v-audio", NULL),
};
static struct regulator_consumer_supply ab8500_vamic1_consumers[] = {
/* AB8500 audio-codec Mic1 supply */
- REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("v-amic1", NULL),
};
static struct regulator_consumer_supply ab8500_vamic2_consumers[] = {
/* AB8500 audio-codec Mic2 supply */
- REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("v-amic2", NULL),
};
static struct regulator_consumer_supply ab8500_vdmic_consumers[] = {
/* AB8500 audio-codec DMic supply */
- REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("v-dmic", NULL),
};
static struct regulator_consumer_supply ab8500_vintcore_consumers[] = {
@@ -102,74 +172,85 @@ static struct regulator_consumer_supply ab8500_vintcore_consumers[] = {
};
static struct regulator_consumer_supply ab8500_vana_consumers[] = {
- /* External displays, connector on board, 1v8 power supply */
- REGULATOR_SUPPLY("vsmps2", "mcde.0"),
+ /* DB8500 DSI */
+ REGULATOR_SUPPLY("vdddsi1v2", "mcde"),
+ /* DB8500 CSI */
+ REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab8500_sysclkreq_2_consumers[] = {
+ /* CG2900 device */
+ REGULATOR_SUPPLY("gbf_1v8", "cg2900-uart.0"),
+};
+
+static struct regulator_consumer_supply ab8500_sysclkreq_4_consumers[] = {
+ /* CW1200 device */
+ REGULATOR_SUPPLY("wlan_1v8", "cw1200_wlan.0"),
};
/* ab8500 regulator register initialization */
-struct ab8500_regulator_reg_init
-ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
+static struct ab8500_regulator_reg_init ab8500_reg_init[] = {
/*
* VanaRequestCtrl = HP/LP depending on VxRequest
* VextSupply1RequestCtrl = HP/LP depending on VxRequest
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00),
/*
* VextSupply2RequestCtrl = HP/LP depending on VxRequest
* VextSupply3RequestCtrl = HP/LP depending on VxRequest
* Vaux1RequestCtrl = HP/LP depending on VxRequest
* Vaux2RequestCtrl = HP/LP depending on VxRequest
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00),
/*
* Vaux3RequestCtrl = HP/LP depending on VxRequest
* SwHPReq = Control through SWValid disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00),
/*
* VanaSysClkReq1HPValid = disabled
* Vaux1SysClkReq1HPValid = disabled
* Vaux2SysClkReq1HPValid = disabled
* Vaux3SysClkReq1HPValid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00),
/*
* VextSupply1SysClkReq1HPValid = disabled
* VextSupply2SysClkReq1HPValid = disabled
* VextSupply3SysClkReq1HPValid = SysClkReq1 controlled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x40),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40),
/*
* VanaHwHPReq1Valid = disabled
* Vaux1HwHPreq1Valid = disabled
* Vaux2HwHPReq1Valid = disabled
* Vaux3HwHPReqValid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00),
/*
* VextSupply1HwHPReq1Valid = disabled
* VextSupply2HwHPReq1Valid = disabled
* VextSupply3HwHPReq1Valid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00),
/*
* VanaHwHPReq2Valid = disabled
* Vaux1HwHPReq2Valid = disabled
* Vaux2HwHPReq2Valid = disabled
* Vaux3HwHPReq2Valid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00),
/*
* VextSupply1HwHPReq2Valid = disabled
* VextSupply2HwHPReq2Valid = disabled
* VextSupply3HwHPReq2Valid = HWReq2 controlled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x04),
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04),
/*
* VanaSwHPReqValid = disabled
* Vaux1SwHPReqValid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00),
/*
* Vaux2SwHPReqValid = disabled
* Vaux3SwHPReqValid = disabled
@@ -177,7 +258,7 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* VextSupply2SwHPReqValid = disabled
* VextSupply3SwHPReqValid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00),
/*
* SysClkReq2Valid1 = SysClkReq2 controlled
* SysClkReq3Valid1 = disabled
@@ -187,7 +268,7 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* SysClkReq7Valid1 = disabled
* SysClkReq8Valid1 = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0x2a),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a),
/*
* SysClkReq2Valid2 = disabled
* SysClkReq3Valid2 = disabled
@@ -197,7 +278,7 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* SysClkReq7Valid2 = disabled
* SysClkReq8Valid2 = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0x20),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20),
/*
* VTVoutEna = disabled
* Vintcore12Ena = disabled
@@ -205,66 +286,62 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* Vintcore12LP = inactive (HP)
* VTVoutLP = inactive (HP)
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0x10),
+ INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10),
/*
* VaudioEna = disabled
* VdmicEna = disabled
* Vamic1Ena = disabled
* Vamic2Ena = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00),
/*
* Vamic1_dzout = high-Z when Vamic1 is disabled
* Vamic2_dzout = high-Z when Vamic2 is disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00),
/*
- * VPll = Hw controlled
+ * VPll = Hw controlled (NOTE! PRCMU bits)
* VanaRegu = force off
*/
- INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x02),
+ INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02),
/*
* VrefDDREna = disabled
* VrefDDRSleepMode = inactive (no pulldown)
*/
- INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00),
/*
- * VextSupply1Regu = HW control
- * VextSupply2Regu = HW control
- * VextSupply3Regu = HW control
+ * VextSupply1Regu = force LP
+ * VextSupply2Regu = force OFF
+ * VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP)
* ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0
* ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0
*/
- INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0x2a),
+ INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13),
/*
* Vaux1Regu = force HP
* Vaux2Regu = force off
*/
- INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x01),
- /*
- * Vaux3regu = force off
- */
- INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01),
/*
- * Vsmps1 = 1.15V
+ * Vaux3Regu = force off
*/
- INIT_REGULATOR_REGISTER(AB8500_VSMPS1SEL1, 0x24),
+ INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00),
/*
- * Vaux1Sel = 2.5 V
+ * Vaux1Sel = 2.8 V
*/
- INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x08),
+ INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C),
/*
* Vaux2Sel = 2.9 V
*/
- INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0d),
+ INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d),
/*
* Vaux3Sel = 2.91 V
*/
- INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07),
+ INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07),
/*
* VextSupply12LP = disabled (no LP)
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00),
/*
* Vaux1Disch = short discharge time
* Vaux2Disch = short discharge time
@@ -273,23 +350,24 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* VTVoutDisch = short discharge time
* VaudioDisch = short discharge time
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00),
/*
* VanaDisch = short discharge time
* VdmicPullDownEna = pulldown disabled when Vdmic is disabled
* VdmicDisch = short discharge time
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00),
};
/* AB8500 regulators */
-struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
+static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
/* supplies to the display/camera */
[AB8500_LDO_AUX1] = {
+ .supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-DISPLAY",
- .min_uV = 2500000,
- .max_uV = 2900000,
+ .min_uV = 2800000,
+ .max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
REGULATOR_CHANGE_STATUS,
.boot_on = 1, /* display is on at boot */
@@ -306,24 +384,32 @@ struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
},
/* supplies to the on-board eMMC */
[AB8500_LDO_AUX2] = {
+ .supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-eMMC1",
.min_uV = 1100000,
.max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
- REGULATOR_CHANGE_STATUS,
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers),
.consumer_supplies = ab8500_vaux2_consumers,
},
/* supply for VAUX3, supplies to SDcard slots */
[AB8500_LDO_AUX3] = {
+ .supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-MMC-SD",
.min_uV = 1100000,
.max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
- REGULATOR_CHANGE_STATUS,
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers),
.consumer_supplies = ab8500_vaux3_consumers,
@@ -377,18 +463,167 @@ struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
[AB8500_LDO_INTCORE] = {
.constraints = {
.name = "V-INTCORE",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .min_uV = 1250000,
+ .max_uV = 1350000,
+ .input_uV = 1800000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE |
+ REGULATOR_CHANGE_DRMS,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers),
.consumer_supplies = ab8500_vintcore_consumers,
},
- /* supply for U8500 CSI/DSI, VANA LDO */
+ /* supply for U8500 CSI-DSI, VANA LDO */
[AB8500_LDO_ANA] = {
.constraints = {
- .name = "V-CSI/DSI",
+ .name = "V-CSI-DSI",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers),
.consumer_supplies = ab8500_vana_consumers,
},
+ /* sysclkreq 2 pin */
+ [AB8500_SYSCLKREQ_2] = {
+ .constraints = {
+ .name = "V-SYSCLKREQ-2",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab8500_sysclkreq_2_consumers),
+ .consumer_supplies = ab8500_sysclkreq_2_consumers,
+ },
+ /* sysclkreq 4 pin */
+ [AB8500_SYSCLKREQ_4] = {
+ .constraints = {
+ .name = "V-SYSCLKREQ-4",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab8500_sysclkreq_4_consumers),
+ .consumer_supplies = ab8500_sysclkreq_4_consumers,
+ },
};
+
+/* supply for VextSupply3 */
+static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = {
+ /* SIM supply for 3 V SIM cards */
+ REGULATOR_SUPPLY("vinvsim", "sim-detect.0"),
+};
+
+/* extended configuration for VextSupply2, only used for HREFP_V20 boards */
+static struct ab8500_ext_regulator_cfg ab8500_ext_supply2 = {
+ .hwreq = true,
+};
+
+/*
+ * AB8500 external regulators
+ */
+static struct regulator_init_data ab8500_ext_regulators[] = {
+ /* fixed Vbat supplies VSMPS1_EXT_1V8 */
+ [AB8500_EXT_SUPPLY1] = {
+ .constraints = {
+ .name = "ab8500-ext-supply1",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .initial_mode = REGULATOR_MODE_IDLE,
+ .boot_on = 1,
+ .always_on = 1,
+ },
+ },
+ /* fixed Vbat supplies VSMPS2_EXT_1V36 and VSMPS5_EXT_1V15 */
+ [AB8500_EXT_SUPPLY2] = {
+ .constraints = {
+ .name = "ab8500-ext-supply2",
+ .min_uV = 1360000,
+ .max_uV = 1360000,
+ },
+ },
+ /* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */
+ [AB8500_EXT_SUPPLY3] = {
+ .constraints = {
+ .name = "ab8500-ext-supply3",
+ .min_uV = 3400000,
+ .max_uV = 3400000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .boot_on = 1,
+ },
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab8500_ext_supply3_consumers),
+ .consumer_supplies = ab8500_ext_supply3_consumers,
+ },
+};
+
+struct ab8500_regulator_platform_data ab8500_regulator_plat_data = {
+ .reg_init = ab8500_reg_init,
+ .num_reg_init = ARRAY_SIZE(ab8500_reg_init),
+ .regulator = ab8500_regulators,
+ .num_regulator = ARRAY_SIZE(ab8500_regulators),
+ .ext_regulator = ab8500_ext_regulators,
+ .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators),
+};
+
+static void ab8500_modify_reg_init(int id, u8 mask, u8 value)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(ab8500_reg_init) - 1; i >= 0; i--) {
+ if (ab8500_reg_init[i].id == id) {
+ u8 initval = ab8500_reg_init[i].value;
+ initval = (initval & ~mask) | (value & mask);
+ ab8500_reg_init[i].value = initval;
+
+ BUG_ON(mask & ~ab8500_reg_init[i].mask);
+ return;
+ }
+ }
+
+ BUG_ON(1);
+}
+
+void mop500_regulator_init(void)
+{
+ struct regulator_init_data *regulator;
+
+ /*
+ * Temporarily turn on Vaux2 on 8520 machine
+ */
+ if (machine_is_u8520()) {
+ /* Vaux2 initialized to be on */
+ ab8500_modify_reg_init(AB8500_VAUX12REGU, 0x0f, 0x05);
+
+ /* Vaux2 always on */
+ regulator = &ab8500_ext_regulators[AB8500_LDO_AUX2];
+ regulator->constraints.always_on = 1;
+ }
+
+ /*
+ * Handle AB8500_EXT_SUPPLY2 on HREFP_V20_V50 boards (do it for
+ * all HREFP_V20 boards)
+ */
+ if (cpu_is_u8500v20()) {
+ /* VextSupply2RequestCtrl = HP/OFF depending on VxRequest */
+ ab8500_modify_reg_init(AB8500_REGUREQUESTCTRL3, 0x01, 0x01);
+
+ /* VextSupply2SysClkReq1HPValid = SysClkReq1 controlled */
+ ab8500_modify_reg_init(AB8500_REGUSYSCLKREQ1HPVALID2,
+ 0x20, 0x20);
+
+ /* VextSupply2 = force HP at initialization */
+ ab8500_modify_reg_init(AB8500_EXTSUPPLYREGU, 0x0c, 0x04);
+
+ /* enable VextSupply2 during platform active */
+ regulator = &ab8500_ext_regulators[AB8500_EXT_SUPPLY2];
+ regulator->constraints.always_on = 1;
+
+ /* disable VextSupply2 in suspend */
+ regulator = &ab8500_ext_regulators[AB8500_EXT_SUPPLY2];
+ regulator->constraints.state_mem.disabled = 1;
+ regulator->constraints.state_standby.disabled = 1;
+
+ /* enable VextSupply2 HW control (used in suspend) */
+ regulator->driver_data = (void *)&ab8500_ext_supply2;
+ }
+}
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.h b/arch/arm/mach-ux500/board-mop500-regulators.h
index 94992158d96..b5fc81a3649 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.h
+++ b/arch/arm/mach-ux500/board-mop500-regulators.h
@@ -14,9 +14,11 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h>
-extern struct ab8500_regulator_reg_init
-ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS];
-extern struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS];
+extern struct ab8500_regulator_platform_data ab8500_regulator_plat_data;
extern struct regulator_init_data tps61052_regulator;
+extern struct regulator_init_data gpio_wlan_vbat_regulator;
+extern struct regulator_init_data gpio_en_3v3_regulator;
+
+void mop500_regulator_init(void);
#endif
diff --git a/arch/arm/mach-ux500/board-u5500-regulators.c b/arch/arm/mach-ux500/board-u5500-regulators.c
new file mode 100644
index 00000000000..9e343259e53
--- /dev/null
+++ b/arch/arm/mach-ux500/board-u5500-regulators.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab5500.h>
+
+#include "regulator-u5500.h"
+#include "board-u5500.h"
+
+/*
+ * AB5500
+ */
+
+static struct regulator_consumer_supply ab5500_ldo_g_consumers[] = {
+ REGULATOR_SUPPLY("vmmc", "sdi1"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_h_consumers[] = {
+ REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
+ REGULATOR_SUPPLY("vdd", "1-004b"), /* Synaptics */
+ REGULATOR_SUPPLY("vin", "2-0036"), /* LM3530 */
+ REGULATOR_SUPPLY("vcpin", "spi1.0"),
+ REGULATOR_SUPPLY("v-ana", "mmio_camera"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.0"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.1"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_k_consumers[] = {
+ REGULATOR_SUPPLY("v-mmio-camera", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_h_consumers_pre_r3a[] = {
+ REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
+ REGULATOR_SUPPLY("vdd", "1-004b"), /* Synaptics */
+ REGULATOR_SUPPLY("vin", "2-0036"), /* LM3530 */
+ REGULATOR_SUPPLY("vcpin", "spi1.0"),
+ REGULATOR_SUPPLY("v-ana", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_k_consumers_pre_r3a[] = {
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.0"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.1"),
+ REGULATOR_SUPPLY("v-mmio-camera", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_l_consumers[] = {
+ REGULATOR_SUPPLY("vmmc", "sdi0"),
+ REGULATOR_SUPPLY("vmmc", "sdi2"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_vdigmic_consumers[] = {
+ REGULATOR_SUPPLY("vdigmic", "ab5500-codec.0"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_sim_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.5"),
+};
+
+static struct regulator_consumer_supply ab5500_bias2_consumers[] = {
+ REGULATOR_SUPPLY("v-amic", NULL),
+};
+
+static struct regulator_init_data
+ab5500_regulator_init_data[AB5500_NUM_REGULATORS] = {
+ /* SD Card */
+ [AB5500_LDO_G] = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 2910000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
+ },
+ .consumer_supplies = ab5500_ldo_g_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_g_consumers),
+ },
+ /* Display */
+ [AB5500_LDO_H] = {
+ .constraints = {
+ .min_uV = 2790000,
+ .max_uV = 2790000,
+ .apply_uV = 1,
+ .boot_on = 1, /* display on during boot */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_ldo_h_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_h_consumers),
+ },
+ /* Camera */
+ [AB5500_LDO_K] = {
+ .constraints = {
+ .min_uV = 2790000,
+ .max_uV = 2790000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_ldo_k_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_k_consumers),
+ },
+ /* External eMMC */
+ [AB5500_LDO_L] = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 2910000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
+ },
+ .consumer_supplies = ab5500_ldo_l_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_l_consumers),
+ },
+ [AB5500_LDO_VDIGMIC] = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_ldo_vdigmic_consumers,
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab5500_ldo_vdigmic_consumers),
+ },
+ [AB5500_LDO_SIM] = {
+ .constraints = {
+ .min_uV = 1875000,
+ .max_uV = 2900000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_ldo_sim_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_sim_consumers),
+ },
+ [AB5500_BIAS2] = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_bias2_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_bias2_consumers),
+ },
+};
+
+static struct ab5500_regulator_data
+ab5500_regulator_data[AB5500_NUM_REGULATORS] = {
+ [AB5500_LDO_H] = {
+ /*
+ * The sub camera on the dev boards needs both supplies to be
+ * on to avoid high leakage.
+ */
+ .off_is_lowpower = true,
+ },
+};
+
+struct ab5500_regulator_platform_data u5500_ab5500_regulator_data = {
+ .regulator = ab5500_regulator_init_data,
+ .data = ab5500_regulator_data,
+ .num_regulator = ARRAY_SIZE(ab5500_regulator_init_data),
+};
+
+
+static void __init u5500_regulators_init_debug(void)
+{
+ const char data[] = "debug";
+ int i;
+
+ for (i = 0; i < 6; i++)
+ platform_device_register_data(NULL, "reg-virt-consumer", i,
+ data, sizeof(data));
+}
+
+static struct regulator_consumer_supply u5500_vio_consumers[] = {
+ REGULATOR_SUPPLY("gbf_1v8", "cg2900-uart.0"),
+};
+
+static struct regulator_init_data u5500_vio_init_data = {
+ .constraints.always_on = 1,
+ .consumer_supplies = u5500_vio_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(u5500_vio_consumers),
+};
+
+static struct fixed_voltage_config u5500_vio_pdata __initdata = {
+ .supply_name = "vio_1v8",
+ .microvolts = 1800000,
+ .init_data = &u5500_vio_init_data,
+ .gpio = -EINVAL,
+};
+
+void __init u5500_regulators_init(void)
+{
+ if (u5500_board_is_pre_r3a()) {
+ struct regulator_init_data *rid = ab5500_regulator_init_data;
+
+ rid[AB5500_LDO_K].consumer_supplies
+ = ab5500_ldo_k_consumers_pre_r3a;
+ rid[AB5500_LDO_K].num_consumer_supplies
+ = ARRAY_SIZE(ab5500_ldo_k_consumers_pre_r3a);
+
+ rid[AB5500_LDO_H].consumer_supplies
+ = ab5500_ldo_h_consumers_pre_r3a;
+ rid[AB5500_LDO_H].num_consumer_supplies
+ = ARRAY_SIZE(ab5500_ldo_h_consumers_pre_r3a);
+ }
+
+ u5500_regulators_init_debug();
+
+ platform_device_register_data(NULL, "reg-fixed-voltage", -1,
+ &u5500_vio_pdata,
+ sizeof(u5500_vio_pdata));
+
+ regulator_has_full_constraints();
+}
diff --git a/arch/arm/mach-ux500/clock-db5500.c b/arch/arm/mach-ux500/clock-db5500.c
new file mode 100644
index 00000000000..aa61b1129e6
--- /dev/null
+++ b/arch/arm/mach-ux500/clock-db5500.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright (C) 2009 ST-Ericsson SA
+ * Copyright (C) 2009 STMicroelectronics
+ *
+ * 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/init.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <plat/pincfg.h>
+
+#include <mach/hardware.h>
+
+#include "clock.h"
+#include "pins-db5500.h"
+
+static DEFINE_MUTEX(sysclk_mutex);
+static DEFINE_MUTEX(pll_mutex);
+
+/* SysClk operations. */
+static int sysclk_enable(struct clk *clk)
+{
+ return prcmu_request_clock(PRCMU_SYSCLK, true);
+}
+
+static void sysclk_disable(struct clk *clk)
+{
+
+ prcmu_request_clock(PRCMU_SYSCLK, false);
+ return;
+}
+
+static struct clkops sysclk_ops = {
+ .enable = sysclk_enable,
+ .disable = sysclk_disable,
+};
+
+static int rtc_clk_enable(struct clk *clk)
+{
+ return ab5500_clock_rtc_enable(clk->cg_sel, true);
+}
+
+static void rtc_clk_disable(struct clk *clk)
+{
+ int ret = ab5500_clock_rtc_enable(clk->cg_sel, false);
+
+ if (ret)
+ pr_err("clock: %s failed to disable: %d\n", clk->name, ret);
+}
+
+static struct clkops rtc_clk_ops = {
+ .enable = rtc_clk_enable,
+ .disable = rtc_clk_disable,
+};
+
+static pin_cfg_t clkout0_pins[] = {
+ GPIO161_CLKOUT_0 | PIN_OUTPUT_LOW,
+};
+
+static pin_cfg_t clkout1_pins[] = {
+ GPIO162_CLKOUT_1 | PIN_OUTPUT_LOW,
+};
+
+static int clkout0_enable(struct clk *clk)
+{
+ return nmk_config_pins(clkout0_pins, ARRAY_SIZE(clkout0_pins));
+}
+
+static void clkout0_disable(struct clk *clk)
+{
+ int r;
+
+ r = nmk_config_pins_sleep(clkout0_pins, ARRAY_SIZE(clkout0_pins));
+ if (!r)
+ return;
+
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+static int clkout1_enable(struct clk *clk)
+{
+ return nmk_config_pins(clkout1_pins, ARRAY_SIZE(clkout0_pins));
+}
+
+static void clkout1_disable(struct clk *clk)
+{
+ int r;
+
+ r = nmk_config_pins_sleep(clkout1_pins, ARRAY_SIZE(clkout1_pins));
+ if (!r)
+ return;
+
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+static struct clkops clkout0_ops = {
+ .enable = clkout0_enable,
+ .disable = clkout0_disable,
+};
+
+static struct clkops clkout1_ops = {
+ .enable = clkout1_enable,
+ .disable = clkout1_disable,
+};
+
+#define PRCM_CLKOCR2 0x58C
+#define PRCM_CLKOCR2_REFCLK (1 << 0)
+#define PRCM_CLKOCR2_STATIC0 (1 << 2)
+
+static int clkout2_enable(struct clk *clk)
+{
+ prcmu_write(PRCM_CLKOCR2, PRCM_CLKOCR2_REFCLK);
+ return 0;
+}
+
+static void clkout2_disable(struct clk *clk)
+{
+ prcmu_write(PRCM_CLKOCR2, PRCM_CLKOCR2_STATIC0);
+}
+
+static struct clkops clkout2_ops = {
+ .enable = clkout2_enable,
+ .disable = clkout2_disable,
+};
+
+#define DEF_PER1_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST1_BASE, _cg_bit, &per1clk)
+#define DEF_PER2_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST2_BASE, _cg_bit, &per2clk)
+#define DEF_PER3_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST3_BASE, _cg_bit, &per3clk)
+#define DEF_PER5_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST5_BASE, _cg_bit, &per5clk)
+#define DEF_PER6_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST6_BASE, _cg_bit, &per6clk)
+
+#define DEF_PER1_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST1_BASE, _cg_bit, _parent, &per1clk)
+#define DEF_PER2_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST2_BASE, _cg_bit, _parent, &per2clk)
+#define DEF_PER3_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST3_BASE, _cg_bit, _parent, &per3clk)
+#define DEF_PER5_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST5_BASE, _cg_bit, _parent, &per5clk)
+#define DEF_PER6_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST6_BASE, _cg_bit, _parent, &per6clk)
+
+/* Clock sources. */
+
+static struct clk soc0_pll = {
+ .name = "soc0_pll",
+ .ops = &prcmu_clk_ops,
+ .mutex = &pll_mutex,
+ .cg_sel = PRCMU_PLLSOC0,
+};
+
+static struct clk soc1_pll = {
+ .name = "soc1_pll",
+ .ops = &prcmu_clk_ops,
+ .mutex = &pll_mutex,
+ .cg_sel = PRCMU_PLLSOC1,
+};
+
+static struct clk ddr_pll = {
+ .name = "ddr_pll",
+ .ops = &prcmu_clk_ops,
+ .mutex = &pll_mutex,
+ .cg_sel = PRCMU_PLLDDR,
+};
+
+static struct clk sysclk = {
+ .name = "sysclk",
+ .ops = &sysclk_ops,
+ .rate = 26000000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk rtc32k = {
+ .name = "rtc32k",
+ .rate = 32768,
+};
+
+static struct clk kbd32k = {
+ .name = "kbd32k",
+ .rate = 32768,
+};
+
+static struct clk clk_dummy = {
+ .name = "dummy",
+};
+
+static struct clk rtc_clk1 = {
+ .name = "rtc_clk1",
+ .ops = &rtc_clk_ops,
+ .cg_sel = 1,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk clkout0 = {
+ .name = "clkout0",
+ .ops = &clkout0_ops,
+ .parent = &sysclk,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk clkout1 = {
+ .name = "clkout1",
+ .ops = &clkout1_ops,
+ .parent = &sysclk,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk clkout2 = {
+ .name = "clkout2",
+ .ops = &clkout2_ops,
+ .parent = &sysclk,
+ .mutex = &sysclk_mutex,
+};
+
+static DEFINE_MUTEX(parented_prcmu_mutex);
+
+#define DEF_PRCMU_CLK_PARENT(_name, _cg_sel, _rate, _parent) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .rate = _rate, \
+ .parent = _parent, \
+ .mutex = &parented_prcmu_mutex, \
+ }
+
+static DEFINE_MUTEX(prcmu_client_mutex);
+
+#define DEF_PRCMU_CLIENT_CLK(_name, _cg_sel, _rate) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .rate = _rate, \
+ .mutex = &prcmu_client_mutex, \
+ }
+
+static DEF_PRCMU_CLK(dmaclk, PRCMU_DMACLK, 200000000);
+static DEF_PRCMU_CLK(b2r2clk, PRCMU_B2R2CLK, 200000000);
+static DEF_PRCMU_CLK(sgaclk, PRCMU_SGACLK, 199900000);
+static DEF_PRCMU_CLK(uartclk, PRCMU_UARTCLK, 36360000);
+static DEF_PRCMU_CLK(msp02clk, PRCMU_MSP02CLK, 13000000);
+static DEF_PRCMU_CLIENT_CLK(msp1clk, PRCMU_MSP1CLK, 26000000);
+static DEF_PRCMU_CLIENT_CLK(cdclk, PRCMU_CDCLK, 26000000);
+static DEF_PRCMU_CLK(i2cclk, PRCMU_I2CCLK, 24000000);
+static DEF_PRCMU_CLK_PARENT(irdaclk, PRCMU_IRDACLK, 48000000, &soc1_pll);
+static DEF_PRCMU_CLK_PARENT(irrcclk, PRCMU_IRRCCLK, 48000000, &soc1_pll);
+static DEF_PRCMU_CLK(rngclk, PRCMU_RNGCLK, 26000000);
+static DEF_PRCMU_CLK(pwmclk, PRCMU_PWMCLK, 26000000);
+static DEF_PRCMU_CLK(sdmmcclk, PRCMU_SDMMCCLK, 50000000);
+static DEF_PRCMU_CLK(spare1clk, PRCMU_SPARE1CLK, 50000000);
+static DEF_PRCMU_CLK(per1clk, PRCMU_PER1CLK, 133330000);
+static DEF_PRCMU_CLK(per2clk, PRCMU_PER2CLK, 133330000);
+static DEF_PRCMU_CLK(per3clk, PRCMU_PER3CLK, 133330000);
+static DEF_PRCMU_CLK(per5clk, PRCMU_PER5CLK, 133330000);
+static DEF_PRCMU_CLK(per6clk, PRCMU_PER6CLK, 133330000);
+static DEF_PRCMU_CLK(hdmiclk, PRCMU_HDMICLK, 26000000);
+static DEF_PRCMU_CLK(apeatclk, PRCMU_APEATCLK, 200000000);
+static DEF_PRCMU_CLK(apetraceclk, PRCMU_APETRACECLK, 266000000);
+static DEF_PRCMU_CLK(mcdeclk, PRCMU_MCDECLK, 160000000);
+static DEF_PRCMU_CLK(tvclk, PRCMU_TVCLK, 40000000);
+static DEF_PRCMU_CLK(dsialtclk, PRCMU_DSIALTCLK, 400000000);
+static DEF_PRCMU_CLK(timclk, PRCMU_TIMCLK, 3250000);
+static DEF_PRCMU_CLK_PARENT(svaclk, PRCMU_SVACLK, 156000000, &soc1_pll);
+static DEF_PRCMU_CLK(siaclk, PRCMU_SIACLK, 133330000);
+
+/* PRCC PClocks */
+
+static DEF_PER1_PCLK(0, p1_pclk0);
+static DEF_PER1_PCLK(1, p1_pclk1);
+static DEF_PER1_PCLK(2, p1_pclk2);
+static DEF_PER1_PCLK(3, p1_pclk3);
+static DEF_PER1_PCLK(4, p1_pclk4);
+static DEF_PER1_PCLK(5, p1_pclk5);
+static DEF_PER1_PCLK(6, p1_pclk6);
+
+static DEF_PER2_PCLK(0, p2_pclk0);
+static DEF_PER2_PCLK(1, p2_pclk1);
+
+static DEF_PER3_PCLK(0, p3_pclk0);
+static DEF_PER3_PCLK(1, p3_pclk1);
+static DEF_PER3_PCLK(2, p3_pclk2);
+
+static DEF_PER5_PCLK(0, p5_pclk0);
+static DEF_PER5_PCLK(1, p5_pclk1);
+static DEF_PER5_PCLK(2, p5_pclk2);
+static DEF_PER5_PCLK(3, p5_pclk3);
+static DEF_PER5_PCLK(4, p5_pclk4);
+static DEF_PER5_PCLK(5, p5_pclk5);
+static DEF_PER5_PCLK(6, p5_pclk6);
+static DEF_PER5_PCLK(7, p5_pclk7);
+static DEF_PER5_PCLK(8, p5_pclk8);
+static DEF_PER5_PCLK(9, p5_pclk9);
+static DEF_PER5_PCLK(10, p5_pclk10);
+static DEF_PER5_PCLK(11, p5_pclk11);
+static DEF_PER5_PCLK(12, p5_pclk12);
+static DEF_PER5_PCLK(13, p5_pclk13);
+static DEF_PER5_PCLK(14, p5_pclk14);
+static DEF_PER5_PCLK(15, p5_pclk15);
+
+static DEF_PER6_PCLK(0, p6_pclk0);
+static DEF_PER6_PCLK(1, p6_pclk1);
+static DEF_PER6_PCLK(2, p6_pclk2);
+static DEF_PER6_PCLK(3, p6_pclk3);
+static DEF_PER6_PCLK(4, p6_pclk4);
+static DEF_PER6_PCLK(5, p6_pclk5);
+static DEF_PER6_PCLK(6, p6_pclk6);
+static DEF_PER6_PCLK(7, p6_pclk7);
+
+/* MSP0 */
+static DEF_PER1_KCLK(0, p1_msp0_kclk, &msp02clk);
+static DEF_PER_CLK(p1_msp0_clk, &p1_pclk0, &p1_msp0_kclk);
+
+/* SDI0 */
+static DEF_PER1_KCLK(1, p1_sdi0_kclk, &spare1clk); /* &sdmmcclk on v1 */
+static DEF_PER_CLK(p1_sdi0_clk, &p1_pclk1, &p1_sdi0_kclk);
+
+/* SDI2 */
+static DEF_PER1_KCLK(2, p1_sdi2_kclk, &sdmmcclk);
+static DEF_PER_CLK(p1_sdi2_clk, &p1_pclk2, &p1_sdi2_kclk);
+
+/* UART0 */
+static DEF_PER1_KCLK(3, p1_uart0_kclk, &uartclk);
+static DEF_PER_CLK(p1_uart0_clk, &p1_pclk3, &p1_uart0_kclk);
+
+/* I2C1 */
+static DEF_PER1_KCLK(4, p1_i2c1_kclk, &i2cclk);
+static DEF_PER_CLK(p1_i2c1_clk, &p1_pclk4, &p1_i2c1_kclk);
+
+/* PWM */
+static DEF_PER3_KCLK(0, p3_pwm_kclk, &pwmclk);
+static DEF_PER_CLK(p3_pwm_clk, &p3_pclk1, &p3_pwm_kclk);
+
+/* KEYPAD */
+static DEF_PER3_KCLK(0, p3_keypad_kclk, &kbd32k);
+static DEF_PER_CLK(p3_keypad_clk, &p3_pclk0, &p3_keypad_kclk);
+
+/* MSP2 */
+static DEF_PER5_KCLK(0, p5_msp2_kclk, &msp02clk);
+static DEF_PER_CLK(p5_msp2_clk, &p5_pclk0, &p5_msp2_kclk);
+
+/* UART1 */
+static DEF_PER5_KCLK(1, p5_uart1_kclk, &uartclk);
+static DEF_PER_CLK(p5_uart1_clk, &p5_pclk1, &p5_uart1_kclk);
+
+/* UART2 */
+static DEF_PER5_KCLK(2, p5_uart2_kclk, &uartclk);
+static DEF_PER_CLK(p5_uart2_clk, &p5_pclk2, &p5_uart2_kclk);
+
+/* UART3 */
+static DEF_PER5_KCLK(3, p5_uart3_kclk, &uartclk);
+static DEF_PER_CLK(p5_uart3_clk, &p5_pclk3, &p5_uart3_kclk);
+
+/* SDI1 */
+static DEF_PER5_KCLK(4, p5_sdi1_kclk, &sdmmcclk);
+static DEF_PER_CLK(p5_sdi1_clk, &p5_pclk4, &p5_sdi1_kclk);
+
+/* SDI3 */
+static DEF_PER5_KCLK(5, p5_sdi3_kclk, &sdmmcclk);
+static DEF_PER_CLK(p5_sdi3_clk, &p5_pclk5, &p5_sdi3_kclk);
+
+/* SDI4 */
+static DEF_PER5_KCLK(6, p5_sdi4_kclk, &sdmmcclk);
+static DEF_PER_CLK(p5_sdi4_clk, &p5_pclk6, &p5_sdi4_kclk);
+
+/* I2C2 */
+static DEF_PER5_KCLK(7, p5_i2c2_kclk, &i2cclk);
+static DEF_PER_CLK(p5_i2c2_clk, &p5_pclk7, &p5_i2c2_kclk);
+
+/* I2C3 */
+static DEF_PER5_KCLK(8, p5_i2c3_kclk, &i2cclk);
+static DEF_PER_CLK(p5_i2c3_clk, &p5_pclk8, &p5_i2c3_kclk);
+
+/* IRRC */
+static DEF_PER5_KCLK(9, p5_irrc_kclk, &irrcclk);
+static DEF_PER_CLK(p5_irrc_clk, &p5_pclk9, &p5_irrc_kclk);
+
+/* IRDA */
+static DEF_PER5_KCLK(10, p5_irda_kclk, &irdaclk);
+static DEF_PER_CLK(p5_irda_clk, &p5_pclk10, &p5_irda_kclk);
+
+/* RNG */
+static DEF_PER6_KCLK(0, p6_rng_kclk, &rngclk);
+static DEF_PER_CLK(p6_rng_clk, &p6_pclk0, &p6_rng_kclk);
+
+/* MTU:S */
+
+/* MTU0 */
+static DEF_PER_CLK(p6_mtu0_clk, &p6_pclk6, &timclk);
+
+/* MTU1 */
+static DEF_PER_CLK(p6_mtu1_clk, &p6_pclk7, &timclk);
+
+static struct clk *db5500_dbg_clks[] __initdata = {
+ /* Clock sources */
+ &soc0_pll,
+ &soc1_pll,
+ &ddr_pll,
+ &sysclk,
+ &rtc32k,
+
+ /* PRCMU clocks */
+ &sgaclk,
+ &siaclk,
+ &svaclk,
+ &uartclk,
+ &msp02clk,
+ &msp1clk,
+ &cdclk,
+ &i2cclk,
+ &irdaclk,
+ &irrcclk,
+ &sdmmcclk,
+ &spare1clk,
+ &per1clk,
+ &per2clk,
+ &per3clk,
+ &per5clk,
+ &per6clk,
+ &hdmiclk,
+ &apeatclk,
+ &apetraceclk,
+ &mcdeclk,
+ &dsialtclk,
+ &dmaclk,
+ &b2r2clk,
+ &tvclk,
+ &rngclk,
+ &pwmclk,
+
+ /* PRCC clocks */
+ &p1_pclk0,
+ &p1_pclk1,
+ &p1_pclk2,
+ &p1_pclk3,
+ &p1_pclk4,
+ &p1_pclk5,
+ &p1_pclk6,
+
+ &p2_pclk0,
+ &p2_pclk1,
+
+ &p3_pclk0,
+ &p3_pclk1,
+ &p3_pclk2,
+
+ &p5_pclk0,
+ &p5_pclk1,
+ &p5_pclk2,
+ &p5_pclk3,
+ &p5_pclk4,
+ &p5_pclk5,
+ &p5_pclk6,
+ &p5_pclk7,
+ &p5_pclk8,
+ &p5_pclk9,
+ &p5_pclk10,
+ &p5_pclk11,
+ &p5_pclk12,
+ &p5_pclk13,
+ &p5_pclk14,
+ &p5_pclk15,
+
+ &p6_pclk0,
+ &p6_pclk1,
+ &p6_pclk2,
+ &p6_pclk3,
+ &p6_pclk4,
+ &p6_pclk5,
+ &p6_pclk6,
+ &p6_pclk7,
+
+ /* Clock sources */
+ &clkout0,
+ &clkout1,
+ &clkout2,
+ &rtc_clk1,
+};
+
+static struct clk_lookup u8500_common_clock_sources[] = {
+ CLK_LOOKUP(soc0_pll, NULL, "soc0_pll"),
+ CLK_LOOKUP(soc1_pll, NULL, "soc1_pll"),
+ CLK_LOOKUP(ddr_pll, NULL, "ddr_pll"),
+ CLK_LOOKUP(sysclk, NULL, "sysclk"),
+ CLK_LOOKUP(rtc32k, NULL, "clk32k"),
+};
+
+static struct clk_lookup db5500_prcmu_clocks[] = {
+ CLK_LOOKUP(sgaclk, "mali", NULL),
+ CLK_LOOKUP(siaclk, "mmio_camera", "sia"),
+ CLK_LOOKUP(svaclk, "hva", NULL),
+ CLK_LOOKUP(uartclk, "UART", NULL),
+ CLK_LOOKUP(msp02clk, "MSP02", NULL),
+ CLK_LOOKUP(msp1clk, "ux500-msp-i2s.1", NULL),
+ CLK_LOOKUP(cdclk, "cable_detect.0", NULL),
+ CLK_LOOKUP(i2cclk, "I2C", NULL),
+ CLK_LOOKUP(sdmmcclk, "sdmmc", NULL),
+ CLK_LOOKUP(per1clk, "PERIPH1", NULL),
+ CLK_LOOKUP(per2clk, "PERIPH2", NULL),
+ CLK_LOOKUP(per3clk, "PERIPH3", NULL),
+ CLK_LOOKUP(per5clk, "PERIPH5", NULL),
+ CLK_LOOKUP(per6clk, "PERIPH6", NULL),
+ CLK_LOOKUP(hdmiclk, "mcde", "hdmi"),
+ CLK_LOOKUP(apeatclk, "apeat", NULL),
+ CLK_LOOKUP(apetraceclk, "apetrace", NULL),
+ CLK_LOOKUP(mcdeclk, "mcde", NULL),
+ CLK_LOOKUP(mcdeclk, "mcde", "mcde"),
+ CLK_LOOKUP(dmaclk, "dma40.0", NULL),
+ CLK_LOOKUP(b2r2clk, "b2r2", NULL),
+ CLK_LOOKUP(b2r2clk, "b2r2_bus", NULL),
+ CLK_LOOKUP(b2r2clk, "U8500-B2R2.0", NULL),
+ CLK_LOOKUP(tvclk, "tv", NULL),
+ CLK_LOOKUP(tvclk, "mcde", "tv"),
+};
+
+static struct clk_lookup db5500_prcc_clocks[] = {
+ CLK_LOOKUP(p1_msp0_clk, "ux500-msp-i2s.0", NULL),
+ CLK_LOOKUP(p1_sdi0_clk, "sdi0", NULL),
+ CLK_LOOKUP(p1_sdi2_clk, "sdi2", NULL),
+ CLK_LOOKUP(p1_uart0_clk, "uart0", NULL),
+ CLK_LOOKUP(p1_i2c1_clk, "nmk-i2c.1", NULL),
+ CLK_LOOKUP(p1_pclk5, "gpio.0", NULL),
+ CLK_LOOKUP(p1_pclk5, "gpio.1", NULL),
+ CLK_LOOKUP(p1_pclk6, "fsmc", NULL),
+
+ CLK_LOOKUP(p2_pclk0, "musb-ux500.0", "usb"),
+ CLK_LOOKUP(p2_pclk1, "gpio.2", NULL),
+
+ CLK_LOOKUP(p3_keypad_clk, "db5500-keypad", NULL),
+ CLK_LOOKUP(p3_pwm_clk, "pwm", NULL),
+ CLK_LOOKUP(p3_pclk2, "gpio.4", NULL),
+
+ CLK_LOOKUP(p5_msp2_clk, "ux500-msp-i2s.2", NULL),
+ CLK_LOOKUP(p5_uart1_clk, "uart1", NULL),
+ CLK_LOOKUP(p5_uart2_clk, "uart2", NULL),
+ CLK_LOOKUP(p5_uart3_clk, "uart3", NULL),
+ CLK_LOOKUP(p5_sdi1_clk, "sdi1", NULL),
+ CLK_LOOKUP(p5_sdi3_clk, "sdi3", NULL),
+ CLK_LOOKUP(p5_sdi4_clk, "sdi4", NULL),
+ CLK_LOOKUP(p5_i2c2_clk, "nmk-i2c.2", NULL),
+ CLK_LOOKUP(p5_i2c3_clk, "nmk-i2c.3", NULL),
+ CLK_LOOKUP(p5_irrc_clk, "irrc", NULL),
+ CLK_LOOKUP(p5_irda_clk, "irda", NULL),
+ CLK_LOOKUP(p5_pclk11, "spi0", NULL),
+ CLK_LOOKUP(p5_pclk12, "spi1", NULL),
+ CLK_LOOKUP(p5_pclk13, "spi2", NULL),
+ CLK_LOOKUP(p5_pclk14, "spi3", NULL),
+ CLK_LOOKUP(p5_pclk15, "gpio.5", NULL),
+ CLK_LOOKUP(p5_pclk15, "gpio.6", NULL),
+ CLK_LOOKUP(p5_pclk15, "gpio.7", NULL),
+
+ CLK_LOOKUP(p6_rng_clk, "rng", NULL),
+ CLK_LOOKUP(p6_pclk1, "cryp0", NULL),
+ CLK_LOOKUP(p6_pclk2, "hash0", NULL),
+ CLK_LOOKUP(p6_pclk3, "pka", NULL),
+ CLK_LOOKUP(p6_pclk4, "hash1", NULL),
+ CLK_LOOKUP(p6_pclk1, "cryp1", NULL),
+ CLK_LOOKUP(p6_pclk5, "cfgreg", NULL),
+ CLK_LOOKUP(p6_mtu0_clk, "mtu0", NULL),
+ CLK_LOOKUP(p6_mtu1_clk, "mtu1", NULL),
+
+ /*
+ * Dummy clock sets up the GPIOs.
+ */
+ CLK_LOOKUP(clk_dummy, "gpio.3", NULL),
+ CLK_LOOKUP(rtc32k, "rtc-pl031", NULL),
+};
+
+static struct clk_lookup db5500_clkouts[] = {
+ CLK_LOOKUP(clkout1, "mmio_camera", "primary-cam"),
+ CLK_LOOKUP(clkout1, "mmio_camera", "secondary-cam"),
+ CLK_LOOKUP(clkout2, "ab5500-usb.0", "sysclk"),
+ CLK_LOOKUP(clkout2, "ab5500-codec.0", "sysclk"),
+};
+
+static struct clk_lookup u5500_clocks[] = {
+ CLK_LOOKUP(rtc_clk1, "cg2900-uart.0", "lpoclk"),
+};
+
+static const char *db5500_boot_clk[] __initdata = {
+ "spi0",
+ "spi1",
+ "spi2",
+ "spi3",
+ "uart0",
+ "uart1",
+ "uart2",
+ "uart3",
+ "sdi0",
+ "sdi1",
+ "sdi2",
+ "sdi3",
+ "sdi4",
+};
+
+static struct clk *boot_clks[ARRAY_SIZE(db5500_boot_clk)] __initdata;
+
+static int __init db5500_boot_clk_disable(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(db5500_boot_clk); i++) {
+ clk_disable(boot_clks[i]);
+ clk_put(boot_clks[i]);
+ }
+
+ return 0;
+}
+late_initcall_sync(db5500_boot_clk_disable);
+
+static void __init db5500_boot_clk_enable(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(db5500_boot_clk); i++) {
+ boot_clks[i] = clk_get_sys(db5500_boot_clk[i], NULL);
+ BUG_ON(IS_ERR(boot_clks[i]));
+ clk_enable(boot_clks[i]);
+ }
+}
+
+static void configure_clkouts(void)
+{
+ /* div parameter does not matter for sel0 REF_CLK */
+ WARN_ON(prcmu_config_clkout(DB5500_CLKOUT0,
+ DB5500_CLKOUT_REF_CLK_SEL0, 0));
+ WARN_ON(prcmu_config_clkout(DB5500_CLKOUT1,
+ DB5500_CLKOUT_REF_CLK_SEL0, 0));
+}
+
+static struct clk *db5500_clks_tobe_disabled[] __initdata = {
+ &siaclk,
+ &sgaclk,
+ &sdmmcclk,
+ &p1_pclk0,
+ &p1_pclk6,
+ &p3_keypad_clk,
+ &p3_pclk1,
+ &p5_pclk0,
+ &p5_pclk11,
+ &p5_pclk12,
+ &p5_pclk13,
+ &p5_pclk14,
+ &p6_pclk4,
+ &p6_pclk5,
+ &p6_pclk7,
+ &p5_uart1_clk,
+ &p5_uart2_clk,
+ &p5_uart3_clk,
+ &p5_sdi1_clk,
+ &p5_sdi3_clk,
+ &p5_sdi4_clk,
+ &p5_i2c3_clk,
+ &pwmclk,
+ &svaclk,
+ &cdclk,
+ &clkout2,
+};
+
+static int __init init_clock_states(void)
+{
+ int i = 0;
+ /*
+ * The following clks are shared with secure world.
+ * Currently this leads to a limitation where we need to
+ * enable them at all times.
+ */
+ clk_enable(&p6_pclk1);
+ clk_enable(&p6_pclk2);
+ clk_enable(&p6_pclk3);
+ clk_enable(&p6_rng_clk);
+
+ /*
+ * Disable clocks that are on at boot, but should be off.
+ */
+ for (i = 0; i < ARRAY_SIZE(db5500_clks_tobe_disabled); i++) {
+ if (!clk_enable(db5500_clks_tobe_disabled[i]))
+ clk_disable(db5500_clks_tobe_disabled[i]);
+ }
+ return 0;
+}
+late_initcall(init_clock_states);
+
+int __init db5500_clk_init(void)
+{
+ if (ux500_is_svp()) {
+ prcmu_clk_ops.enable = NULL;
+ prcmu_clk_ops.disable = NULL;
+ prcc_pclk_ops.enable = NULL;
+ prcc_pclk_ops.disable = NULL;
+ prcc_kclk_ops.enable = NULL;
+ prcc_kclk_ops.disable = NULL;
+ }
+ prcmu_clk_ops.get_rate = NULL;
+
+ clkdev_add_table(u8500_common_clock_sources,
+ ARRAY_SIZE(u8500_common_clock_sources));
+
+ clkdev_add_table(db5500_prcmu_clocks, ARRAY_SIZE(db5500_prcmu_clocks));
+ clkdev_add_table(db5500_prcc_clocks, ARRAY_SIZE(db5500_prcc_clocks));
+ clkdev_add_table(db5500_clkouts, ARRAY_SIZE(db5500_clkouts));
+ clkdev_add_table(u5500_clocks, ARRAY_SIZE(u5500_clocks));
+
+ db5500_boot_clk_enable();
+
+ /*
+ * The following clks are shared with secure world.
+ * Currently this leads to a limitation where we need to
+ * enable them at all times.
+ */
+ clk_enable(&p6_pclk1);
+ clk_enable(&p6_pclk2);
+ clk_enable(&p6_pclk3);
+ clk_enable(&p6_rng_clk);
+
+ configure_clkouts();
+
+ return 0;
+}
+
+int __init db5500_clk_debug_init(void)
+{
+ return dbx500_clk_debug_init(db5500_dbg_clks,
+ ARRAY_SIZE(db5500_dbg_clks));
+}
diff --git a/arch/arm/mach-ux500/clock-db8500.c b/arch/arm/mach-ux500/clock-db8500.c
new file mode 100644
index 00000000000..d41a22dd041
--- /dev/null
+++ b/arch/arm/mach-ux500/clock-db8500.c
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (C) 2009-2011 ST-Ericsson SA
+ * Copyright (C) 2009 STMicroelectronics
+ *
+ * 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/init.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+
+#include <plat/pincfg.h>
+
+#include <mach/hardware.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include "clock.h"
+#include "pins-db8500.h"
+#include "product.h"
+#include "prcc.h"
+
+static DEFINE_MUTEX(soc0_pll_mutex);
+static DEFINE_MUTEX(soc1_pll_mutex);
+static DEFINE_MUTEX(sysclk_mutex);
+static DEFINE_MUTEX(ab_ulpclk_mutex);
+static DEFINE_MUTEX(ab_intclk_mutex);
+static DEFINE_MUTEX(clkout0_mutex);
+static DEFINE_MUTEX(dsi_pll_mutex);
+
+static struct delayed_work sysclk_disable_work;
+
+/* PLL operations. */
+
+static unsigned long pll_get_rate(struct clk *clk)
+{
+ return prcmu_clock_rate(clk->cg_sel);
+}
+
+static struct clkops pll_ops = {
+ .get_rate = pll_get_rate,
+};
+
+/* SysClk operations. */
+
+static int request_sysclk(bool enable)
+{
+ static int requests;
+
+ if ((enable && (requests++ == 0)) || (!enable && (--requests == 0)))
+ return prcmu_request_clock(PRCMU_SYSCLK, enable);
+ return 0;
+}
+
+static int sysclk_enable(struct clk *clk)
+{
+ static bool swat_enable;
+ int r;
+
+ if (!swat_enable) {
+ r = ab8500_sysctrl_set(AB8500_SWATCTRL,
+ AB8500_SWATCTRL_SWATENABLE);
+ if (r)
+ return r;
+
+ swat_enable = true;
+ }
+
+ r = request_sysclk(true);
+ if (r)
+ return r;
+
+ if (clk->cg_sel) {
+ r = ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1, (u8)clk->cg_sel);
+ if (r)
+ (void)request_sysclk(false);
+ }
+ return r;
+}
+
+static void sysclk_disable(struct clk *clk)
+{
+ int r;
+
+ if (clk->cg_sel) {
+ r = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ (u8)clk->cg_sel);
+ if (r)
+ goto disable_failed;
+ }
+ r = request_sysclk(false);
+ if (r)
+ goto disable_failed;
+ return;
+
+disable_failed:
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+static struct clkops sysclk_ops = {
+ .enable = sysclk_enable,
+ .disable = sysclk_disable,
+};
+
+/* AB8500 UlpClk operations */
+
+static int ab_ulpclk_enable(struct clk *clk)
+{
+ int err;
+
+ if (clk->regulator == NULL) {
+ struct regulator *reg;
+
+ reg = regulator_get(NULL, "v-intcore");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ clk->regulator = reg;
+ }
+ err = regulator_set_optimum_mode(clk->regulator, 1500);
+ if (unlikely(err < 0))
+ goto regulator_enable_error;
+ err = regulator_enable(clk->regulator);
+ if (unlikely(err))
+ goto regulator_enable_error;
+ err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCONF,
+ AB8500_SYSULPCLKCONF_ULPCLKCONF_MASK);
+ if (unlikely(err))
+ goto enable_error;
+ err = ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_ULPCLKREQ);
+ if (unlikely(err))
+ goto enable_error;
+ /* Unknown/undocumented PLL locking time => wait 1 ms. */
+ mdelay(1);
+ return 0;
+
+enable_error:
+ (void)regulator_disable(clk->regulator);
+regulator_enable_error:
+ return err;
+}
+
+static void ab_ulpclk_disable(struct clk *clk)
+{
+ int err;
+
+ err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_ULPCLKREQ);
+ if (unlikely(regulator_disable(clk->regulator) || err))
+ goto out_err;
+
+ regulator_set_optimum_mode(clk->regulator, 0);
+
+ return;
+
+out_err:
+ pr_err("clock: %s failed to disable %s.\n", __func__, clk->name);
+}
+
+static struct clkops ab_ulpclk_ops = {
+ .enable = ab_ulpclk_enable,
+ .disable = ab_ulpclk_disable,
+};
+
+/* AB8500 intclk operations */
+
+enum ab_intclk_parent {
+ AB_INTCLK_PARENT_SYSCLK,
+ AB_INTCLK_PARENT_ULPCLK,
+ AB_INTCLK_PARENTS_END,
+ NUM_AB_INTCLK_PARENTS
+};
+
+static int ab_intclk_enable(struct clk *clk)
+{
+ if (clk->parent == clk->parents[AB_INTCLK_PARENT_ULPCLK]) {
+ return ab8500_sysctrl_write(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK,
+ (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT));
+ }
+ return 0;
+}
+
+static void ab_intclk_disable(struct clk *clk)
+{
+ if (clk->parent == clk->parents[AB_INTCLK_PARENT_SYSCLK])
+ return;
+
+ if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK)) {
+ pr_err("clock: %s failed to disable %s.\n", __func__,
+ clk->name);
+ }
+}
+
+static int ab_intclk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int err;
+
+ if (!clk->enabled)
+ return 0;
+
+ err = __clk_enable(parent, clk->mutex);
+
+ if (unlikely(err))
+ goto parent_enable_error;
+
+ if (parent == clk->parents[AB_INTCLK_PARENT_ULPCLK]) {
+ err = ab8500_sysctrl_write(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK,
+ (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT));
+ } else {
+ err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK);
+ }
+ if (unlikely(err))
+ goto config_error;
+
+ __clk_disable(clk->parent, clk->mutex);
+
+ return 0;
+
+config_error:
+ __clk_disable(parent, clk->mutex);
+parent_enable_error:
+ return err;
+}
+
+static struct clkops ab_intclk_ops = {
+ .enable = ab_intclk_enable,
+ .disable = ab_intclk_disable,
+ .set_parent = ab_intclk_set_parent,
+};
+
+/* AB8500 audio clock operations */
+
+static int audioclk_enable(struct clk *clk)
+{
+ return ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_AUDIOCLKENA);
+}
+
+static void audioclk_disable(struct clk *clk)
+{
+ if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_AUDIOCLKENA)) {
+ pr_err("clock: %s failed to disable %s.\n", __func__,
+ clk->name);
+ }
+}
+
+static struct clkops audioclk_ops = {
+ .enable = audioclk_enable,
+ .disable = audioclk_disable,
+};
+
+/* Primary camera clock operations */
+static int clkout0_enable(struct clk *clk)
+{
+ int r;
+
+ if (clk->regulator == NULL) {
+ struct regulator *reg;
+
+ reg = regulator_get(NULL, "v-ape");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ clk->regulator = reg;
+ }
+ r = regulator_enable(clk->regulator);
+ if (r)
+ goto regulator_failed;
+ r = prcmu_config_clkout(0, PRCMU_CLKSRC_CLK38M, 4);
+ if (r)
+ goto config_failed;
+ r = nmk_config_pin(GPIO227_CLKOUT1, false);
+ if (r)
+ goto gpio_failed;
+ return r;
+
+gpio_failed:
+ (void)prcmu_config_clkout(0, PRCMU_CLKSRC_CLK38M, 0);
+config_failed:
+ (void)regulator_disable(clk->regulator);
+regulator_failed:
+ return r;
+}
+
+static void clkout0_disable(struct clk *clk)
+{
+ int r;
+
+ r = nmk_config_pin((GPIO227_GPIO | PIN_OUTPUT_LOW), false);
+ if (r)
+ goto disable_failed;
+ (void)prcmu_config_clkout(0, PRCMU_CLKSRC_CLK38M, 0);
+ (void)regulator_disable(clk->regulator);
+ return;
+
+disable_failed:
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+/* Touch screen/secondary camera clock operations. */
+static int clkout1_enable(struct clk *clk)
+{
+ int r;
+
+ if (clk->regulator == NULL) {
+ struct regulator *reg;
+
+ reg = regulator_get(NULL, "v-ape");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ clk->regulator = reg;
+ }
+ r = regulator_enable(clk->regulator);
+ if (r)
+ goto regulator_failed;
+ r = prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 4);
+ if (r)
+ goto config_failed;
+ r = nmk_config_pin(GPIO228_CLKOUT2, false);
+ if (r)
+ goto gpio_failed;
+ return r;
+
+gpio_failed:
+ (void)prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 0);
+config_failed:
+ (void)regulator_disable(clk->regulator);
+regulator_failed:
+ return r;
+}
+
+static void clkout1_disable(struct clk *clk)
+{
+ int r;
+
+ r = nmk_config_pin((GPIO228_GPIO | PIN_OUTPUT_LOW), false);
+ if (r)
+ goto disable_failed;
+ (void)prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 0);
+ (void)regulator_disable(clk->regulator);
+ return;
+
+disable_failed:
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+static struct clkops clkout0_ops = {
+ .enable = clkout0_enable,
+ .disable = clkout0_disable,
+};
+
+static struct clkops clkout1_ops = {
+ .enable = clkout1_enable,
+ .disable = clkout1_disable,
+};
+
+#define DEF_PER1_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST1_BASE, _cg_bit, &per1clk)
+#define DEF_PER2_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST2_BASE, _cg_bit, &per2clk)
+#define DEF_PER3_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST3_BASE, _cg_bit, &per3clk)
+#define DEF_PER5_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST5_BASE, _cg_bit, &per5clk)
+#define DEF_PER6_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST6_BASE, _cg_bit, &per6clk)
+
+#define DEF_PER1_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST1_BASE, _cg_bit, _parent, &per1clk)
+#define DEF_PER2_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST2_BASE, _cg_bit, _parent, &per2clk)
+#define DEF_PER3_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST3_BASE, _cg_bit, _parent, &per3clk)
+#define DEF_PER5_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST5_BASE, _cg_bit, _parent, &per5clk)
+#define DEF_PER6_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST6_BASE, _cg_bit, _parent, &per6clk)
+
+/* Clock sources. */
+
+static struct clk soc0_pll = {
+ .name = "soc0_pll",
+ .ops = &prcmu_clk_ops,
+ .cg_sel = PRCMU_PLLSOC0,
+ .mutex = &soc0_pll_mutex,
+};
+
+static struct clk soc1_pll = {
+ .name = "soc1_pll",
+ .ops = &prcmu_clk_ops,
+ .cg_sel = PRCMU_PLLSOC1,
+ .mutex = &soc1_pll_mutex,
+};
+
+static struct clk ddr_pll = {
+ .name = "ddr_pll",
+ .ops = &pll_ops,
+ .cg_sel = PRCMU_PLLDDR,
+};
+
+static struct clk ulp38m4 = {
+ .name = "ulp38m4",
+ .rate = 38400000,
+};
+
+static struct clk sysclk = {
+ .name = "sysclk",
+ .ops = &sysclk_ops,
+ .rate = 38400000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk sysclk2 = {
+ .name = "sysclk2",
+ .ops = &sysclk_ops,
+ .cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ,
+ .rate = 38400000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk sysclk3 = {
+ .name = "sysclk3",
+ .ops = &sysclk_ops,
+ .cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ,
+ .rate = 38400000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk sysclk4 = {
+ .name = "sysclk4",
+ .ops = &sysclk_ops,
+ .cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ,
+ .rate = 38400000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk rtc32k = {
+ .name = "rtc32k",
+ .rate = 32768,
+};
+
+static struct clk clkout0 = {
+ .name = "clkout0",
+ .ops = &clkout0_ops,
+ .parent = &ulp38m4,
+ .rate = 9600000,
+ .mutex = &clkout0_mutex,
+};
+
+static struct clk clkout1 = {
+ .name = "clkout1",
+ .ops = &clkout1_ops,
+ .parent = &sysclk,
+ .rate = 9600000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk ab_ulpclk = {
+ .name = "ab_ulpclk",
+ .ops = &ab_ulpclk_ops,
+ .rate = 38400000,
+ .mutex = &ab_ulpclk_mutex,
+};
+
+static struct clk *ab_intclk_parents[NUM_AB_INTCLK_PARENTS] = {
+ [AB_INTCLK_PARENT_SYSCLK] = &sysclk,
+ [AB_INTCLK_PARENT_ULPCLK] = &ab_ulpclk,
+ [AB_INTCLK_PARENTS_END] = NULL,
+};
+
+static struct clk ab_intclk = {
+ .name = "ab_intclk",
+ .ops = &ab_intclk_ops,
+ .mutex = &ab_intclk_mutex,
+ .parent = &sysclk,
+ .parents = ab_intclk_parents,
+};
+
+static struct clk audioclk = {
+ .name = "audioclk",
+ .ops = &audioclk_ops,
+ .mutex = &ab_intclk_mutex,
+ .parent = &ab_intclk,
+};
+
+static DEF_PRCMU_CLK(sgaclk, PRCMU_SGACLK, 320000000);
+static DEF_PRCMU_CLK(uartclk, PRCMU_UARTCLK, 38400000);
+static DEF_PRCMU_CLK(msp02clk, PRCMU_MSP02CLK, 19200000);
+static DEF_PRCMU_CLK(msp1clk, PRCMU_MSP1CLK, 19200000);
+static DEF_PRCMU_CLK(i2cclk, PRCMU_I2CCLK, 24000000);
+static DEF_PRCMU_CLK(slimclk, PRCMU_SLIMCLK, 19200000);
+static DEF_PRCMU_CLK(per1clk, PRCMU_PER1CLK, 133330000);
+static DEF_PRCMU_CLK(per2clk, PRCMU_PER2CLK, 133330000);
+static DEF_PRCMU_CLK(per3clk, PRCMU_PER3CLK, 133330000);
+static DEF_PRCMU_CLK(per5clk, PRCMU_PER5CLK, 133330000);
+static DEF_PRCMU_CLK(per6clk, PRCMU_PER6CLK, 133330000);
+static DEF_PRCMU_CLK(per7clk, PRCMU_PER7CLK, 100000000);
+static DEF_PRCMU_SCALABLE_CLK(lcdclk, PRCMU_LCDCLK);
+static DEF_PRCMU_OPP100_CLK(bmlclk, PRCMU_BMLCLK, 200000000);
+static DEF_PRCMU_SCALABLE_CLK(hsitxclk, PRCMU_HSITXCLK);
+static DEF_PRCMU_SCALABLE_CLK(hsirxclk, PRCMU_HSIRXCLK);
+static DEF_PRCMU_SCALABLE_CLK(hdmiclk, PRCMU_HDMICLK);
+static DEF_PRCMU_CLK(apeatclk, PRCMU_APEATCLK, 160000000);
+static DEF_PRCMU_CLK(apetraceclk, PRCMU_APETRACECLK, 160000000);
+static DEF_PRCMU_CLK(mcdeclk, PRCMU_MCDECLK, 160000000);
+static DEF_PRCMU_OPP100_CLK(ipi2cclk, PRCMU_IPI2CCLK, 24000000);
+static DEF_PRCMU_CLK(dsialtclk, PRCMU_DSIALTCLK, 384000000);
+static DEF_PRCMU_CLK(dmaclk, PRCMU_DMACLK, 200000000);
+static DEF_PRCMU_CLK(b2r2clk, PRCMU_B2R2CLK, 200000000);
+static DEF_PRCMU_SCALABLE_CLK(tvclk, PRCMU_TVCLK);
+/* TODO: For SSPCLK, the spec says 24MHz, while the old driver says 48MHz. */
+static DEF_PRCMU_CLK(sspclk, PRCMU_SSPCLK, 24000000);
+static DEF_PRCMU_CLK(rngclk, PRCMU_RNGCLK, 19200000);
+static DEF_PRCMU_CLK(uiccclk, PRCMU_UICCCLK, 48000000);
+static DEF_PRCMU_CLK(timclk, PRCMU_TIMCLK, 2400000);
+static DEF_PRCMU_CLK(sdmmcclk, PRCMU_SDMMCCLK, 50000000);
+
+static struct clk dsi_pll = {
+ .name = "dsi_pll",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_PLLDSI,
+ .parent = &hdmiclk,
+ .mutex = &dsi_pll_mutex,
+};
+
+static struct clk dsi0clk = {
+ .name = "dsi0clk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI0CLK,
+ .parent = &dsi_pll,
+ .mutex = &dsi_pll_mutex,
+};
+
+static struct clk dsi1clk = {
+ .name = "dsi1clk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI1CLK,
+ .parent = &dsi_pll,
+ .mutex = &dsi_pll_mutex,
+};
+
+static struct clk dsi0escclk = {
+ .name = "dsi0escclk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI0ESCCLK,
+ .parent = &tvclk,
+};
+
+static struct clk dsi1escclk = {
+ .name = "dsi1escclk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI1ESCCLK,
+ .parent = &tvclk,
+};
+
+static struct clk dsi2escclk = {
+ .name = "dsi2escclk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI2ESCCLK,
+ .parent = &tvclk,
+};
+
+/* PRCC PClocks */
+
+static DEF_PER1_PCLK(0, p1_pclk0);
+static DEF_PER1_PCLK(1, p1_pclk1);
+static DEF_PER1_PCLK(2, p1_pclk2);
+static DEF_PER1_PCLK(3, p1_pclk3);
+static DEF_PER1_PCLK(4, p1_pclk4);
+static DEF_PER1_PCLK(5, p1_pclk5);
+static DEF_PER1_PCLK(6, p1_pclk6);
+static DEF_PER1_PCLK(7, p1_pclk7);
+static DEF_PER1_PCLK(8, p1_pclk8);
+static DEF_PER1_PCLK(9, p1_pclk9);
+static DEF_PER1_PCLK(10, p1_pclk10);
+static DEF_PER1_PCLK(11, p1_pclk11);
+
+static DEF_PER2_PCLK(0, p2_pclk0);
+static DEF_PER2_PCLK(1, p2_pclk1);
+static DEF_PER2_PCLK(2, p2_pclk2);
+static DEF_PER2_PCLK(3, p2_pclk3);
+static DEF_PER2_PCLK(4, p2_pclk4);
+static DEF_PER2_PCLK(5, p2_pclk5);
+static DEF_PER2_PCLK(6, p2_pclk6);
+static DEF_PER2_PCLK(7, p2_pclk7);
+static DEF_PER2_PCLK(8, p2_pclk8);
+static DEF_PER2_PCLK(9, p2_pclk9);
+static DEF_PER2_PCLK(10, p2_pclk10);
+static DEF_PER2_PCLK(11, p2_pclk11);
+
+static DEF_PER3_PCLK(0, p3_pclk0);
+static DEF_PER3_PCLK(1, p3_pclk1);
+static DEF_PER3_PCLK(2, p3_pclk2);
+static DEF_PER3_PCLK(3, p3_pclk3);
+static DEF_PER3_PCLK(4, p3_pclk4);
+static DEF_PER3_PCLK(5, p3_pclk5);
+static DEF_PER3_PCLK(6, p3_pclk6);
+static DEF_PER3_PCLK(7, p3_pclk7);
+static DEF_PER3_PCLK(8, p3_pclk8);
+
+static DEF_PER5_PCLK(0, p5_pclk0);
+static DEF_PER5_PCLK(1, p5_pclk1);
+
+static DEF_PER6_PCLK(0, p6_pclk0);
+static DEF_PER6_PCLK(1, p6_pclk1);
+static DEF_PER6_PCLK(2, p6_pclk2);
+static DEF_PER6_PCLK(3, p6_pclk3);
+static DEF_PER6_PCLK(4, p6_pclk4);
+static DEF_PER6_PCLK(5, p6_pclk5);
+static DEF_PER6_PCLK(6, p6_pclk6);
+static DEF_PER6_PCLK(7, p6_pclk7);
+
+/* UART0 */
+static DEF_PER1_KCLK(0, p1_uart0_kclk, &uartclk);
+static DEF_PER_CLK(p1_uart0_clk, &p1_pclk0, &p1_uart0_kclk);
+
+/* UART1 */
+static DEF_PER1_KCLK(1, p1_uart1_kclk, &uartclk);
+static DEF_PER_CLK(p1_uart1_clk, &p1_pclk1, &p1_uart1_kclk);
+
+/* I2C1 */
+static DEF_PER1_KCLK(2, p1_i2c1_kclk, &i2cclk);
+static DEF_PER_CLK(p1_i2c1_clk, &p1_pclk2, &p1_i2c1_kclk);
+
+/* MSP0 */
+static DEF_PER1_KCLK(3, p1_msp0_kclk, &msp02clk);
+static DEF_PER_CLK(p1_msp0_clk, &p1_pclk3, &p1_msp0_kclk);
+
+/* MSP1 */
+static DEF_PER1_KCLK(4, p1_msp1_kclk, &msp1clk);
+static DEF_PER_CLK(p1_msp1_clk, &p1_pclk4, &p1_msp1_kclk);
+
+/* SDI0 */
+static DEF_PER1_KCLK(5, p1_sdi0_kclk, &sdmmcclk);
+static DEF_PER_CLK(p1_sdi0_clk, &p1_pclk5, &p1_sdi0_kclk);
+
+/* I2C2 */
+static DEF_PER1_KCLK(6, p1_i2c2_kclk, &i2cclk);
+static DEF_PER_CLK(p1_i2c2_clk, &p1_pclk6, &p1_i2c2_kclk);
+
+/* SLIMBUS0 */
+static DEF_PER1_KCLK(3, p1_slimbus0_kclk, &slimclk);
+static DEF_PER_CLK(p1_slimbus0_clk, &p1_pclk8, &p1_slimbus0_kclk);
+
+/* I2C4 */
+static DEF_PER1_KCLK(9, p1_i2c4_kclk, &i2cclk);
+static DEF_PER_CLK(p1_i2c4_clk, &p1_pclk10, &p1_i2c4_kclk);
+
+/* MSP3 */
+static DEF_PER1_KCLK(10, p1_msp3_kclk, &msp1clk);
+static DEF_PER_CLK(p1_msp3_clk, &p1_pclk11, &p1_msp3_kclk);
+
+/* I2C3 */
+static DEF_PER2_KCLK(0, p2_i2c3_kclk, &i2cclk);
+static DEF_PER_CLK(p2_i2c3_clk, &p2_pclk0, &p2_i2c3_kclk);
+
+/* SDI4 */
+static DEF_PER2_KCLK(2, p2_sdi4_kclk, &sdmmcclk);
+static DEF_PER_CLK(p2_sdi4_clk, &p2_pclk4, &p2_sdi4_kclk);
+
+/* MSP2 */
+static DEF_PER2_KCLK(3, p2_msp2_kclk, &msp02clk);
+static DEF_PER_CLK(p2_msp2_clk, &p2_pclk5, &p2_msp2_kclk);
+
+/* SDI1 */
+static DEF_PER2_KCLK(4, p2_sdi1_kclk, &sdmmcclk);
+static DEF_PER_CLK(p2_sdi1_clk, &p2_pclk6, &p2_sdi1_kclk);
+
+/* SDI3 */
+static DEF_PER2_KCLK(5, p2_sdi3_kclk, &sdmmcclk);
+static DEF_PER_CLK(p2_sdi3_clk, &p2_pclk7, &p2_sdi3_kclk);
+
+/* HSIR */
+static struct clk p2_ssirx_kclk = {
+ .name = "p2_ssirx_kclk",
+ .ops = &prcc_kclk_rec_ops,
+ .io_base = U8500_CLKRST2_BASE,
+ .cg_sel = BIT(6),
+ .parent = &hsirxclk,
+ .clock = &per2clk,
+};
+
+/* HSIT */
+static struct clk p2_ssitx_kclk = {
+ .name = "p2_ssitx_kclk",
+ .ops = &prcc_kclk_rec_ops,
+ .io_base = U8500_CLKRST2_BASE,
+ .cg_sel = BIT(7),
+ .parent = &hsitxclk,
+ .clock = &per2clk,
+};
+
+/* SSP0 */
+static DEF_PER3_KCLK(1, p3_ssp0_kclk, &sspclk);
+static DEF_PER_CLK(p3_ssp0_clk, &p3_pclk1, &p3_ssp0_kclk);
+
+/* SSP1 */
+static DEF_PER3_KCLK(2, p3_ssp1_kclk, &sspclk);
+static DEF_PER_CLK(p3_ssp1_clk, &p3_pclk2, &p3_ssp1_kclk);
+
+/* I2C0 */
+static DEF_PER3_KCLK(3, p3_i2c0_kclk, &i2cclk);
+static DEF_PER_CLK(p3_i2c0_clk, &p3_pclk3, &p3_i2c0_kclk);
+
+/* SDI2 */
+static DEF_PER3_KCLK(4, p3_sdi2_kclk, &sdmmcclk);
+static DEF_PER_CLK(p3_sdi2_clk, &p3_pclk4, &p3_sdi2_kclk);
+
+/* SKE */
+static DEF_PER3_KCLK(5, p3_ske_kclk, &rtc32k);
+static DEF_PER_CLK(p3_ske_clk, &p3_pclk5, &p3_ske_kclk);
+
+/* UART2 */
+static DEF_PER3_KCLK(6, p3_uart2_kclk, &uartclk);
+static DEF_PER_CLK(p3_uart2_clk, &p3_pclk6, &p3_uart2_kclk);
+
+/* SDI5 */
+static DEF_PER3_KCLK(7, p3_sdi5_kclk, &sdmmcclk);
+static DEF_PER_CLK(p3_sdi5_clk, &p3_pclk7, &p3_sdi5_kclk);
+
+/* RNG */
+static DEF_PER6_KCLK(0, p6_rng_kclk, &rngclk);
+static DEF_PER_CLK(p6_rng_clk, &p6_pclk0, &p6_rng_kclk);
+
+/* MTU:S */
+
+/* MTU0 */
+static DEF_PER_CLK(p6_mtu0_clk, &p6_pclk6, &timclk);
+
+/* MTU1 */
+static DEF_PER_CLK(p6_mtu1_clk, &p6_pclk7, &timclk);
+
+/*
+ * TODO: Ensure names match with devices and then remove unnecessary entries
+ * when all drivers use the clk API.
+ */
+
+static struct clk_lookup u8500_clocks[] = {
+ CLK_LOOKUP(soc0_pll, NULL, "soc0_pll"),
+ CLK_LOOKUP(soc1_pll, NULL, "soc1_pll"),
+ CLK_LOOKUP(ddr_pll, NULL, "ddr_pll"),
+ CLK_LOOKUP(ulp38m4, NULL, "ulp38m4"),
+ CLK_LOOKUP(sysclk, NULL, "sysclk"),
+ CLK_LOOKUP(rtc32k, NULL, "clk32k"),
+ CLK_LOOKUP(sysclk, "ab8500-usb.0", "sysclk"),
+ CLK_LOOKUP(sysclk, "ab8500-codec.0", "sysclk"),
+ CLK_LOOKUP(ab_ulpclk, "ab8500-codec.0", "ulpclk"),
+ CLK_LOOKUP(ab_intclk, "ab8500-codec.0", "intclk"),
+ CLK_LOOKUP(audioclk, "ab8500-codec.0", "audioclk"),
+ CLK_LOOKUP(ab_intclk, "ab8500-pwm.1", NULL),
+
+ CLK_LOOKUP(clkout0, "pri-cam", NULL),
+ CLK_LOOKUP(clkout1, "3-005c", NULL),
+ CLK_LOOKUP(clkout1, "3-005d", NULL),
+ CLK_LOOKUP(clkout1, "sec-cam", NULL),
+
+ /* prcmu */
+ CLK_LOOKUP(sgaclk, "mali", NULL),
+ CLK_LOOKUP(uartclk, "UART", NULL),
+ CLK_LOOKUP(msp02clk, "MSP02", NULL),
+ CLK_LOOKUP(i2cclk, "I2C", NULL),
+ CLK_LOOKUP(sdmmcclk, "sdmmc", NULL),
+ CLK_LOOKUP(slimclk, "slim", NULL),
+ CLK_LOOKUP(per1clk, "PERIPH1", NULL),
+ CLK_LOOKUP(per2clk, "PERIPH2", NULL),
+ CLK_LOOKUP(per3clk, "PERIPH3", NULL),
+ CLK_LOOKUP(per5clk, "PERIPH5", NULL),
+ CLK_LOOKUP(per6clk, "PERIPH6", NULL),
+ CLK_LOOKUP(per7clk, "PERIPH7", NULL),
+ CLK_LOOKUP(lcdclk, "lcd", NULL),
+ CLK_LOOKUP(bmlclk, "bml", NULL),
+ CLK_LOOKUP(p2_ssitx_kclk, "ste_hsi.0", "hsit_hsitxclk"),
+ CLK_LOOKUP(p2_ssirx_kclk, "ste_hsi.0", "hsir_hsirxclk"),
+ CLK_LOOKUP(lcdclk, "mcde", "lcd"),
+ CLK_LOOKUP(hdmiclk, "hdmi", NULL),
+ CLK_LOOKUP(hdmiclk, "mcde", "hdmi"),
+ CLK_LOOKUP(apeatclk, "apeat", NULL),
+ CLK_LOOKUP(apetraceclk, "apetrace", NULL),
+ CLK_LOOKUP(mcdeclk, "mcde", NULL),
+ CLK_LOOKUP(mcdeclk, "mcde", "mcde"),
+ CLK_LOOKUP(ipi2cclk, "ipi2", NULL),
+ CLK_LOOKUP(dmaclk, "dma40.0", NULL),
+ CLK_LOOKUP(b2r2clk, "b2r2", NULL),
+ CLK_LOOKUP(b2r2clk, "b2r2_core", NULL),
+ CLK_LOOKUP(b2r2clk, "U8500-B2R2.0", NULL),
+ CLK_LOOKUP(tvclk, "tv", NULL),
+ CLK_LOOKUP(tvclk, "mcde", "tv"),
+ CLK_LOOKUP(msp1clk, "MSP1", NULL),
+ CLK_LOOKUP(dsialtclk, "dsialt", NULL),
+ CLK_LOOKUP(sspclk, "SSP", NULL),
+ CLK_LOOKUP(rngclk, "rngclk", NULL),
+ CLK_LOOKUP(uiccclk, "uicc", NULL),
+ CLK_LOOKUP(dsi0clk, "mcde", "dsihs0"),
+ CLK_LOOKUP(dsi1clk, "mcde", "dsihs1"),
+ CLK_LOOKUP(dsi_pll, "mcde", "dsihs2"),
+ CLK_LOOKUP(dsi0escclk, "mcde", "dsilp0"),
+ CLK_LOOKUP(dsi1escclk, "mcde", "dsilp1"),
+ CLK_LOOKUP(dsi2escclk, "mcde", "dsilp2"),
+
+ /* PERIPH 1 */
+ CLK_LOOKUP(p1_msp3_clk, "msp3", NULL),
+ CLK_LOOKUP(p1_msp3_clk, "ux500-msp-i2s.3", NULL),
+ CLK_LOOKUP(p1_msp3_kclk, "ab8500-codec.0", "msp3-kernel"),
+ CLK_LOOKUP(p1_pclk11, "ab8500-codec.0", "msp3-bus"),
+ CLK_LOOKUP(p1_uart0_clk, "uart0", NULL),
+ CLK_LOOKUP(p1_uart1_clk, "uart1", NULL),
+ CLK_LOOKUP(p1_i2c1_clk, "nmk-i2c.1", NULL),
+ CLK_LOOKUP(p1_msp0_clk, "msp0", NULL),
+ CLK_LOOKUP(p1_msp0_clk, "ux500-msp-i2s.0", NULL),
+ CLK_LOOKUP(p1_sdi0_clk, "sdi0", NULL),
+ CLK_LOOKUP(p1_i2c2_clk, "nmk-i2c.2", NULL),
+ CLK_LOOKUP(p1_slimbus0_clk, "slimbus0", NULL),
+ CLK_LOOKUP(p1_pclk9, "gpio.0", NULL),
+ CLK_LOOKUP(p1_pclk9, "gpio.1", NULL),
+ CLK_LOOKUP(p1_pclk9, "gpioblock0", NULL),
+ CLK_LOOKUP(p1_msp1_clk, "msp1", NULL),
+ CLK_LOOKUP(p1_msp1_clk, "ux500-msp-i2s.1", NULL),
+ CLK_LOOKUP(p1_msp1_kclk, "ab8500-codec.0", "msp1-kernel"),
+ CLK_LOOKUP(p1_pclk4, "ab8500-codec.0", "msp1-bus"),
+ CLK_LOOKUP(p1_pclk7, "spi3", NULL),
+ CLK_LOOKUP(p1_i2c4_clk, "nmk-i2c.4", NULL),
+
+ /* PERIPH 2 */
+ CLK_LOOKUP(p2_i2c3_clk, "nmk-i2c.3", NULL),
+ CLK_LOOKUP(p2_pclk1, "spi2", NULL),
+ CLK_LOOKUP(p2_pclk2, "spi1", NULL),
+ CLK_LOOKUP(p2_pclk3, "pwl", NULL),
+ CLK_LOOKUP(p2_sdi4_clk, "sdi4", NULL),
+ CLK_LOOKUP(p2_msp2_clk, "msp2", NULL),
+ CLK_LOOKUP(p2_msp2_clk, "ux500-msp-i2s.2", NULL),
+ CLK_LOOKUP(p2_sdi1_clk, "sdi1", NULL),
+ CLK_LOOKUP(p2_sdi3_clk, "sdi3", NULL),
+ CLK_LOOKUP(p2_pclk8, "spi0", NULL),
+ CLK_LOOKUP(p2_pclk9, "ste_hsi.0", "hsir_hclk"),
+ CLK_LOOKUP(p2_pclk10, "ste_hsi.0", "hsit_hclk"),
+ CLK_LOOKUP(p2_pclk11, "gpio.6", NULL),
+ CLK_LOOKUP(p2_pclk11, "gpio.7", NULL),
+ CLK_LOOKUP(p2_pclk11, "gpioblock1", NULL),
+
+ /* PERIPH 3 */
+ CLK_LOOKUP(p3_pclk0, NULL, "fsmc"),
+ CLK_LOOKUP(p3_i2c0_clk, "nmk-i2c.0", NULL),
+ CLK_LOOKUP(p3_sdi2_clk, "sdi2", NULL),
+ CLK_LOOKUP(p3_ske_clk, "ske", NULL),
+ CLK_LOOKUP(p3_ske_clk, "nmk-ske-keypad", NULL),
+ CLK_LOOKUP(p3_uart2_clk, "uart2", NULL),
+ CLK_LOOKUP(p3_sdi5_clk, "sdi5", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpio.2", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpio.3", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpio.4", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpio.5", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpioblock2", NULL),
+ CLK_LOOKUP(p3_ssp0_clk, "ssp0", NULL),
+ CLK_LOOKUP(p3_ssp1_clk, "ssp1", NULL),
+
+ /* PERIPH 5 */
+ CLK_LOOKUP(p5_pclk1, "gpio.8", NULL),
+ CLK_LOOKUP(p5_pclk1, "gpioblock3", NULL),
+ CLK_LOOKUP(p5_pclk0, "musb-ux500.0", "usb"),
+
+ /* PERIPH 6 */
+ CLK_LOOKUP(p6_pclk1, "cryp0", NULL),
+ CLK_LOOKUP(p6_pclk2, "hash0", NULL),
+ CLK_LOOKUP(p6_pclk3, "pka", NULL),
+ CLK_LOOKUP(p6_pclk5, "cfgreg", NULL),
+ CLK_LOOKUP(p6_mtu0_clk, "mtu0", NULL),
+ CLK_LOOKUP(p6_mtu1_clk, "mtu1", NULL),
+ CLK_LOOKUP(p6_pclk4, "hash1", NULL),
+ CLK_LOOKUP(p6_pclk1, "cryp1", NULL),
+ CLK_LOOKUP(p6_rng_clk, "rng", NULL),
+
+};
+
+static struct clk_lookup u8500_v2_sysclks[] = {
+ CLK_LOOKUP(sysclk2, NULL, "sysclk2"),
+ CLK_LOOKUP(sysclk3, NULL, "sysclk3"),
+ CLK_LOOKUP(sysclk4, NULL, "sysclk4"),
+};
+
+static void sysclk_init_disable(struct work_struct *not_used)
+{
+ int i;
+
+ mutex_lock(&sysclk_mutex);
+
+ /* Enable SWAT */
+ if (ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE))
+ goto err_swat;
+
+ for (i = 0; i < ARRAY_SIZE(u8500_v2_sysclks); i++) {
+ struct clk *clk = u8500_v2_sysclks[i].clk;
+
+ /* Disable sysclks */
+ if (!clk->enabled && clk->cg_sel) {
+ if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ (u8)clk->cg_sel))
+ goto err_sysclk;
+ }
+ }
+ goto unlock_and_exit;
+
+err_sysclk:
+ pr_err("clock: Disable %s failed", u8500_v2_sysclks[i].clk->name);
+ ab8500_sysctrl_clear(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE);
+ goto unlock_and_exit;
+
+err_swat:
+ pr_err("clock: Enable SWAT failed");
+
+unlock_and_exit:
+ mutex_unlock(&sysclk_mutex);
+}
+
+static struct clk *db8500_dbg_clks[] __initdata = {
+ /* Clock sources */
+ &soc0_pll,
+ &soc1_pll,
+ &ddr_pll,
+ &ulp38m4,
+ &sysclk,
+ &rtc32k,
+ /* PRCMU clocks */
+ &sgaclk,
+ &uartclk,
+ &msp02clk,
+ &msp1clk,
+ &i2cclk,
+ &sdmmcclk,
+ &slimclk,
+ &per1clk,
+ &per2clk,
+ &per3clk,
+ &per5clk,
+ &per6clk,
+ &per7clk,
+ &lcdclk,
+ &bmlclk,
+ &hsitxclk,
+ &hsirxclk,
+ &hdmiclk,
+ &apeatclk,
+ &apetraceclk,
+ &mcdeclk,
+ &ipi2cclk,
+ &dsialtclk,
+ &dsi_pll,
+ &dsi0clk,
+ &dsi1clk,
+ &dsi0escclk,
+ &dsi1escclk,
+ &dsi2escclk,
+ &dmaclk,
+ &b2r2clk,
+ &tvclk,
+ &sspclk,
+ &rngclk,
+ &uiccclk,
+ &sysclk2,
+ &clkout0,
+ &clkout1,
+ &p1_pclk0,
+ &p1_pclk1,
+ &p1_pclk2,
+ &p1_pclk3,
+ &p1_pclk4,
+ &p1_pclk5,
+ &p1_pclk6,
+ &p1_pclk7,
+ &p1_pclk8,
+ &p1_pclk9,
+ &p1_pclk10,
+ &p1_pclk11,
+ &p2_pclk0,
+ &p2_pclk1,
+ &p2_pclk2,
+ &p2_pclk3,
+ &p2_pclk4,
+ &p2_pclk5,
+ &p2_pclk6,
+ &p2_pclk7,
+ &p2_pclk8,
+ &p2_pclk9,
+ &p2_pclk10,
+ &p2_pclk11,
+ &p3_pclk0,
+ &p3_pclk1,
+ &p3_pclk2,
+ &p3_pclk3,
+ &p3_pclk4,
+ &p3_pclk5,
+ &p3_pclk6,
+ &p3_pclk7,
+ &p3_pclk8,
+ &p5_pclk0,
+ &p5_pclk1,
+ &p6_pclk0,
+ &p6_pclk1,
+ &p6_pclk2,
+ &p6_pclk3,
+ &p6_pclk4,
+ &p6_pclk5,
+ &p6_pclk6,
+ &p6_pclk7,
+};
+
+/* List of clocks which might be enabled from the bootloader */
+
+/*
+ * SOC settings enable bus + kernel clocks of all periphs without
+ * properly configuring the parents of the kernel clocks for all units.
+ * Enable and Disable them all to get them into a known and working state.
+ */
+static struct clk *loader_enabled_clk[] __initdata = {
+ /* periph 1 */
+ &p1_uart0_clk,
+ &p1_uart1_clk,
+ &p1_i2c1_clk,
+ &p1_msp0_clk,
+ &p1_msp1_clk,
+ &p1_sdi0_clk,
+ &p1_i2c2_clk,
+ &p1_pclk7, /* spi3 */
+ &p1_pclk9, /* gpioctrl */
+ &p1_i2c4_clk,
+
+ /* periph 2 */
+ &p2_i2c3_clk,
+ &p2_pclk1, /* spi2 */
+ &p2_pclk2, /* spi1 */
+ /* pwl has an unknown kclk parent, ignore it */
+ &p2_sdi4_clk,
+ &p2_msp2_clk,
+ &p2_sdi1_clk,
+ &p2_sdi3_clk,
+ &p2_pclk8, /* spi0 */
+ &p2_ssirx_kclk, /* hsir kernel */
+ &p2_ssitx_kclk, /* hsit kernel */
+ &p2_pclk9, /* hsir bus */
+ &p2_pclk10, /* hsit bus */
+ &p2_pclk11, /* gpioctrl */
+ /* periph 3 */
+ &p3_pclk0, /* fsmc */
+ &p3_ssp0_clk,
+ &p3_ssp1_clk,
+ &p3_i2c0_clk,
+ &p3_sdi2_clk,
+ &p3_ske_clk,
+ &p3_uart2_clk,
+ &p3_sdi5_clk,
+ &p3_pclk8, /* gpio */
+ /* periph 5 */
+ &p5_pclk0, /* usb */
+ &p5_pclk1, /* gpio */
+ /* periph 6 */
+ /* Leave out rng, cryp0, hash0 and pka */
+ &p6_pclk4, /* hash1 */
+ &p6_pclk5, /* cr */
+ &p6_mtu0_clk,
+ &p6_mtu1_clk,
+ /* periph 7 */
+ &per7clk, /* PERIPH7 */
+
+ &bmlclk, /* BML */
+ &dsialtclk, /* dsialt */
+ &hsirxclk, /* hsirx */
+ &hsitxclk, /* hsitx */
+ &ipi2cclk, /* ipi2 */
+ &lcdclk, /* mcde */
+ &b2r2clk, /* b2r2_bus */
+};
+
+static int __init init_clock_states(void)
+{
+ unsigned int i;
+ /*
+ * Disable peripheral clocks enabled by bootloader/default
+ * but without drivers
+ */
+ for (i = 0; i < ARRAY_SIZE(loader_enabled_clk); i++)
+ if (!clk_enable(loader_enabled_clk[i]))
+ clk_disable(loader_enabled_clk[i]);
+
+ /*
+ * APEATCLK and APETRACECLK are enabled at boot and needed
+ * in order to debug with Lauterbach
+ */
+ if (!clk_enable(&apeatclk)) {
+ if (!ux500_jtag_enabled())
+ clk_disable(&apeatclk);
+ }
+ if (!clk_enable(&apetraceclk)) {
+ if (!ux500_jtag_enabled())
+ clk_disable(&apetraceclk);
+ }
+
+ INIT_DELAYED_WORK(&sysclk_disable_work, sysclk_init_disable);
+ schedule_delayed_work(&sysclk_disable_work, 10 * HZ);
+
+ return 0;
+}
+late_initcall(init_clock_states);
+
+static void __init configure_c2_clocks(void)
+{
+ sgaclk.parent = &soc0_pll;
+ sgaclk.mutex = &soc0_pll_mutex;
+}
+
+int __init db8500_clk_init(void)
+{
+ struct prcmu_fw_version *fw_version;
+
+ /*
+ * Disable pwl's and slimbus' bus and kernel clocks without touching
+ * any parents. Because for slimbus, the prcmu fw has not correctly
+ * configured the clocks at boot and for pwl the kclk parent
+ * is unknown.
+ */
+
+ /* slimbus' bus and kernel clocks */
+ writel(1 << 8, __io_address(U8500_CLKRST1_BASE) + PRCC_PCKDIS);
+ writel(1 << 8, __io_address(U8500_CLKRST1_BASE) + PRCC_KCKDIS);
+ /* pwl's bus and kernel clocks */
+ writel(1 << 3, __io_address(U8500_CLKRST2_BASE) + PRCC_PCKDIS);
+ writel(1 << 1, __io_address(U8500_CLKRST2_BASE) + PRCC_KCKDIS);
+
+ fw_version = prcmu_get_fw_version();
+ if (fw_version != NULL)
+ switch (fw_version->project) {
+ case PRCMU_FW_PROJECT_U8500_C2:
+ case PRCMU_FW_PROJECT_U9500_C2:
+ case PRCMU_FW_PROJECT_U8520:
+ configure_c2_clocks();
+ break;
+ default:
+ break;
+ }
+ clkdev_add_table(u8500_v2_sysclks,
+ ARRAY_SIZE(u8500_v2_sysclks));
+ clkdev_add_table(u8500_clocks,
+ ARRAY_SIZE(u8500_clocks));
+#ifdef CONFIG_DEBUG_FS
+ clk_debugfs_add_table(u8500_v2_sysclks, ARRAY_SIZE(u8500_v2_sysclks));
+ clk_debugfs_add_table(u8500_clocks, ARRAY_SIZE(u8500_clocks));
+#endif
+ return 0;
+}
+
+int __init db8500_clk_debug_init(void)
+{
+ return dbx500_clk_debug_init(db8500_dbg_clks,
+ ARRAY_SIZE(db8500_dbg_clks));
+}
diff --git a/arch/arm/mach-ux500/clock-debug.c b/arch/arm/mach-ux500/clock-debug.c
new file mode 100644
index 00000000000..1ebc69fe061
--- /dev/null
+++ b/arch/arm/mach-ux500/clock-debug.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+
+#include "clock.h"
+
+struct clk_debug_info {
+ struct clk *clk;
+ struct dentry *dir;
+ struct dentry *enable;
+ struct dentry *requests;
+ int enabled;
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *clk_dir;
+static struct dentry *clk_show;
+static struct dentry *clk_show_enabled_only;
+
+static struct clk_debug_info *cdi;
+static int num_clks;
+
+static int clk_show_print(struct seq_file *s, void *p)
+{
+ int i;
+ int enabled_only = (int)s->private;
+
+ seq_printf(s, "\n%-20s %10s %s\n", "name", "rate",
+ "enabled (kernel + debug)");
+ for (i = 0; i < num_clks; i++) {
+ if (enabled_only && !cdi[i].clk->enabled)
+ continue;
+ seq_printf(s,
+ "%-20s %10lu %5d + %d\n",
+ cdi[i].clk->name,
+ clk_get_rate(cdi[i].clk),
+ cdi[i].clk->enabled - cdi[i].enabled,
+ cdi[i].enabled);
+ }
+
+ return 0;
+}
+
+static int clk_show_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_show_print, inode->i_private);
+}
+
+static int clk_enable_print(struct seq_file *s, void *p)
+{
+ struct clk_debug_info *cdi = s->private;
+
+ return seq_printf(s, "%d\n", cdi->enabled);
+}
+
+static int clk_enable_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_enable_print, inode->i_private);
+}
+
+static ssize_t clk_enable_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct clk_debug_info *cdi;
+ long user_val;
+ int err;
+
+ cdi = ((struct seq_file *)(file->private_data))->private;
+
+ err = kstrtol_from_user(user_buf, count, 0, &user_val);
+
+ if (err)
+ return err;
+
+ if ((user_val > 0) && (!cdi->enabled)) {
+ err = clk_enable(cdi->clk);
+ if (err) {
+ pr_err("clock: clk_enable(%s) failed.\n",
+ cdi->clk->name);
+ return -EFAULT;
+ }
+ cdi->enabled = 1;
+ } else if ((user_val <= 0) && (cdi->enabled)) {
+ clk_disable(cdi->clk);
+ cdi->enabled = 0;
+ }
+ return count;
+}
+
+static int clk_requests_print(struct seq_file *s, void *p)
+{
+ struct clk_debug_info *cdi = s->private;
+
+ return seq_printf(s, "%d\n", cdi->clk->enabled);
+}
+
+static int clk_requests_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_requests_print, inode->i_private);
+}
+
+static const struct file_operations clk_enable_fops = {
+ .open = clk_enable_open,
+ .write = clk_enable_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations clk_requests_fops = {
+ .open = clk_requests_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations clk_show_fops = {
+ .open = clk_show_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int create_clk_dirs(struct clk_debug_info *cdi, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ cdi[i].dir = debugfs_create_dir(cdi[i].clk->name, clk_dir);
+ if (!cdi[i].dir)
+ goto no_dir;
+ }
+
+ for (i = 0; i < size; i++) {
+ cdi[i].enable = debugfs_create_file("enable",
+ (S_IRUGO | S_IWUGO),
+ cdi[i].dir, &cdi[i],
+ &clk_enable_fops);
+ if (!cdi[i].enable)
+ goto no_enable;
+ }
+ for (i = 0; i < size; i++) {
+ cdi[i].requests = debugfs_create_file("requests", S_IRUGO,
+ cdi[i].dir, &cdi[i],
+ &clk_requests_fops);
+ if (!cdi[i].requests)
+ goto no_requests;
+ }
+ return 0;
+
+no_requests:
+ while (i--)
+ debugfs_remove(cdi[i].requests);
+ i = size;
+no_enable:
+ while (i--)
+ debugfs_remove(cdi[i].enable);
+ i = size;
+no_dir:
+ while (i--)
+ debugfs_remove(cdi[i].dir);
+
+ return -ENOMEM;
+}
+
+int __init dbx500_clk_debug_init(struct clk **clks, int num)
+{
+ int i;
+
+ cdi = kcalloc(sizeof(struct clk_debug_info), num, GFP_KERNEL);
+ if (!cdi)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ cdi[i].clk = clks[i];
+
+ num_clks = num;
+
+ clk_dir = debugfs_create_dir("clk", NULL);
+ if (!clk_dir)
+ goto no_dir;
+
+ clk_show = debugfs_create_file("show", S_IRUGO, clk_dir, (void *)0,
+ &clk_show_fops);
+ if (!clk_show)
+ goto no_show;
+
+ clk_show_enabled_only = debugfs_create_file("show-enabled-only",
+ S_IRUGO, clk_dir, (void *)1,
+ &clk_show_fops);
+ if (!clk_show_enabled_only)
+ goto no_enabled_only;
+
+ if (create_clk_dirs(cdi, num))
+ goto no_clks;
+
+ return 0;
+
+no_clks:
+ debugfs_remove(clk_show_enabled_only);
+no_enabled_only:
+ debugfs_remove(clk_show);
+no_show:
+ debugfs_remove(clk_dir);
+no_dir:
+ kfree(cdi);
+ return -ENOMEM;
+}
+
+static int __init clk_debug_init(void)
+{
+ if (cpu_is_u8500())
+ db8500_clk_debug_init();
+ else if (cpu_is_u5500())
+ db5500_clk_debug_init();
+
+ return 0;
+}
+module_init(clk_debug_init);
+
+#endif /* CONFIG_DEBUG_FS */
diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c
index ec35f0aa566..b8bac2d6966 100644
--- a/arch/arm/mach-ux500/clock.c
+++ b/arch/arm/mach-ux500/clock.c
@@ -7,18 +7,11 @@
* published by the Free Software Foundation.
*/
#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/clkdev.h>
-#include <linux/cpufreq.h>
-
-#include <plat/mtu.h>
-#include <mach/hardware.h>
-#include "clock.h"
+#include <linux/spinlock.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/mfd/dbx500-prcmu.h>
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
@@ -26,477 +19,495 @@
static LIST_HEAD(clk_list);
#endif
-#define PRCC_PCKEN 0x00
-#define PRCC_PCKDIS 0x04
-#define PRCC_KCKEN 0x08
-#define PRCC_KCKDIS 0x0C
-
-#define PRCM_YYCLKEN0_MGT_SET 0x510
-#define PRCM_YYCLKEN1_MGT_SET 0x514
-#define PRCM_YYCLKEN0_MGT_CLR 0x518
-#define PRCM_YYCLKEN1_MGT_CLR 0x51C
-#define PRCM_YYCLKEN0_MGT_VAL 0x520
-#define PRCM_YYCLKEN1_MGT_VAL 0x524
-
-#define PRCM_SVAMMDSPCLK_MGT 0x008
-#define PRCM_SIAMMDSPCLK_MGT 0x00C
-#define PRCM_SGACLK_MGT 0x014
-#define PRCM_UARTCLK_MGT 0x018
-#define PRCM_MSP02CLK_MGT 0x01C
-#define PRCM_MSP1CLK_MGT 0x288
-#define PRCM_I2CCLK_MGT 0x020
-#define PRCM_SDMMCCLK_MGT 0x024
-#define PRCM_SLIMCLK_MGT 0x028
-#define PRCM_PER1CLK_MGT 0x02C
-#define PRCM_PER2CLK_MGT 0x030
-#define PRCM_PER3CLK_MGT 0x034
-#define PRCM_PER5CLK_MGT 0x038
-#define PRCM_PER6CLK_MGT 0x03C
-#define PRCM_PER7CLK_MGT 0x040
-#define PRCM_LCDCLK_MGT 0x044
-#define PRCM_BMLCLK_MGT 0x04C
-#define PRCM_HSITXCLK_MGT 0x050
-#define PRCM_HSIRXCLK_MGT 0x054
-#define PRCM_HDMICLK_MGT 0x058
-#define PRCM_APEATCLK_MGT 0x05C
-#define PRCM_APETRACECLK_MGT 0x060
-#define PRCM_MCDECLK_MGT 0x064
-#define PRCM_IPI2CCLK_MGT 0x068
-#define PRCM_DSIALTCLK_MGT 0x06C
-#define PRCM_DMACLK_MGT 0x074
-#define PRCM_B2R2CLK_MGT 0x078
-#define PRCM_TVCLK_MGT 0x07C
-#define PRCM_TCR 0x1C8
-#define PRCM_TCR_STOPPED (1 << 16)
-#define PRCM_TCR_DOZE_MODE (1 << 17)
-#define PRCM_UNIPROCLK_MGT 0x278
-#define PRCM_SSPCLK_MGT 0x280
-#define PRCM_RNGCLK_MGT 0x284
-#define PRCM_UICCCLK_MGT 0x27C
-
-#define PRCM_MGT_ENABLE (1 << 8)
-
-static DEFINE_SPINLOCK(clocks_lock);
-
-static void __clk_enable(struct clk *clk)
-{
- if (clk->enabled++ == 0) {
- if (clk->parent_cluster)
- __clk_enable(clk->parent_cluster);
-
- if (clk->parent_periph)
- __clk_enable(clk->parent_periph);
-
- if (clk->ops && clk->ops->enable)
- clk->ops->enable(clk);
+#include "clock.h"
+#include "prcc.h"
+
+DEFINE_MUTEX(clk_opp100_mutex);
+static DEFINE_SPINLOCK(clk_spin_lock);
+#define NO_LOCK &clk_spin_lock
+
+static void __iomem *prcmu_base;
+
+static void __clk_lock(struct clk *clk, void *last_lock, unsigned long *flags)
+{
+ if (clk->mutex != last_lock) {
+ if (clk->mutex == NULL)
+ spin_lock_irqsave(&clk_spin_lock, *flags);
+ else
+ mutex_lock(clk->mutex);
}
}
-int clk_enable(struct clk *clk)
+static void __clk_unlock(struct clk *clk, void *last_lock, unsigned long flags)
+{
+ if (clk->mutex != last_lock) {
+ if (clk->mutex == NULL)
+ spin_unlock_irqrestore(&clk_spin_lock, flags);
+ else
+ mutex_unlock(clk->mutex);
+ }
+}
+
+void __clk_disable(struct clk *clk, void *current_lock)
{
unsigned long flags;
- spin_lock_irqsave(&clocks_lock, flags);
- __clk_enable(clk);
- spin_unlock_irqrestore(&clocks_lock, flags);
+ if (clk == NULL)
+ return;
- return 0;
+ __clk_lock(clk, current_lock, &flags);
+
+ if (clk->enabled && (--clk->enabled == 0)) {
+ if ((clk->ops != NULL) && (clk->ops->disable != NULL))
+ clk->ops->disable(clk);
+ __clk_disable(clk->parent, clk->mutex);
+ __clk_disable(clk->bus_parent, clk->mutex);
+ }
+
+ __clk_unlock(clk, current_lock, flags);
+
+ return;
}
-EXPORT_SYMBOL(clk_enable);
-static void __clk_disable(struct clk *clk)
+int __clk_enable(struct clk *clk, void *current_lock)
{
- if (--clk->enabled == 0) {
- if (clk->ops && clk->ops->disable)
- clk->ops->disable(clk);
+ int err;
+ unsigned long flags;
- if (clk->parent_periph)
- __clk_disable(clk->parent_periph);
+ if (clk == NULL)
+ return 0;
- if (clk->parent_cluster)
- __clk_disable(clk->parent_cluster);
+ __clk_lock(clk, current_lock, &flags);
+
+ if (!clk->enabled) {
+ err = __clk_enable(clk->bus_parent, clk->mutex);
+ if (unlikely(err))
+ goto bus_parent_error;
+
+ err = __clk_enable(clk->parent, clk->mutex);
+ if (unlikely(err))
+ goto parent_error;
+
+ if ((clk->ops != NULL) && (clk->ops->enable != NULL)) {
+ err = clk->ops->enable(clk);
+ if (unlikely(err))
+ goto enable_error;
+ }
}
+ clk->enabled++;
+
+ __clk_unlock(clk, current_lock, flags);
+
+ return 0;
+
+enable_error:
+ __clk_disable(clk->parent, clk->mutex);
+parent_error:
+ __clk_disable(clk->bus_parent, clk->mutex);
+bus_parent_error:
+
+ __clk_unlock(clk, current_lock, flags);
+
+ return err;
}
-void clk_disable(struct clk *clk)
+unsigned long __clk_get_rate(struct clk *clk, void *current_lock)
{
+ unsigned long rate;
unsigned long flags;
- WARN_ON(!clk->enabled);
+ if (clk == NULL)
+ return 0;
+
+ __clk_lock(clk, current_lock, &flags);
- spin_lock_irqsave(&clocks_lock, flags);
- __clk_disable(clk);
- spin_unlock_irqrestore(&clocks_lock, flags);
+ if ((clk->ops != NULL) && (clk->ops->get_rate != NULL))
+ rate = clk->ops->get_rate(clk);
+ else if (clk->rate)
+ rate = clk->rate;
+ else
+ rate = __clk_get_rate(clk->parent, clk->mutex);
+
+ __clk_unlock(clk, current_lock, flags);
+
+ return rate;
}
-EXPORT_SYMBOL(clk_disable);
-/*
- * The MTU has a separate, rather complex muxing setup
- * with alternative parents (peripheral cluster or
- * ULP or fixed 32768 Hz) depending on settings
- */
-static unsigned long clk_mtu_get_rate(struct clk *clk)
+static long __clk_round_rate(struct clk *clk, unsigned long rate)
{
- void __iomem *addr;
- u32 tcr;
- int mtu = (int) clk->data;
- /*
- * One of these is selected eventually
- * TODO: Replace the constant with a reference
- * to the ULP source once this is modeled.
- */
- unsigned long clk32k = 32768;
- unsigned long mturate;
- unsigned long retclk;
-
- if (cpu_is_u5500())
- addr = __io_address(U5500_PRCMU_BASE);
- else if (cpu_is_u8500())
- addr = __io_address(U8500_PRCMU_BASE);
- else
- ux500_unknown_soc();
+ if ((clk->ops != NULL) && (clk->ops->round_rate != NULL))
+ return clk->ops->round_rate(clk, rate);
- /*
- * On a startup, always conifgure the TCR to the doze mode;
- * bootloaders do it for us. Do this in the kernel too.
- */
- writel(PRCM_TCR_DOZE_MODE, addr + PRCM_TCR);
+ return -ENOSYS;
+}
- tcr = readl(addr + PRCM_TCR);
+static int __clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ if ((clk->ops != NULL) && (clk->ops->set_rate != NULL))
+ return clk->ops->set_rate(clk, rate);
- /* Get the rate from the parent as a default */
- if (clk->parent_periph)
- mturate = clk_get_rate(clk->parent_periph);
- else if (clk->parent_cluster)
- mturate = clk_get_rate(clk->parent_cluster);
- else
- /* We need to be connected SOMEWHERE */
- BUG();
+ return -ENOSYS;
+}
- /* Return the clock selected for this MTU */
- if (tcr & (1 << mtu))
- retclk = clk32k;
- else
- retclk = mturate;
+int clk_enable(struct clk *clk)
+{
+ if (clk == NULL)
+ return -EINVAL;
- pr_info("MTU%d clock rate: %lu Hz\n", mtu, retclk);
- return retclk;
+ return __clk_enable(clk, NO_LOCK);
}
+EXPORT_SYMBOL(clk_enable);
-unsigned long clk_get_rate(struct clk *clk)
+void clk_disable(struct clk *clk)
{
- unsigned long rate;
- /*
- * If there is a custom getrate callback for this clock,
- * it will take precedence.
- */
- if (clk->get_rate)
- return clk->get_rate(clk);
-
- if (clk->ops && clk->ops->get_rate)
- return clk->ops->get_rate(clk);
-
- rate = clk->rate;
- if (!rate) {
- if (clk->parent_periph)
- rate = clk_get_rate(clk->parent_periph);
- else if (clk->parent_cluster)
- rate = clk_get_rate(clk->parent_cluster);
- }
+ if (clk == NULL)
+ return;
- return rate;
+ WARN_ON(!clk->enabled);
+ __clk_disable(clk, NO_LOCK);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (clk == NULL)
+ return 0;
+
+ return __clk_get_rate(clk, NO_LOCK);
}
EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
- /*TODO*/
- return rate;
+ long rounded_rate;
+ unsigned long flags;
+
+ if (clk == NULL)
+ return -EINVAL;
+
+ __clk_lock(clk, NO_LOCK, &flags);
+
+ rounded_rate = __clk_round_rate(clk, rate);
+
+ __clk_unlock(clk, NO_LOCK, flags);
+
+ return rounded_rate;
}
EXPORT_SYMBOL(clk_round_rate);
+long clk_round_rate_rec(struct clk *clk, unsigned long rate)
+{
+ long rounded_rate;
+ unsigned long flags;
+
+ if ((clk == NULL) || (clk->parent == NULL))
+ return -EINVAL;
+
+ __clk_lock(clk->parent, clk->mutex, &flags);
+
+ rounded_rate = __clk_round_rate(clk->parent, rate);
+
+ __clk_unlock(clk->parent, clk->mutex, flags);
+
+ return rounded_rate;
+}
+
+static void lock_parent_rate(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (clk->parent == NULL)
+ return;
+
+ __clk_lock(clk->parent, clk->mutex, &flags);
+
+ lock_parent_rate(clk->parent);
+ clk->parent->rate_locked++;
+
+ __clk_unlock(clk->parent, clk->mutex, flags);
+}
+
+static void unlock_parent_rate(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (clk->parent == NULL)
+ return;
+
+ __clk_lock(clk->parent, clk->mutex, &flags);
+
+ unlock_parent_rate(clk->parent);
+ clk->parent->rate_locked--;
+
+ __clk_unlock(clk->parent, clk->mutex, flags);
+}
+
int clk_set_rate(struct clk *clk, unsigned long rate)
{
- clk->rate = rate;
- return 0;
+ int err;
+ unsigned long flags;
+
+ if (clk == NULL)
+ return -EINVAL;
+
+ __clk_lock(clk, NO_LOCK, &flags);
+
+ if (clk->enabled) {
+ err = -EBUSY;
+ goto unlock_and_return;
+ }
+ if (clk->rate_locked) {
+ err = -EAGAIN;
+ goto unlock_and_return;
+ }
+
+ lock_parent_rate(clk);
+ err = __clk_set_rate(clk, rate);
+ unlock_parent_rate(clk);
+
+unlock_and_return:
+ __clk_unlock(clk, NO_LOCK, flags);
+
+ return err;
}
EXPORT_SYMBOL(clk_set_rate);
+int clk_set_rate_rec(struct clk *clk, unsigned long rate)
+{
+ int err;
+ unsigned long flags;
+
+ if ((clk == NULL) || (clk->parent == NULL))
+ return -EINVAL;
+
+ __clk_lock(clk->parent, clk->mutex, &flags);
+
+ if (clk->parent->enabled) {
+ err = -EBUSY;
+ goto unlock_and_return;
+ }
+ if (clk->parent->rate_locked != 1) {
+ err = -EAGAIN;
+ goto unlock_and_return;
+ }
+ err = __clk_set_rate(clk->parent, rate);
+
+unlock_and_return:
+ __clk_unlock(clk->parent, clk->mutex, flags);
+
+ return err;
+}
+
int clk_set_parent(struct clk *clk, struct clk *parent)
{
- /*TODO*/
- return -ENOSYS;
+ int err = 0;
+ unsigned long flags;
+ struct clk **p;
+
+ if ((clk == NULL) || (clk->parents == NULL))
+ return -EINVAL;
+ for (p = clk->parents; *p != parent; p++) {
+ if (*p == NULL) /* invalid parent */
+ return -EINVAL;
+ }
+
+ __clk_lock(clk, NO_LOCK, &flags);
+
+ if ((clk->ops != NULL) && (clk->ops->set_parent != NULL)) {
+ err = clk->ops->set_parent(clk, parent);
+ if (err)
+ goto unlock_and_return;
+ } else if (clk->enabled) {
+ err = __clk_enable(parent, clk->mutex);
+ if (err)
+ goto unlock_and_return;
+ __clk_disable(clk->parent, clk->mutex);
+ }
+
+ clk->parent = parent;
+
+unlock_and_return:
+ __clk_unlock(clk, NO_LOCK, flags);
+
+ return err;
}
-EXPORT_SYMBOL(clk_set_parent);
-static void clk_prcmu_enable(struct clk *clk)
+/* PRCMU clock operations. */
+
+static int prcmu_clk_enable(struct clk *clk)
{
- void __iomem *cg_set_reg = __io_address(U8500_PRCMU_BASE)
- + PRCM_YYCLKEN0_MGT_SET + clk->prcmu_cg_off;
+ return prcmu_request_clock(clk->cg_sel, true);
+}
+
+static void prcmu_clk_disable(struct clk *clk)
+{
+ if (prcmu_request_clock(clk->cg_sel, false)) {
+ pr_err("clock: %s failed to disable %s.\n", __func__,
+ clk->name);
+ }
+}
+
+static int request_ape_opp100(bool enable)
+{
+ static unsigned int requests;
+
+ if (enable) {
+ if (0 == requests++) {
+ return prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
+ "clock", 100);
+ }
+ } else if (1 == requests--) {
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "clock");
+ }
+ return 0;
+}
+
+static int prcmu_opp100_clk_enable(struct clk *clk)
+{
+ int r;
- writel(1 << clk->prcmu_cg_bit, cg_set_reg);
+ r = request_ape_opp100(true);
+ if (r) {
+ pr_err("clock: %s failed to request APE OPP 100%% for %s.\n",
+ __func__, clk->name);
+ return r;
+ }
+ return prcmu_request_clock(clk->cg_sel, true);
+}
+
+static void prcmu_opp100_clk_disable(struct clk *clk)
+{
+ if (prcmu_request_clock(clk->cg_sel, false))
+ goto out_error;
+ if (request_ape_opp100(false))
+ goto out_error;
+ return;
+
+out_error:
+ pr_err("clock: %s failed to disable %s.\n", __func__, clk->name);
+}
+
+static unsigned long prcmu_clk_get_rate(struct clk *clk)
+{
+ return prcmu_clock_rate(clk->cg_sel);
}
-static void clk_prcmu_disable(struct clk *clk)
+static long prcmu_clk_round_rate(struct clk *clk, unsigned long rate)
{
- void __iomem *cg_clr_reg = __io_address(U8500_PRCMU_BASE)
- + PRCM_YYCLKEN0_MGT_CLR + clk->prcmu_cg_off;
+ return prcmu_round_clock_rate(clk->cg_sel, rate);
+}
- writel(1 << clk->prcmu_cg_bit, cg_clr_reg);
+static int prcmu_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ return prcmu_set_clock_rate(clk->cg_sel, rate);
}
-static struct clkops clk_prcmu_ops = {
- .enable = clk_prcmu_enable,
- .disable = clk_prcmu_disable,
+struct clkops prcmu_clk_ops = {
+ .enable = prcmu_clk_enable,
+ .disable = prcmu_clk_disable,
+ .get_rate = prcmu_clk_get_rate,
+};
+
+struct clkops prcmu_scalable_clk_ops = {
+ .enable = prcmu_clk_enable,
+ .disable = prcmu_clk_disable,
+ .get_rate = prcmu_clk_get_rate,
+ .round_rate = prcmu_clk_round_rate,
+ .set_rate = prcmu_clk_set_rate,
};
-static unsigned int clkrst_base[] = {
- [1] = U8500_CLKRST1_BASE,
- [2] = U8500_CLKRST2_BASE,
- [3] = U8500_CLKRST3_BASE,
- [5] = U8500_CLKRST5_BASE,
- [6] = U8500_CLKRST6_BASE,
+struct clkops prcmu_opp100_clk_ops = {
+ .enable = prcmu_opp100_clk_enable,
+ .disable = prcmu_opp100_clk_disable,
+ .get_rate = prcmu_clk_get_rate,
};
-static void clk_prcc_enable(struct clk *clk)
+/* PRCC clock operations. */
+
+static int prcc_pclk_enable(struct clk *clk)
{
- void __iomem *addr = __io_address(clkrst_base[clk->cluster]);
+ void __iomem *io_base = __io_address(clk->io_base);
+
+ writel(clk->cg_sel, (io_base + PRCC_PCKEN));
+ while (!(readl(io_base + PRCC_PCKSR) & clk->cg_sel))
+ cpu_relax();
+ return 0;
+}
- if (clk->prcc_kernel != -1)
- writel(1 << clk->prcc_kernel, addr + PRCC_KCKEN);
+static void prcc_pclk_disable(struct clk *clk)
+{
+ void __iomem *io_base = __io_address(clk->io_base);
- if (clk->prcc_bus != -1)
- writel(1 << clk->prcc_bus, addr + PRCC_PCKEN);
+ writel(clk->cg_sel, (io_base + PRCC_PCKDIS));
}
-static void clk_prcc_disable(struct clk *clk)
+struct clkops prcc_pclk_ops = {
+ .enable = prcc_pclk_enable,
+ .disable = prcc_pclk_disable,
+};
+
+static int prcc_kclk_enable(struct clk *clk)
{
- void __iomem *addr = __io_address(clkrst_base[clk->cluster]);
+ int err;
+ void __iomem *io_base = __io_address(clk->io_base);
+
+ err = __clk_enable(clk->clock, clk->mutex);
+ if (err)
+ return err;
- if (clk->prcc_bus != -1)
- writel(1 << clk->prcc_bus, addr + PRCC_PCKDIS);
+ writel(clk->cg_sel, (io_base + PRCC_KCKEN));
+ while (!(readl(io_base + PRCC_KCKSR) & clk->cg_sel))
+ cpu_relax();
- if (clk->prcc_kernel != -1)
- writel(1 << clk->prcc_kernel, addr + PRCC_KCKDIS);
+ __clk_disable(clk->clock, clk->mutex);
+
+ return 0;
}
-static struct clkops clk_prcc_ops = {
- .enable = clk_prcc_enable,
- .disable = clk_prcc_disable,
+static void prcc_kclk_disable(struct clk *clk)
+{
+ void __iomem *io_base = __io_address(clk->io_base);
+
+ (void)__clk_enable(clk->clock, clk->mutex);
+ writel(clk->cg_sel, (io_base + PRCC_KCKDIS));
+ __clk_disable(clk->clock, clk->mutex);
+}
+
+struct clkops prcc_kclk_ops = {
+ .enable = prcc_kclk_enable,
+ .disable = prcc_kclk_disable,
};
-static struct clk clk_32khz = {
- .name = "clk_32khz",
- .rate = 32000,
+struct clkops prcc_kclk_rec_ops = {
+ .enable = prcc_kclk_enable,
+ .disable = prcc_kclk_disable,
+ .round_rate = clk_round_rate_rec,
+ .set_rate = clk_set_rate_rec,
};
-/*
- * PRCMU level clock gating
- */
+#ifdef CONFIG_CPU_FREQ
+extern unsigned long dbx500_cpufreq_getfreq(void);
-/* Bank 0 */
-static DEFINE_PRCMU_CLK(svaclk, 0x0, 2, SVAMMDSPCLK);
-static DEFINE_PRCMU_CLK(siaclk, 0x0, 3, SIAMMDSPCLK);
-static DEFINE_PRCMU_CLK(sgaclk, 0x0, 4, SGACLK);
-static DEFINE_PRCMU_CLK_RATE(uartclk, 0x0, 5, UARTCLK, 38400000);
-static DEFINE_PRCMU_CLK(msp02clk, 0x0, 6, MSP02CLK);
-static DEFINE_PRCMU_CLK(msp1clk, 0x0, 7, MSP1CLK); /* v1 */
-static DEFINE_PRCMU_CLK_RATE(i2cclk, 0x0, 8, I2CCLK, 48000000);
-static DEFINE_PRCMU_CLK_RATE(sdmmcclk, 0x0, 9, SDMMCCLK, 100000000);
-static DEFINE_PRCMU_CLK(slimclk, 0x0, 10, SLIMCLK);
-static DEFINE_PRCMU_CLK(per1clk, 0x0, 11, PER1CLK);
-static DEFINE_PRCMU_CLK(per2clk, 0x0, 12, PER2CLK);
-static DEFINE_PRCMU_CLK(per3clk, 0x0, 13, PER3CLK);
-static DEFINE_PRCMU_CLK(per5clk, 0x0, 14, PER5CLK);
-static DEFINE_PRCMU_CLK_RATE(per6clk, 0x0, 15, PER6CLK, 133330000);
-static DEFINE_PRCMU_CLK(lcdclk, 0x0, 17, LCDCLK);
-static DEFINE_PRCMU_CLK(bmlclk, 0x0, 18, BMLCLK);
-static DEFINE_PRCMU_CLK(hsitxclk, 0x0, 19, HSITXCLK);
-static DEFINE_PRCMU_CLK(hsirxclk, 0x0, 20, HSIRXCLK);
-static DEFINE_PRCMU_CLK(hdmiclk, 0x0, 21, HDMICLK);
-static DEFINE_PRCMU_CLK(apeatclk, 0x0, 22, APEATCLK);
-static DEFINE_PRCMU_CLK(apetraceclk, 0x0, 23, APETRACECLK);
-static DEFINE_PRCMU_CLK(mcdeclk, 0x0, 24, MCDECLK);
-static DEFINE_PRCMU_CLK(ipi2clk, 0x0, 25, IPI2CCLK);
-static DEFINE_PRCMU_CLK(dsialtclk, 0x0, 26, DSIALTCLK); /* v1 */
-static DEFINE_PRCMU_CLK(dmaclk, 0x0, 27, DMACLK);
-static DEFINE_PRCMU_CLK(b2r2clk, 0x0, 28, B2R2CLK);
-static DEFINE_PRCMU_CLK(tvclk, 0x0, 29, TVCLK);
-static DEFINE_PRCMU_CLK(uniproclk, 0x0, 30, UNIPROCLK); /* v1 */
-static DEFINE_PRCMU_CLK_RATE(sspclk, 0x0, 31, SSPCLK, 48000000); /* v1 */
-
-/* Bank 1 */
-static DEFINE_PRCMU_CLK(rngclk, 0x4, 0, RNGCLK); /* v1 */
-static DEFINE_PRCMU_CLK(uiccclk, 0x4, 1, UICCCLK); /* v1 */
+unsigned long clk_smp_twd_get_rate(struct clk *clk)
+{
+ return dbx500_cpufreq_getfreq() / 2;
+}
-/*
- * PRCC level clock gating
- * Format: per#, clk, PCKEN bit, KCKEN bit, parent
- */
+static struct clkops clk_smp_twd_ops = {
+ .get_rate = clk_smp_twd_get_rate,
+};
-/* Peripheral Cluster #1 */
-static DEFINE_PRCC_CLK(1, i2c4, 10, 9, &clk_i2cclk);
-static DEFINE_PRCC_CLK(1, gpio0, 9, -1, NULL);
-static DEFINE_PRCC_CLK(1, slimbus0, 8, 8, &clk_slimclk);
-static DEFINE_PRCC_CLK(1, spi3, 7, -1, NULL);
-static DEFINE_PRCC_CLK(1, i2c2, 6, 6, &clk_i2cclk);
-static DEFINE_PRCC_CLK(1, sdi0, 5, 5, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(1, msp1, 4, 4, &clk_msp1clk);
-static DEFINE_PRCC_CLK(1, msp0, 3, 3, &clk_msp02clk);
-static DEFINE_PRCC_CLK(1, i2c1, 2, 2, &clk_i2cclk);
-static DEFINE_PRCC_CLK(1, uart1, 1, 1, &clk_uartclk);
-static DEFINE_PRCC_CLK(1, uart0, 0, 0, &clk_uartclk);
-
-/* Peripheral Cluster #2 */
-static DEFINE_PRCC_CLK(2, gpio1, 11, -1, NULL);
-static DEFINE_PRCC_CLK(2, ssitx, 10, 7, NULL);
-static DEFINE_PRCC_CLK(2, ssirx, 9, 6, NULL);
-static DEFINE_PRCC_CLK(2, spi0, 8, -1, NULL);
-static DEFINE_PRCC_CLK(2, sdi3, 7, 5, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(2, sdi1, 6, 4, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(2, msp2, 5, 3, &clk_msp02clk);
-static DEFINE_PRCC_CLK(2, sdi4, 4, 2, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(2, pwl, 3, 1, NULL);
-static DEFINE_PRCC_CLK(2, spi1, 2, -1, NULL);
-static DEFINE_PRCC_CLK(2, spi2, 1, -1, NULL);
-static DEFINE_PRCC_CLK(2, i2c3, 0, 0, &clk_i2cclk);
-
-/* Peripheral Cluster #3 */
-static DEFINE_PRCC_CLK(3, gpio2, 8, -1, NULL);
-static DEFINE_PRCC_CLK(3, sdi5, 7, 7, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(3, uart2, 6, 6, &clk_uartclk);
-static DEFINE_PRCC_CLK(3, ske, 5, 5, &clk_32khz);
-static DEFINE_PRCC_CLK(3, sdi2, 4, 4, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(3, i2c0, 3, 3, &clk_i2cclk);
-static DEFINE_PRCC_CLK(3, ssp1, 2, 2, &clk_sspclk);
-static DEFINE_PRCC_CLK(3, ssp0, 1, 1, &clk_sspclk);
-static DEFINE_PRCC_CLK(3, fsmc, 0, -1, NULL);
-
-/* Peripheral Cluster #4 is in the always on domain */
-
-/* Peripheral Cluster #5 */
-static DEFINE_PRCC_CLK(5, gpio3, 1, -1, NULL);
-static DEFINE_PRCC_CLK(5, usb, 0, 0, NULL);
-
-/* Peripheral Cluster #6 */
-
-/* MTU ID in data */
-static DEFINE_PRCC_CLK_CUSTOM(6, mtu1, 8, -1, NULL, clk_mtu_get_rate, 1);
-static DEFINE_PRCC_CLK_CUSTOM(6, mtu0, 7, -1, NULL, clk_mtu_get_rate, 0);
-static DEFINE_PRCC_CLK(6, cfgreg, 6, 6, NULL);
-static DEFINE_PRCC_CLK(6, hash1, 5, -1, NULL);
-static DEFINE_PRCC_CLK(6, unipro, 4, 1, &clk_uniproclk);
-static DEFINE_PRCC_CLK(6, pka, 3, -1, NULL);
-static DEFINE_PRCC_CLK(6, hash0, 2, -1, NULL);
-static DEFINE_PRCC_CLK(6, cryp0, 1, -1, NULL);
-static DEFINE_PRCC_CLK(6, rng, 0, 0, &clk_rngclk);
-
-static struct clk clk_dummy_apb_pclk = {
- .name = "apb_pclk",
+static struct clk clk_smp_twd = {
+ .name = "smp_twd",
+ .ops = &clk_smp_twd_ops,
};
-static struct clk_lookup u8500_clks[] = {
- CLK(dummy_apb_pclk, NULL, "apb_pclk"),
-
- /* Peripheral Cluster #1 */
- CLK(gpio0, "gpio.0", NULL),
- CLK(gpio0, "gpio.1", NULL),
- CLK(slimbus0, "slimbus0", NULL),
- CLK(i2c2, "nmk-i2c.2", NULL),
- CLK(sdi0, "sdi0", NULL),
- CLK(msp0, "msp0", NULL),
- CLK(i2c1, "nmk-i2c.1", NULL),
- CLK(uart1, "uart1", NULL),
- CLK(uart0, "uart0", NULL),
-
- /* Peripheral Cluster #3 */
- CLK(gpio2, "gpio.2", NULL),
- CLK(gpio2, "gpio.3", NULL),
- CLK(gpio2, "gpio.4", NULL),
- CLK(gpio2, "gpio.5", NULL),
- CLK(sdi5, "sdi5", NULL),
- CLK(uart2, "uart2", NULL),
- CLK(ske, "ske", NULL),
- CLK(ske, "nmk-ske-keypad", NULL),
- CLK(sdi2, "sdi2", NULL),
- CLK(i2c0, "nmk-i2c.0", NULL),
- CLK(fsmc, "fsmc", NULL),
-
- /* Peripheral Cluster #5 */
- CLK(gpio3, "gpio.8", NULL),
-
- /* Peripheral Cluster #6 */
- CLK(hash1, "hash1", NULL),
- CLK(pka, "pka", NULL),
- CLK(hash0, "hash0", NULL),
- CLK(cryp0, "cryp0", NULL),
-
- /* PRCMU level clock gating */
-
- /* Bank 0 */
- CLK(svaclk, "sva", NULL),
- CLK(siaclk, "sia", NULL),
- CLK(sgaclk, "sga", NULL),
- CLK(slimclk, "slim", NULL),
- CLK(lcdclk, "lcd", NULL),
- CLK(bmlclk, "bml", NULL),
- CLK(hsitxclk, "stm-hsi.0", NULL),
- CLK(hsirxclk, "stm-hsi.1", NULL),
- CLK(hdmiclk, "hdmi", NULL),
- CLK(apeatclk, "apeat", NULL),
- CLK(apetraceclk, "apetrace", NULL),
- CLK(mcdeclk, "mcde", NULL),
- CLK(ipi2clk, "ipi2", NULL),
- CLK(dmaclk, "dma40.0", NULL),
- CLK(b2r2clk, "b2r2", NULL),
- CLK(tvclk, "tv", NULL),
-
- /* Peripheral Cluster #1 */
- CLK(i2c4, "nmk-i2c.4", NULL),
- CLK(spi3, "spi3", NULL),
- CLK(msp1, "msp1", NULL),
-
- /* Peripheral Cluster #2 */
- CLK(gpio1, "gpio.6", NULL),
- CLK(gpio1, "gpio.7", NULL),
- CLK(ssitx, "ssitx", NULL),
- CLK(ssirx, "ssirx", NULL),
- CLK(spi0, "spi0", NULL),
- CLK(sdi3, "sdi3", NULL),
- CLK(sdi1, "sdi1", NULL),
- CLK(msp2, "msp2", NULL),
- CLK(sdi4, "sdi4", NULL),
- CLK(pwl, "pwl", NULL),
- CLK(spi1, "spi1", NULL),
- CLK(spi2, "spi2", NULL),
- CLK(i2c3, "nmk-i2c.3", NULL),
-
- /* Peripheral Cluster #3 */
- CLK(ssp1, "ssp1", NULL),
- CLK(ssp0, "ssp0", NULL),
-
- /* Peripheral Cluster #5 */
- CLK(usb, "musb-ux500.0", "usb"),
-
- /* Peripheral Cluster #6 */
- CLK(mtu1, "mtu1", NULL),
- CLK(mtu0, "mtu0", NULL),
- CLK(cfgreg, "cfgreg", NULL),
- CLK(hash1, "hash1", NULL),
- CLK(unipro, "unipro", NULL),
- CLK(rng, "rng", NULL),
-
- /* PRCMU level clock gating */
-
- /* Bank 0 */
- CLK(uniproclk, "uniproclk", NULL),
- CLK(dsialtclk, "dsialt", NULL),
-
- /* Bank 1 */
- CLK(rngclk, "rng", NULL),
- CLK(uiccclk, "uicc", NULL),
+static struct clk_lookup clk_smp_twd_lookup = {
+ .clk = &clk_smp_twd,
+ .dev_id = "smp_twd",
};
+#endif
#ifdef CONFIG_DEBUG_FS
/*
@@ -585,8 +596,8 @@ err_out:
static int clk_debugfs_register_one(struct clk *c)
{
- struct clk *pa = c->parent_periph;
- struct clk *bpa = c->parent_cluster;
+ struct clk *pa = c->parent;
+ struct clk *bpa = c->bus_parent;
if (!(bpa && !pa)) {
c->dent = clk_debugfs_register_dir(c,
@@ -610,8 +621,8 @@ static int clk_debugfs_register_one(struct clk *c)
static int clk_debugfs_register(struct clk *c)
{
int err;
- struct clk *pa = c->parent_periph;
- struct clk *bpa = c->parent_cluster;
+ struct clk *pa = c->parent;
+ struct clk *bpa = c->bus_parent;
if (pa && (!pa->dent && !pa->dent_bus)) {
err = clk_debugfs_register(pa);
@@ -658,66 +669,25 @@ err_out:
late_initcall(clk_debugfs_init);
#endif /* defined(CONFIG_DEBUG_FS) */
-unsigned long clk_smp_twd_rate = 500000000;
-
-unsigned long clk_smp_twd_get_rate(struct clk *clk)
-{
- return clk_smp_twd_rate;
-}
-
-static struct clk clk_smp_twd = {
- .get_rate = clk_smp_twd_get_rate,
- .name = "smp_twd",
-};
-
-static struct clk_lookup clk_smp_twd_lookup = {
- .dev_id = "smp_twd",
- .clk = &clk_smp_twd,
-};
-
-#ifdef CONFIG_CPU_FREQ
-
-static int clk_twd_cpufreq_transition(struct notifier_block *nb,
- unsigned long state, void *data)
-{
- struct cpufreq_freqs *f = data;
-
- if (state == CPUFREQ_PRECHANGE) {
- /* Save frequency in simple Hz */
- clk_smp_twd_rate = (f->new * 1000) / 2;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block clk_twd_cpufreq_nb = {
- .notifier_call = clk_twd_cpufreq_transition,
-};
-
-static int clk_init_smp_twd_cpufreq(void)
-{
- return cpufreq_register_notifier(&clk_twd_cpufreq_nb,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-late_initcall(clk_init_smp_twd_cpufreq);
-
-#endif
-
int __init clk_init(void)
{
- if (cpu_is_u5500()) {
- /* Clock tree for U5500 not implemented yet */
- clk_prcc_ops.enable = clk_prcc_ops.disable = NULL;
- clk_prcmu_ops.enable = clk_prcmu_ops.disable = NULL;
- clk_uartclk.rate = 36360000;
- clk_sdmmcclk.rate = 99900000;
+ if (cpu_is_u8500()) {
+ prcmu_base = __io_address(U8500_PRCMU_BASE);
+ } else if (cpu_is_u5500()) {
+ prcmu_base = __io_address(U5500_PRCMU_BASE);
+ } else {
+ pr_err("clock: Unknown DB Asic.\n");
+ return -EIO;
}
- clkdev_add_table(u8500_clks, ARRAY_SIZE(u8500_clks));
- clkdev_add(&clk_smp_twd_lookup);
+ if (cpu_is_u8500())
+ db8500_clk_init();
+ else if (cpu_is_u5500())
+ db5500_clk_init();
-#ifdef CONFIG_DEBUG_FS
- clk_debugfs_add_table(u8500_clks, ARRAY_SIZE(u8500_clks));
+#ifdef CONFIG_CPU_FREQ
+ clkdev_add(&clk_smp_twd_lookup);
#endif
+
return 0;
}
diff --git a/arch/arm/mach-ux500/clock.h b/arch/arm/mach-ux500/clock.h
index d776ada08db..2403e51dc7f 100644
--- a/arch/arm/mach-ux500/clock.h
+++ b/arch/arm/mach-ux500/clock.h
@@ -1,11 +1,61 @@
/*
- * Copyright (C) 2010 ST-Ericsson
+ * Copyright (C) 2010 ST-Ericsson SA
* Copyright (C) 2009 STMicroelectronics
*
* 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 UX500_CLOCK_H
+#define UX500_CLOCK_H
+
+#include <linux/clkdev.h>
+
+/**
+ * struct clk
+ * @ops: The hardware specific operations defined for the clock.
+ * @name: The name of the clock.
+ * @mutex: The mutex to lock when operating on the clock. %NULL means that
+ * the common clock spinlock will be used.
+ * @enabled: A reference counter of the enable requests for the clock.
+ * @rate_locked: A rate lock counter used by clk_set_rate().
+ * @opp100: A flag saying whether the clock is requested to run at the
+ * OPP 100%% frequency.
+ * @rate: The frequency of the clock. For scalable and scaling clocks,
+ * this is the OPP 100%% frequency.
+ * @io_base: An IO memory base address, meaningful only when considered
+ * together with the defined @ops.
+ * @cg_sel: Clock gate selector, meaningful only when considered together
+ * with the specified @ops.
+ * @parent: The current (or only) parent clock of the clock.
+ * @bus_parent: The (optional) auxiliary bus clock "parent" of the clock.
+ * @parents: A list of the possible parents the clock can have. This should
+ * be a %NULL-terminated &struct_clk array. Present if and only
+ * if clk_set_parent() is implemented for the clock.
+ * @regulator: The regulator needed to have the clock functional, if any.
+ * @clock: The clock needed to control the clock, if any.
+ */
+struct clk {
+ const struct clkops *ops;
+ const char *name;
+ struct mutex *mutex;
+ unsigned int enabled;
+ unsigned int rate_locked;
+ bool opp100;
+ unsigned long rate;
+ unsigned int io_base;
+ u32 cg_sel;
+ struct clk *parent;
+ struct clk *bus_parent;
+ struct clk **parents;
+ struct regulator *regulator;
+ struct clk *clock;
+ struct list_head list;
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *dent; /* For visible tree hierarchy */
+ struct dentry *dent_bus; /* For visible tree hierarchy */
+#endif
+};
/**
* struct clkops - ux500 clock operations
@@ -18,135 +68,120 @@
* NULL, the rate in the struct clk will be used.
*/
struct clkops {
- void (*enable) (struct clk *);
- void (*disable) (struct clk *);
- unsigned long (*get_rate) (struct clk *);
+ int (*enable)(struct clk *);
+ void (*disable)(struct clk *);
+ unsigned long (*get_rate)(struct clk *);
+ int (*set_rate)(struct clk *, unsigned long);
+ long (*round_rate)(struct clk *, unsigned long);
int (*set_parent)(struct clk *, struct clk *);
};
-/**
- * struct clk - ux500 clock structure
- * @ops: pointer to clkops struct used to control this clock
- * @name: name, for debugging
- * @enabled: refcount. positive if enabled, zero if disabled
- * @get_rate: custom callback for getting the clock rate
- * @data: custom per-clock data for example for the get_rate
- * callback
- * @rate: fixed rate for clocks which don't implement
- * ops->getrate
- * @prcmu_cg_off: address offset of the combined enable/disable register
- * (used on u8500v1)
- * @prcmu_cg_bit: bit in the combined enable/disable register (used on
- * u8500v1)
- * @prcmu_cg_mgt: address of the enable/disable register (used on
- * u8500ed)
- * @cluster: peripheral cluster number
- * @prcc_bus: bit for the bus clock in the peripheral's CLKRST
- * @prcc_kernel: bit for the kernel clock in the peripheral's CLKRST.
- * -1 if no kernel clock exists.
- * @parent_cluster: pointer to parent's cluster clk struct
- * @parent_periph: pointer to parent's peripheral clk struct
- *
- * Peripherals are organised into clusters, and each cluster has an associated
- * bus clock. Some peripherals also have a parent peripheral clock.
- *
- * In order to enable a clock for a peripheral, we need to enable:
- * (1) the parent cluster (bus) clock at the PRCMU level
- * (2) the parent peripheral clock (if any) at the PRCMU level
- * (3) the peripheral's bus & kernel clock at the PRCC level
- *
- * (1) and (2) are handled by defining clk structs (DEFINE_PRCMU_CLK) for each
- * of the cluster and peripheral clocks, and hooking these as the parents of
- * the individual peripheral clocks.
- *
- * (3) is handled by specifying the bits in the PRCC control registers required
- * to enable these clocks and modifying them in the ->enable and
- * ->disable callbacks of the peripheral clocks (DEFINE_PRCC_CLK).
- *
- * This structure describes both the PRCMU-level clocks and PRCC-level clocks.
- * The prcmu_* fields are only used for the PRCMU clocks, and the cluster,
- * prcc, and parent pointers are only used for the PRCC-level clocks.
- */
-struct clk {
- const struct clkops *ops;
- const char *name;
- unsigned int enabled;
- unsigned long (*get_rate)(struct clk *);
- void *data;
-
- unsigned long rate;
- struct list_head list;
+extern struct clkops prcmu_clk_ops;
+extern struct clkops prcmu_scalable_clk_ops;
+extern struct clkops prcmu_opp100_clk_ops;
+extern struct mutex clk_opp100_mutex;
+extern struct clkops prcc_pclk_ops;
+extern struct clkops prcc_kclk_ops;
+extern struct clkops prcc_kclk_rec_ops;
+extern struct clkops sga_clk_ops;
- /* These three are only for PRCMU clks */
+#define CLK_LOOKUP(_clk, _dev_id, _con_id) \
+ { .dev_id = _dev_id, .con_id = _con_id, .clk = &_clk }
- unsigned int prcmu_cg_off;
- unsigned int prcmu_cg_bit;
- unsigned int prcmu_cg_mgt;
-
- /* The rest are only for PRCC clks */
-
- int cluster;
- unsigned int prcc_bus;
- unsigned int prcc_kernel;
-
- struct clk *parent_cluster;
- struct clk *parent_periph;
-#if defined(CONFIG_DEBUG_FS)
- struct dentry *dent; /* For visible tree hierarchy */
- struct dentry *dent_bus; /* For visible tree hierarchy */
-#endif
-};
+/* Define PRCMU Clock */
+#define DEF_PRCMU_CLK(_name, _cg_sel, _rate) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .rate = _rate, \
+ }
-#define DEFINE_PRCMU_CLK(_name, _cg_off, _cg_bit, _reg) \
-struct clk clk_##_name = { \
- .name = #_name, \
- .ops = &clk_prcmu_ops, \
- .prcmu_cg_off = _cg_off, \
- .prcmu_cg_bit = _cg_bit, \
- .prcmu_cg_mgt = PRCM_##_reg##_MGT \
+#define DEF_PRCMU_SCALABLE_CLK(_name, _cg_sel) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_scalable_clk_ops, \
+ .cg_sel = _cg_sel, \
}
-#define DEFINE_PRCMU_CLK_RATE(_name, _cg_off, _cg_bit, _reg, _rate) \
-struct clk clk_##_name = { \
- .name = #_name, \
- .ops = &clk_prcmu_ops, \
- .prcmu_cg_off = _cg_off, \
- .prcmu_cg_bit = _cg_bit, \
- .rate = _rate, \
- .prcmu_cg_mgt = PRCM_##_reg##_MGT \
+/* Use this for clocks that are only defined at OPP 100%. */
+#define DEF_PRCMU_OPP100_CLK(_name, _cg_sel, _rate) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_opp100_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .rate = _rate, \
+ .mutex = &clk_opp100_mutex, \
}
-#define DEFINE_PRCC_CLK(_pclust, _name, _bus_en, _kernel_en, _kernclk) \
-struct clk clk_##_name = { \
- .name = #_name, \
- .ops = &clk_prcc_ops, \
- .cluster = _pclust, \
- .prcc_bus = _bus_en, \
- .prcc_kernel = _kernel_en, \
- .parent_cluster = &clk_per##_pclust##clk, \
- .parent_periph = _kernclk \
+/* Define PRCC clock */
+#define DEF_PRCC_PCLK(_name, _io_base, _cg_bit, _parent) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcc_pclk_ops, \
+ .io_base = _io_base, \
+ .cg_sel = BIT(_cg_bit), \
+ .parent = _parent, \
}
-#define DEFINE_PRCC_CLK_CUSTOM(_pclust, _name, _bus_en, _kernel_en, _kernclk, _callback, _data) \
-struct clk clk_##_name = { \
- .name = #_name, \
- .ops = &clk_prcc_ops, \
- .cluster = _pclust, \
- .prcc_bus = _bus_en, \
- .prcc_kernel = _kernel_en, \
- .parent_cluster = &clk_per##_pclust##clk, \
- .parent_periph = _kernclk, \
- .get_rate = _callback, \
- .data = (void *) _data \
+#define DEF_PRCC_KCLK(_name, _io_base, _cg_bit, _parent, _clock) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcc_kclk_ops, \
+ .io_base = _io_base, \
+ .cg_sel = BIT(_cg_bit), \
+ .parent = _parent, \
+ .clock = _clock, \
}
+#define DEF_PER_CLK(_name, _bus_parent, _parent) \
+ struct clk _name = { \
+ .name = #_name, \
+ .parent = _parent, \
+ .bus_parent = _bus_parent, \
+ }
-#define CLK(_clk, _devname, _conname) \
- { \
- .clk = &clk_##_clk, \
- .dev_id = _devname, \
- .con_id = _conname, \
+#define DEF_MTU_CLK(_cg_sel, _name, _bus_parent) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &mtu_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .bus_parent = _bus_parent, \
}
-int __init clk_db8500_ed_fixup(void);
+/* Functions defined in clock.c */
int __init clk_init(void);
+void clks_register(struct clk_lookup *clks, size_t num);
+int __clk_enable(struct clk *clk, void *current_lock);
+void __clk_disable(struct clk *clk, void *current_lock);
+unsigned long __clk_get_rate(struct clk *clk, void *current_lock);
+long clk_round_rate_rec(struct clk *clk, unsigned long rate);
+int clk_set_rate_rec(struct clk *clk, unsigned long rate);
+
+#ifdef CONFIG_DEBUG_FS
+int dbx500_clk_debug_init(struct clk **clks, int num);
+void clk_debugfs_add_table(struct clk_lookup *cl, size_t num);
+#else
+static inline int dbx500_clk_debug_init(struct clk **clks, int num)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_UX500_SOC_DB8500
+int __init db8500_clk_init(void);
+int __init db8500_clk_debug_init(void);
+#else
+static inline int db8500_clk_init(void) { return 0; }
+static inline int db8500_clk_debug_init(void) { return 0; }
+#endif
+
+#ifdef CONFIG_UX500_SOC_DB5500
+int __init db5500_clk_init(void);
+int __init db5500_clk_debug_init(void);
+#else
+static inline int db5500_clk_init(void) { return 0; }
+static inline int db5500_clk_debug_init(void) { return 0; }
+#endif
+
+#endif
diff --git a/arch/arm/mach-ux500/cpuidle.c b/arch/arm/mach-ux500/cpuidle.c
new file mode 100644
index 00000000000..b54884bd254
--- /dev/null
+++ b/arch/arm/mach-ux500/cpuidle.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2012 Linaro : Daniel Lezcano <daniel.lezcano@linaro.org> (IBM)
+ *
+ * Based on the work of Rickard Andersson <rickard.andersson@stericsson.com>
+ * and Jonas Aaberg <jonas.aberg@stericsson.com>.
+ *
+ * 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/module.h>
+#include <linux/cpuidle.h>
+#include <linux/clockchips.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+#include <linux/smp.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <asm/cpuidle.h>
+#include <asm/proc-fns.h>
+
+static atomic_t master = ATOMIC_INIT(0);
+static DEFINE_SPINLOCK(master_lock);
+static DEFINE_PER_CPU(struct cpuidle_device, ux500_cpuidle_device);
+
+static inline int ux500_enter_idle(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ int this_cpu = smp_processor_id();
+ bool recouple = false;
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &this_cpu);
+
+ if (atomic_inc_return(&master) == num_online_cpus()) {
+
+ /* With this lock, we prevent the other cpu to exit and enter
+ * this function again and become the master */
+ if (!spin_trylock(&master_lock))
+ goto wfi;
+
+ /* decouple the gic from the A9 cores */
+ if (prcmu_gic_decouple())
+ goto out;
+
+ /* If an error occur, we will have to recouple the gic
+ * manually */
+ recouple = true;
+
+ /* At this state, as the gic is decoupled, if the other
+ * cpu is in WFI, we have the guarantee it won't be wake
+ * up, so we can safely go to retention */
+ if (!prcmu_is_cpu_in_wfi(this_cpu ? 0 : 1))
+ goto out;
+
+ /* The prcmu will be in charge of watching the interrupts
+ * and wake up the cpus */
+ if (prcmu_copy_gic_settings())
+ goto out;
+
+ /* Check in the meantime an interrupt did
+ * not occur on the gic ... */
+ if (prcmu_gic_pending_irq())
+ goto out;
+
+ /* ... and the prcmu */
+ if (prcmu_pending_irq())
+ goto out;
+
+ /* Go to the retention state, the prcmu will wait for the
+ * cpu to go WFI and this is what happens after exiting this
+ * 'master' critical section */
+ if (prcmu_set_power_state(PRCMU_AP_IDLE, true, true))
+ goto out;
+
+ /* When we switch to retention, the prcmu is in charge
+ * of recoupling the gic automatically */
+ recouple = false;
+
+ spin_unlock(&master_lock);
+ }
+wfi:
+ cpu_do_idle();
+out:
+ atomic_dec(&master);
+
+ if (recouple) {
+ prcmu_gic_recouple();
+ spin_unlock(&master_lock);
+ }
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &this_cpu);
+
+ return index;
+}
+
+static struct cpuidle_driver ux500_idle_driver = {
+ .name = "ux500_idle",
+ .owner = THIS_MODULE,
+ .en_core_tk_irqen = 1,
+ .states = {
+ ARM_CPUIDLE_WFI_STATE,
+ {
+ .enter = ux500_enter_idle,
+ .exit_latency = 70,
+ .target_residency = 260,
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .name = "ApIdle",
+ .desc = "ARM Retention",
+ },
+ },
+ .safe_state_index = 0,
+ .state_count = 2,
+};
+
+/*
+ * For each cpu, setup the broadcast timer because we will
+ * need to migrate the timers for the states >= ApIdle.
+ */
+static void ux500_setup_broadcast_timer(void *arg)
+{
+ int cpu = smp_processor_id();
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
+}
+
+int __init ux500_idle_init(void)
+{
+ int ret, cpu;
+ struct cpuidle_device *device;
+
+ /* Configure wake up reasons */
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
+ PRCMU_WAKEUP(ABB));
+
+ /*
+ * Configure the timer broadcast for each cpu, that must
+ * be done from the cpu context, so we use a smp cross
+ * call with 'on_each_cpu'.
+ */
+ on_each_cpu(ux500_setup_broadcast_timer, NULL, 1);
+
+ ret = cpuidle_register_driver(&ux500_idle_driver);
+ if (ret) {
+ printk(KERN_ERR "failed to register ux500 idle driver\n");
+ return ret;
+ }
+
+ for_each_online_cpu(cpu) {
+ device = &per_cpu(ux500_cpuidle_device, cpu);
+ device->cpu = cpu;
+ ret = cpuidle_register_device(device);
+ if (ret) {
+ printk(KERN_ERR "Failed to register cpuidle "
+ "device for cpu%d\n", cpu);
+ goto out_unregister;
+ }
+ }
+out:
+ return ret;
+
+out_unregister:
+ for_each_online_cpu(cpu) {
+ device = &per_cpu(ux500_cpuidle_device, cpu);
+ cpuidle_unregister_device(device);
+ }
+
+ cpuidle_unregister_driver(&ux500_idle_driver);
+ goto out;
+}
+
+device_initcall(ux500_idle_init);
diff --git a/arch/arm/mach-ux500/hotplug.c b/arch/arm/mach-ux500/hotplug.c
index c76f0f456f0..bf7e81705d2 100644
--- a/arch/arm/mach-ux500/hotplug.c
+++ b/arch/arm/mach-ux500/hotplug.c
@@ -11,20 +11,31 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/smp.h>
+#include <linux/completion.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
+#include <mach/context.h>
+
extern volatile int pen_release;
+static DECLARE_COMPLETION(cpu_killed);
+
static inline void platform_do_lowpower(unsigned int cpu)
{
flush_cache_all();
- /* we put the platform to just WFI */
for (;;) {
- __asm__ __volatile__("dsb\n\t" "wfi\n\t"
- : : : "memory");
+
+ context_varm_save_core();
+ context_save_cpu_registers();
+
+ context_save_to_sram_and_wfi(false);
+
+ context_restore_cpu_registers();
+ context_varm_restore_core();
+
if (pen_release == cpu_logical_map(cpu)) {
/*
* OK, proper wakeup, we're done
@@ -36,7 +47,7 @@ static inline void platform_do_lowpower(unsigned int cpu)
int platform_cpu_kill(unsigned int cpu)
{
- return 1;
+ return wait_for_completion_timeout(&cpu_killed, 5000);
}
/*
@@ -46,6 +57,19 @@ int platform_cpu_kill(unsigned int cpu)
*/
void platform_cpu_die(unsigned int cpu)
{
+#ifdef DEBUG
+ unsigned int this_cpu = hard_smp_processor_id();
+
+ if (cpu != this_cpu) {
+ printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
+ this_cpu, cpu);
+ BUG();
+ }
+#endif
+
+ printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);
+ complete(&cpu_killed);
+
/* directly enter low power state, skipping secure registers */
platform_do_lowpower(cpu);
}
diff --git a/arch/arm/mach-ux500/include/mach/context.h b/arch/arm/mach-ux500/include/mach/context.h
new file mode 100644
index 00000000000..a3490121b67
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/context.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Rickard Andersson <rickard.andersson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include <linux/notifier.h>
+
+#ifdef CONFIG_UX500_CONTEXT
+
+/* Defines to be with
+ * context_ape_notifier_register
+ */
+#define CONTEXT_APE_SAVE 0 /* APE save */
+#define CONTEXT_APE_RESTORE 1 /* APE restore */
+
+/* Defines to be with
+ * context_arm_notifier_register
+ */
+#define CONTEXT_ARM_CORE_SAVE 0 /* Called for each ARM core */
+#define CONTEXT_ARM_CORE_RESTORE 1 /* Called for each ARM core */
+#define CONTEXT_ARM_COMMON_SAVE 2 /* Called when ARM common is saved */
+#define CONTEXT_ARM_COMMON_RESTORE 3 /* Called when ARM common is restored */
+
+int context_ape_notifier_register(struct notifier_block *nb);
+int context_ape_notifier_unregister(struct notifier_block *nb);
+
+int context_arm_notifier_register(struct notifier_block *nb);
+int context_arm_notifier_unregister(struct notifier_block *nb);
+
+void context_vape_save(void);
+void context_vape_restore(void);
+
+void context_fsmc_save(void);
+void context_fsmc_restore(void);
+
+void context_gpio_save(void);
+void context_gpio_restore(void);
+void context_gpio_restore_mux(void);
+void context_gpio_mux_safe_switch(bool begin);
+
+void context_gic_dist_disable_unneeded_irqs(void);
+
+void context_varm_save_common(void);
+void context_varm_restore_common(void);
+
+void context_varm_save_core(void);
+void context_varm_restore_core(void);
+
+void context_save_cpu_registers(void);
+void context_restore_cpu_registers(void);
+
+void context_save_to_sram_and_wfi(bool cleanL2cache);
+
+void context_clean_l1_cache_all(void);
+void context_save_arm_registers(u32 **backup_stack);
+void context_restore_arm_registers(u32 **backup_stack);
+
+void context_save_cp15_registers(u32 **backup_stack);
+void context_restore_cp15_registers(u32 **backup_stack);
+
+void context_save_to_sram_and_wfi_internal(u32 backup_sram_storage,
+ bool cleanL2cache);
+
+/* DB specific functions in either context-db8500 or context-db5500 */
+void u8500_context_save_icn(void);
+void u8500_context_restore_icn(void);
+void u8500_context_init(void);
+
+void u5500_context_save_icn(void);
+void u5500_context_restore_icn(void);
+void u5500_context_init(void);
+
+void u9540_context_save_icn(void);
+void u9540_context_restore_icn(void);
+void u9540_context_init(void);
+#else
+
+static inline void context_varm_save_core(void) {}
+static inline void context_save_cpu_registers(void) {}
+static inline void context_save_to_sram_and_wfi(bool cleanL2cache) {}
+static inline void context_restore_cpu_registers(void) {}
+static inline void context_varm_restore_core(void) {}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/pm-timer.h b/arch/arm/mach-ux500/include/mach/pm-timer.h
new file mode 100644
index 00000000000..f5fafbbaa77
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/pm-timer.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ *
+ * License Terms: GNU General Public License v2
+ *
+ */
+
+#ifndef PM_TIMER_H
+#define PM_TIMER_H
+
+#include <linux/ktime.h>
+
+#ifdef CONFIG_UX500_CPUIDLE_DEBUG
+ktime_t u8500_rtc_exit_latency_get(void);
+void ux500_rtcrtt_measure_latency(bool enable);
+#else
+static inline ktime_t u8500_rtc_exit_latency_get(void)
+{
+ return ktime_set(0, 0);
+}
+static inline void ux500_rtcrtt_measure_latency(bool enable) { }
+
+#endif
+
+void ux500_rtcrtt_off(void);
+void ux500_rtcrtt_next(u32 time_us);
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/pm.h b/arch/arm/mach-ux500/include/mach/pm.h
new file mode 100644
index 00000000000..c6f1b0adca5
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/pm.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#ifndef PM_COMMON_H
+#define PM_COMMON_H
+
+#ifdef CONFIG_PM
+#include <linux/mfd/dbx500-prcmu.h>
+
+/**
+ * ux500_pm_gic_decouple()
+ *
+ * Decouple GIC from the interrupt bus.
+ */
+void ux500_pm_gic_decouple(void);
+
+/**
+ * ux500_pm_gic_recouple()
+ *
+ * Recouple GIC with the interrupt bus.
+ */
+void ux500_pm_gic_recouple(void);
+
+/**
+ * ux500_pm_gic_pending_interrupt()
+ *
+ * returns true, if there are pending interrupts.
+ */
+bool ux500_pm_gic_pending_interrupt(void);
+
+/**
+ * ux500_pm_prcmu_pending_interrupt()
+ *
+ * returns true, if there are pending interrupts.
+ */
+bool ux500_pm_prcmu_pending_interrupt(void);
+
+/**
+ * ux500_pm_prcmu_set_ioforce()
+ *
+ * @enable: Enable/disable
+ *
+ * Enable/disable the gpio-ring
+ */
+void ux500_pm_prcmu_set_ioforce(bool enable);
+
+/**
+ * ux500_pm_prcmu_copy_gic_settings()
+ *
+ * This function copies all the gic interrupt settings to the prcmu.
+ * This is needed for the system to catch interrupts in ApIdle
+ */
+void ux500_pm_prcmu_copy_gic_settings(void);
+
+/**
+ * ux500_pm_gpio_save_wake_up_status()
+ *
+ * This function is called when the prcmu has woken the ARM
+ * but before ioforce is disabled.
+ */
+void ux500_pm_gpio_save_wake_up_status(void);
+
+/**
+ * ux500_pm_gpio_read_wake_up_status()
+ *
+ * @bank_number: The gpio bank.
+ *
+ * Returns the WKS register settings for given bank number.
+ * The WKS register is cleared when ioforce is released therefore
+ * this function is needed.
+ */
+u32 ux500_pm_gpio_read_wake_up_status(unsigned int bank_number);
+
+/**
+ * ux500_pm_other_cpu_wfi()
+ *
+ * Returns true if the other CPU is in WFI.
+ */
+bool ux500_pm_other_cpu_wfi(void);
+
+struct dev_pm_domain;
+extern struct dev_pm_domain ux500_dev_power_domain;
+extern struct dev_pm_domain ux500_amba_dev_power_domain;
+
+#else
+u32 ux500_pm_gpio_read_wake_up_status(unsigned int bank_number)
+{
+ return 0;
+}
+
+/**
+ * ux500_pm_prcmu_set_ioforce()
+ *
+ * @enable: Enable/disable
+ *
+ * Enable/disable the gpio-ring
+ */
+static inline void ux500_pm_prcmu_set_ioforce(bool enable) { }
+
+#endif
+
+extern int ux500_console_uart_gpio_pin;
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/prcmu-debug.h b/arch/arm/mach-ux500/include/mach/prcmu-debug.h
new file mode 100644
index 00000000000..38f5ad94864
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/prcmu-debug.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Martin Persson for ST-Ericsson
+ * Etienne Carriere <etienne.carriere@stericsson.com> for ST-Ericsson
+ *
+ */
+
+#ifndef PRCMU_DEBUG_H
+#define PRCMU_DEBUG_H
+
+#ifdef CONFIG_DBX500_PRCMU_DEBUG
+void prcmu_debug_ape_opp_log(u8 opp);
+void prcmu_debug_ddr_opp_log(u8 opp);
+void prcmu_debug_arm_opp_log(u32 value);
+void prcmu_debug_dump_data_mem(void);
+void prcmu_debug_dump_regs(void);
+void prcmu_debug_register_interrupt(u32 mailbox);
+void prcmu_debug_register_mbox0_event(u32 ev, u32 mask);
+#else
+static inline void prcmu_debug_ape_opp_log(u8 opp) {}
+static inline void prcmu_debug_ddr_opp_log(u8 opp) {}
+static inline void prcmu_debug_arm_opp_log(u32 value) {}
+static inline void prcmu_debug_dump_data_mem(void) {}
+static inline void prcmu_debug_dump_regs(void) {}
+static inline void prcmu_debug_register_interrupt(u32 mailbox) {}
+static inline void prcmu_debug_register_mbox0_event(u32 ev, u32 mask) {}
+#endif
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/suspend.h b/arch/arm/mach-ux500/include/mach/suspend.h
new file mode 100644
index 00000000000..5a8df72be2e
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/suspend.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef __MACH_SUSPEND_H
+#define __MACH_SUSPEND_H
+
+#ifdef CONFIG_UX500_SUSPEND
+void suspend_block_sleep(void);
+void suspend_unblock_sleep(void);
+void suspend_set_pins_force_fn(void (*force)(void), void (*force_mux)(void));
+#else
+static inline void suspend_block_sleep(void) { }
+static inline void suspend_unblock_sleep(void) { }
+static inline void suspend_set_pins_force_fn(void (*force)(void),
+ void (*force_mux)(void)) { }
+#endif
+
+#endif /* __MACH_SUSPEND_H */
diff --git a/arch/arm/mach-ux500/pm/Kconfig b/arch/arm/mach-ux500/pm/Kconfig
new file mode 100644
index 00000000000..12004ba9858
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/Kconfig
@@ -0,0 +1,70 @@
+config DBX500_PRCMU_QOS_POWER
+ bool "DBX500 PRCMU power QoS support"
+ depends on (MFD_DB5500_PRCMU || MFD_DB8500_PRCMU)
+ default y
+ help
+ Add support for PRCMU power Quality of Service
+
+config UX500_CONTEXT
+ bool "Context save/restore support for UX500"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && PM
+ help
+ This is needed for ApSleep and deeper sleep states.
+
+config UX500_PM_PERFORMANCE
+ bool "Performance supervision"
+ depends on DBX500_PRCMU_QOS_POWER
+ default y
+ help
+ Enable supervision of events which may require a boost
+ of platform performance.
+
+config UX500_CONSOLE_UART_GPIO_PIN
+ int "The pin number of the console UART GPIO pin"
+ default 29
+ depends on UX500_SUSPEND_DBG_WAKE_ON_UART || UX500_CPUIDLE_DEBUG
+ help
+ GPIO pin number of the GPIO pin connected to the console UART RX line.
+
+ Board-specific code can change this.
+
+config UX500_SUSPEND
+ bool "Suspend to mem and standby support"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && PM && SUSPEND
+ select UX500_CONTEXT
+ help
+ Add support for suspend.
+
+config UX500_SUSPEND_STANDBY
+ bool "Suspend Standby goes to ApSleep"
+ depends on UX500_SUSPEND
+ help
+ If yes, echo standby > /sys/power/state puts the system into ApSleep.
+
+config UX500_SUSPEND_MEM
+ bool "Suspend Mem goes to ApDeepSleep"
+ depends on UX500_SUSPEND
+ help
+ If yes, echo mem > /sys/power/state puts the system into ApDeepSleep else
+ it will do the same as echo standby > /sys/power/state.
+
+config UX500_SUSPEND_DBG
+ bool "Suspend debug"
+ depends on UX500_SUSPEND && DEBUG_FS
+ help
+ Add debug support for suspend.
+
+config UX500_SUSPEND_DBG_WAKE_ON_UART
+ bool "Suspend wakes on console UART"
+ depends on UX500_SUSPEND_DBG
+ help
+ Wake up on uart interrupts. Makes it possible for the console to wake up system.
+
+config UX500_USECASE_GOVERNOR
+ bool "UX500 use-case governor"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && \
+ (CPU_FREQ && CPU_IDLE && HOTPLUG_CPU && \
+ EARLYSUSPEND && UX500_L2X0_PREFETCH_CTRL && PM)
+ default y
+ help
+ Adjusts CPU_IDLE, CPU_FREQ, HOTPLUG_CPU and L2 cache parameters
diff --git a/arch/arm/mach-ux500/pm/Makefile b/arch/arm/mach-ux500/pm/Makefile
new file mode 100644
index 00000000000..ef33bdf0d4e
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/Makefile
@@ -0,0 +1,12 @@
+#
+# Power save related files
+#
+obj-y := pm.o runtime.o
+
+obj-$(CONFIG_DBX500_PRCMU_QOS_POWER) += prcmu-qos-power.o
+obj-$(CONFIG_UX500_CONTEXT) += context.o context_arm.o context-db8500.o context-db5500.o context-db9540.o
+obj-$(CONFIG_UX500_CPUIDLE) += timer.o
+obj-$(CONFIG_UX500_SUSPEND) += suspend.o
+obj-$(CONFIG_UX500_SUSPEND_DBG) += suspend_dbg.o
+obj-$(CONFIG_UX500_PM_PERFORMANCE) += performance.o
+obj-$(CONFIG_UX500_USECASE_GOVERNOR) += usecase_gov.o
diff --git a/arch/arm/mach-ux500/pm/context-db5500.c b/arch/arm/mach-ux500/pm/context-db5500.c
new file mode 100644
index 00000000000..9842785c05a
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context-db5500.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>,
+ * Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Sundar Iyer <sundar.iyer@stericsson.com>,
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/context.h>
+
+/* These registers are DB5500 specific */
+#define NODE_HIBW1_ESRAM_IN_0_PRIORITY 0x0
+#define NODE_HIBW1_ESRAM_IN_1_PRIORITY 0x4
+
+#define NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT 0x18
+#define NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT 0x1C
+#define NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT 0x20
+
+#define NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT 0x24
+#define NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT 0x28
+#define NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT 0x2C
+
+#define NODE_HIBW1_DDR_IN_0_PRIORITY 0x400
+#define NODE_HIBW1_DDR_IN_1_PRIORITY 0x404
+#define NODE_HIBW1_DDR_IN_2_PRIORITY 0x408
+
+#define NODE_HIBW1_DDR_IN_0_LIMIT 0x424
+#define NODE_HIBW1_DDR_IN_1_LIMIT 0x428
+#define NODE_HIBW1_DDR_IN_2_LIMIT 0x42C
+
+#define NODE_HIBW1_DDR_OUT_0_PRIORITY 0x430
+
+#define NODE_HIBW2_ESRAM_IN_0_PRIORITY 0x800
+#define NODE_HIBW2_ESRAM_IN_1_PRIORITY 0x804
+
+#define NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT 0x818
+#define NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT 0x81C
+#define NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT 0x820
+
+#define NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT 0x824
+#define NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT 0x828
+#define NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT 0x82C
+
+#define NODE_HIBW2_DDR_IN_0_PRIORITY 0xC00
+#define NODE_HIBW2_DDR_IN_1_PRIORITY 0xC04
+#define NODE_HIBW2_DDR_IN_2_PRIORITY 0xC08
+#define NODE_HIBW2_DDR_IN_3_PRIORITY 0xC0C
+
+#define NODE_HIBW2_DDR_IN_0_LIMIT 0xC30
+#define NODE_HIBW2_DDR_IN_1_LIMIT 0xC34
+#define NODE_HIBW2_DDR_IN_2_LIMIT 0xC38
+#define NODE_HIBW2_DDR_IN_3_LIMIT 0xC3C
+
+#define NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC40
+
+#define NODE_ESRAM0_IN_0_PRIORITY 0x1000
+#define NODE_ESRAM0_IN_1_PRIORITY 0x1004
+#define NODE_ESRAM0_IN_2_PRIORITY 0x1008
+
+#define NODE_ESRAM0_IN_0_LIMIT 0x1024
+#define NODE_ESRAM0_IN_1_LIMIT 0x1028
+#define NODE_ESRAM0_IN_2_LIMIT 0x102C
+#define NODE_ESRAM0_OUT_0_PRIORITY 0x1030
+
+#define NODE_ESRAM1_2_IN_0_PRIORITY 0x1400
+#define NODE_ESRAM1_2_IN_1_PRIORITY 0x1404
+#define NODE_ESRAM1_2_IN_2_PRIORITY 0x1408
+
+#define NODE_ESRAM1_2_IN_0_ARB_1_LIMIT 0x1424
+#define NODE_ESRAM1_2_IN_1_ARB_1_LIMIT 0x1428
+#define NODE_ESRAM1_2_IN_2_ARB_1_LIMIT 0x142C
+#define NODE_ESRAM1_2_OUT_0_PRIORITY 0x1430
+
+#define NODE_ESRAM3_4_IN_0_PRIORITY 0x1800
+#define NODE_ESRAM3_4_IN_1_PRIORITY 0x1804
+#define NODE_ESRAM3_4_IN_2_PRIORITY 0x1808
+
+#define NODE_ESRAM3_4_IN_0_ARB_1_LIMIT 0x1824
+#define NODE_ESRAM3_4_IN_1_ARB_1_LIMIT 0x1828
+#define NODE_ESRAM3_4_IN_2_ARB_1_LIMIT 0x182C
+#define NODE_ESRAM3_4_OUT_0_PRIORITY 0x1830
+
+/*
+ * Save ICN (Interconnect or Interconnect nodes) configuration registers
+ * TODO: This can be optimized, for example if we have
+ * a static ICN configuration.
+ */
+
+static struct {
+ void __iomem *base;
+ u32 hibw1_esram_in_pri[2];
+ u32 hibw1_esram_in0_arb[3];
+ u32 hibw1_esram_in1_arb[3];
+ u32 hibw1_ddr_in_prio[3];
+ u32 hibw1_ddr_in_limit[3];
+ u32 hibw1_ddr_out_prio_reg;
+
+ /* HiBw2 node registers */
+ u32 hibw2_esram_in_pri[2];
+ u32 hibw2_esram_in0_arblimit[3];
+ u32 hibw2_esram_in1_arblimit[3];
+ u32 hibw2_ddr_in_prio[4];
+ u32 hibw2_ddr_in_limit[4];
+ u32 hibw2_ddr_out_prio_reg;
+
+ /* ESRAM node registers */
+ u32 esram_in_prio[3];
+ u32 esram_in_lim[3];
+ u32 esram_out_prio_reg;
+
+ u32 esram12_in_prio[3];
+ u32 esram12_in_arb_lim[3];
+ u32 esram12_out_prio_reg;
+
+ u32 esram34_in_prio[3];
+ u32 esram34_in_arb_lim[3];
+ u32 esram34_out_prio;
+} context_icn;
+
+
+void u5500_context_save_icn(void)
+{
+ void __iomem *base = context_icn.base;
+
+ /* hibw1 */
+ context_icn.hibw1_esram_in_pri[0] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw1_esram_in_pri[1] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw1_esram_in0_arb[0] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in0_arb[1] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in0_arb[2] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in1_arb[0] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in1_arb[1] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in1_arb[2] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw1_ddr_in_prio[0] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[1] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[2] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ context_icn.hibw1_ddr_in_limit[0] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_0_LIMIT);
+ context_icn.hibw1_ddr_in_limit[1] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_1_LIMIT);
+ context_icn.hibw1_ddr_in_limit[2] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ context_icn.hibw1_ddr_out_prio_reg =
+ readl_relaxed(base + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ /* hibw2 */
+ context_icn.hibw2_esram_in_pri[0] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw2_esram_in_pri[1] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw2_esram_in0_arblimit[0] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[1] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[2] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw2_esram_in1_arblimit[0] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[1] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[2] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw2_ddr_in_prio[0] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[1] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[2] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_2_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[3] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_3_PRIORITY);
+
+ context_icn.hibw2_ddr_in_limit[0] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_0_LIMIT);
+ context_icn.hibw2_ddr_in_limit[1] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_1_LIMIT);
+ context_icn.hibw2_ddr_in_limit[2] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_2_LIMIT);
+ context_icn.hibw2_ddr_in_limit[3] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_3_LIMIT);
+
+ context_icn.hibw2_ddr_out_prio_reg =
+ readl_relaxed(base + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ /* ESRAM0 */
+ context_icn.esram_in_prio[0] =
+ readl_relaxed(base + NODE_ESRAM0_IN_0_PRIORITY);
+ context_icn.esram_in_prio[1] =
+ readl_relaxed(base + NODE_ESRAM0_IN_1_PRIORITY);
+ context_icn.esram_in_prio[2] =
+ readl_relaxed(base + NODE_ESRAM0_IN_2_PRIORITY);
+
+ context_icn.esram_in_lim[0] =
+ readl_relaxed(base + NODE_ESRAM0_IN_0_LIMIT);
+ context_icn.esram_in_lim[1] =
+ readl_relaxed(base + NODE_ESRAM0_IN_1_LIMIT);
+ context_icn.esram_in_lim[2] =
+ readl_relaxed(base + NODE_ESRAM0_IN_2_LIMIT);
+
+ context_icn.esram_out_prio_reg =
+ readl_relaxed(base + NODE_ESRAM0_OUT_0_PRIORITY);
+
+ /* ESRAM1-2 */
+ context_icn.esram12_in_prio[0] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_0_PRIORITY);
+ context_icn.esram12_in_prio[1] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_1_PRIORITY);
+ context_icn.esram12_in_prio[2] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_2_PRIORITY);
+
+ context_icn.esram12_in_arb_lim[0] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[1] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[2] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+
+ context_icn.esram12_out_prio_reg =
+ readl_relaxed(base + NODE_ESRAM1_2_OUT_0_PRIORITY);
+
+ /* ESRAM3-4 */
+ context_icn.esram34_in_prio[0] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_0_PRIORITY);
+ context_icn.esram34_in_prio[1] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_1_PRIORITY);
+ context_icn.esram34_in_prio[2] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_2_PRIORITY);
+
+ context_icn.esram34_in_arb_lim[0] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[1] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[2] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+
+ context_icn.esram34_out_prio =
+ readl_relaxed(base + NODE_ESRAM3_4_OUT_0_PRIORITY);
+}
+
+/*
+ * Restore ICN configuration registers
+ */
+void u5500_context_restore_icn(void)
+{
+ void __iomem *base = context_icn.base;
+
+ /* hibw1 */
+ writel_relaxed(context_icn.hibw1_esram_in_pri[0],
+ base + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[1],
+ base + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[0],
+ base + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[1],
+ base + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[2],
+ base + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[0],
+ base + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[1],
+ base + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[2],
+ base + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[0],
+ base + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[1],
+ base + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[2],
+ base + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[0],
+ base + NODE_HIBW1_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[1],
+ base + NODE_HIBW1_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[2],
+ base + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_out_prio_reg,
+ base + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ /* hibw2 */
+ writel_relaxed(context_icn.hibw2_esram_in_pri[0],
+ base + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_esram_in_pri[1],
+ base + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[0],
+ base + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[1],
+ base + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[2],
+ base + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[0],
+ base + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[1],
+ base + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[2],
+ base + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[0],
+ base + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[1],
+ base + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[2],
+ base + NODE_HIBW2_DDR_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[3],
+ base + NODE_HIBW2_DDR_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[0],
+ base + NODE_HIBW2_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[1],
+ base + NODE_HIBW2_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[2],
+ base + NODE_HIBW2_DDR_IN_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[3],
+ base + NODE_HIBW2_DDR_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr_out_prio_reg,
+ base + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ /* ESRAM0 */
+ writel_relaxed(context_icn.esram_in_prio[0],
+ base + NODE_ESRAM0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram_in_prio[1],
+ base + NODE_ESRAM0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram_in_prio[2],
+ base + NODE_ESRAM0_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.esram_in_lim[0],
+ base + NODE_ESRAM0_IN_0_LIMIT);
+ writel_relaxed(context_icn.esram_in_lim[1],
+ base + NODE_ESRAM0_IN_1_LIMIT);
+ writel_relaxed(context_icn.esram_in_lim[2],
+ base + NODE_ESRAM0_IN_2_LIMIT);
+
+ writel_relaxed(context_icn.esram_out_prio_reg,
+ base + NODE_ESRAM0_OUT_0_PRIORITY);
+
+ /* ESRAM1-2 */
+ writel_relaxed(context_icn.esram12_in_prio[0],
+ base + NODE_ESRAM1_2_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[1],
+ base + NODE_ESRAM1_2_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[2],
+ base + NODE_ESRAM1_2_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_arb_lim[0],
+ base + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[1],
+ base + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[2],
+ base + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+
+ writel_relaxed(context_icn.esram12_out_prio_reg,
+ base + NODE_ESRAM1_2_OUT_0_PRIORITY);
+
+ /* ESRAM3-4 */
+ writel_relaxed(context_icn.esram34_in_prio[0],
+ base + NODE_ESRAM3_4_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[1],
+ base + NODE_ESRAM3_4_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[2],
+ base + NODE_ESRAM3_4_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.esram34_in_arb_lim[0],
+ base + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[1],
+ base + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[2],
+ base + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+
+ writel_relaxed(context_icn.esram34_out_prio,
+ base + NODE_ESRAM3_4_OUT_0_PRIORITY);
+
+}
+
+void u5500_context_init(void)
+{
+ context_icn.base = ioremap(U5500_ICN_BASE, SZ_8K);
+}
diff --git a/arch/arm/mach-ux500/pm/context-db8500.c b/arch/arm/mach-ux500/pm/context-db8500.c
new file mode 100644
index 00000000000..3ba73e51a6d
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context-db8500.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer for ST-Ericsson
+ *
+ */
+
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/context.h>
+
+/*
+ * ST-Interconnect context
+ */
+
+/* priority, bw limiter register offsets */
+#define NODE_HIBW1_ESRAM_IN_0_PRIORITY 0x00
+#define NODE_HIBW1_ESRAM_IN_1_PRIORITY 0x04
+#define NODE_HIBW1_ESRAM_IN_2_PRIORITY 0x08
+#define NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT 0x24
+#define NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT 0x28
+#define NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT 0x2C
+#define NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT 0x30
+#define NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT 0x34
+#define NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT 0x38
+#define NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT 0x3C
+#define NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT 0x40
+#define NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT 0x44
+#define NODE_HIBW1_DDR_IN_0_PRIORITY 0x400
+#define NODE_HIBW1_DDR_IN_1_PRIORITY 0x404
+#define NODE_HIBW1_DDR_IN_2_PRIORITY 0x408
+#define NODE_HIBW1_DDR_IN_0_LIMIT 0x424
+#define NODE_HIBW1_DDR_IN_1_LIMIT 0x428
+#define NODE_HIBW1_DDR_IN_2_LIMIT 0x42C
+#define NODE_HIBW1_DDR_OUT_0_PRIORITY 0x430
+#define NODE_HIBW2_ESRAM_IN_0_PRIORITY 0x800
+#define NODE_HIBW2_ESRAM_IN_1_PRIORITY 0x804
+#define NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT 0x818
+#define NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT 0x81C
+#define NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT 0x820
+#define NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT 0x824
+#define NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT 0x828
+#define NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT 0x82C
+#define NODE_HIBW2_DDR_IN_0_PRIORITY 0xC00
+#define NODE_HIBW2_DDR_IN_1_PRIORITY 0xC04
+#define NODE_HIBW2_DDR_IN_2_PRIORITY 0xC08
+
+#define NODE_HIBW2_DDR_IN_0_LIMIT 0xC24
+#define NODE_HIBW2_DDR_IN_1_LIMIT 0xC28
+#define NODE_HIBW2_DDR_IN_2_LIMIT 0xC2C
+#define NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC30
+
+/*
+ * Note the following addresses are presented in
+ * db8500 design spec v3.1 and v3.3, table 10.
+ * But their addresses are not the same as in the
+ * description. The addresses in the description
+ * of each registers are correct.
+ * NODE_HIBW2_DDR_IN_3_LIMIT is only present in v1.
+ *
+ * Faulty registers addresses in table 10:
+ * NODE_HIBW2_DDR_IN_2_LIMIT 0xC38
+ * NODE_HIBW2_DDR_IN_3_LIMIT 0xC3C
+ * NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC40
+ */
+
+#define NODE_ESRAM0_IN_0_PRIORITY 0x1000
+#define NODE_ESRAM0_IN_1_PRIORITY 0x1004
+#define NODE_ESRAM0_IN_2_PRIORITY 0x1008
+#define NODE_ESRAM0_IN_3_PRIORITY 0x100C
+#define NODE_ESRAM0_IN_0_LIMIT 0x1030
+#define NODE_ESRAM0_IN_1_LIMIT 0x1034
+#define NODE_ESRAM0_IN_2_LIMIT 0x1038
+#define NODE_ESRAM0_IN_3_LIMIT 0x103C
+/* common */
+#define NODE_ESRAM1_2_IN_0_PRIORITY 0x1400
+#define NODE_ESRAM1_2_IN_1_PRIORITY 0x1404
+#define NODE_ESRAM1_2_IN_2_PRIORITY 0x1408
+#define NODE_ESRAM1_2_IN_3_PRIORITY 0x140C
+#define NODE_ESRAM1_2_IN_0_ARB_1_LIMIT 0x1430
+#define NODE_ESRAM1_2_IN_0_ARB_2_LIMIT 0x1434
+#define NODE_ESRAM1_2_IN_1_ARB_1_LIMIT 0x1438
+#define NODE_ESRAM1_2_IN_1_ARB_2_LIMIT 0x143C
+#define NODE_ESRAM1_2_IN_2_ARB_1_LIMIT 0x1440
+#define NODE_ESRAM1_2_IN_2_ARB_2_LIMIT 0x1444
+#define NODE_ESRAM1_2_IN_3_ARB_1_LIMIT 0x1448
+#define NODE_ESRAM1_2_IN_3_ARB_2_LIMIT 0x144C
+
+#define NODE_ESRAM3_4_IN_0_PRIORITY 0x1800
+#define NODE_ESRAM3_4_IN_1_PRIORITY 0x1804
+#define NODE_ESRAM3_4_IN_2_PRIORITY 0x1808
+#define NODE_ESRAM3_4_IN_3_PRIORITY 0x180C
+#define NODE_ESRAM3_4_IN_0_ARB_1_LIMIT 0x1830
+#define NODE_ESRAM3_4_IN_0_ARB_2_LIMIT 0x1834
+#define NODE_ESRAM3_4_IN_1_ARB_1_LIMIT 0x1838
+#define NODE_ESRAM3_4_IN_1_ARB_2_LIMIT 0x183C
+#define NODE_ESRAM3_4_IN_2_ARB_1_LIMIT 0x1840
+#define NODE_ESRAM3_4_IN_2_ARB_2_LIMIT 0x1844
+#define NODE_ESRAM3_4_IN_3_ARB_1_LIMIT 0x1848
+#define NODE_ESRAM3_4_IN_3_ARB_2_LIMIT 0x184C
+
+static struct {
+ void __iomem *base;
+ u32 hibw1_esram_in_pri[3];
+ u32 hibw1_esram_in0_arb[3];
+ u32 hibw1_esram_in1_arb[3];
+ u32 hibw1_esram_in2_arb[3];
+ u32 hibw1_ddr_in_prio[3];
+ u32 hibw1_ddr_in_limit[3];
+ u32 hibw1_ddr_out_prio;
+
+ /* HiBw2 node registers */
+ u32 hibw2_esram_in_pri[2];
+ u32 hibw2_esram_in0_arblimit[3];
+ u32 hibw2_esram_in1_arblimit[3];
+ u32 hibw2_ddr_in_prio[4];
+ u32 hibw2_ddr_in_limit[4];
+ u32 hibw2_ddr_out_prio;
+
+ /* ESRAM node registers */
+ u32 esram_in_prio[4];
+ u32 esram_in_lim[4];
+ u32 esram0_in_prio[4];
+ u32 esram0_in_lim[4];
+ u32 esram12_in_prio[4];
+ u32 esram12_in_arb_lim[8];
+ u32 esram34_in_prio[4];
+ u32 esram34_in_arb_lim[8];
+} context_icn;
+
+/**
+ * u8500_context_save_icn() - save ICN context
+ *
+ */
+void u8500_context_save_icn(void)
+{
+ void __iomem *b = context_icn.base;
+
+ context_icn.hibw1_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw1_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ context_icn.hibw1_esram_in_pri[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+
+ context_icn.hibw1_esram_in0_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in0_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in0_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in1_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in1_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in1_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in2_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in2_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in2_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ context_icn.hibw1_ddr_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ context_icn.hibw1_ddr_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_0_LIMIT);
+ context_icn.hibw1_ddr_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_1_LIMIT);
+ context_icn.hibw1_ddr_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ context_icn.hibw1_ddr_out_prio =
+ readl_relaxed(b + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ context_icn.hibw2_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw2_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw2_esram_in0_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw2_esram_in1_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw2_ddr_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_2_PRIORITY);
+
+ context_icn.hibw2_ddr_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_0_LIMIT);
+ context_icn.hibw2_ddr_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_1_LIMIT);
+
+ context_icn.hibw2_ddr_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_2_LIMIT);
+
+ context_icn.hibw2_ddr_out_prio =
+ readl_relaxed(b + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ context_icn.esram0_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_PRIORITY);
+ context_icn.esram0_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_PRIORITY);
+ context_icn.esram0_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_PRIORITY);
+ context_icn.esram0_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ context_icn.esram0_in_lim[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_LIMIT);
+ context_icn.esram0_in_lim[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_LIMIT);
+ context_icn.esram0_in_lim[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_LIMIT);
+ context_icn.esram0_in_lim[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_LIMIT);
+
+ context_icn.esram12_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ context_icn.esram12_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ context_icn.esram12_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ context_icn.esram12_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ context_icn.esram12_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ context_icn.esram34_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ context_icn.esram34_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ context_icn.esram34_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ context_icn.esram34_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ context_icn.esram34_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+}
+
+/**
+ * u8500_context_restore_icn() - restore ICN context
+ *
+ */
+void u8500_context_restore_icn(void)
+{
+ void __iomem *b = context_icn.base;
+
+ writel_relaxed(context_icn.hibw1_esram_in_pri[0],
+ b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[1],
+ b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[2],
+ b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[0],
+ b + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[1],
+ b + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[2],
+ b + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[0],
+ b + NODE_HIBW1_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[1],
+ b + NODE_HIBW1_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[2],
+ b + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_out_prio,
+ b + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in_pri[0],
+ b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_esram_in_pri[1],
+ b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[0],
+ b + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[1],
+ b + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[2],
+ b + NODE_HIBW2_DDR_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[0],
+ b + NODE_HIBW2_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[1],
+ b + NODE_HIBW2_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[2],
+ b + NODE_HIBW2_DDR_IN_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_out_prio,
+ b + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.esram0_in_prio[0],
+ b + NODE_ESRAM0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[1],
+ b + NODE_ESRAM0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[2],
+ b + NODE_ESRAM0_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[3],
+ b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram0_in_lim[0],
+ b + NODE_ESRAM0_IN_0_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[1],
+ b + NODE_ESRAM0_IN_1_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[2],
+ b + NODE_ESRAM0_IN_2_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[3],
+ b + NODE_ESRAM0_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.esram12_in_prio[0],
+ b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[1],
+ b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[2],
+ b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[3],
+ b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_arb_lim[0],
+ b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[1],
+ b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[2],
+ b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[3],
+ b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[4],
+ b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[5],
+ b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[6],
+ b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[7],
+ b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ writel_relaxed(context_icn.esram34_in_prio[0],
+ b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[1],
+ b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[2],
+ b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[3],
+ b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram34_in_arb_lim[0],
+ b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[1],
+ b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[2],
+ b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[3],
+ b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[4],
+ b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[5],
+ b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[6],
+ b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[7],
+ b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+}
+
+void u8500_context_init(void)
+{
+ context_icn.base = ioremap(U8500_ICN_BASE, SZ_8K);
+}
diff --git a/arch/arm/mach-ux500/pm/context-db9540.c b/arch/arm/mach-ux500/pm/context-db9540.c
new file mode 100644
index 00000000000..8d8aaeb9d8f
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context-db9540.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer for ST-Ericsson
+ *
+ */
+
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/context.h>
+
+/*
+ * ST-Interconnect context
+ */
+
+/* priority, bw limiter register offsets */
+#define NODE_HIBW1_ESRAM_IN_0_PRIORITY 0x00
+#define NODE_HIBW1_ESRAM_IN_1_PRIORITY 0x04
+#define NODE_HIBW1_ESRAM_IN_2_PRIORITY 0x08
+#define NODE_HIBW1_ESRAM_IN_3_PRIORITY 0x0C
+#define NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT 0x30
+#define NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT 0x34
+#define NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT 0x38
+#define NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT 0x3C
+#define NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT 0x40
+#define NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT 0x44
+#define NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT 0x48
+#define NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT 0x4C
+#define NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT 0x50
+#define NODE_HIBW1_ESRAM_IN_3_ARB_1_LIMIT 0x54
+#define NODE_HIBW1_ESRAM_IN_3_ARB_2_LIMIT 0x58
+#define NODE_HIBW1_ESRAM_IN_3_ARB_3_LIMIT 0x5C
+#define NODE_HIBW1_DDR0_IN_0_PRIORITY 0x400
+#define NODE_HIBW1_DDR0_IN_1_PRIORITY 0x404
+#define NODE_HIBW1_DDR0_IN_2_PRIORITY 0x408
+#define NODE_HIBW1_DDR0_IN_3_PRIORITY 0x40C
+#define NODE_HIBW1_DDR0_IN_0_LIMIT 0x430
+#define NODE_HIBW1_DDR0_IN_1_LIMIT 0x434
+#define NODE_HIBW1_DDR0_IN_2_LIMIT 0x438
+#define NODE_HIBW1_DDR0_IN_3_LIMIT 0x43C
+#define NODE_HIBW1_DDR0_OUT_0_PRIORITY 0x440
+#define NODE_HIBW2_ESRAM_IN_0_PRIORITY 0x800
+#define NODE_HIBW2_ESRAM_IN_1_PRIORITY 0x804
+#define NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT 0x818
+#define NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT 0x81C
+#define NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT 0x820
+#define NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT 0x824
+#define NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT 0x828
+#define NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT 0x82C
+
+#define NODE_HIBW2_DDR0_IN_0_PRIORITY 0xC00
+#define NODE_HIBW2_DDR0_IN_1_PRIORITY 0xC04
+
+#define NODE_HIBW2_DDR0_IN_0_LIMIT 0xC18
+#define NODE_HIBW2_DDR0_IN_1_LIMIT 0xC1C
+
+#define NODE_HIBW2_DDR0_OUT_0_PRIORITY 0xC20
+
+/*
+ * Note the following addresses are presented in
+ * db8500 design spec v3.1 and v3.3, table 10.
+ * But their addresses are not the same as in the
+ * description. The addresses in the description
+ * of each registers are correct.
+ * NODE_HIBW2_DDR_IN_3_LIMIT is only present in v1.
+ *
+ * Faulty registers addresses in table 10:
+ * NODE_HIBW2_DDR_IN_2_LIMIT 0xC38
+ * NODE_HIBW2_DDR_IN_3_LIMIT 0xC3C
+ * NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC40
+ */
+
+#define NODE_ESRAM0_IN_0_PRIORITY 0x1000
+#define NODE_ESRAM0_IN_1_PRIORITY 0x1004
+#define NODE_ESRAM0_IN_2_PRIORITY 0x1008
+#define NODE_ESRAM0_IN_3_PRIORITY 0x100C
+#define NODE_ESRAM0_IN_0_LIMIT 0x1030
+#define NODE_ESRAM0_IN_1_LIMIT 0x1034
+#define NODE_ESRAM0_IN_2_LIMIT 0x1038
+#define NODE_ESRAM0_IN_3_LIMIT 0x103C
+#define NODE_ESRAM0_OUT_0_PRIORITY 0x1040
+
+/* common */
+#define NODE_ESRAM1_2_IN_0_PRIORITY 0x1400
+#define NODE_ESRAM1_2_IN_1_PRIORITY 0x1404
+#define NODE_ESRAM1_2_IN_2_PRIORITY 0x1408
+#define NODE_ESRAM1_2_IN_3_PRIORITY 0x140C
+#define NODE_ESRAM1_2_IN_0_ARB_1_LIMIT 0x1430
+#define NODE_ESRAM1_2_IN_0_ARB_2_LIMIT 0x1434
+#define NODE_ESRAM1_2_IN_1_ARB_1_LIMIT 0x1438
+#define NODE_ESRAM1_2_IN_1_ARB_2_LIMIT 0x143C
+#define NODE_ESRAM1_2_IN_2_ARB_1_LIMIT 0x1440
+#define NODE_ESRAM1_2_IN_2_ARB_2_LIMIT 0x1444
+#define NODE_ESRAM1_2_IN_3_ARB_1_LIMIT 0x1448
+#define NODE_ESRAM1_2_IN_3_ARB_2_LIMIT 0x144C
+
+#define NODE_ESRAM3_4_IN_0_PRIORITY 0x1800
+#define NODE_ESRAM3_4_IN_1_PRIORITY 0x1804
+#define NODE_ESRAM3_4_IN_2_PRIORITY 0x1808
+#define NODE_ESRAM3_4_IN_3_PRIORITY 0x180C
+#define NODE_ESRAM3_4_IN_0_ARB_1_LIMIT 0x1830
+#define NODE_ESRAM3_4_IN_0_ARB_2_LIMIT 0x1834
+#define NODE_ESRAM3_4_IN_1_ARB_1_LIMIT 0x1838
+#define NODE_ESRAM3_4_IN_1_ARB_2_LIMIT 0x183C
+#define NODE_ESRAM3_4_IN_2_ARB_1_LIMIT 0x1840
+#define NODE_ESRAM3_4_IN_2_ARB_2_LIMIT 0x1844
+#define NODE_ESRAM3_4_IN_3_ARB_1_LIMIT 0x1848
+#define NODE_ESRAM3_4_IN_3_ARB_2_LIMIT 0x184C
+
+#define NODE_HIBW1_DDR1_IN_0_PRIORITY_REG 0x1C00
+#define NODE_HIBW1_DDR1_IN_1_PRIORITY_REG 0x1C04
+#define NODE_HIBW1_DDR1_IN_2_PRIORITY_REG 0x1C08
+#define NODE_HIBW1_DDR1_IN_3_PRIORITY_REG 0x1C0C
+
+#define NODE_HIBW1_DDR1_IN_0_LIMIT_REG 0x1C30
+#define NODE_HIBW1_DDR1_IN_1_LIMIT_REG 0x1C34
+#define NODE_HIBW1_DDR1_IN_2_LIMIT_REG 0x1C38
+#define NODE_HIBW1_DDR1_IN_3_LIMIT_REG 0x1C3C
+
+#define NODE_HIBW1_DDR1_OUT_0_PRIORITY_REG 0x1C40
+
+#define NODE_HIBW2_DDR1_IN_0_PRIORITY_REG 0x2000
+#define NODE_HIBW2_DDR1_IN_1_PRIORITY_REG 0x2004
+
+#define NODE_HIBW2_DDR1_IN_0_LIMIT_REG 0x2018
+#define NODE_HIBW2_DDR1_IN_1_LIMIT_REG 0x201C
+#define NODE_HIBW2_DDR1_OUT_0_PRIORITY_REG 0x2020
+#define NODE_DDR_AWQOS_DSP_DDR0_REG 0x340C
+#define NODE_DDR_AWQOS_DSP_DDR1_REG 0x3400
+#define NODE_DDR_ARQOS_DSP_DDR0_REG 0x3408
+#define NODE_DDR_ARQOS_DSP_DDR1_REG 0x3404
+#define NODE_DDR_AWQOS_ARM_DDR_REG 0x3410
+#define NODE_DDR_ARQOS_ARM_DDR_REG 0x3414
+#define NODE_SGA_AWQOS_DDR0_HIBW2_REG 0x3418
+#define NODE_SGA_ARQOS_DDR0_HIBW2_REG 0x341C
+#define NODE_SGA_AWQOS_SGA_HIBW2_REG 0x3420
+#define NODE_SGA_ARQOS_SGA_HIBW2_REG 0x3424
+
+static struct {
+ void __iomem *base;
+ u32 hibw1_esram_in_pri[4];
+ u32 hibw1_esram_in0_arb[3];
+ u32 hibw1_esram_in1_arb[3];
+ u32 hibw1_esram_in2_arb[3];
+ u32 hibw1_esram_in3_arb[3];
+ u32 hibw1_ddr0_in_prio[4];
+ u32 hibw1_ddr0_in_limit[4];
+ u32 hibw1_ddr0_out_prio;
+ u32 hibw1_ddr1_in_prio[4];
+ u32 hibw1_ddr1_in_limit[4];
+ u32 hibw1_ddr1_out_prio;
+
+ /* HiBw2 node registers */
+ u32 hibw2_esram_in_pri[2];
+ u32 hibw2_esram_in0_arblimit[3];
+ u32 hibw2_esram_in1_arblimit[3];
+ u32 hibw2_ddr0_in_prio[2];
+ u32 hibw2_ddr0_in_limit[2];
+ u32 hibw2_ddr0_out_prio;
+ u32 hibw2_ddr1_in_prio[2];
+ u32 hibw2_ddr1_in_limit[2];
+ u32 hibw2_ddr1_out_prio;
+
+ /* ESRAM node registers */
+ u32 esram0_in_prio[4];
+ u32 esram0_in_lim[4];
+ u32 esram0_out_prio;
+ u32 esram12_in_prio[4];
+ u32 esram12_in_arb_lim[8];
+ u32 esram34_in_prio[4];
+ u32 esram34_in_arb_lim[8];
+
+ u32 ddr_awqos_dsp_ddr0;
+ u32 ddr_awqos_dsp_ddr1;
+ u32 ddr_arqos_dsp_ddr0;
+ u32 ddr_arqos_dsp_ddr1;
+ u32 ddr_awqos_arm_ddr;
+ u32 ddr_arqos_arm_ddr;
+
+ u32 sga_awqos_ddr0_hibw2;
+ u32 sga_arqos_ddr0_hibw2;
+
+ u32 sga_awqos_sga_hibw2;
+ u32 sga_arqos_sga_hibw2;
+} context_icn;
+
+/**
+ * u8500_context_save_icn() - save ICN context
+ *
+ */
+void u9540_context_save_icn(void)
+{
+ void __iomem *b = context_icn.base;
+
+ context_icn.hibw1_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw1_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ context_icn.hibw1_esram_in_pri[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+ context_icn.hibw1_esram_in_pri[3] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_3_PRIORITY);
+
+ context_icn.hibw1_esram_in0_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in0_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in0_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in1_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in1_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in1_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in2_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in2_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in2_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in3_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_3_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in3_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_3_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in3_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_3_ARB_3_LIMIT);
+
+ context_icn.hibw1_ddr0_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_0_PRIORITY);
+ context_icn.hibw1_ddr0_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_1_PRIORITY);
+ context_icn.hibw1_ddr0_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_2_PRIORITY);
+ context_icn.hibw1_ddr0_in_prio[3] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_3_PRIORITY);
+
+ context_icn.hibw1_ddr0_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_0_LIMIT);
+ context_icn.hibw1_ddr0_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_1_LIMIT);
+ context_icn.hibw1_ddr0_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_2_LIMIT);
+ context_icn.hibw1_ddr0_in_limit[3] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_3_LIMIT);
+
+ context_icn.hibw1_ddr0_out_prio =
+ readl_relaxed(b + NODE_HIBW1_DDR0_OUT_0_PRIORITY);
+
+ context_icn.hibw1_ddr1_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_0_PRIORITY_REG);
+ context_icn.hibw1_ddr1_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_1_PRIORITY_REG);
+ context_icn.hibw1_ddr1_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_2_PRIORITY_REG);
+ context_icn.hibw1_ddr1_in_prio[3] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_3_PRIORITY_REG);
+
+ context_icn.hibw1_ddr1_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_0_LIMIT_REG);
+ context_icn.hibw1_ddr1_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_1_LIMIT_REG);
+ context_icn.hibw1_ddr1_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_2_LIMIT_REG);
+ context_icn.hibw1_ddr1_in_limit[3] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_3_LIMIT_REG);
+
+ context_icn.hibw1_ddr1_out_prio =
+ readl_relaxed(b + NODE_HIBW1_DDR1_OUT_0_PRIORITY_REG);
+
+ context_icn.hibw2_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw2_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw2_esram_in0_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw2_esram_in1_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw2_ddr0_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR0_IN_0_PRIORITY);
+ context_icn.hibw2_ddr0_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR0_IN_1_PRIORITY);
+
+ context_icn.hibw2_ddr0_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR0_IN_0_LIMIT);
+ context_icn.hibw2_ddr0_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR0_IN_1_LIMIT);
+
+ context_icn.hibw2_ddr0_out_prio =
+ readl_relaxed(b + NODE_HIBW2_DDR0_OUT_0_PRIORITY);
+
+ context_icn.hibw2_ddr1_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR1_IN_0_PRIORITY_REG);
+ context_icn.hibw2_ddr1_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR1_IN_1_PRIORITY_REG);
+
+ context_icn.hibw2_ddr1_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR1_IN_0_LIMIT_REG);
+ context_icn.hibw2_ddr1_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR1_IN_1_LIMIT_REG);
+
+ context_icn.hibw2_ddr1_out_prio =
+ readl_relaxed(b + NODE_HIBW2_DDR1_OUT_0_PRIORITY_REG);
+
+ context_icn.esram0_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_PRIORITY);
+ context_icn.esram0_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_PRIORITY);
+ context_icn.esram0_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_PRIORITY);
+ context_icn.esram0_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ context_icn.esram0_in_lim[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_LIMIT);
+ context_icn.esram0_in_lim[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_LIMIT);
+ context_icn.esram0_in_lim[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_LIMIT);
+ context_icn.esram0_in_lim[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_LIMIT);
+
+ context_icn.esram0_out_prio =
+ readl_relaxed(b + NODE_ESRAM0_OUT_0_PRIORITY);
+
+ context_icn.esram12_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ context_icn.esram12_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ context_icn.esram12_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ context_icn.esram12_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ context_icn.esram12_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ context_icn.esram34_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ context_icn.esram34_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ context_icn.esram34_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ context_icn.esram34_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ context_icn.esram34_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+
+ context_icn.ddr_awqos_dsp_ddr0 =
+ readl_relaxed(b + NODE_DDR_AWQOS_DSP_DDR0_REG);
+ context_icn.ddr_awqos_dsp_ddr1 =
+ readl_relaxed(b + NODE_DDR_AWQOS_DSP_DDR1_REG);
+ context_icn.ddr_arqos_dsp_ddr0 =
+ readl_relaxed(b + NODE_DDR_ARQOS_DSP_DDR0_REG);
+ context_icn.ddr_arqos_dsp_ddr1 =
+ readl_relaxed(b + NODE_DDR_ARQOS_DSP_DDR1_REG);
+ context_icn.ddr_awqos_arm_ddr =
+ readl_relaxed(b + NODE_DDR_AWQOS_ARM_DDR_REG);
+ context_icn.ddr_arqos_arm_ddr =
+ readl_relaxed(b + NODE_DDR_ARQOS_ARM_DDR_REG);
+
+ context_icn.sga_awqos_ddr0_hibw2 =
+ readl_relaxed(b + NODE_SGA_AWQOS_DDR0_HIBW2_REG);
+ context_icn.sga_arqos_ddr0_hibw2 =
+ readl_relaxed(b + NODE_SGA_ARQOS_DDR0_HIBW2_REG);
+
+ context_icn.sga_awqos_sga_hibw2 =
+ readl_relaxed(b + NODE_SGA_AWQOS_SGA_HIBW2_REG);
+ context_icn.sga_arqos_sga_hibw2 =
+ readl_relaxed(b + NODE_SGA_ARQOS_SGA_HIBW2_REG);
+}
+
+/**
+ * u9540_context_restore_icn() - restore ICN context
+ *
+ */
+void u9540_context_restore_icn(void)
+{
+ void __iomem *b = context_icn.base;
+
+ writel_relaxed(context_icn.hibw1_esram_in_pri[0],
+ b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[1],
+ b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[2],
+ b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[3],
+ b + NODE_HIBW1_ESRAM_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in3_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in3_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_3_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in3_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_3_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr0_in_prio[0],
+ b + NODE_HIBW1_DDR0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr0_in_prio[1],
+ b + NODE_HIBW1_DDR0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr0_in_prio[2],
+ b + NODE_HIBW1_DDR0_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr0_in_prio[3],
+ b + NODE_HIBW1_DDR0_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr0_in_limit[0],
+ b + NODE_HIBW1_DDR0_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr0_in_limit[1],
+ b + NODE_HIBW1_DDR0_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr0_in_limit[2],
+ b + NODE_HIBW1_DDR0_IN_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr0_in_limit[3],
+ b + NODE_HIBW1_DDR0_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr0_out_prio,
+ b + NODE_HIBW1_DDR0_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr1_in_prio[0],
+ b + NODE_HIBW1_DDR1_IN_0_PRIORITY_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_prio[1],
+ b + NODE_HIBW1_DDR1_IN_1_PRIORITY_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_prio[2],
+ b + NODE_HIBW1_DDR1_IN_2_PRIORITY_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_prio[3],
+ b + NODE_HIBW1_DDR1_IN_3_PRIORITY_REG);
+
+ writel_relaxed(context_icn.hibw1_ddr1_in_limit[0],
+ b + NODE_HIBW1_DDR1_IN_0_LIMIT_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_limit[1],
+ b + NODE_HIBW1_DDR1_IN_1_LIMIT_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_limit[2],
+ b + NODE_HIBW1_DDR1_IN_2_LIMIT_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_limit[3],
+ b + NODE_HIBW1_DDR1_IN_3_LIMIT_REG);
+
+ writel_relaxed(context_icn.hibw1_ddr1_out_prio,
+ b + NODE_HIBW1_DDR1_OUT_0_PRIORITY_REG);
+
+ writel_relaxed(context_icn.hibw2_esram_in_pri[0],
+ b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_esram_in_pri[1],
+ b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr0_in_prio[0],
+ b + NODE_HIBW2_DDR0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr0_in_prio[1],
+ b + NODE_HIBW2_DDR0_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_ddr0_in_limit[0],
+ b + NODE_HIBW2_DDR0_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr0_in_limit[1],
+ b + NODE_HIBW2_DDR0_IN_1_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr0_out_prio,
+ b + NODE_HIBW2_DDR0_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_ddr1_in_prio[0],
+ b + NODE_HIBW2_DDR1_IN_0_PRIORITY_REG);
+ writel_relaxed(context_icn.hibw2_ddr1_in_prio[1],
+ b + NODE_HIBW2_DDR1_IN_1_PRIORITY_REG);
+
+ writel_relaxed(context_icn.hibw2_ddr1_in_limit[0],
+ b + NODE_HIBW2_DDR1_IN_0_LIMIT_REG);
+ writel_relaxed(context_icn.hibw2_ddr1_in_limit[1],
+ b + NODE_HIBW2_DDR1_IN_1_LIMIT_REG);
+
+ writel_relaxed(context_icn.hibw2_ddr1_out_prio,
+ b + NODE_HIBW2_DDR1_OUT_0_PRIORITY_REG);
+
+ writel_relaxed(context_icn.esram0_in_prio[0],
+ b + NODE_ESRAM0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[1],
+ b + NODE_ESRAM0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[2],
+ b + NODE_ESRAM0_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[3],
+ b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram0_in_lim[0],
+ b + NODE_ESRAM0_IN_0_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[1],
+ b + NODE_ESRAM0_IN_1_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[2],
+ b + NODE_ESRAM0_IN_2_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[3],
+ b + NODE_ESRAM0_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.esram0_out_prio,
+ b + NODE_ESRAM0_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_prio[0],
+ b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[1],
+ b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[2],
+ b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[3],
+ b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_arb_lim[0],
+ b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[1],
+ b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[2],
+ b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[3],
+ b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[4],
+ b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[5],
+ b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[6],
+ b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[7],
+ b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ writel_relaxed(context_icn.esram34_in_prio[0],
+ b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[1],
+ b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[2],
+ b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[3],
+ b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram34_in_arb_lim[0],
+ b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[1],
+ b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[2],
+ b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[3],
+ b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[4],
+ b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[5],
+ b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[6],
+ b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[7],
+ b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+
+ writel_relaxed(context_icn.ddr_awqos_dsp_ddr0,
+ b + NODE_DDR_AWQOS_DSP_DDR0_REG);
+ writel_relaxed(context_icn.ddr_awqos_dsp_ddr1,
+ b + NODE_DDR_AWQOS_DSP_DDR1_REG);
+ writel_relaxed(context_icn.ddr_arqos_dsp_ddr0,
+ b + NODE_DDR_ARQOS_DSP_DDR0_REG);
+ writel_relaxed(context_icn.ddr_arqos_dsp_ddr1,
+ b + NODE_DDR_ARQOS_DSP_DDR1_REG);
+ writel_relaxed(context_icn.ddr_awqos_arm_ddr,
+ b + NODE_DDR_AWQOS_ARM_DDR_REG);
+ writel_relaxed(context_icn.ddr_arqos_arm_ddr,
+ b + NODE_DDR_ARQOS_ARM_DDR_REG);
+
+ writel_relaxed(context_icn.sga_awqos_ddr0_hibw2,
+ b + NODE_SGA_AWQOS_DDR0_HIBW2_REG);
+ writel_relaxed(context_icn.sga_arqos_ddr0_hibw2,
+ b + NODE_SGA_ARQOS_DDR0_HIBW2_REG);
+
+ writel_relaxed(context_icn.sga_awqos_sga_hibw2,
+ b + NODE_SGA_AWQOS_SGA_HIBW2_REG);
+ writel_relaxed(context_icn.sga_arqos_sga_hibw2,
+ b + NODE_SGA_ARQOS_SGA_HIBW2_REG);
+}
+
+void u9540_context_init(void)
+{
+ context_icn.base = ioremap(U8500_ICN_BASE, SZ_16K);
+}
diff --git a/arch/arm/mach-ux500/pm/context.c b/arch/arm/mach-ux500/pm/context.c
new file mode 100644
index 00000000000..5902b861930
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context.c
@@ -0,0 +1,1002 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>,
+ * Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com>,
+ * Sundar Iyer for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/smp.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio/nomadik.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/pm.h>
+#include <mach/context.h>
+
+#include <asm/hardware/gic.h>
+#include <asm/smp_twd.h>
+
+#include "scu.h"
+#include "../product.h"
+#include "../prcc.h"
+
+#include "../board-pins-sleep-force.h"
+
+#define GPIO_NUM_BANKS 9
+#define GPIO_NUM_SAVE_REGISTERS 7
+
+/*
+ * TODO:
+ * - Use the "UX500*"-macros instead where possible
+ */
+
+#define U8500_BACKUPRAM_SIZE SZ_64K
+
+#define U8500_PUBLIC_BOOT_ROM_BASE (U8500_BOOT_ROM_BASE + 0x17000)
+#define U9540_PUBLIC_BOOT_ROM_BASE (U9540_BOOT_ROM_BASE + 0x17000)
+#define U5500_PUBLIC_BOOT_ROM_BASE (U5500_BOOT_ROM_BASE + 0x18000)
+
+/*
+ * Special dedicated addresses in backup RAM. The 5500 addresses are identical
+ * to the 8500 ones.
+ */
+#define U8500_EXT_RAM_LOC_BACKUPRAM_ADDR 0x80151FDC
+#define U8500_CPU0_CP15_CR_BACKUPRAM_ADDR 0x80151F80
+#define U8500_CPU1_CP15_CR_BACKUPRAM_ADDR 0x80151FA0
+
+#define U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR 0x80151FD8
+#define U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR 0x80151FE0
+
+#define GIC_DIST_ENABLE_NS 0x0
+
+/* 32 interrupts fits in 4 bytes */
+#define GIC_DIST_ENABLE_SET_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - \
+ IRQ_SHPI_START) / 32)
+#define GIC_DIST_ENABLE_SET_CPU_NUM (IRQ_SHPI_START / 32)
+#define GIC_DIST_ENABLE_SET_SPI0 GIC_DIST_ENABLE_SET
+#define GIC_DIST_ENABLE_SET_SPI32 (GIC_DIST_ENABLE_SET + IRQ_SHPI_START / 8)
+
+#define GIC_DIST_ENABLE_CLEAR_0 GIC_DIST_ENABLE_CLEAR
+#define GIC_DIST_ENABLE_CLEAR_32 (GIC_DIST_ENABLE_CLEAR + 4)
+#define GIC_DIST_ENABLE_CLEAR_64 (GIC_DIST_ENABLE_CLEAR + 8)
+#define GIC_DIST_ENABLE_CLEAR_96 (GIC_DIST_ENABLE_CLEAR + 12)
+#define GIC_DIST_ENABLE_CLEAR_128 (GIC_DIST_ENABLE_CLEAR + 16)
+
+#define GIC_DIST_PRI_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - IRQ_SHPI_START) / 4)
+#define GIC_DIST_PRI_CPU_NUM (IRQ_SHPI_START / 4)
+#define GIC_DIST_PRI_SPI0 GIC_DIST_PRI
+#define GIC_DIST_PRI_SPI32 (GIC_DIST_PRI + IRQ_SHPI_START)
+
+#define GIC_DIST_SPI_TARGET_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - \
+ IRQ_SHPI_START) / 4)
+#define GIC_DIST_SPI_TARGET_CPU_NUM (IRQ_SHPI_START / 4)
+#define GIC_DIST_SPI_TARGET_SPI0 GIC_DIST_TARGET
+#define GIC_DIST_SPI_TARGET_SPI32 (GIC_DIST_TARGET + IRQ_SHPI_START)
+
+/* 16 interrupts per 4 bytes */
+#define GIC_DIST_CONFIG_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - IRQ_SHPI_START) \
+ / 16)
+#define GIC_DIST_CONFIG_CPU_NUM (IRQ_SHPI_START / 16)
+#define GIC_DIST_CONFIG_SPI0 GIC_DIST_CONFIG
+#define GIC_DIST_CONFIG_SPI32 (GIC_DIST_CONFIG + IRQ_SHPI_START / 4)
+
+/* TODO! Move STM reg offsets to suitable place */
+#define STM_CR_OFFSET 0x00
+#define STM_MMC_OFFSET 0x08
+#define STM_TER_OFFSET 0x10
+
+#define TPIU_PORT_SIZE 0x4
+#define TPIU_TRIGGER_COUNTER 0x104
+#define TPIU_TRIGGER_MULTIPLIER 0x108
+#define TPIU_CURRENT_TEST_PATTERN 0x204
+#define TPIU_TEST_PATTERN_REPEAT 0x208
+#define TPIU_FORMATTER 0x304
+#define TPIU_FORMATTER_SYNC 0x308
+#define TPIU_LOCK_ACCESS_REGISTER 0xFB0
+
+#define TPIU_UNLOCK_CODE 0xc5acce55
+
+#define SCU_FILTER_STARTADDR 0x40
+#define SCU_FILTER_ENDADDR 0x44
+#define SCU_ACCESS_CTRL_SAC 0x50
+
+/* The context of the Trace Port Interface Unit (TPIU) */
+static struct {
+ void __iomem *base;
+ u32 port_size;
+ u32 trigger_counter;
+ u32 trigger_multiplier;
+ u32 current_test_pattern;
+ u32 test_pattern_repeat;
+ u32 formatter;
+ u32 formatter_sync;
+} context_tpiu;
+
+static struct {
+ void __iomem *base;
+ u32 cr;
+ u32 mmc;
+ u32 ter;
+} context_stm_ape;
+
+struct context_gic_cpu {
+ void __iomem *base;
+ u32 ctrl;
+ u32 primask;
+ u32 binpoint;
+};
+static DEFINE_PER_CPU(struct context_gic_cpu, context_gic_cpu);
+
+static struct {
+ void __iomem *base;
+ u32 ns;
+ u32 enable_set[GIC_DIST_ENABLE_SET_COMMON_NUM]; /* IRQ 32 to 160 */
+ u32 priority_level[GIC_DIST_PRI_COMMON_NUM];
+ u32 spi_target[GIC_DIST_SPI_TARGET_COMMON_NUM];
+ u32 config[GIC_DIST_CONFIG_COMMON_NUM];
+} context_gic_dist_common;
+
+struct context_gic_dist_cpu {
+ void __iomem *base;
+ u32 enable_set[GIC_DIST_ENABLE_SET_CPU_NUM]; /* IRQ 0 to 31 */
+ u32 priority_level[GIC_DIST_PRI_CPU_NUM];
+ u32 spi_target[GIC_DIST_SPI_TARGET_CPU_NUM];
+ u32 config[GIC_DIST_CONFIG_CPU_NUM];
+};
+static DEFINE_PER_CPU(struct context_gic_dist_cpu, context_gic_dist_cpu);
+
+static struct {
+ void __iomem *base;
+ u32 ctrl;
+ u32 cpu_pwrstatus;
+ u32 inv_all_nonsecure;
+ u32 filter_start_addr;
+ u32 filter_end_addr;
+ u32 access_ctrl_sac;
+} context_scu;
+
+#define UX500_NR_PRCC_BANKS 5
+static struct {
+ void __iomem *base;
+ struct clk *clk;
+ u32 bus_clk;
+ u32 kern_clk;
+} context_prcc[UX500_NR_PRCC_BANKS];
+
+static u32 backup_sram_storage[NR_CPUS] = {
+ IO_ADDRESS(U8500_CPU0_CP15_CR_BACKUPRAM_ADDR),
+ IO_ADDRESS(U8500_CPU1_CP15_CR_BACKUPRAM_ADDR),
+};
+
+static u32 gpio_bankaddr[GPIO_NUM_BANKS] = {IO_ADDRESS(U8500_GPIOBANK0_BASE),
+ IO_ADDRESS(U8500_GPIOBANK1_BASE),
+ IO_ADDRESS(U8500_GPIOBANK2_BASE),
+ IO_ADDRESS(U8500_GPIOBANK3_BASE),
+ IO_ADDRESS(U8500_GPIOBANK4_BASE),
+ IO_ADDRESS(U8500_GPIOBANK5_BASE),
+ IO_ADDRESS(U8500_GPIOBANK6_BASE),
+ IO_ADDRESS(U8500_GPIOBANK7_BASE),
+ IO_ADDRESS(U8500_GPIOBANK8_BASE)
+};
+
+static u32 gpio_save[GPIO_NUM_BANKS][GPIO_NUM_SAVE_REGISTERS];
+
+void __iomem *fsmc_base_addr;
+static u32 fsmc_bcr0;
+/*
+ * Stacks and stack pointers
+ */
+static DEFINE_PER_CPU(u32[128], varm_registers_backup_stack);
+static DEFINE_PER_CPU(u32 *, varm_registers_pointer);
+
+static DEFINE_PER_CPU(u32[128], varm_cp15_backup_stack);
+static DEFINE_PER_CPU(u32 *, varm_cp15_pointer);
+
+static ATOMIC_NOTIFIER_HEAD(context_ape_notifier_list);
+static ATOMIC_NOTIFIER_HEAD(context_arm_notifier_list);
+
+/*
+ * Register a simple callback for handling vape context save/restore
+ */
+int context_ape_notifier_register(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&context_ape_notifier_list, nb);
+}
+EXPORT_SYMBOL(context_ape_notifier_register);
+
+/*
+ * Remove a previously registered callback
+ */
+int context_ape_notifier_unregister(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&context_ape_notifier_list,
+ nb);
+}
+EXPORT_SYMBOL(context_ape_notifier_unregister);
+
+/*
+ * Register a simple callback for handling varm context save/restore
+ */
+int context_arm_notifier_register(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&context_arm_notifier_list, nb);
+}
+EXPORT_SYMBOL(context_arm_notifier_register);
+
+/*
+ * Remove a previously registered callback
+ */
+int context_arm_notifier_unregister(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&context_arm_notifier_list,
+ nb);
+}
+EXPORT_SYMBOL(context_arm_notifier_unregister);
+
+static void save_prcc(void)
+{
+ int i;
+
+ for (i = 0; i < UX500_NR_PRCC_BANKS; i++) {
+ clk_enable(context_prcc[i].clk);
+
+ context_prcc[i].bus_clk =
+ readl(context_prcc[i].base + PRCC_PCKSR);
+ context_prcc[i].kern_clk =
+ readl(context_prcc[i].base + PRCC_KCKSR);
+
+ clk_disable(context_prcc[i].clk);
+ }
+}
+
+static void restore_prcc(void)
+{
+ int i;
+
+ for (i = 0; i < UX500_NR_PRCC_BANKS; i++) {
+ clk_enable(context_prcc[i].clk);
+
+ writel(~context_prcc[i].bus_clk,
+ context_prcc[i].base + PRCC_PCKDIS);
+ writel(~context_prcc[i].kern_clk,
+ context_prcc[i].base + PRCC_KCKDIS);
+
+ writel(context_prcc[i].bus_clk,
+ context_prcc[i].base + PRCC_PCKEN);
+ writel(context_prcc[i].kern_clk,
+ context_prcc[i].base + PRCC_KCKEN);
+ /*
+ * Consider having a while over KCK/BCK_STATUS
+ * to check that all clocks get disabled/enabled
+ */
+
+ clk_disable(context_prcc[i].clk);
+ }
+}
+
+static void save_stm_ape(void)
+{
+ /*
+ * TODO: Check with PRCMU developers how STM is handled by PRCMU
+ * firmware. According to DB5500 design spec there is a "flush"
+ * mechanism supposed to be used by the PRCMU before power down,
+ * PRCMU fw might save/restore the following three registers
+ * at the same time.
+ */
+ context_stm_ape.cr = readl(context_stm_ape.base +
+ STM_CR_OFFSET);
+ context_stm_ape.mmc = readl(context_stm_ape.base +
+ STM_MMC_OFFSET);
+ context_stm_ape.ter = readl(context_stm_ape.base +
+ STM_TER_OFFSET);
+}
+
+static void restore_stm_ape(void)
+{
+ writel(context_stm_ape.ter,
+ context_stm_ape.base + STM_TER_OFFSET);
+ writel(context_stm_ape.mmc,
+ context_stm_ape.base + STM_MMC_OFFSET);
+ writel(context_stm_ape.cr,
+ context_stm_ape.base + STM_CR_OFFSET);
+}
+
+static bool inline tpiu_clocked(void)
+{
+ return ux500_jtag_enabled();
+}
+
+/*
+ * Save the context of the Trace Port Interface Unit (TPIU).
+ * Saving/restoring is needed for the PTM tracing to work together
+ * with the sleep states ApSleep and ApDeepSleep.
+ */
+static void save_tpiu(void)
+{
+ if (!tpiu_clocked())
+ return;
+
+ context_tpiu.port_size = readl(context_tpiu.base +
+ TPIU_PORT_SIZE);
+ context_tpiu.trigger_counter = readl(context_tpiu.base +
+ TPIU_TRIGGER_COUNTER);
+ context_tpiu.trigger_multiplier = readl(context_tpiu.base +
+ TPIU_TRIGGER_MULTIPLIER);
+ context_tpiu.current_test_pattern = readl(context_tpiu.base +
+ TPIU_CURRENT_TEST_PATTERN);
+ context_tpiu.test_pattern_repeat = readl(context_tpiu.base +
+ TPIU_TEST_PATTERN_REPEAT);
+ context_tpiu.formatter = readl(context_tpiu.base +
+ TPIU_FORMATTER);
+ context_tpiu.formatter_sync = readl(context_tpiu.base +
+ TPIU_FORMATTER_SYNC);
+}
+
+/*
+ * Restore the context of the Trace Port Interface Unit (TPIU).
+ * Saving/restoring is needed for the PTM tracing to work together
+ * with the sleep states ApSleep and ApDeepSleep.
+ */
+static void restore_tpiu(void)
+{
+ if (!tpiu_clocked())
+ return;
+
+ writel(TPIU_UNLOCK_CODE,
+ context_tpiu.base + TPIU_LOCK_ACCESS_REGISTER);
+
+ writel(context_tpiu.port_size,
+ context_tpiu.base + TPIU_PORT_SIZE);
+ writel(context_tpiu.trigger_counter,
+ context_tpiu.base + TPIU_TRIGGER_COUNTER);
+ writel(context_tpiu.trigger_multiplier,
+ context_tpiu.base + TPIU_TRIGGER_MULTIPLIER);
+ writel(context_tpiu.current_test_pattern,
+ context_tpiu.base + TPIU_CURRENT_TEST_PATTERN);
+ writel(context_tpiu.test_pattern_repeat,
+ context_tpiu.base + TPIU_TEST_PATTERN_REPEAT);
+ writel(context_tpiu.formatter,
+ context_tpiu.base + TPIU_FORMATTER);
+ writel(context_tpiu.formatter_sync,
+ context_tpiu.base + TPIU_FORMATTER_SYNC);
+}
+
+/*
+ * Save GIC CPU IF registers
+ *
+ * This is per cpu so it needs to be called for each one.
+ */
+static void save_gic_if_cpu(struct context_gic_cpu *c_gic_cpu)
+{
+ c_gic_cpu->ctrl = readl_relaxed(c_gic_cpu->base + GIC_CPU_CTRL);
+ c_gic_cpu->primask = readl_relaxed(c_gic_cpu->base + GIC_CPU_PRIMASK);
+ c_gic_cpu->binpoint = readl_relaxed(c_gic_cpu->base + GIC_CPU_BINPOINT);
+}
+
+/*
+ * Restore GIC CPU IF registers
+ *
+ * This is per cpu so it needs to be called for each one.
+ */
+static void restore_gic_if_cpu(struct context_gic_cpu *c_gic_cpu)
+{
+ writel_relaxed(c_gic_cpu->ctrl, c_gic_cpu->base + GIC_CPU_CTRL);
+ writel_relaxed(c_gic_cpu->primask, c_gic_cpu->base + GIC_CPU_PRIMASK);
+ writel_relaxed(c_gic_cpu->binpoint, c_gic_cpu->base + GIC_CPU_BINPOINT);
+}
+
+/*
+ * Save GIC Distributor Common registers
+ *
+ * This context is common. Only one CPU needs to call.
+ *
+ * Save SPI (Shared Peripheral Interrupt) settings, IRQ 32-159.
+ */
+static void save_gic_dist_common(void)
+{
+ int i;
+
+ context_gic_dist_common.ns = readl_relaxed(context_gic_dist_common.base
+ + GIC_DIST_ENABLE_NS);
+
+ for (i = 0; i < GIC_DIST_ENABLE_SET_COMMON_NUM; i++)
+ context_gic_dist_common.enable_set[i] =
+ readl_relaxed(context_gic_dist_common.base +
+ GIC_DIST_ENABLE_SET_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_PRI_COMMON_NUM; i++)
+ context_gic_dist_common.priority_level[i] =
+ readl_relaxed(context_gic_dist_common.base +
+ GIC_DIST_PRI_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_SPI_TARGET_COMMON_NUM; i++)
+ context_gic_dist_common.spi_target[i] =
+ readl_relaxed(context_gic_dist_common.base +
+ GIC_DIST_SPI_TARGET_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_CONFIG_COMMON_NUM; i++)
+ context_gic_dist_common.config[i] =
+ readl_relaxed(context_gic_dist_common.base +
+ GIC_DIST_CONFIG_SPI32 + i * 4);
+}
+
+/*
+ * Restore GIC Distributor Common registers
+ *
+ * This context is common. Only one CPU needs to call.
+ *
+ * Save SPI (Shared Peripheral Interrupt) settings, IRQ 32-159.
+ */
+static void restore_gic_dist_common(void)
+{
+ int i;
+
+ for (i = 0; i < GIC_DIST_CONFIG_COMMON_NUM; i++)
+ writel_relaxed(context_gic_dist_common.config[i],
+ context_gic_dist_common.base +
+ GIC_DIST_CONFIG_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_SPI_TARGET_COMMON_NUM; i++)
+ writel_relaxed(context_gic_dist_common.spi_target[i],
+ context_gic_dist_common.base +
+ GIC_DIST_SPI_TARGET_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_PRI_COMMON_NUM; i++)
+ writel_relaxed(context_gic_dist_common.priority_level[i],
+ context_gic_dist_common.base +
+ GIC_DIST_PRI_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_ENABLE_SET_COMMON_NUM; i++)
+ writel_relaxed(context_gic_dist_common.enable_set[i],
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_SET_SPI32 + i * 4);
+
+ writel_relaxed(context_gic_dist_common.ns,
+ context_gic_dist_common.base + GIC_DIST_ENABLE_NS);
+}
+
+/*
+ * Save GIC Dist CPU registers
+ *
+ * This needs to be called by all cpu:s which will not call
+ * save_gic_dist_common(). Only the registers of the GIC which are
+ * banked will be saved.
+ */
+static void save_gic_dist_cpu(struct context_gic_dist_cpu *c_gic)
+{
+ int i;
+
+ for (i = 0; i < GIC_DIST_ENABLE_SET_CPU_NUM; i++)
+ c_gic->enable_set[i] =
+ readl_relaxed(c_gic->base +
+ GIC_DIST_ENABLE_SET_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_PRI_CPU_NUM; i++)
+ c_gic->priority_level[i] =
+ readl_relaxed(c_gic->base +
+ GIC_DIST_PRI_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_SPI_TARGET_CPU_NUM; i++)
+ c_gic->spi_target[i] =
+ readl_relaxed(c_gic->base +
+ GIC_DIST_SPI_TARGET_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_CONFIG_CPU_NUM; i++)
+ c_gic->config[i] =
+ readl_relaxed(c_gic->base +
+ GIC_DIST_CONFIG_SPI0 + i * 4);
+}
+
+/*
+ * Restore GIC Dist CPU registers
+ *
+ * This needs to be called by all cpu:s which will not call
+ * restore_gic_dist_common(). Only the registers of the GIC which are
+ * banked will be saved.
+ */
+static void restore_gic_dist_cpu(struct context_gic_dist_cpu *c_gic)
+{
+ int i;
+
+ for (i = 0; i < GIC_DIST_CONFIG_CPU_NUM; i++)
+ writel_relaxed(c_gic->config[i],
+ c_gic->base +
+ GIC_DIST_CONFIG_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_SPI_TARGET_CPU_NUM; i++)
+ writel_relaxed(c_gic->spi_target[i],
+ c_gic->base +
+ GIC_DIST_SPI_TARGET_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_PRI_CPU_NUM; i++)
+ writel_relaxed(c_gic->priority_level[i],
+ c_gic->base +
+ GIC_DIST_PRI_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_ENABLE_SET_CPU_NUM; i++)
+ writel_relaxed(c_gic->enable_set[i],
+ c_gic->base +
+ GIC_DIST_ENABLE_SET_SPI0 + i * 4);
+}
+
+/*
+ * Disable interrupts that are not necessary
+ * to have turned on during ApDeepSleep.
+ */
+void context_gic_dist_disable_unneeded_irqs(void)
+{
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_0);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_32);
+
+ /* Leave PRCMU IRQ 0 and 1 enabled */
+ writel(0xffff3fff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_64);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_96);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_128);
+}
+
+static void save_scu(void)
+{
+ context_scu.ctrl =
+ readl_relaxed(context_scu.base + SCU_CTRL);
+ context_scu.cpu_pwrstatus =
+ readl_relaxed(context_scu.base + SCU_CPU_STATUS);
+ context_scu.inv_all_nonsecure =
+ readl_relaxed(context_scu.base + SCU_INVALIDATE);
+ context_scu.filter_start_addr =
+ readl_relaxed(context_scu.base + SCU_FILTER_STARTADDR);
+ context_scu.filter_end_addr =
+ readl_relaxed(context_scu.base + SCU_FILTER_ENDADDR);
+ context_scu.access_ctrl_sac =
+ readl_relaxed(context_scu.base + SCU_ACCESS_CTRL_SAC);
+}
+
+static void restore_scu(void)
+{
+ writel_relaxed(context_scu.ctrl,
+ context_scu.base + SCU_CTRL);
+ writel_relaxed(context_scu.cpu_pwrstatus,
+ context_scu.base + SCU_CPU_STATUS);
+ writel_relaxed(context_scu.inv_all_nonsecure,
+ context_scu.base + SCU_INVALIDATE);
+ writel_relaxed(context_scu.filter_start_addr,
+ context_scu.base + SCU_FILTER_STARTADDR);
+ writel_relaxed(context_scu.filter_end_addr,
+ context_scu.base + SCU_FILTER_ENDADDR);
+ writel_relaxed(context_scu.access_ctrl_sac,
+ context_scu.base + SCU_ACCESS_CTRL_SAC);
+}
+
+/*
+ * Save VAPE context
+ */
+void context_vape_save(void)
+{
+ atomic_notifier_call_chain(&context_ape_notifier_list,
+ CONTEXT_APE_SAVE, NULL);
+
+ if (cpu_is_u5500())
+ u5500_context_save_icn();
+ if (cpu_is_u8500())
+ u8500_context_save_icn();
+ if (cpu_is_u9540())
+ u9540_context_save_icn();
+
+ save_stm_ape();
+
+ save_tpiu();
+
+ save_prcc();
+}
+
+/*
+ * Restore VAPE context
+ */
+void context_vape_restore(void)
+{
+ restore_prcc();
+
+ restore_tpiu();
+
+ restore_stm_ape();
+
+ if (cpu_is_u5500())
+ u5500_context_restore_icn();
+ if (cpu_is_u8500())
+ u8500_context_restore_icn();
+ if (cpu_is_u9540())
+ u9540_context_restore_icn();
+
+ atomic_notifier_call_chain(&context_ape_notifier_list,
+ CONTEXT_APE_RESTORE, NULL);
+}
+
+/*
+ * Save FSMC registers that will be reset
+ * during power save.
+ */
+void context_fsmc_save(void)
+{
+ fsmc_base_addr = ioremap_nocache(U8500_FSMC_BASE, 8);
+ fsmc_bcr0 = readl(fsmc_base_addr);
+}
+
+/*
+ * Restore FSMC registers that will be reset
+ * during power save.
+ */
+void context_fsmc_restore(void)
+{
+ writel(fsmc_bcr0, fsmc_base_addr);
+ iounmap(fsmc_base_addr);
+}
+
+/*
+ * Save GPIO registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_save(void)
+{
+ int i;
+
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ gpio_save[i][0] = readl(gpio_bankaddr[i] + NMK_GPIO_AFSLA);
+ gpio_save[i][1] = readl(gpio_bankaddr[i] + NMK_GPIO_AFSLB);
+ gpio_save[i][2] = readl(gpio_bankaddr[i] + NMK_GPIO_PDIS);
+ gpio_save[i][3] = readl(gpio_bankaddr[i] + NMK_GPIO_DIR);
+ gpio_save[i][4] = readl(gpio_bankaddr[i] + NMK_GPIO_DAT);
+ gpio_save[i][6] = readl(gpio_bankaddr[i] + NMK_GPIO_SLPC);
+ }
+
+}
+
+/*
+ * Restore GPIO registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_restore(void)
+{
+ int i;
+ u32 output_state;
+ u32 pull_up;
+ u32 pull_down;
+ u32 pull;
+
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(gpio_save[i][2], gpio_bankaddr[i] + NMK_GPIO_PDIS);
+
+ writel(gpio_save[i][3], gpio_bankaddr[i] + NMK_GPIO_DIR);
+
+ /* Set the high outputs. outpute_state = GPIO_DIR & GPIO_DAT */
+ output_state = gpio_save[i][3] & gpio_save[i][4];
+ writel(output_state, gpio_bankaddr[i] + NMK_GPIO_DATS);
+
+ /*
+ * Set the low outputs.
+ * outpute_state = ~(GPIO_DIR & GPIO_DAT) & GPIO_DIR
+ */
+ output_state = ~(gpio_save[i][3] & gpio_save[i][4]) &
+ gpio_save[i][3];
+ writel(output_state, gpio_bankaddr[i] + NMK_GPIO_DATC);
+
+ /*
+ * Restore pull up/down.
+ * Only write pull up/down settings on inputs where
+ * PDIS is not set.
+ * pull = (~GPIO_DIR & ~GPIO_PDIS)
+ */
+ pull = (~gpio_save[i][3] & ~gpio_save[i][2]);
+ nmk_gpio_read_pull(i, &pull_up);
+
+ pull_down = pull & ~pull_up;
+ pull_up = pull & pull_up;
+ /* Set pull ups */
+ writel(pull_up, gpio_bankaddr[i] + NMK_GPIO_DATS);
+ /* Set pull downs */
+ writel(pull_down, gpio_bankaddr[i] + NMK_GPIO_DATC);
+
+ writel(gpio_save[i][6], gpio_bankaddr[i] + NMK_GPIO_SLPC);
+ }
+
+}
+
+/*
+ * Restore GPIO mux registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_restore_mux(void)
+{
+ int i;
+
+ /* Change mux settings */
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(gpio_save[i][0], gpio_bankaddr[i] + NMK_GPIO_AFSLA);
+ writel(gpio_save[i][1], gpio_bankaddr[i] + NMK_GPIO_AFSLB);
+ }
+}
+
+/*
+ * Safe sequence used to switch IOs between GPIO and Alternate-C mode:
+ * - Save SLPM registers (Not done.)
+ * - Set SLPM=0 for the IOs you want to switch. (We assume that all
+ * SLPM registers already are 0 except for the ones that wants to
+ * have the mux connected in sleep (e.g modem STM)).
+ * - Configure the GPIO registers for the IOs that are being switched
+ * - Set IOFORCE=1
+ * - Modify the AFLSA/B registers for the IOs that are being switched
+ * - Set IOFORCE=0
+ * - Restore SLPM registers (Not done.)
+ * - Any spurious wake up event during switch sequence to be ignored
+ * and cleared
+ */
+void context_gpio_mux_safe_switch(bool begin)
+{
+ int i;
+
+ static u32 rwimsc[GPIO_NUM_BANKS];
+ static u32 fwimsc[GPIO_NUM_BANKS];
+
+ if (begin) {
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ /* Save registers */
+ rwimsc[i] = readl(gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ fwimsc[i] = readl(gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+
+ /* Prevent spurious wakeups */
+ writel(0, gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ writel(0, gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+ }
+
+ ux500_pm_prcmu_set_ioforce(true);
+ } else {
+ ux500_pm_prcmu_set_ioforce(false);
+
+ /* Restore wake up settings */
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(rwimsc[i], gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ writel(fwimsc[i], gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+ }
+ }
+}
+
+/*
+ * Save common
+ *
+ * This function must be called once for all cores before going to deep sleep.
+ */
+void context_varm_save_common(void)
+{
+ atomic_notifier_call_chain(&context_arm_notifier_list,
+ CONTEXT_ARM_COMMON_SAVE, NULL);
+
+ /* Save common parts */
+ save_gic_dist_common();
+ save_scu();
+}
+
+/*
+ * Restore common
+ *
+ * This function must be called once for all cores when waking up from deep
+ * sleep.
+ */
+void context_varm_restore_common(void)
+{
+ /* Restore common parts */
+ restore_scu();
+ restore_gic_dist_common();
+
+ atomic_notifier_call_chain(&context_arm_notifier_list,
+ CONTEXT_ARM_COMMON_RESTORE, NULL);
+}
+
+/*
+ * Save core
+ *
+ * This function must be called once for each cpu core before going to deep
+ * sleep.
+ */
+void context_varm_save_core(void)
+{
+ int cpu = smp_processor_id();
+
+ atomic_notifier_call_chain(&context_arm_notifier_list,
+ CONTEXT_ARM_CORE_SAVE, NULL);
+
+ per_cpu(varm_cp15_pointer, cpu) = per_cpu(varm_cp15_backup_stack, cpu);
+
+ /* Save core */
+ twd_save();
+ save_gic_if_cpu(&per_cpu(context_gic_cpu, cpu));
+ save_gic_dist_cpu(&per_cpu(context_gic_dist_cpu, cpu));
+ context_save_cp15_registers(&per_cpu(varm_cp15_pointer, cpu));
+}
+
+/*
+ * Restore core
+ *
+ * This function must be called once for each cpu core when waking up from
+ * deep sleep.
+ */
+void context_varm_restore_core(void)
+{
+ int cpu = smp_processor_id();
+
+ /* Restore core */
+ context_restore_cp15_registers(&per_cpu(varm_cp15_pointer, cpu));
+ restore_gic_dist_cpu(&per_cpu(context_gic_dist_cpu, cpu));
+ restore_gic_if_cpu(&per_cpu(context_gic_cpu, cpu));
+ twd_restore();
+
+ atomic_notifier_call_chain(&context_arm_notifier_list,
+ CONTEXT_ARM_CORE_RESTORE, NULL);
+}
+
+/*
+ * Save CPU registers
+ *
+ * This function saves ARM registers.
+ */
+void context_save_cpu_registers(void)
+{
+ int cpu = smp_processor_id();
+
+ per_cpu(varm_registers_pointer, cpu) =
+ per_cpu(varm_registers_backup_stack, cpu);
+ context_save_arm_registers(&per_cpu(varm_registers_pointer, cpu));
+}
+
+/*
+ * Restore CPU registers
+ *
+ * This function restores ARM registers.
+ */
+void context_restore_cpu_registers(void)
+{
+ int cpu = smp_processor_id();
+
+ context_restore_arm_registers(&per_cpu(varm_registers_pointer, cpu));
+}
+
+/*
+ * This function stores CP15 registers related to cache and mmu
+ * in backup SRAM. It also stores stack pointer, CPSR
+ * and return address for the PC in backup SRAM and
+ * does wait for interrupt.
+ */
+void context_save_to_sram_and_wfi(bool cleanL2cache)
+{
+ int cpu = smp_processor_id();
+
+ context_save_to_sram_and_wfi_internal(backup_sram_storage[cpu],
+ cleanL2cache);
+}
+
+static int __init context_init(void)
+{
+ int i;
+ void __iomem *ux500_backup_ptr;
+
+ /* allocate backup pointer for RAM data */
+ ux500_backup_ptr = (void *)__get_free_pages(GFP_KERNEL,
+ get_order(U8500_BACKUPRAM_SIZE));
+
+ if (!ux500_backup_ptr) {
+ pr_warning("context: could not allocate backup memory\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * ROM code addresses to store backup contents,
+ * pass the physical address of back up to ROM code
+ */
+ writel(virt_to_phys(ux500_backup_ptr),
+ IO_ADDRESS(U8500_EXT_RAM_LOC_BACKUPRAM_ADDR));
+
+ if (cpu_is_u5500()) {
+ writel(IO_ADDRESS(U5500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ writel(IO_ADDRESS(U5500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ context_tpiu.base = ioremap(U5500_TPIU_BASE, SZ_4K);
+ context_stm_ape.base = ioremap(U5500_STM_REG_BASE, SZ_4K);
+ context_scu.base = ioremap(U5500_SCU_BASE, SZ_4K);
+
+ context_prcc[0].base = ioremap(U5500_CLKRST1_BASE, SZ_4K);
+ context_prcc[1].base = ioremap(U5500_CLKRST2_BASE, SZ_4K);
+ context_prcc[2].base = ioremap(U5500_CLKRST3_BASE, SZ_4K);
+ context_prcc[3].base = ioremap(U5500_CLKRST5_BASE, SZ_4K);
+ context_prcc[4].base = ioremap(U5500_CLKRST6_BASE, SZ_4K);
+
+ context_gic_dist_common.base = ioremap(U5500_GIC_DIST_BASE, SZ_4K);
+ per_cpu(context_gic_cpu, 0).base = ioremap(U5500_GIC_CPU_BASE, SZ_4K);
+ } else if (cpu_is_u8500() || cpu_is_u9540()) {
+ /* Give logical address to backup RAM. For both CPUs */
+ if (cpu_is_u9540()) {
+ writel(IO_ADDRESS_DB9540_ROM(U9540_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ writel(IO_ADDRESS_DB9540_ROM(U9540_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+ } else {
+ writel(IO_ADDRESS(U8500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ writel(IO_ADDRESS(U8500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+ }
+
+ context_tpiu.base = ioremap(U8500_TPIU_BASE, SZ_4K);
+ context_stm_ape.base = ioremap(U8500_STM_REG_BASE, SZ_4K);
+ context_scu.base = ioremap(U8500_SCU_BASE, SZ_4K);
+
+ /* PERIPH4 is always on, so no need saving prcc */
+ context_prcc[0].base = ioremap(U8500_CLKRST1_BASE, SZ_4K);
+ context_prcc[1].base = ioremap(U8500_CLKRST2_BASE, SZ_4K);
+ context_prcc[2].base = ioremap(U8500_CLKRST3_BASE, SZ_4K);
+ context_prcc[3].base = ioremap(U8500_CLKRST5_BASE, SZ_4K);
+ context_prcc[4].base = ioremap(U8500_CLKRST6_BASE, SZ_4K);
+
+ context_gic_dist_common.base = ioremap(U8500_GIC_DIST_BASE, SZ_4K);
+ per_cpu(context_gic_cpu, 0).base = ioremap(U8500_GIC_CPU_BASE, SZ_4K);
+ }
+
+ per_cpu(context_gic_dist_cpu, 0).base = context_gic_dist_common.base;
+
+ for (i = 1; i < num_possible_cpus(); i++) {
+ per_cpu(context_gic_cpu, i).base
+ = per_cpu(context_gic_cpu, 0).base;
+ per_cpu(context_gic_dist_cpu, i).base
+ = per_cpu(context_gic_dist_cpu, 0).base;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(context_prcc); i++) {
+ const int clusters[] = {1, 2, 3, 5, 6};
+ char clkname[10];
+
+ snprintf(clkname, sizeof(clkname), "PERIPH%d", clusters[i]);
+
+ context_prcc[i].clk = clk_get_sys(clkname, NULL);
+ BUG_ON(IS_ERR(context_prcc[i].clk));
+ }
+
+ if (cpu_is_u8500()) {
+ u8500_context_init();
+ } else if (cpu_is_u5500()) {
+ u5500_context_init();
+ } else if (cpu_is_u9540()) {
+ u9540_context_init();
+ } else {
+ printk(KERN_ERR "context: unknown hardware!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+subsys_initcall(context_init);
diff --git a/arch/arm/mach-ux500/pm/context_arm.S b/arch/arm/mach-ux500/pm/context_arm.S
new file mode 100644
index 00000000000..edb894d6a35
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context_arm.S
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Rickard Andersson <rickard.andersson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/linkage.h>
+#include <mach/hardware.h>
+#include <asm/hardware/cache-l2x0.h>
+
+/*
+ * Save and increment macro
+ */
+.macro SAVE_AND_INCREMENT FROM_REG TO_REG
+ str \FROM_REG, [\TO_REG], #+4
+.endm
+
+/*
+ * Decrement and restore macro
+ */
+.macro DECREMENT_AND_RESTORE FROM_REG TO_REG
+ ldr \TO_REG, [\FROM_REG, #-4]!
+.endm
+
+/*
+ * Save ARM registers
+ *
+ * This function must be called in supervisor mode.
+ *
+ * r0 = address to backup stack pointer
+ *
+ * Backup stack operations:
+ * + {sp, lr}^
+ * + cpsr
+ * + {r3, r8-r14} (FIQ mode: r3=spsr)
+ * + {r3, r13, r14} (IRQ mode: r3=spsr)
+ * + {r3, r13, r14} (abort mode: r3=spsr)
+ * + {r3, r13, r14} (undef mode: r3=spsr)
+ */
+ .align
+ .section ".text", "ax"
+ENTRY(context_save_arm_registers)
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack
+ ldr r1, [r0] @ Read backup stack pointer
+
+ARM( stmia r1, {sp, lr}^ ) @ Store user mode sp and lr
+ @ registers
+ARM( add r1, r1, #8 ) @ Update backup pointer (not
+ @ done in previous instruction)
+THUMB( str sp, [r1], #+4 )
+THUMB( str lr, [r1], #+4 )
+
+ mrs r2, cpsr @ Get CPSR
+ SAVE_AND_INCREMENT r2 r1 @ Save CPSR register
+ orr r2, r2, #0xc0 @ Disable FIQ and IRQ
+ bic r2, r2, #0x1f @ Setup r2 to change mode
+
+ @ The suffix to CPSR refers to which field(s) of the CPSR is
+ @ rereferenced (you can specify one or more). Defined fields are:
+ @
+ @ c - control
+ @ x - extension
+ @ s - status
+ @ f - flags
+
+ orr r3, r2, #0x11 @ Save FIQ mode registers
+ msr cpsr_cxsf, r3
+ mrs r3, spsr
+ARM( stmia r1!, {r3, r8-r14} )
+THUMB( stmia r1!, {r3, r8-r12, r14} )
+THUMB( str r13, [r1], #+4 )
+
+
+ orr r3, r2, #0x12 @ Save IRQ mode registers
+ msr cpsr_cxsf, r3
+ mrs r3, spsr
+ARM( stmia r1!, {r3, r13, r14} )
+THUMB( stmia r1!, {r3, r14} )
+THUMB( str r13, [r1], #+4 )
+
+ orr r3, r2, #0x17 @ Save abort mode registers +
+ @ common mode registers
+ msr cpsr_cxsf, r3
+ mrs r3, spsr
+ARM( stmia r1!, {r3, r13, r14} )
+THUMB( stmia r1!, {r3, r14} )
+THUMB( str r13, [r1], #+4 )
+
+ orr r3, r2, #0x1B @ Save undef mode registers
+ msr cpsr_cxsf, r3
+ mrs r3, spsr
+ARM( stmia r1!, {r3, r13, r14} )
+THUMB( stmia r1!, {r3, r14} )
+THUMB( str r13, [r1], #+4 )
+
+ orr r3, r2, #0x13 @ Return to supervisor mode
+ msr cpsr_cxsf, r3
+
+ str r1, [r0] @ Write backup stack pointer
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
+
+
+
+/*
+ * Restore ARM registers
+ *
+ * This function must be called in supervisor mode.
+ *
+ * r0 = address to backup stack pointer
+ *
+ * Backup stack operations:
+ * - {r3, r13, r14} (undef mode: spsr=r3)
+ * - {r3, r13, r14} (abort mode: spsr=r3)
+ * - {r3, r13, r14} (IRQ mode: spsr=r3)
+ * - {r3, r8-r14} (FIQ mode: spsr=r3)
+ * - cpsr
+ * - {sp, lr}^
+ */
+ .align
+ .section ".text", "ax"
+ENTRY(context_restore_arm_registers)
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack
+ ldr r1, [r0] @ Read backup stack pointer
+
+ mrs r2, cpsr @ Get CPSR
+ orr r2, r2, #0xc0 @ Disable FIQ and IRQ
+ bic r2, r2, #0x1f @ Setup r2 to change mode
+
+ orr r3, r2, #0x1b @ Restore undef mode registers
+ msr cpsr_cxsf, r3
+ARM( ldmdb r1!, {r3, r13, r14} )
+THUMB( ldr r13, [r1], #-4 )
+THUMB( ldmdb r1!, {r3, r14} )
+ msr spsr_cxsf, r3
+
+ orr r3, r2, #0x17 @ Restore abort mode registers
+ msr cpsr_cxsf, r3
+ARM( ldmdb r1!, {r3, r13, r14} )
+THUMB( ldr r13, [r1], #-4 )
+THUMB( ldmdb r1!, {r3, r14} )
+ msr spsr_cxsf, r3
+
+ orr r3, r2, #0x12 @ Restore IRQ mode registers
+ msr cpsr_cxsf, r3
+ARM( ldmdb r1!, {r3, r13, r14} )
+THUMB( ldr r13, [r1], #-4 )
+THUMB( ldmdb r1!, {r3, r14} )
+ msr spsr_cxsf, r3
+
+ orr r3, r2, #0x11 @ Restore FIQ mode registers
+ msr cpsr_cxsf, r3
+ARM( ldmdb r1!, {r3, r8-r14} )
+THUMB( ldr r13, [r1], #-4 )
+THUMB( ldmdb r1!, {r3, r8-r12, r14} )
+
+ msr spsr_cxsf, r3
+
+ DECREMENT_AND_RESTORE r1 r3 @ Restore cpsr register
+ msr cpsr_cxsf, r3
+
+ARM( ldmdb r1, {sp, lr}^ ) @ Restore sp and lr registers
+ARM( sub r1, r1, #8 ) @ Update backup pointer (not
+ @ done in previous instruction)
+THUMB( ldr lr, [r1], #-4 )
+THUMB( ldr sp, [r1], #-4 )
+
+ str r1, [r0] @ Write backup stack pointer
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
+
+
+
+/*
+ * Save CP15 registers
+ *
+ * This function must be called in supervisor mode.
+ *
+ * r0 = address to backup stack pointer
+ *
+ * TTBR0, TTBR1, TTBRC, DACR CP15 registers are restored by boot ROM from SRAM.
+ */
+ .align 4
+ .section ".text", "ax"
+ENTRY(context_save_cp15_registers)
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack (r3 is saved due
+ @ to 8 byte aligned stack)
+ ldr r1, [r0] @ Read backup stack pointer
+
+ mrc p15, 0, r2, c12, c0, 0 @ Read Non-secure Vector Base
+ @ Address Register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c10, c2, 0 @ Access primary memory region
+ @ remap register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c10, c2, 1 @ Access normal memory region
+ @ remap register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c13, c0, 1 @ Read Context ID Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c13, c0, 2 @ Read Thread ID registers,
+ @ this register is both user
+ @ and privileged R/W accessible
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c13, c0, 3 @ Read Thread ID registers,
+ @ this register is user
+ @ read-only and privileged R/W
+ @ accessible.
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c13, c0, 4 @ Read Thread ID registers,
+ @ this register is privileged
+ @ R/W accessible only.
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 2, r2, c0, c0, 0 @ Cache Size Selection Register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c9, c12, 0 @ Read PMNC Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c12, 1 @ Read PMCNTENSET Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c12, 5 @ Read PMSELR Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c13, 0 @ Read PMCCNTR Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c13, 1 @ Read PMXEVTYPER Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c14, 0 @ Read PMUSERENR Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c14, 1 @ Read PMINTENSET Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c14, 2 @ Read PMINTENCLR Register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c1, c0, 2 @ Read CPACR Register
+ SAVE_AND_INCREMENT r2 r1
+
+ str r1, [r0] @ Write backup stack pointer
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
+
+
+
+/*
+ * Restore CP15 registers
+ *
+ * This function must be called in supervisor mode.
+ *
+ * r0 = address to backup stack pointer
+ */
+ .align 4
+ .section ".text", "ax"
+ENTRY(context_restore_cp15_registers)
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack (r3 is saved due
+ @ to 8 byte aligned stack)
+ ldr r1, [r0] @ Read backup stack pointer
+
+ DECREMENT_AND_RESTORE r1 r2 @ Write CPACR register
+ mcr p15, 0, r2, c1, c0, 2
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c14, 2 @ Write PMINTENCLR Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c14, 1 @ Write PMINTENSET Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c14, 0 @ Write PMUSERENR Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c13, 1 @ Write PMXEVTYPER Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c13, 0 @ Write PMCCNTR Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c12, 5 @ Write PMSELR Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c12, 1 @ Write PMCNTENSET Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c12, 0 @ Write PMNC Register
+
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 2, r2, c0, c0, 0 @ Cache Size Selection Register
+
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c13, c0, 4 @ Write Thread ID registers,
+ @ this register is privileged
+ @ R/W accessible only
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c13, c0, 3 @ Write Thread ID registers,
+ @ this register is user
+ @ read-only and privileged R/W
+ @ accessible
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c13, c0, 2 @ Write Thread ID registers,
+ @ this register is both user
+ @ and privileged R/W accessible
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c13, c0, 1 @ Write Context ID Register
+
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c10, c2, 1 @ Access normal memory region
+ @ remap register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c10, c2, 0 @ Access primary memory region
+ @ remap register
+
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c12, c0, 0 @ Write Non-secure Vector Base
+ @ Address Register
+
+ str r1, [r0] @ Write backup stack pointer
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
+
+
+/*
+ * L1 cache clean function. Commit 'dirty' data from L1
+ * to L2 cache.
+ *
+ * r0, r1, r2, used locally
+ *
+ */
+ .align 4
+ .section ".text", "ax"
+ENTRY(context_clean_l1_cache_all)
+
+ mov r0, #0 @ swith to cache level 0
+ @ (L1 cache)
+ mcr p15, 2, r0, c0, c0, 0 @ select current cache level
+ @ in cssr
+
+ dmb
+ mov r1, #0 @ r1 = way index
+wayLoopL1clean:
+ mov r0, #0 @ r0 = line index
+lineLoopL1clean:
+ mov r2, r1, lsl #30 @ TODO: OK to hard-code
+ @ SoC-specific L1 cache details?
+ mov r3, r0, lsl #5
+ add r2, r3
+@ add r2, r0, lsl #5
+ mcr p15, 0, r2, c7, c10, 2 @ Clean cache by set/way
+ add r0, r0, #1
+ cmp r0, #256 @ TODO: Ok with hard-coded
+ @ set/way sizes or do we have to
+ @ read them from ARM regs? Is it
+ @ set correctly in silicon?
+ bne lineLoopL1clean
+ add r1, r1, #1
+ cmp r1, #4 @ TODO: Ditto, sizes...
+ bne wayLoopL1clean
+
+ dsb
+ isb
+ mov pc, lr
+
+ENDPROC(context_clean_l1_cache_all)
+
+/*
+ * Last saves to backup RAM, cache clean and WFI
+ *
+ * r0 = address to backup_sram_storage base adress
+ * r1 = indicate whether also L2 cache should be cleaned
+ */
+ .align 4
+ .section ".text", "ax"
+ENTRY(context_save_to_sram_and_wfi_internal)
+
+ stmfd sp!, {r2-r12, lr} @ save on stack.
+
+ mrc p15, 0, r2, c1, c0, 0 @ read cp15 system control
+ @ register
+ str r2, [r0, #0x00]
+ mrc p15, 0, r2, c2, c0, 0 @ read cp15 ttb0 register
+ str r2, [r0, #0x04]
+ mrc p15, 0, r2, c2, c0, 1 @ read cp15 ttb1 register
+ str r2, [r0, #0x08]
+ mrc p15, 0, r2, c2, c0, 2 @ read cp15 ttb control register
+ str r2, [r0, #0x0C]
+ mrc p15, 0, r2, c3, c0, 0 @ read domain access control
+ @ register
+ str r2, [r0, #0x10]
+
+ ldr r2, =return_here
+ str r2, [r0, #0x14] @ save program counter restore
+ @ value to backup_sram_storage
+ mrs r2, cpsr
+ str r2, [r0, #0x18] @ save cpsr to
+ @ backup_sram_storage
+ str sp, [r0, #0x1c] @ save sp to backup_sram_storage
+
+ mov r4, r1 @ Set r4 = cleanL2cache, r1
+ @ will be destroyed by
+ @ v7_clean_l1_cache_all
+
+ bl context_clean_l1_cache_all @ Commit all dirty data in L1
+ @ cache to L2 without
+ @ invalidating
+
+ dsb @ data synchronization barrier
+ isb @ instruction synchronization
+ @ barrier
+ wfi @ wait for interrupt
+
+return_here: @ both cores return here
+ @ now we are out deep sleep
+ @ with all the context lost
+ @ except pc, sp and cpsr
+
+ ldmfd sp!, {r2-r12, pc} @ restore from stack
+
diff --git a/arch/arm/mach-ux500/pm/performance.c b/arch/arm/mach-ux500/pm/performance.c
new file mode 100644
index 00000000000..04aca3cb5bd
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/performance.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Johan Rudholm <johan.rudholm@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/genhd.h>
+#include <linux/major.h>
+#include <linux/cdev.h>
+#include <linux/kernel_stat.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/cpu.h>
+#include <linux/pm_qos.h>
+
+#include <mach/irqs.h>
+
+#define WLAN_PROBE_DELAY 3000 /* 3 seconds */
+#define WLAN_LIMIT (3000/3) /* If we have more than 1000 irqs per second */
+
+/*
+ * MMC TODO:
+ * o Develop a more power-aware algorithm
+ * o Make the parameters visible through debugfs
+ * o Get the value of CONFIG_MMC_BLOCK_MINORS in runtime instead, since
+ * it may be altered by drivers/mmc/card/block.c
+ */
+
+/* Sample reads and writes every n ms */
+#define PERF_MMC_PROBE_DELAY 1000
+/* Read threshold, sectors/second */
+#define PERF_MMC_LIMIT_READ 10240
+/* Write threshold, sectors/second */
+#define PERF_MMC_LIMIT_WRITE 8192
+/* Nr of MMC devices */
+#define PERF_MMC_HOSTS 8
+
+/*
+ * Rescan for new MMC devices every
+ * PERF_MMC_PROBE_DELAY * PERF_MMC_RESCAN_CYCLES ms
+ */
+#define PERF_MMC_RESCAN_CYCLES 10
+
+#ifdef CONFIG_MMC_BLOCK
+static struct delayed_work work_mmc;
+#endif
+
+static struct delayed_work work_wlan_workaround;
+static struct pm_qos_request wlan_pm_qos_latency;
+static bool wlan_pm_qos_is_latency_0;
+
+static void wlan_load(struct work_struct *work)
+{
+ int cpu;
+ unsigned int num_irqs = 0;
+ static unsigned int old_num_irqs = UINT_MAX;
+
+ for_each_online_cpu(cpu)
+ num_irqs += kstat_irqs_cpu(IRQ_DB8500_SDMMC1, cpu);
+
+ if ((num_irqs > old_num_irqs) &&
+ (num_irqs - old_num_irqs) > WLAN_LIMIT) {
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "wlan", 125);
+ if (!wlan_pm_qos_is_latency_0) {
+ /*
+ * The wake up latency is set to 0 to prevent
+ * the system from going to sleep. This improves
+ * the wlan throughput in DMA mode.
+ * The wake up latency from sleep adds ~5% overhead
+ * for TX in some cases.
+ * This change doesn't increase performance for wlan
+ * PIO since the CPU usage prevents sleep in this mode.
+ */
+ pm_qos_add_request(&wlan_pm_qos_latency,
+ PM_QOS_CPU_DMA_LATENCY, 0);
+ wlan_pm_qos_is_latency_0 = true;
+ }
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "wlan", 25);
+ if (wlan_pm_qos_is_latency_0) {
+ pm_qos_remove_request(&wlan_pm_qos_latency);
+ wlan_pm_qos_is_latency_0 = false;
+ }
+ }
+
+ old_num_irqs = num_irqs;
+
+ schedule_delayed_work_on(0,
+ &work_wlan_workaround,
+ msecs_to_jiffies(WLAN_PROBE_DELAY));
+}
+
+#ifdef CONFIG_MMC_BLOCK
+/*
+ * Loop through every CONFIG_MMC_BLOCK_MINORS'th minor device for
+ * MMC_BLOCK_MAJOR, get the struct gendisk for each device. Returns
+ * nr of found disks. Populate mmc_disks.
+ */
+static int scan_mmc_devices(struct gendisk *mmc_disks[])
+{
+ dev_t devnr;
+ int i, j = 0, part;
+ struct gendisk *mmc_devices[256 / CONFIG_MMC_BLOCK_MINORS];
+
+ memset(&mmc_devices, 0, sizeof(mmc_devices));
+
+ for (i = 0; i * CONFIG_MMC_BLOCK_MINORS < 256; i++) {
+ devnr = MKDEV(MMC_BLOCK_MAJOR, i * CONFIG_MMC_BLOCK_MINORS);
+ mmc_devices[i] = get_gendisk(devnr, &part);
+
+ /* Invalid capacity of device, do not add to list */
+ if (!mmc_devices[i] || !get_capacity(mmc_devices[i]))
+ continue;
+
+ mmc_disks[j] = mmc_devices[i];
+ j++;
+
+ if (j == PERF_MMC_HOSTS)
+ break;
+ }
+
+ return j;
+}
+
+/*
+ * Sample sectors read and written to any MMC devices, update PRCMU
+ * qos requirement
+ */
+static void mmc_load(struct work_struct *work)
+{
+ static unsigned long long old_sectors_read[PERF_MMC_HOSTS];
+ static unsigned long long old_sectors_written[PERF_MMC_HOSTS];
+ static struct gendisk *mmc_disks[PERF_MMC_HOSTS];
+ static int cycle, nrdisk;
+ static bool old_mode;
+ unsigned long long sectors;
+ bool new_mode = false;
+ int i;
+
+ if (!cycle) {
+ memset(&mmc_disks, 0, sizeof(mmc_disks));
+ nrdisk = scan_mmc_devices(mmc_disks);
+ cycle = PERF_MMC_RESCAN_CYCLES;
+ }
+ cycle--;
+
+ for (i = 0; i < nrdisk; i++) {
+ sectors = part_stat_read(&(mmc_disks[i]->part0),
+ sectors[READ]);
+
+ if (old_sectors_read[i] &&
+ sectors > old_sectors_read[i] &&
+ (sectors - old_sectors_read[i]) >
+ PERF_MMC_LIMIT_READ)
+ new_mode = true;
+
+ old_sectors_read[i] = sectors;
+ sectors = part_stat_read(&(mmc_disks[i]->part0),
+ sectors[WRITE]);
+
+ if (old_sectors_written[i] &&
+ sectors > old_sectors_written[i] &&
+ (sectors - old_sectors_written[i]) >
+ PERF_MMC_LIMIT_WRITE)
+ new_mode = true;
+
+ old_sectors_written[i] = sectors;
+ }
+
+ if (!old_mode && new_mode)
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "mmc", 125);
+
+ if (old_mode && !new_mode)
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "mmc", 25);
+
+ old_mode = new_mode;
+
+ schedule_delayed_work(&work_mmc,
+ msecs_to_jiffies(PERF_MMC_PROBE_DELAY));
+
+}
+#endif /* CONFIG_MMC_BLOCK */
+
+static int __init performance_register(void)
+{
+ int ret;
+
+#ifdef CONFIG_MMC_BLOCK
+ ret = prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "mmc", 25);
+ if (ret) {
+ pr_err("%s: Failed to add PRCMU req for mmc\n", __func__);
+ goto out;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&work_mmc, mmc_load);
+
+ schedule_delayed_work(&work_mmc,
+ msecs_to_jiffies(PERF_MMC_PROBE_DELAY));
+#endif
+
+ ret = prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "wlan", 25);
+ if (ret) {
+ pr_err("%s: Failed to add PRCMU req for wlan\n", __func__);
+ goto out;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&work_wlan_workaround,
+ wlan_load);
+
+ schedule_delayed_work_on(0, &work_wlan_workaround,
+ msecs_to_jiffies(WLAN_PROBE_DELAY));
+out:
+ return ret;
+}
+late_initcall(performance_register);
diff --git a/arch/arm/mach-ux500/pm/pm.c b/arch/arm/mach-ux500/pm/pm.c
new file mode 100644
index 00000000000..880e9763a0f
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/pm.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <asm/hardware/gic.h>
+#include <asm/processor.h>
+
+#include <mach/hardware.h>
+#include <mach/pm.h>
+
+#define STABILIZATION_TIME 30 /* us */
+#define GIC_FREEZE_DELAY 1 /* us */
+
+#define PRCM_ARM_WFI_STANDBY_CPU0_WFI 0x8
+#define PRCM_ARM_WFI_STANDBY_CPU1_WFI 0x10
+
+/* Dual A9 core interrupt management unit registers */
+#define PRCM_A9_MASK_REQ 0x328
+#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1
+#define PRCM_A9_MASK_ACK 0x32c
+
+#define PRCM_ARMITMSK31TO0 0x11c
+#define PRCM_ARMITMSK63TO32 0x120
+#define PRCM_ARMITMSK95TO64 0x124
+#define PRCM_ARMITMSK127TO96 0x128
+#define PRCM_POWER_STATE_VAL 0x25C
+#define PRCM_ARMITVAL31TO0 0x260
+#define PRCM_ARMITVAL63TO32 0x264
+#define PRCM_ARMITVAL95TO64 0x268
+#define PRCM_ARMITVAL127TO96 0x26C
+
+/* ARM WFI Standby signal register */
+#define PRCM_ARM_WFI_STANDBY 0x130
+
+/* IO force */
+#define PRCM_IOCR 0x310
+#define PRCM_IOCR_IOFORCE 0x1
+#ifdef CONFIG_UX500_SUSPEND_DBG_WAKE_ON_UART
+int ux500_console_uart_gpio_pin = CONFIG_UX500_CONSOLE_UART_GPIO_PIN;
+#endif
+static u32 u8500_gpio_banks[] = {U8500_GPIOBANK0_BASE,
+ U8500_GPIOBANK1_BASE,
+ U8500_GPIOBANK2_BASE,
+ U8500_GPIOBANK3_BASE,
+ U8500_GPIOBANK4_BASE,
+ U8500_GPIOBANK5_BASE,
+ U8500_GPIOBANK6_BASE,
+ U8500_GPIOBANK7_BASE,
+ U8500_GPIOBANK8_BASE};
+
+static u32 u5500_gpio_banks[] = {U5500_GPIOBANK0_BASE,
+ U5500_GPIOBANK1_BASE,
+ U5500_GPIOBANK2_BASE,
+ U5500_GPIOBANK3_BASE,
+ U5500_GPIOBANK4_BASE,
+ U5500_GPIOBANK5_BASE,
+ U5500_GPIOBANK6_BASE,
+ U5500_GPIOBANK7_BASE};
+
+static u32 ux500_gpio_wks[ARRAY_SIZE(u8500_gpio_banks)];
+
+inline int ux500_pm_arm_on_ext_clk(bool leave_arm_pll_on)
+{
+ return 0;
+}
+
+/* Decouple GIC from the interrupt bus */
+void ux500_pm_gic_decouple(void)
+{
+ prcmu_write_masked(PRCM_A9_MASK_REQ,
+ PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
+ PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ);
+
+ (void)prcmu_read(PRCM_A9_MASK_REQ);
+
+ /* TODO: Use the ack bit when possible */
+ udelay(GIC_FREEZE_DELAY); /* Wait for the GIC to freeze */
+}
+
+/* Recouple GIC with the interrupt bus */
+void ux500_pm_gic_recouple(void)
+{
+ prcmu_write_masked(PRCM_A9_MASK_REQ,
+ PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
+ 0);
+
+ /* TODO: Use the ack bit when possible */
+}
+
+#define GIC_NUMBER_REGS 5
+bool ux500_pm_gic_pending_interrupt(void)
+{
+ u32 pr; /* Pending register */
+ u32 er; /* Enable register */
+ int i;
+
+ /* 5 registers. STI & PPI not skipped */
+ for (i = 0; i < GIC_NUMBER_REGS; i++) {
+
+ pr = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
+ GIC_DIST_PENDING_SET + i * 4);
+ er = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
+ GIC_DIST_ENABLE_SET + i * 4);
+
+ if (pr & er)
+ return true; /* There is a pending interrupt */
+ }
+ return false;
+}
+
+#define GIC_NUMBER_SPI_REGS 4
+bool ux500_pm_prcmu_pending_interrupt(void)
+{
+ u32 it;
+ u32 im;
+ int i;
+
+ for (i = 0; i < GIC_NUMBER_SPI_REGS; i++) { /* There are 4 registers */
+
+ it = prcmu_read(PRCM_ARMITVAL31TO0 + i * 4);
+ im = prcmu_read(PRCM_ARMITMSK31TO0 + i * 4);
+
+ if (it & im)
+ return true; /* There is a pending interrupt */
+ }
+
+ return false;
+}
+
+void ux500_pm_prcmu_set_ioforce(bool enable)
+{
+ if (enable)
+ prcmu_write_masked(PRCM_IOCR,
+ PRCM_IOCR_IOFORCE,
+ PRCM_IOCR_IOFORCE);
+ else
+ prcmu_write_masked(PRCM_IOCR,
+ PRCM_IOCR_IOFORCE,
+ 0);
+}
+
+void ux500_pm_prcmu_copy_gic_settings(void)
+{
+ u32 er; /* Enable register */
+ int i;
+
+ for (i = 0; i < GIC_NUMBER_SPI_REGS; i++) { /* 4*32 SPI interrupts */
+ /* +1 due to skip STI and PPI */
+ er = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
+ GIC_DIST_ENABLE_SET + (i + 1) * 4);
+ prcmu_write(PRCM_ARMITMSK31TO0 + i * 4, er);
+ }
+}
+
+void ux500_pm_gpio_save_wake_up_status(void)
+{
+ int num_banks;
+ u32 *banks;
+ int i;
+
+ if (cpu_is_u5500()) {
+ num_banks = ARRAY_SIZE(u5500_gpio_banks);
+ banks = u5500_gpio_banks;
+ } else {
+ num_banks = ARRAY_SIZE(u8500_gpio_banks);
+ banks = u8500_gpio_banks;
+ }
+
+ nmk_gpio_clocks_enable();
+
+ for (i = 0; i < num_banks; i++)
+ ux500_gpio_wks[i] = readl(__io_address(banks[i]) + NMK_GPIO_WKS);
+
+ nmk_gpio_clocks_disable();
+}
+
+u32 ux500_pm_gpio_read_wake_up_status(unsigned int bank_num)
+{
+ if (WARN_ON(cpu_is_u5500() && bank_num >=
+ ARRAY_SIZE(u5500_gpio_banks)))
+ return 0;
+
+ if (WARN_ON(cpu_is_u8500() && bank_num >=
+ ARRAY_SIZE(u8500_gpio_banks)))
+ return 0;
+
+ return ux500_gpio_wks[bank_num];
+}
+
+/* Check if the other CPU is in WFI */
+bool ux500_pm_other_cpu_wfi(void)
+{
+ if (smp_processor_id()) {
+ /* We are CPU 1 => check if CPU0 is in WFI */
+ if (prcmu_read(PRCM_ARM_WFI_STANDBY) &
+ PRCM_ARM_WFI_STANDBY_CPU0_WFI)
+ return true;
+ } else {
+ /* We are CPU 0 => check if CPU1 is in WFI */
+ if (prcmu_read(PRCM_ARM_WFI_STANDBY) &
+ PRCM_ARM_WFI_STANDBY_CPU1_WFI)
+ return true;
+ }
+
+ return false;
+}
diff --git a/arch/arm/mach-ux500/pm/prcmu-qos-power.c b/arch/arm/mach-ux500/pm/prcmu-qos-power.c
new file mode 100644
index 00000000000..3bd99b766af
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/prcmu-qos-power.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Martin Persson
+ * Per Fransson <per.xx.fransson@stericsson.com>
+ *
+ * Quality of Service for the U8500 PRCM Unit interface driver
+ *
+ * Strongly influenced by kernel/pm_qos_params.c.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/cpufreq.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/cpufreq-dbx500.h>
+
+#include <mach/prcmu-debug.h>
+
+#define ARM_THRESHOLD_FREQ (400000)
+
+static int qos_delayed_cpufreq_notifier(struct notifier_block *,
+ unsigned long, void *);
+
+static s32 cpufreq_requirement_queued;
+static s32 cpufreq_requirement_set;
+
+/*
+ * locking rule: all changes to requirements or prcmu_qos_object list
+ * and prcmu_qos_objects need to happen with prcmu_qos_lock
+ * held, taken with _irqsave. One lock to rule them all
+ */
+struct requirement_list {
+ struct list_head list;
+ union {
+ s32 value;
+ s32 usec;
+ s32 kbps;
+ };
+ char *name;
+};
+
+static s32 max_compare(s32 v1, s32 v2);
+
+struct prcmu_qos_object {
+ struct requirement_list requirements;
+ struct blocking_notifier_head *notifiers;
+ struct miscdevice prcmu_qos_power_miscdev;
+ char *name;
+ s32 default_value;
+ s32 force_value;
+ atomic_t target_value;
+ s32 (*comparitor)(s32, s32);
+};
+
+static struct prcmu_qos_object null_qos;
+static BLOCKING_NOTIFIER_HEAD(prcmu_ape_opp_notifier);
+static BLOCKING_NOTIFIER_HEAD(prcmu_ddr_opp_notifier);
+
+static struct prcmu_qos_object ape_opp_qos = {
+ .requirements = {
+ LIST_HEAD_INIT(ape_opp_qos.requirements.list)
+ },
+ .notifiers = &prcmu_ape_opp_notifier,
+ .name = "ape_opp",
+ /* Target value in % APE OPP */
+ .default_value = 50,
+ .force_value = 0,
+ .target_value = ATOMIC_INIT(50),
+ .comparitor = max_compare
+};
+
+static struct prcmu_qos_object ddr_opp_qos = {
+ .requirements = {
+ LIST_HEAD_INIT(ddr_opp_qos.requirements.list)
+ },
+ .notifiers = &prcmu_ddr_opp_notifier,
+ .name = "ddr_opp",
+ /* Target value in % DDR OPP */
+ .default_value = 25,
+ .force_value = 0,
+ .target_value = ATOMIC_INIT(25),
+ .comparitor = max_compare
+};
+
+static struct prcmu_qos_object arm_opp_qos = {
+ .requirements = {
+ LIST_HEAD_INIT(arm_opp_qos.requirements.list)
+ },
+ /*
+ * No notifier on ARM opp qos request, since this won't actually
+ * do anything, except changing limits for cpufreq
+ */
+ .name = "arm_opp",
+ /* Target value in % ARM OPP, note can be 125% */
+ .default_value = 25,
+ .force_value = 0,
+ .target_value = ATOMIC_INIT(25),
+ .comparitor = max_compare
+};
+
+static struct prcmu_qos_object *prcmu_qos_array[] = {
+ &null_qos,
+ &ape_opp_qos,
+ &ddr_opp_qos,
+ &arm_opp_qos,
+};
+
+static DEFINE_MUTEX(prcmu_qos_mutex);
+static DEFINE_SPINLOCK(prcmu_qos_lock);
+
+static bool ape_opp_50_partly_25_enabled;
+
+static unsigned long cpufreq_opp_delay = HZ / 5;
+
+unsigned long prcmu_qos_get_cpufreq_opp_delay(void)
+{
+ return cpufreq_opp_delay;
+}
+
+static struct notifier_block qos_delayed_cpufreq_notifier_block = {
+ .notifier_call = qos_delayed_cpufreq_notifier,
+};
+
+void prcmu_qos_set_cpufreq_opp_delay(unsigned long n)
+{
+ if (n == 0) {
+ cpufreq_unregister_notifier(&qos_delayed_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ prcmu_qos_update_requirement(PRCMU_QOS_DDR_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ cpufreq_requirement_set = PRCMU_QOS_DEFAULT_VALUE;
+ cpufreq_requirement_queued = PRCMU_QOS_DEFAULT_VALUE;
+ } else if (cpufreq_opp_delay != 0) {
+ cpufreq_register_notifier(&qos_delayed_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ }
+ cpufreq_opp_delay = n;
+}
+#ifdef CONFIG_CPU_FREQ
+static void update_cpu_limits(s32 extreme_value)
+{
+ int cpu;
+ struct cpufreq_policy policy;
+ int ret;
+ int min_freq, max_freq;
+
+ for_each_online_cpu(cpu) {
+ ret = cpufreq_get_policy(&policy, cpu);
+ if (ret) {
+ pr_err("prcmu qos: get cpufreq policy failed (cpu%d)\n",
+ cpu);
+ continue;
+ }
+
+ ret = dbx500_cpufreq_get_limits(cpu, extreme_value,
+ &min_freq, &max_freq);
+ if (ret)
+ continue;
+ /*
+ * cpufreq fw does not allow frequency change if
+ * "current min freq" > "new max freq" or
+ * "current max freq" < "new min freq".
+ * Thus the intermediate steps below.
+ */
+ if (policy.min > max_freq) {
+ ret = cpufreq_update_freq(cpu, min_freq, policy.max);
+ if (ret)
+ pr_err("prcmu qos: update min cpufreq failed (1)\n");
+ }
+ if (policy.max < min_freq) {
+ ret = cpufreq_update_freq(cpu, policy.min, max_freq);
+ if (ret)
+ pr_err("prcmu qos: update max cpufreq failed (2)\n");
+ }
+
+ ret = cpufreq_update_freq(cpu, min_freq, max_freq);
+ if (ret)
+ pr_err("prcmu qos: update max cpufreq failed (3)\n");
+ }
+
+}
+#else
+static inline void update_cpu_limits(s32 extreme_value) { }
+#endif
+/* static helper function */
+static s32 max_compare(s32 v1, s32 v2)
+{
+ return max(v1, v2);
+}
+
+static void update_target(int target)
+{
+ s32 extreme_value;
+ struct requirement_list *node;
+ unsigned long flags;
+ bool update = false;
+ u8 op;
+
+ mutex_lock(&prcmu_qos_mutex);
+
+ spin_lock_irqsave(&prcmu_qos_lock, flags);
+ extreme_value = prcmu_qos_array[target]->default_value;
+
+ if (prcmu_qos_array[target]->force_value != 0) {
+ extreme_value = prcmu_qos_array[target]->force_value;
+ update = true;
+ } else {
+ list_for_each_entry(node,
+ &prcmu_qos_array[target]->requirements.list,
+ list) {
+ extreme_value = prcmu_qos_array[target]->comparitor(
+ extreme_value, node->value);
+ }
+ if (atomic_read(&prcmu_qos_array[target]->target_value)
+ != extreme_value) {
+ update = true;
+ atomic_set(&prcmu_qos_array[target]->target_value,
+ extreme_value);
+ pr_debug("prcmu qos: new target for qos %d is %d\n",
+ target, atomic_read(
+ &prcmu_qos_array[target]->target_value
+ ));
+ }
+ }
+
+ spin_unlock_irqrestore(&prcmu_qos_lock, flags);
+
+ if (!update)
+ goto unlock_and_return;
+
+ if (prcmu_qos_array[target]->notifiers)
+ blocking_notifier_call_chain(prcmu_qos_array[target]->notifiers,
+ (unsigned long)extreme_value,
+ NULL);
+ switch (target) {
+ case PRCMU_QOS_DDR_OPP:
+ switch (extreme_value) {
+ case 50:
+ op = DDR_50_OPP;
+ pr_debug("prcmu qos: set ddr opp to 50%%\n");
+ break;
+ case 100:
+ op = DDR_100_OPP;
+ pr_debug("prcmu qos: set ddr opp to 100%%\n");
+ break;
+ case 25:
+ /* 25% DDR OPP is not supported on 5500 */
+ if (!cpu_is_u5500()) {
+ op = DDR_25_OPP;
+ pr_debug("prcmu qos: set ddr opp to 25%%\n");
+ break;
+ }
+ default:
+ pr_err("prcmu qos: Incorrect ddr target value (%d)",
+ extreme_value);
+ goto unlock_and_return;
+ }
+ prcmu_set_ddr_opp(op);
+ prcmu_debug_ddr_opp_log(op);
+ break;
+ case PRCMU_QOS_APE_OPP:
+ switch (extreme_value) {
+ case 50:
+ if (ape_opp_50_partly_25_enabled)
+ op = APE_50_PARTLY_25_OPP;
+ else
+ op = APE_50_OPP;
+ pr_debug("prcmu qos: set ape opp to 50%%\n");
+ break;
+ case 100:
+ op = APE_100_OPP;
+ pr_debug("prcmu qos: set ape opp to 100%%\n");
+ break;
+ default:
+ pr_err("prcmu qos: Incorrect ape target value (%d)",
+ extreme_value);
+ goto unlock_and_return;
+ }
+ (void)prcmu_set_ape_opp(op);
+ prcmu_debug_ape_opp_log(op);
+ break;
+ case PRCMU_QOS_ARM_OPP:
+ {
+ mutex_unlock(&prcmu_qos_mutex);
+ /*
+ * We can't hold the mutex since changing cpufreq
+ * will trigger an prcmu fw callback.
+ */
+ update_cpu_limits(extreme_value);
+ /* Return since the lock is unlocked */
+ return;
+
+ break;
+ }
+ default:
+ pr_err("prcmu qos: Incorrect target\n");
+ break;
+ }
+
+unlock_and_return:
+ mutex_unlock(&prcmu_qos_mutex);
+}
+
+void prcmu_qos_force_opp(int prcmu_qos_class, s32 i)
+{
+ prcmu_qos_array[prcmu_qos_class]->force_value = i;
+ update_target(prcmu_qos_class);
+}
+
+void prcmu_qos_voice_call_override(bool enable)
+{
+ int ape_opp;
+
+ mutex_lock(&prcmu_qos_mutex);
+
+ ape_opp_50_partly_25_enabled = enable;
+
+ ape_opp = prcmu_get_ape_opp();
+
+ if (ape_opp == APE_50_OPP) {
+ if (enable)
+ prcmu_set_ape_opp(APE_50_PARTLY_25_OPP);
+ else
+ prcmu_set_ape_opp(APE_50_OPP);
+ }
+
+ mutex_unlock(&prcmu_qos_mutex);
+}
+
+/**
+ * prcmu_qos_requirement - returns current prcmu qos expectation
+ * @prcmu_qos_class: identification of which qos value is requested
+ *
+ * This function returns the current target value in an atomic manner.
+ */
+int prcmu_qos_requirement(int prcmu_qos_class)
+{
+ return atomic_read(&prcmu_qos_array[prcmu_qos_class]->target_value);
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_requirement);
+
+/**
+ * prcmu_qos_add_requirement - inserts new qos request into the list
+ * @prcmu_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the prcmu_qos_class list of requested
+ * qos performance characteristics. It recomputes the aggregate QoS
+ * expectations for the prcmu_qos_class of parameters.
+ */
+int prcmu_qos_add_requirement(int prcmu_qos_class, char *name, s32 value)
+{
+ struct requirement_list *dep;
+ unsigned long flags;
+
+ dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL);
+ if (dep == NULL)
+ return -ENOMEM;
+
+ if (value == PRCMU_QOS_DEFAULT_VALUE)
+ dep->value = prcmu_qos_array[prcmu_qos_class]->default_value;
+ else
+ dep->value = value;
+ dep->name = kstrdup(name, GFP_KERNEL);
+ if (!dep->name)
+ goto cleanup;
+
+ spin_lock_irqsave(&prcmu_qos_lock, flags);
+ list_add(&dep->list,
+ &prcmu_qos_array[prcmu_qos_class]->requirements.list);
+ spin_unlock_irqrestore(&prcmu_qos_lock, flags);
+ update_target(prcmu_qos_class);
+
+ return 0;
+
+cleanup:
+ kfree(dep);
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_add_requirement);
+
+/**
+ * prcmu_qos_update_requirement - modifies an existing qos request
+ * @prcmu_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ * @value: defines the qos request
+ *
+ * Updates an existing qos requirement for the prcmu_qos_class of parameters
+ * along with updating the target prcmu_qos_class value.
+ *
+ * If the named request isn't in the list then no change is made.
+ */
+int prcmu_qos_update_requirement(int prcmu_qos_class, char *name, s32 new_value)
+{
+ unsigned long flags;
+ struct requirement_list *node;
+ int pending_update = 0;
+
+ spin_lock_irqsave(&prcmu_qos_lock, flags);
+ list_for_each_entry(node,
+ &prcmu_qos_array[prcmu_qos_class]->requirements.list, list) {
+ if (strcmp(node->name, name) == 0) {
+ if (new_value == PRCMU_QOS_DEFAULT_VALUE)
+ node->value =
+ prcmu_qos_array[prcmu_qos_class]->default_value;
+ else
+ node->value = new_value;
+ pending_update = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&prcmu_qos_lock, flags);
+ if (pending_update)
+ update_target(prcmu_qos_class);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_update_requirement);
+
+/**
+ * prcmu_qos_remove_requirement - modifies an existing qos request
+ * @prcmu_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ *
+ * Will remove named qos request from prcmu_qos_class list of parameters and
+ * recompute the current target value for the prcmu_qos_class.
+ */
+void prcmu_qos_remove_requirement(int prcmu_qos_class, char *name)
+{
+ unsigned long flags;
+ struct requirement_list *node;
+ int pending_update = 0;
+
+ spin_lock_irqsave(&prcmu_qos_lock, flags);
+ list_for_each_entry(node,
+ &prcmu_qos_array[prcmu_qos_class]->requirements.list, list) {
+ if (strcmp(node->name, name) == 0) {
+ kfree(node->name);
+ list_del(&node->list);
+ kfree(node);
+ pending_update = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&prcmu_qos_lock, flags);
+ if (pending_update)
+ update_target(prcmu_qos_class);
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_remove_requirement);
+
+/**
+ * prcmu_qos_add_notifier - sets notification entry for changes to target value
+ * @prcmu_qos_class: identifies which qos target changes should be notified.
+ * @notifier: notifier block managed by caller.
+ *
+ * will register the notifier into a notification chain that gets called
+ * upon changes to the prcmu_qos_class target value.
+ */
+int prcmu_qos_add_notifier(int prcmu_qos_class, struct notifier_block *notifier)
+{
+ int retval = -EINVAL;
+
+ if (prcmu_qos_array[prcmu_qos_class]->notifiers)
+ retval = blocking_notifier_chain_register(
+ prcmu_qos_array[prcmu_qos_class]->notifiers, notifier);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_add_notifier);
+
+/**
+ * prcmu_qos_remove_notifier - deletes notification entry from chain.
+ * @prcmu_qos_class: identifies which qos target changes are notified.
+ * @notifier: notifier block to be removed.
+ *
+ * will remove the notifier from the notification chain that gets called
+ * upon changes to the prcmu_qos_class target value.
+ */
+int prcmu_qos_remove_notifier(int prcmu_qos_class,
+ struct notifier_block *notifier)
+{
+ int retval = -EINVAL;
+ if (prcmu_qos_array[prcmu_qos_class]->notifiers)
+ retval = blocking_notifier_chain_unregister(
+ prcmu_qos_array[prcmu_qos_class]->notifiers, notifier);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_remove_notifier);
+
+#define USER_QOS_NAME_LEN 32
+
+static int prcmu_qos_power_open(struct inode *inode, struct file *filp,
+ long prcmu_qos_class)
+{
+ int ret;
+ char name[USER_QOS_NAME_LEN];
+
+ filp->private_data = (void *)prcmu_qos_class;
+ snprintf(name, USER_QOS_NAME_LEN, "file_%08x", (unsigned int)filp);
+ ret = prcmu_qos_add_requirement(prcmu_qos_class, name,
+ PRCMU_QOS_DEFAULT_VALUE);
+ if (ret >= 0)
+ return 0;
+
+ return -EPERM;
+}
+
+
+static int prcmu_qos_ape_power_open(struct inode *inode, struct file *filp)
+{
+ return prcmu_qos_power_open(inode, filp, PRCMU_QOS_APE_OPP);
+}
+
+static int prcmu_qos_ddr_power_open(struct inode *inode, struct file *filp)
+{
+ return prcmu_qos_power_open(inode, filp, PRCMU_QOS_DDR_OPP);
+}
+
+static int prcmu_qos_arm_power_open(struct inode *inode, struct file *filp)
+{
+ return prcmu_qos_power_open(inode, filp, PRCMU_QOS_ARM_OPP);
+}
+
+static int prcmu_qos_power_release(struct inode *inode, struct file *filp)
+{
+ int prcmu_qos_class;
+ char name[USER_QOS_NAME_LEN];
+
+ prcmu_qos_class = (long)filp->private_data;
+ snprintf(name, USER_QOS_NAME_LEN, "file_%08x", (unsigned int)filp);
+ prcmu_qos_remove_requirement(prcmu_qos_class, name);
+
+ return 0;
+}
+
+static ssize_t prcmu_qos_power_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ s32 value;
+ int prcmu_qos_class;
+ char name[USER_QOS_NAME_LEN];
+
+ prcmu_qos_class = (long)filp->private_data;
+ if (count != sizeof(s32))
+ return -EINVAL;
+ if (copy_from_user(&value, buf, sizeof(s32)))
+ return -EFAULT;
+ snprintf(name, USER_QOS_NAME_LEN, "file_%08x", (unsigned int)filp);
+ prcmu_qos_update_requirement(prcmu_qos_class, name, value);
+
+ return sizeof(s32);
+}
+
+/* Functions to provide QoS to user space */
+static const struct file_operations prcmu_qos_ape_power_fops = {
+ .write = prcmu_qos_power_write,
+ .open = prcmu_qos_ape_power_open,
+ .release = prcmu_qos_power_release,
+};
+
+/* Functions to provide QoS to user space */
+static const struct file_operations prcmu_qos_ddr_power_fops = {
+ .write = prcmu_qos_power_write,
+ .open = prcmu_qos_ddr_power_open,
+ .release = prcmu_qos_power_release,
+};
+
+static const struct file_operations prcmu_qos_arm_power_fops = {
+ .write = prcmu_qos_power_write,
+ .open = prcmu_qos_arm_power_open,
+ .release = prcmu_qos_power_release,
+};
+
+static int register_prcmu_qos_misc(struct prcmu_qos_object *qos,
+ const struct file_operations *fops)
+{
+ qos->prcmu_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
+ qos->prcmu_qos_power_miscdev.name = qos->name;
+ qos->prcmu_qos_power_miscdev.fops = fops;
+
+ return misc_register(&qos->prcmu_qos_power_miscdev);
+}
+
+static void qos_delayed_work_up_fn(struct work_struct *work)
+{
+ prcmu_qos_update_requirement(PRCMU_QOS_DDR_OPP, "cpufreq", 100);
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "cpufreq", 100);
+ cpufreq_requirement_set = 100;
+}
+
+static void qos_delayed_work_down_fn(struct work_struct *work)
+{
+ prcmu_qos_update_requirement(PRCMU_QOS_DDR_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ cpufreq_requirement_set = PRCMU_QOS_DEFAULT_VALUE;
+}
+
+static DECLARE_DELAYED_WORK(qos_delayed_work_up, qos_delayed_work_up_fn);
+static DECLARE_DELAYED_WORK(qos_delayed_work_down, qos_delayed_work_down_fn);
+
+static int qos_delayed_cpufreq_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct cpufreq_freqs *freq = data;
+ s32 new_ddr_target;
+
+ /* Only react once per transition and only for one core, e.g. core 0 */
+ if (event != CPUFREQ_POSTCHANGE || freq->cpu != 0)
+ return 0;
+
+ /*
+ * APE and DDR OPP are always handled together in this solution.
+ * Hence no need to check both DDR and APE opp in the code below.
+ */
+
+ /* Which DDR OPP are we aiming for? */
+ if (freq->new > ARM_THRESHOLD_FREQ)
+ new_ddr_target = 100;
+ else
+ new_ddr_target = PRCMU_QOS_DEFAULT_VALUE;
+
+ if (new_ddr_target == cpufreq_requirement_queued) {
+ /*
+ * We're already at, or going to, the target requirement.
+ * This is only a fluctuation within the interval
+ * corresponding to the same DDR requirement.
+ */
+ return 0;
+ }
+ cpufreq_requirement_queued = new_ddr_target;
+
+ if (freq->new > ARM_THRESHOLD_FREQ) {
+ cancel_delayed_work_sync(&qos_delayed_work_down);
+ /*
+ * Only schedule this requirement if it is not the current
+ * one.
+ */
+ if (new_ddr_target != cpufreq_requirement_set)
+ schedule_delayed_work(&qos_delayed_work_up,
+ cpufreq_opp_delay);
+ } else {
+ cancel_delayed_work_sync(&qos_delayed_work_up);
+ /*
+ * Only schedule this requirement if it is not the current
+ * one.
+ */
+ if (new_ddr_target != cpufreq_requirement_set)
+ schedule_delayed_work(&qos_delayed_work_down,
+ cpufreq_opp_delay);
+ }
+
+ return 0;
+}
+
+static int __init prcmu_qos_power_init(void)
+{
+ int ret;
+
+ /* 25% DDR OPP is not supported on u5500 */
+ if (cpu_is_u5500()) {
+ ddr_opp_qos.default_value = 50;
+ atomic_set(&ddr_opp_qos.target_value, 50);
+ }
+
+ ret = register_prcmu_qos_misc(&ape_opp_qos, &prcmu_qos_ape_power_fops);
+ if (ret < 0) {
+ pr_err("prcmu ape qos: setup failed\n");
+ return ret;
+ }
+
+ ret = register_prcmu_qos_misc(&ddr_opp_qos, &prcmu_qos_ddr_power_fops);
+ if (ret < 0) {
+ pr_err("prcmu ddr qos: setup failed\n");
+ return ret;
+ }
+
+ ret = register_prcmu_qos_misc(&arm_opp_qos, &prcmu_qos_arm_power_fops);
+ if (ret < 0) {
+ pr_err("prcmu arm qos: setup failed\n");
+ return ret;
+ }
+
+ prcmu_qos_add_requirement(PRCMU_QOS_DDR_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ cpufreq_requirement_set = PRCMU_QOS_DEFAULT_VALUE;
+ cpufreq_requirement_queued = PRCMU_QOS_DEFAULT_VALUE;
+
+ cpufreq_register_notifier(&qos_delayed_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ return ret;
+}
+
+late_initcall(prcmu_qos_power_init);
diff --git a/arch/arm/mach-ux500/pm/runtime.c b/arch/arm/mach-ux500/pm/runtime.c
new file mode 100644
index 00000000000..8608c43479e
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/runtime.c
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Rabin Vincent <rabin.vincent@stericsson> for ST-Ericsson
+ *
+ * Based on:
+ * Runtime PM support code for SuperH Mobile ARM
+ * Copyright (C) 2009-2010 Magnus Damm
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/amba/bus.h>
+#include <linux/regulator/dbx500-prcmu.h>
+#include <linux/clk.h>
+#include <linux/gfp.h>
+#include <plat/pincfg.h>
+
+#include "../pins.h"
+
+#ifdef CONFIG_PM_RUNTIME
+#define BIT_ONCE 0
+#define BIT_ACTIVE 1
+#define BIT_ENABLED 2
+
+struct pm_runtime_data {
+ unsigned long flags;
+ struct ux500_regulator *regulator;
+ struct ux500_pins *pins;
+};
+
+static void __devres_release(struct device *dev, void *res)
+{
+ struct pm_runtime_data *prd = res;
+
+ dev_dbg(dev, "__devres_release()\n");
+
+ if (test_bit(BIT_ENABLED, &prd->flags)) {
+ if (prd->pins)
+ ux500_pins_disable(prd->pins);
+ if (prd->regulator)
+ ux500_regulator_atomic_disable(prd->regulator);
+ }
+
+ if (test_bit(BIT_ACTIVE, &prd->flags)) {
+ if (prd->pins)
+ ux500_pins_put(prd->pins);
+ if (prd->regulator)
+ ux500_regulator_put(prd->regulator);
+ }
+}
+
+static struct pm_runtime_data *__to_prd(struct device *dev)
+{
+ return devres_find(dev, __devres_release, NULL, NULL);
+}
+
+static void platform_pm_runtime_init(struct device *dev,
+ struct pm_runtime_data *prd)
+{
+ prd->pins = ux500_pins_get(dev_name(dev));
+
+ prd->regulator = ux500_regulator_get(dev);
+ if (IS_ERR(prd->regulator))
+ prd->regulator = NULL;
+
+ if (prd->pins || prd->regulator) {
+ dev_info(dev, "managed by runtime pm: %s%s\n",
+ prd->pins ? "pins " : "",
+ prd->regulator ? "regulator " : "");
+
+ set_bit(BIT_ACTIVE, &prd->flags);
+ }
+}
+
+static void platform_pm_runtime_bug(struct device *dev,
+ struct pm_runtime_data *prd)
+{
+ if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags))
+ dev_err(dev, "runtime pm suspend before resume\n");
+}
+
+static void platform_pm_runtime_used(struct device *dev,
+ struct pm_runtime_data *prd)
+{
+ if (prd)
+ set_bit(BIT_ONCE, &prd->flags);
+}
+
+static int ux500_pd_runtime_idle(struct device *dev)
+{
+ return pm_runtime_suspend(dev);
+}
+
+static void ux500_pd_disable(struct pm_runtime_data *prd)
+{
+ if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
+
+ if (prd->pins)
+ ux500_pins_disable(prd->pins);
+
+ if (prd->regulator)
+ ux500_regulator_atomic_disable(prd->regulator);
+
+ clear_bit(BIT_ENABLED, &prd->flags);
+ }
+}
+
+static int ux500_pd_runtime_suspend(struct device *dev)
+{
+ int ret;
+ struct pm_runtime_data *prd = __to_prd(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ platform_pm_runtime_bug(dev, prd);
+
+ ret = pm_generic_runtime_suspend(dev);
+ if (ret)
+ return ret;
+
+ ux500_pd_disable(prd);
+
+ return 0;
+}
+
+static void ux500_pd_enable(struct pm_runtime_data *prd)
+{
+ if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
+ if (prd->pins)
+ ux500_pins_enable(prd->pins);
+
+ if (prd->regulator)
+ ux500_regulator_atomic_enable(prd->regulator);
+
+ set_bit(BIT_ENABLED, &prd->flags);
+ }
+}
+
+static int ux500_pd_runtime_resume(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ platform_pm_runtime_used(dev, prd);
+ ux500_pd_enable(prd);
+
+ return pm_generic_runtime_resume(dev);
+}
+
+static int ux500_pd_suspend_noirq(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /* Only handle devices that use runtime pm */
+ if (!prd || !test_bit(BIT_ONCE, &prd->flags))
+ return 0;
+
+ /* Already is runtime suspended? Nothing to do. */
+ if (pm_runtime_status_suspended(dev))
+ return 0;
+
+ /*
+ * We get here only if the device was not runtime suspended for some
+ * reason. We still need to do the power save stuff when going into
+ * suspend, so force it here.
+ */
+ return ux500_pd_runtime_suspend(dev);
+}
+
+static int ux500_pd_resume_noirq(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /* Only handle devices that use runtime pm */
+ if (!prd || !test_bit(BIT_ONCE, &prd->flags))
+ return 0;
+
+ /*
+ * Already was runtime suspended? No need to resume here, runtime
+ * resume will take care of it.
+ */
+ if (pm_runtime_status_suspended(dev))
+ return 0;
+
+ /*
+ * We get here only if the device was not runtime suspended,
+ * but we forced it down in suspend_noirq above. Bring it
+ * up since pm-runtime thinks it is not suspended.
+ */
+ return ux500_pd_runtime_resume(dev);
+}
+#ifdef CONFIG_UX500_SUSPEND
+static int ux500_pd_amba_suspend_noirq(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+ int (*callback)(struct device *) = NULL;
+ int ret = 0;
+ bool is_suspended = pm_runtime_status_suspended(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do not bypass AMBA bus pm functions by calling generic
+ * pm directly. A future fix could be to implement a
+ * "pm_bus_generic_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->suspend_noirq;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_suspend_noirq(dev);
+
+ if (!ret && !is_suspended)
+ ux500_pd_disable(prd);
+
+ return ret;
+}
+
+static int ux500_pd_amba_resume_noirq(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+ int (*callback)(struct device *) = NULL;
+ int ret = 0;
+ bool is_suspended = pm_runtime_status_suspended(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do not bypass AMBA bus pm functions by calling generic
+ * pm directly. A future fix could be to implement a
+ * "pm_bus_generic_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->resume_noirq;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_resume_noirq(dev);
+
+ if (!ret && !is_suspended)
+ ux500_pd_enable(prd);
+
+ return ret;
+}
+#else
+static int ux500_pd_amba_suspend_noirq(struct device *dev)
+{
+ return 0;
+}
+static int ux500_pd_amba_resume_noirq(struct device *dev)
+{
+ return 0;
+}
+#endif
+static int ux500_pd_amba_runtime_suspend(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+ int (*callback)(struct device *) = NULL;
+ int ret;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do this first, to make sure pins is not in undefined state after
+ * drivers has run their runtime suspend. This also means that drivers
+ * are not able to use their pins/regulators during runtime suspend.
+ */
+ ux500_pd_disable(prd);
+
+ /*
+ * Do not bypass AMBA bus pm functions by calling generic
+ * pm directly. A future fix could be to implement a
+ * "pm_bus_generic_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->runtime_suspend;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_runtime_suspend(dev);
+
+ if (ret)
+ ux500_pd_enable(prd);
+
+ return ret;
+}
+
+static int ux500_pd_amba_runtime_resume(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+ int (*callback)(struct device *) = NULL;
+ int ret;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do not bypass AMBA bus pm functions by calling generic
+ * pm directly. A future fix could be to implement a
+ * "pm_bus_generic_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->runtime_resume;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_runtime_resume(dev);
+
+ /*
+ * Restore pins/regulator after drivers has runtime resumed, due
+ * to that we must not have pins in undefined state. This also means
+ * that drivers are not able to use their pins/regulators during
+ * runtime resume.
+ */
+ if (!ret)
+ ux500_pd_enable(prd);
+
+ return ret;
+}
+
+static int ux500_pd_amba_runtime_idle(struct device *dev)
+{
+ int (*callback)(struct device *) = NULL;
+ int ret;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do not bypass AMBA bus runtime functions by calling generic runtime
+ * directly. A future fix could be to implement a
+ * "pm_bus_generic_runtime_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->runtime_idle;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_runtime_idle(dev);
+
+ return ret;
+}
+
+static int ux500_pd_bus_notify(struct notifier_block *nb,
+ unsigned long action,
+ void *data,
+ bool enable)
+{
+ struct device *dev = data;
+ struct pm_runtime_data *prd;
+
+ dev_dbg(dev, "%s() %ld !\n", __func__, action);
+
+ if (action == BUS_NOTIFY_BIND_DRIVER) {
+ prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
+ if (prd) {
+ devres_add(dev, prd);
+ platform_pm_runtime_init(dev, prd);
+ if (enable)
+ ux500_pd_enable(prd);
+ } else
+ dev_err(dev, "unable to alloc memory for runtime pm\n");
+ }
+
+ return 0;
+}
+
+static int ux500_pd_plat_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ return ux500_pd_bus_notify(nb, action, data, false);
+}
+
+static int ux500_pd_amba_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ return ux500_pd_bus_notify(nb, action, data, true);
+}
+
+#else /* CONFIG_PM_RUNTIME */
+
+#define ux500_pd_suspend_noirq NULL
+#define ux500_pd_resume_noirq NULL
+#define ux500_pd_runtime_idle NULL
+#define ux500_pd_runtime_suspend NULL
+#define ux500_pd_runtime_resume NULL
+
+static int ux500_pd_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct ux500_regulator *regulator = NULL;
+ struct ux500_pins *pins = NULL;
+ struct device *dev = data;
+ const char *onoff = NULL;
+
+ dev_dbg(dev, "%s() %ld !\n", __func__, action);
+
+ switch (action) {
+ case BUS_NOTIFY_BIND_DRIVER:
+ pins = ux500_pins_get(dev_name(dev));
+ if (pins) {
+ ux500_pins_enable(pins);
+ ux500_pins_put(pins);
+ }
+
+ regulator = ux500_regulator_get(dev);
+ if (IS_ERR(regulator))
+ regulator = NULL;
+ else {
+ ux500_regulator_atomic_enable(regulator);
+ ux500_regulator_put(regulator);
+ }
+
+ onoff = "on";
+ break;
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ pins = ux500_pins_get(dev_name(dev));
+ if (pins) {
+ ux500_pins_disable(pins);
+ ux500_pins_put(pins);
+ }
+
+ regulator = ux500_regulator_get(dev);
+ if (IS_ERR(regulator))
+ regulator = NULL;
+ else {
+ ux500_regulator_atomic_disable(regulator);
+ ux500_regulator_put(regulator);
+ }
+
+ onoff = "off";
+ break;
+ }
+
+ if (pins || regulator) {
+ dev_info(dev, "runtime pm disabled, forced %s: %s%s\n",
+ onoff,
+ pins ? "pins " : "",
+ regulator ? "regulator " : "");
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_PM_RUNTIME */
+
+struct dev_pm_domain ux500_amba_dev_power_domain = {
+ .ops = {
+ .suspend = amba_pm_suspend,
+ .resume = amba_pm_resume,
+ .freeze = amba_pm_freeze,
+ .thaw = amba_pm_thaw,
+ .poweroff = amba_pm_poweroff,
+ .restore = amba_pm_restore,
+ SET_RUNTIME_PM_OPS(ux500_pd_amba_runtime_suspend,
+ ux500_pd_amba_runtime_resume,
+ ux500_pd_amba_runtime_idle)
+ .suspend_noirq = ux500_pd_amba_suspend_noirq,
+ .resume_noirq = ux500_pd_amba_resume_noirq,
+ },
+};
+
+struct dev_pm_domain ux500_dev_power_domain = {
+ .ops = {
+ SET_RUNTIME_PM_OPS(ux500_pd_runtime_suspend,
+ ux500_pd_runtime_resume,
+ ux500_pd_runtime_idle)
+ USE_PLATFORM_PM_SLEEP_OPS
+ .suspend_noirq = ux500_pd_suspend_noirq,
+ .resume_noirq = ux500_pd_resume_noirq,
+ },
+};
+
+static struct notifier_block ux500_pd_platform_notifier = {
+ .notifier_call = ux500_pd_plat_bus_notify,
+};
+
+static struct notifier_block ux500_pd_amba_notifier = {
+ .notifier_call = ux500_pd_amba_bus_notify,
+};
+
+static int __init ux500_pm_runtime_platform_init(void)
+{
+ bus_register_notifier(&platform_bus_type, &ux500_pd_platform_notifier);
+ return 0;
+}
+core_initcall(ux500_pm_runtime_platform_init);
+
+/*
+ * The amba bus itself gets registered in a core_initcall, so we can't use
+ * that.
+ */
+static int __init ux500_pm_runtime_amba_init(void)
+{
+ bus_register_notifier(&amba_bustype, &ux500_pd_amba_notifier);
+ return 0;
+}
+arch_initcall(ux500_pm_runtime_amba_init);
diff --git a/arch/arm/mach-ux500/pm/scu.h b/arch/arm/mach-ux500/pm/scu.h
new file mode 100644
index 00000000000..a09e86a9d3c
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/scu.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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.
+ */
+
+#ifndef __ASMARM_ARCH_SCU_H
+#define __ASMARM_ARCH_SCU_H
+
+#include <mach/hardware.h>
+
+#define SCU_BASE U8500_SCU_BASE
+/*
+ * * SCU registers
+ * */
+#define SCU_CTRL 0x00
+#define SCU_CONFIG 0x04
+#define SCU_CPU_STATUS 0x08
+#define SCU_INVALIDATE 0x0c
+#define SCU_FPGA_REVISION 0x10
+
+#endif
diff --git a/arch/arm/mach-ux500/pm/suspend.c b/arch/arm/mach-ux500/pm/suspend.c
new file mode 100644
index 00000000000..df527964182
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/suspend.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com>,
+ * Sundar Iyer for ST-Ericsson.
+ */
+
+#include <linux/suspend.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab8500-debug.h>
+#include <linux/regulator/dbx500-prcmu.h>
+
+#include <mach/context.h>
+#include <mach/pm.h>
+#include <mach/id.h>
+
+#include "suspend_dbg.h"
+
+static void (*pins_suspend_force)(void);
+static void (*pins_suspend_force_mux)(void);
+
+static suspend_state_t suspend_state = PM_SUSPEND_ON;
+
+void suspend_set_pins_force_fn(void (*force)(void), void (*force_mux)(void))
+{
+ pins_suspend_force = force;
+ pins_suspend_force_mux = force_mux;
+}
+
+static atomic_t block_sleep = ATOMIC_INIT(0);
+
+void suspend_block_sleep(void)
+{
+ atomic_inc(&block_sleep);
+}
+
+void suspend_unblock_sleep(void)
+{
+ atomic_dec(&block_sleep);
+}
+
+static bool sleep_is_blocked(void)
+{
+ return (atomic_read(&block_sleep) != 0);
+}
+
+static int suspend(bool do_deepsleep)
+{
+ bool pins_force = pins_suspend_force_mux && pins_suspend_force;
+ int ret = 0;
+
+ if (sleep_is_blocked()) {
+ pr_info("suspend/resume: interrupted by modem.\n");
+ return -EBUSY;
+ }
+
+ nmk_gpio_clocks_enable();
+
+ ux500_suspend_dbg_add_wake_on_uart();
+
+ nmk_gpio_wakeups_suspend();
+
+ /* configure the prcm for a sleep wakeup */
+ if (cpu_is_u9500())
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ABB) | PRCMU_WAKEUP(HSI0));
+ else
+#if defined(CONFIG_RTC_DRV_PL031)
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ABB) | PRCMU_WAKEUP(RTC));
+#else
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ABB));
+#endif
+
+ context_vape_save();
+
+ context_fsmc_save();
+
+ if (pins_force) {
+ /*
+ * Save GPIO settings before applying power save
+ * settings
+ */
+ context_gpio_save();
+
+ /* Apply GPIO power save mux settings */
+ context_gpio_mux_safe_switch(true);
+ pins_suspend_force_mux();
+ context_gpio_mux_safe_switch(false);
+
+ /* Apply GPIO power save settings */
+ pins_suspend_force();
+ }
+
+ ux500_pm_gic_decouple();
+
+ if (ux500_pm_gic_pending_interrupt()) {
+ pr_info("suspend/resume: pending interrupt\n");
+
+ /* Recouple GIC with the interrupt bus */
+ ux500_pm_gic_recouple();
+ ret = -EBUSY;
+
+ goto exit;
+ }
+ ux500_pm_prcmu_set_ioforce(true);
+
+ if (do_deepsleep) {
+ context_varm_save_common();
+ context_varm_save_core();
+ context_gic_dist_disable_unneeded_irqs();
+ context_save_cpu_registers();
+
+ /*
+ * Due to we have only 100us between requesting a powerstate
+ * and wfi, we clean the cache before as well to assure the
+ * final cache clean before wfi has as little as possible to
+ * do.
+ */
+ context_clean_l1_cache_all();
+
+ (void) prcmu_set_power_state(PRCMU_AP_DEEP_SLEEP,
+ false, false);
+ context_save_to_sram_and_wfi(true);
+
+ context_restore_cpu_registers();
+ context_varm_restore_core();
+ context_varm_restore_common();
+
+ } else {
+
+ context_clean_l1_cache_all();
+ (void) prcmu_set_power_state(APEXECUTE_TO_APSLEEP,
+ false, false);
+ dsb();
+ __asm__ __volatile__("wfi\n\t" : : : "memory");
+ }
+
+ context_vape_restore();
+
+ context_fsmc_restore();
+
+ /* If GPIO woke us up then save the pins that caused the wake up */
+ ux500_pm_gpio_save_wake_up_status();
+
+ ux500_suspend_dbg_sleep_status(do_deepsleep);
+
+ /* APE was turned off, restore IO ring */
+ ux500_pm_prcmu_set_ioforce(false);
+
+exit:
+ if (pins_force) {
+ /* Restore gpio settings */
+ context_gpio_mux_safe_switch(true);
+ context_gpio_restore_mux();
+ context_gpio_mux_safe_switch(false);
+ context_gpio_restore();
+ }
+
+ /* This is what cpuidle wants */
+ if (cpu_is_u9500())
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
+ PRCMU_WAKEUP(ABB) | PRCMU_WAKEUP(HSI0));
+ else
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
+ PRCMU_WAKEUP(ABB));
+
+ nmk_gpio_wakeups_resume();
+
+ ux500_suspend_dbg_remove_wake_on_uart();
+
+ nmk_gpio_clocks_disable();
+
+ return ret;
+}
+
+static int ux500_suspend_enter(suspend_state_t state)
+{
+ if (ux500_suspend_enabled()) {
+ if (ux500_suspend_deepsleep_enabled() &&
+ state == PM_SUSPEND_MEM)
+ return suspend(true);
+ if (ux500_suspend_sleep_enabled())
+ return suspend(false);
+ }
+
+ ux500_suspend_dbg_add_wake_on_uart();
+ /*
+ * Set IOFORCE in order to wake on GPIO the same way
+ * as in deeper sleep.
+ * (U5500 is not ready for IOFORCE)
+ */
+ if (!cpu_is_u5500())
+ ux500_pm_prcmu_set_ioforce(true);
+
+ dsb();
+ __asm__ __volatile__("wfi\n\t" : : : "memory");
+
+ if (!cpu_is_u5500())
+ ux500_pm_prcmu_set_ioforce(false);
+ ux500_suspend_dbg_remove_wake_on_uart();
+
+ return 0;
+}
+
+static int ux500_suspend_valid(suspend_state_t state)
+{
+ return state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY;
+}
+
+static int ux500_suspend_prepare(void)
+{
+ int ret;
+
+ ret = regulator_suspend_prepare(suspend_state);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ux500_suspend_prepare_late(void)
+{
+ /* ESRAM to retention instead of OFF until ROM is fixed */
+ (void) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+
+ ab8500_regulator_debug_force();
+ ux500_regulator_suspend_debug();
+ return 0;
+}
+
+static void ux500_suspend_wake(void)
+{
+ ux500_regulator_resume_debug();
+ ab8500_regulator_debug_restore();
+ (void) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+}
+
+static void ux500_suspend_finish(void)
+{
+ (void)regulator_suspend_finish();
+}
+
+static int ux500_suspend_begin(suspend_state_t state)
+{
+ (void) prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "suspend", 125);
+ suspend_state = state;
+ return ux500_suspend_dbg_begin(state);
+}
+
+static void ux500_suspend_end(void)
+{
+ (void) prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "suspend", 25);
+ suspend_state = PM_SUSPEND_ON;
+}
+
+static struct platform_suspend_ops ux500_suspend_ops = {
+ .enter = ux500_suspend_enter,
+ .valid = ux500_suspend_valid,
+ .prepare = ux500_suspend_prepare,
+ .prepare_late = ux500_suspend_prepare_late,
+ .wake = ux500_suspend_wake,
+ .finish = ux500_suspend_finish,
+ .begin = ux500_suspend_begin,
+ .end = ux500_suspend_end,
+};
+
+static __init int ux500_suspend_init(void)
+{
+ ux500_suspend_dbg_init();
+ prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "suspend", 25);
+ suspend_set_ops(&ux500_suspend_ops);
+ return 0;
+}
+device_initcall(ux500_suspend_init);
diff --git a/arch/arm/mach-ux500/pm/suspend_dbg.c b/arch/arm/mach-ux500/pm/suspend_dbg.c
new file mode 100644
index 00000000000..1b7d871ba52
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/suspend_dbg.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/suspend.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/pm.h>
+
+#ifdef CONFIG_UX500_SUSPEND_STANDBY
+static u32 sleep_enabled = 1;
+#else
+static u32 sleep_enabled;
+#endif
+
+#ifdef CONFIG_UX500_SUSPEND_MEM
+static u32 deepsleep_enabled = 1;
+#else
+static u32 deepsleep_enabled;
+#endif
+
+static u32 suspend_enabled = 1;
+
+static u32 deepsleeps_done;
+static u32 deepsleeps_failed;
+static u32 sleeps_done;
+static u32 sleeps_failed;
+static u32 suspend_count;
+
+#ifdef CONFIG_UX500_SUSPEND_DBG_WAKE_ON_UART
+void ux500_suspend_dbg_add_wake_on_uart(void)
+{
+ irq_set_irq_wake(GPIO_TO_IRQ(ux500_console_uart_gpio_pin), 1);
+ irq_set_irq_type(GPIO_TO_IRQ(ux500_console_uart_gpio_pin),
+ IRQ_TYPE_EDGE_BOTH);
+}
+
+void ux500_suspend_dbg_remove_wake_on_uart(void)
+{
+ irq_set_irq_wake(GPIO_TO_IRQ(ux500_console_uart_gpio_pin), 0);
+}
+#endif
+
+bool ux500_suspend_enabled(void)
+{
+ return suspend_enabled != 0;
+}
+
+bool ux500_suspend_sleep_enabled(void)
+{
+ return sleep_enabled != 0;
+}
+
+bool ux500_suspend_deepsleep_enabled(void)
+{
+ return deepsleep_enabled != 0;
+}
+
+void ux500_suspend_dbg_sleep_status(bool is_deepsleep)
+{
+ enum prcmu_power_status prcmu_status;
+
+ prcmu_status = prcmu_get_power_state_result();
+
+ if (is_deepsleep) {
+ pr_info("Returning from ApDeepSleep. PRCMU ret: 0x%x - %s\n",
+ prcmu_status,
+ prcmu_status == PRCMU_DEEP_SLEEP_OK ?
+ "Success" : "Fail!");
+ if (prcmu_status == PRCMU_DEEP_SLEEP_OK)
+ deepsleeps_done++;
+ else
+ deepsleeps_failed++;
+ } else {
+ pr_info("Returning from ApSleep. PRCMU ret: 0x%x - %s\n",
+ prcmu_status,
+ prcmu_status == PRCMU_SLEEP_OK ? "Success" : "Fail!");
+ if (prcmu_status == PRCMU_SLEEP_OK)
+ sleeps_done++;
+ else
+ sleeps_failed++;
+ }
+}
+
+int ux500_suspend_dbg_begin(suspend_state_t state)
+{
+ suspend_count++;
+ return 0;
+}
+
+void ux500_suspend_dbg_init(void)
+{
+ struct dentry *suspend_dir;
+ struct dentry *file;
+
+ suspend_dir = debugfs_create_dir("suspend", NULL);
+ if (IS_ERR_OR_NULL(suspend_dir))
+ return;
+
+ file = debugfs_create_bool("sleep", S_IWUGO | S_IRUGO,
+ suspend_dir,
+ &sleep_enabled);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_bool("deepsleep", S_IWUGO | S_IRUGO,
+ suspend_dir,
+ &deepsleep_enabled);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_bool("enable", S_IWUGO | S_IRUGO,
+ suspend_dir,
+ &suspend_enabled);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_u32("count", S_IRUGO,
+ suspend_dir,
+ &suspend_count);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_u32("sleep_count", S_IRUGO,
+ suspend_dir,
+ &sleeps_done);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_u32("deepsleep_count", S_IRUGO,
+ suspend_dir,
+ &deepsleeps_done);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+
+ file = debugfs_create_u32("sleep_failed", S_IRUGO,
+ suspend_dir,
+ &sleeps_failed);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_u32("deepsleep_failed", S_IRUGO,
+ suspend_dir,
+ &deepsleeps_failed);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ return;
+error:
+ if (!IS_ERR_OR_NULL(suspend_dir))
+ debugfs_remove_recursive(suspend_dir);
+}
diff --git a/arch/arm/mach-ux500/pm/suspend_dbg.h b/arch/arm/mach-ux500/pm/suspend_dbg.h
new file mode 100644
index 00000000000..29bfec7e269
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/suspend_dbg.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ */
+
+#ifndef UX500_SUSPEND_DBG_H
+#define UX500_SUSPEND_DBG_H
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+
+#ifdef CONFIG_UX500_SUSPEND_DBG_WAKE_ON_UART
+void ux500_suspend_dbg_add_wake_on_uart(void);
+void ux500_suspend_dbg_remove_wake_on_uart(void);
+#else
+static inline void ux500_suspend_dbg_add_wake_on_uart(void) { }
+static inline void ux500_suspend_dbg_remove_wake_on_uart(void) { }
+#endif
+
+#ifdef CONFIG_UX500_SUSPEND_DBG
+bool ux500_suspend_enabled(void);
+bool ux500_suspend_sleep_enabled(void);
+bool ux500_suspend_deepsleep_enabled(void);
+void ux500_suspend_dbg_sleep_status(bool is_deepsleep);
+void ux500_suspend_dbg_init(void);
+int ux500_suspend_dbg_begin(suspend_state_t state);
+
+#else
+static inline bool ux500_suspend_enabled(void)
+{
+ return true;
+}
+static inline bool ux500_suspend_sleep_enabled(void)
+{
+#ifdef CONFIG_UX500_SUSPEND_STANDBY
+ return true;
+#else
+ return false;
+#endif
+}
+static inline bool ux500_suspend_deepsleep_enabled(void)
+{
+#ifdef CONFIG_UX500_SUSPEND_MEM
+ return true;
+#else
+ return false;
+#endif
+}
+static inline void ux500_suspend_dbg_sleep_status(bool is_deepsleep) { }
+static inline void ux500_suspend_dbg_init(void) { }
+
+static inline int ux500_suspend_dbg_begin(suspend_state_t state)
+{
+ return 0;
+}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-ux500/pm/timer.c b/arch/arm/mach-ux500/pm/timer.c
new file mode 100644
index 00000000000..fc81c2c15ad
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/timer.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * The RTC timer block is a ST Microelectronics variant of ARM PL031.
+ * Clockwatch part is the same as PL031, while the timer part is only
+ * present on the ST Microelectronics variant.
+ * Here only the timer part is used.
+ *
+ * The timer part is quite troublesome to program correctly. Lots
+ * of long delays must be there in order to secure that you actually get what
+ * you wrote.
+ *
+ * In other words, this timer is and should only used from cpuidle during
+ * special conditions when the surroundings are know in order to be able
+ * to remove the number of delays.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <linux/delay.h>
+
+#include <asm/errno.h>
+
+#include <mach/hardware.h>
+
+#define RTC_IMSC 0x10
+#define RTC_MIS 0x18
+#define RTC_ICR 0x1C
+#define RTC_TDR 0x20
+#define RTC_TLR1 0x24
+#define RTC_TCR 0x28
+
+#define RTC_TLR2 0x2C
+#define RTC_TPR1 0x3C
+
+#define RTC_TCR_RTTOS (1 << 0)
+#define RTC_TCR_RTTEN (1 << 1)
+#define RTC_TCR_RTTSS (1 << 2)
+
+#define RTC_IMSC_TIMSC (1 << 1)
+#define RTC_ICR_TIC (1 << 1)
+#define RTC_MIS_RTCTMIS (1 << 1)
+
+#define RTC_TCR_RTTPS_2 (1 << 4)
+#define RTC_TCR_RTTPS_3 (2 << 4)
+#define RTC_TCR_RTTPS_4 (3 << 4)
+#define RTC_TCR_RTTPS_5 (4 << 4)
+#define RTC_TCR_RTTPS_6 (5 << 4)
+#define RTC_TCR_RTTPS_7 (6 << 4)
+#define RTC_TCR_RTTPS_8 (7 << 4)
+
+#define WRITE_DELAY 130 /* 4 cycles plus margin */
+
+/*
+ * Count down measure point. It just have to be high to differ
+ * from scheduled values.
+ */
+#define MEASURE_VAL 0xffffffff
+
+/* Just a value bigger than any reason able scheduled timeout. */
+#define MEASURE_VAL_LIMIT 0xf0000000
+
+#define TICKS_TO_NS(x) ((s64)x * 30518)
+#define US_TO_TICKS(x) ((u32)((1000 * x) / 30518))
+
+static void __iomem *rtc_base;
+static bool measure_latency;
+
+#ifdef CONFIG_UX500_CPUIDLE_DEBUG
+
+/*
+ * The plan here is to be able to measure the ApSleep/ApDeepSleep exit latency
+ * by having a know timer pattern.
+ * The first entry in the pattern, LR1, is the value that the scheduler
+ * wants us to sleep. The second pattern in a high value, too large to be
+ * scheduled, so we can differ between a running scheduled value and a
+ * time measure value.
+ * When a RTT interrupt has occured, the block will automatically start
+ * to execute the measure value in LR2 and when the ARM is awake, it reads
+ * how far the RTT has decreased the value loaded from LR2 and from that
+ * calculate how long time it took to wake up.
+ */
+ktime_t u8500_rtc_exit_latency_get(void)
+{
+ u32 ticks;
+
+ if (measure_latency) {
+ ticks = MEASURE_VAL - readl(rtc_base + RTC_TDR);
+
+ /*
+ * Check if we are actually counting on a LR2 value.
+ * If not we have woken on another interrupt.
+ */
+ if (ticks < MEASURE_VAL_LIMIT) {
+ /* convert 32 kHz ticks to ns */
+ return ktime_set(0, TICKS_TO_NS(ticks));
+ }
+ }
+ return ktime_set(0, 0);
+}
+
+static void measure_latency_start(void)
+{
+ udelay(WRITE_DELAY);
+ /*
+ * Disable RTT and clean self-start due to we want to restart,
+ * not continue from current pattern. (See below)
+ */
+ writel(0, rtc_base + RTC_TCR);
+ udelay(WRITE_DELAY);
+
+ /*
+ * Program LR2 (load register two) to maximum value to ease
+ * identification of timer interrupt vs other.
+ */
+ writel(MEASURE_VAL, rtc_base + RTC_TLR2);
+ /*
+ * Set Load Register execution pattern, bit clear
+ * means pick LR1, bit set means LR2
+ * 0xfe, binary 11111110 means first do LR1 then do
+ * LR2 seven times
+ */
+ writel(0xfe, rtc_base + RTC_TPR1);
+
+ udelay(WRITE_DELAY);
+
+ /*
+ * Enable self-start, plus a pattern of eight.
+ */
+ writel(RTC_TCR_RTTSS | RTC_TCR_RTTPS_8,
+ rtc_base + RTC_TCR);
+ udelay(WRITE_DELAY);
+}
+
+void ux500_rtcrtt_measure_latency(bool enable)
+{
+ if (enable) {
+ measure_latency_start();
+ } else {
+ writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
+ writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
+ writel(RTC_IMSC_TIMSC, rtc_base + RTC_IMSC);
+ }
+ measure_latency = enable;
+}
+#else
+static inline void measure_latency_start(void) { }
+static inline void ux500_rtcrtt_measure_latency(bool enable)
+{
+ writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
+ writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
+ writel(RTC_IMSC_TIMSC, rtc_base + RTC_IMSC);
+}
+#endif
+
+void ux500_rtcrtt_off(void)
+{
+ if (measure_latency) {
+ measure_latency_start();
+ } else {
+ /* Disable, self start and oneshot mode */
+ writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
+
+ /* Clear eventual interrupts */
+ if (readl(rtc_base + RTC_MIS) & RTC_MIS_RTCTMIS)
+ writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
+ }
+}
+
+void ux500_rtcrtt_next(u32 time_us)
+{
+ writel(US_TO_TICKS(time_us), rtc_base + RTC_TLR1);
+}
+
+static int __init ux500_rtcrtt_init(void)
+{
+ if (cpu_is_u8500() || cpu_is_u9540()) {
+ rtc_base = __io_address(U8500_RTC_BASE);
+ } else if (cpu_is_u5500()) {
+ rtc_base = __io_address(U5500_RTC_BASE);
+ } else {
+ pr_err("timer-rtt: Unknown DB Asic!\n");
+ return -EINVAL;
+ }
+ ux500_rtcrtt_measure_latency(false);
+ return 0;
+}
+subsys_initcall(ux500_rtcrtt_init);
diff --git a/arch/arm/mach-ux500/pm/usecase_gov.c b/arch/arm/mach-ux500/pm/usecase_gov.c
new file mode 100644
index 00000000000..1fd4fbb9830
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/usecase_gov.c
@@ -0,0 +1,962 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Alexandre Torgue <alexandre.torgue@stericsson.com> for ST-Ericsson
+ * Author: Vincent Guittot <vincent.guittot@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/io.h>
+#include <linux/earlysuspend.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/tick.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/kernel_stat.h>
+#include <linux/ktime.h>
+#include <linux/cpufreq.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/cpufreq-dbx500.h>
+
+#include "../../../../drivers/cpuidle/cpuidle-dbx500.h"
+
+
+#define CPULOAD_MEAS_DELAY 3000 /* 3 secondes of delta */
+
+/* debug */
+static unsigned long debug;
+
+#define hp_printk \
+ if (debug) \
+ printk \
+
+enum ux500_uc {
+ UX500_UC_NORMAL = 0,
+ UX500_UC_AUTO, /* Add use case below this. */
+ UX500_UC_VC,
+ UX500_UC_LPA,
+ UX500_UC_USER, /* Add use case above this. */
+ UX500_UC_MAX,
+};
+
+/* cpu load monitor struct */
+#define LOAD_MONITOR 4
+struct hotplug_cpu_info {
+ cputime64_t prev_cpu_wall;
+ cputime64_t prev_cpu_idle;
+ cputime64_t prev_cpu_io;
+ unsigned int load[LOAD_MONITOR];
+ unsigned int idx;
+};
+
+static DEFINE_PER_CPU(struct hotplug_cpu_info, hotplug_info);
+
+/* Auto trigger criteria */
+/* loadavg threshold */
+static unsigned long lower_threshold = 175;
+static unsigned long upper_threshold = 450;
+/* load balancing */
+static unsigned long max_unbalance = 210;
+/* trend load */
+static unsigned long trend_unbalance = 40;
+static unsigned long min_trend = 5;
+/* instant load */
+static unsigned long max_instant = 85;
+
+/* Number of interrupts per second before exiting auto mode */
+static u32 exit_irq_per_s = 1000;
+static u64 old_num_irqs;
+
+static DEFINE_MUTEX(usecase_mutex);
+static DEFINE_MUTEX(state_mutex);
+static bool user_config_updated;
+static enum ux500_uc current_uc = UX500_UC_MAX;
+static bool is_work_scheduled;
+static bool is_early_suspend;
+static bool uc_master_enable = true;
+
+static unsigned int cpuidle_deepest_state;
+
+struct usecase_config {
+ char *name;
+ /* Minimum required ARM OPP. if no requirement set 25 */
+ unsigned int min_arm_opp;
+ unsigned int max_arm_opp;
+ unsigned long cpuidle_multiplier;
+ bool second_cpu_online;
+ bool l2_prefetch_en;
+ bool enable;
+ unsigned int forced_state; /* Forced cpu idle state. */
+ bool vc_override; /* QOS override for voice-call. */
+};
+
+static struct usecase_config usecase_conf[UX500_UC_MAX] = {
+ [UX500_UC_NORMAL] = {
+ .name = "normal",
+ .min_arm_opp = 25,
+ .cpuidle_multiplier = 1024,
+ .second_cpu_online = true,
+ .l2_prefetch_en = true,
+ .enable = true,
+ .forced_state = 0,
+ .vc_override = false,
+ },
+ [UX500_UC_AUTO] = {
+ .name = "auto",
+ .min_arm_opp = 25,
+ .cpuidle_multiplier = 0,
+ .second_cpu_online = false,
+ .l2_prefetch_en = true,
+ .enable = false,
+ .forced_state = 0,
+ .vc_override = false,
+ },
+ [UX500_UC_VC] = {
+ .name = "voice-call",
+ .min_arm_opp = 50,
+ .cpuidle_multiplier = 0,
+ .second_cpu_online = true,
+ .l2_prefetch_en = false,
+ .enable = false,
+ .forced_state = 0,
+ .vc_override = true,
+ },
+ [UX500_UC_LPA] = {
+ .name = "low-power-audio",
+ .min_arm_opp = 50,
+ .cpuidle_multiplier = 0,
+ .second_cpu_online = false,
+ .l2_prefetch_en = false,
+ .enable = false,
+ .forced_state = 0, /* Updated dynamically */
+ .vc_override = false,
+ },
+};
+
+/* daemon */
+static struct delayed_work work_usecase;
+static struct early_suspend usecase_early_suspend;
+
+/* calculate loadavg */
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+
+extern int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max);
+extern int cpuidle_set_multiplier(unsigned int value);
+extern int cpuidle_force_state(unsigned int state);
+
+static unsigned long determine_loadavg(void)
+{
+ unsigned long avg = 0;
+ unsigned long avnrun[3];
+
+ get_avenrun(avnrun, FIXED_1 / 200, 0);
+ avg += (LOAD_INT(avnrun[0]) * 100) + (LOAD_FRAC(avnrun[0]) % 100);
+
+ return avg;
+}
+
+static unsigned long determine_cpu_load(void)
+{
+ int i;
+ unsigned long total_load = 0;
+
+ /* get cpu load of each cpu */
+ for_each_online_cpu(i) {
+ unsigned int load;
+ unsigned int idle_time, wall_time;
+ cputime64_t cur_wall_time, cur_idle_time;
+ struct hotplug_cpu_info *info;
+
+ info = &per_cpu(hotplug_info, i);
+
+ /* update both cur_idle_time and cur_wall_time */
+ cur_idle_time = get_cpu_idle_time_us(i, &cur_wall_time);
+
+ /* how much wall time has passed since last iteration? */
+ wall_time = (unsigned int) cputime64_sub(cur_wall_time,
+ info->prev_cpu_wall);
+ info->prev_cpu_wall = cur_wall_time;
+
+ /* how much idle time has passed since last iteration? */
+ idle_time = (unsigned int) cputime64_sub(cur_idle_time,
+ info->prev_cpu_idle);
+ info->prev_cpu_idle = cur_idle_time;
+
+ if (unlikely(!wall_time || wall_time < idle_time))
+ continue;
+
+ /* load is the percentage of time not spent in idle */
+ load = 100 * (wall_time - idle_time) / wall_time;
+ info->load[info->idx++] = load;
+ if (info->idx >= LOAD_MONITOR)
+ info->idx = 0;
+
+ hp_printk("cpu %d load %u ", i, load);
+
+ total_load += load;
+ }
+
+ return total_load;
+}
+
+static unsigned long determine_cpu_load_trend(void)
+{
+ int i, k;
+ unsigned long total_load = 0;
+
+ /* Get cpu load of each cpu */
+ for_each_online_cpu(i) {
+ unsigned int load = 0;
+ struct hotplug_cpu_info *info;
+
+ info = &per_cpu(hotplug_info, i);
+
+ for (k = 0; k < LOAD_MONITOR; k++)
+ load += info->load[k];
+
+ load /= LOAD_MONITOR;
+
+ hp_printk("cpu %d load trend %u\n", i, load);
+
+ total_load += load;
+ }
+
+ return total_load;
+}
+
+static unsigned long determine_cpu_balance_trend(void)
+{
+ int i, k;
+ unsigned long total_load = 0;
+ unsigned long min_load = (unsigned long) (-1);
+
+ /* Get cpu load of each cpu */
+ for_each_online_cpu(i) {
+ unsigned int load = 0;
+ struct hotplug_cpu_info *info;
+ info = &per_cpu(hotplug_info, i);
+
+ for (k = 0; k < LOAD_MONITOR; k++)
+ load += info->load[k];
+
+ load /= LOAD_MONITOR;
+
+ if (min_load > load)
+ min_load = load;
+ total_load += load;
+ }
+
+ if (min_load > min_trend)
+ total_load = (100 * total_load) / min_load;
+ else
+ total_load = 50 << num_online_cpus();
+
+ return total_load;
+}
+
+static void init_cpu_load_trend(void)
+{
+ int i;
+
+ for_each_possible_cpu(i) {
+ struct hotplug_cpu_info *info;
+ int j;
+
+ info = &per_cpu(hotplug_info, i);
+
+ info->prev_cpu_idle = get_cpu_idle_time_us(i,
+ &(info->prev_cpu_wall));
+ info->prev_cpu_io = get_cpu_iowait_time_us(i,
+ &(info->prev_cpu_wall));
+
+ for (j = 0; j < LOAD_MONITOR; j++) {
+ info->load[j] = 100;
+ }
+ info->idx = 0;
+ }
+}
+
+static u32 get_num_interrupts_per_s(void)
+{
+ int cpu;
+ int i;
+ u64 num_irqs = 0;
+ ktime_t now;
+ static ktime_t last;
+ unsigned int delta;
+ u32 irqs = 0;
+
+ now = ktime_get();
+
+ for_each_possible_cpu(cpu) {
+ for (i = 0; i < NR_IRQS; i++)
+ num_irqs += kstat_irqs_cpu(i, cpu);
+ }
+ pr_debug("%s: total num irqs: %lld, previous %lld\n",
+ __func__, num_irqs, old_num_irqs);
+
+ if (old_num_irqs > 0) {
+ delta = (u32)ktime_to_ms(ktime_sub(now, last)) / 1000;
+ irqs = ((u32)(num_irqs - old_num_irqs)) / delta;
+ }
+
+ old_num_irqs = num_irqs;
+ last = now;
+
+ pr_debug("delta irqs per sec:%d\n", irqs);
+
+ return irqs;
+}
+
+static int set_cpufreq(int cpu, int min_freq, int max_freq)
+{
+ int ret;
+ struct cpufreq_policy policy;
+
+ pr_debug("set cpu freq: min %d max: %d\n", min_freq, max_freq);
+
+ ret = cpufreq_get_policy(&policy, cpu);
+ if (ret < 0) {
+ pr_err("usecase-gov: failed to read policy\n");
+ return ret;
+ }
+
+ if (policy.min > max_freq) {
+ ret = cpufreq_update_freq(cpu, min_freq, policy.max);
+ if (ret)
+ pr_err("usecase-gov: update min cpufreq failed (1)\n");
+ }
+ if (policy.max < min_freq) {
+ ret = cpufreq_update_freq(cpu, policy.min, max_freq);
+ if (ret)
+ pr_err("usecase-gov: update max cpufreq failed (2)\n");
+ }
+
+ ret = cpufreq_update_freq(cpu, min_freq, max_freq);
+ if (ret)
+ pr_err("usecase-gov: update min-max cpufreq failed\n");
+
+ return ret;
+}
+
+static void set_cpu_config(enum ux500_uc new_uc)
+{
+ bool update = false;
+ int cpu;
+ int max_freq, min_freq;
+
+ if (new_uc != current_uc)
+ update = true;
+ else if ((user_config_updated) && (new_uc == UX500_UC_USER))
+ update = true;
+
+ pr_debug("%s: new_usecase=%d, current_usecase=%d, update=%d\n",
+ __func__, new_uc, current_uc, update);
+
+ if (!update)
+ goto exit;
+
+ /* Cpu hotplug */
+ if (!(usecase_conf[new_uc].second_cpu_online) &&
+ (num_online_cpus() > 1))
+ cpu_down(1);
+ else if ((usecase_conf[new_uc].second_cpu_online) &&
+ (num_online_cpus() < 2))
+ cpu_up(1);
+
+ if(usecase_conf[new_uc].max_arm_opp)
+ max_freq = dbx500_cpufreq_percent2freq(usecase_conf[new_uc].max_arm_opp);
+ else
+ /* Maximum OPP is 125% */
+ max_freq = dbx500_cpufreq_percent2freq(125);
+
+ min_freq = dbx500_cpufreq_percent2freq(usecase_conf[new_uc].min_arm_opp);
+
+ for_each_online_cpu(cpu) {
+ set_cpufreq(cpu,
+ min_freq,
+ max_freq);
+ }
+
+ /* Kinda doing the job twice, but this is needed for reference keeping */
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "usecase", usecase_conf[new_uc].min_arm_opp);
+
+ /* Cpu idle */
+ cpuidle_set_multiplier(usecase_conf[new_uc].cpuidle_multiplier);
+
+ /* L2 prefetch */
+ if (usecase_conf[new_uc].l2_prefetch_en)
+ outer_prefetch_enable();
+ else
+ outer_prefetch_disable();
+
+ /* Force cpuidle state */
+ cpuidle_force_state(usecase_conf[new_uc].forced_state);
+
+ /* QOS override */
+ prcmu_qos_voice_call_override(usecase_conf[new_uc].vc_override);
+
+ current_uc = new_uc;
+
+exit:
+ /* Its ok to clear even if new_uc != UX500_UC_USER */
+ user_config_updated = false;
+}
+
+void usecase_update_governor_state(void)
+{
+ bool cancel_work = false;
+
+ /*
+ * usecase_mutex will have to be unlocked to ensure safe exit of
+ * delayed_usecase_work(). Protect this function with its own mutex
+ * from being executed by multiple threads at that point.
+ */
+ mutex_lock(&state_mutex);
+ mutex_lock(&usecase_mutex);
+
+ if (uc_master_enable && (usecase_conf[UX500_UC_AUTO].enable ||
+ usecase_conf[UX500_UC_USER].enable)) {
+ /*
+ * Usecases are enabled. If we are in early suspend put
+ * governor to work.
+ */
+ if (is_early_suspend && !is_work_scheduled) {
+ schedule_delayed_work_on(0, &work_usecase,
+ msecs_to_jiffies(CPULOAD_MEAS_DELAY));
+ is_work_scheduled = true;
+ } else if (!is_early_suspend && is_work_scheduled) {
+ /* Exiting from early suspend. */
+ cancel_work = true;
+ }
+
+ } else if (is_work_scheduled) {
+ /* No usecase enabled or governor is not enabled. */
+ cancel_work = true;
+ }
+
+ if (cancel_work) {
+ /*
+ * usecase_mutex is used by delayed_usecase_work() so it must
+ * be unlocked before we call to cacnel the work.
+ */
+ mutex_unlock(&usecase_mutex);
+ cancel_delayed_work_sync(&work_usecase);
+ mutex_lock(&usecase_mutex);
+
+ is_work_scheduled = false;
+
+ /* Set the default settings before exiting. */
+ set_cpu_config(UX500_UC_NORMAL);
+ }
+
+ mutex_unlock(&usecase_mutex);
+ mutex_unlock(&state_mutex);
+}
+
+/*
+ * Start load measurment every 6 s in order detrmine if can unplug one CPU.
+ * In order to not corrupt measurment, the first load average is not done
+ * here call in early suspend.
+ */
+static void usecase_earlysuspend_callback(struct early_suspend *h)
+{
+ init_cpu_load_trend();
+
+ is_early_suspend = true;
+
+ usecase_update_governor_state();
+}
+
+/* Stop measurement, call LCD early resume */
+static void usecase_lateresume_callback(struct early_suspend *h)
+{
+ is_early_suspend = false;
+
+ usecase_update_governor_state();
+}
+
+static void delayed_usecase_work(struct work_struct *work)
+{
+ unsigned long avg, load, trend, balance;
+ bool inc_perf = false;
+ bool dec_perf = false;
+ u32 irqs_per_s;
+
+ /* determine loadavg */
+ avg = determine_loadavg();
+ hp_printk("loadavg = %lu lower th %lu upper th %lu\n",
+ avg, lower_threshold, upper_threshold);
+
+ /* determine instant load */
+ load = determine_cpu_load();
+ hp_printk("cpu instant load = %lu max %lu\n", load, max_instant);
+
+ /* determine load trend */
+ trend = determine_cpu_load_trend();
+ hp_printk("cpu load trend = %lu min %lu unbal %lu\n",
+ trend, min_trend, trend_unbalance);
+
+ /* determine load balancing */
+ balance = determine_cpu_balance_trend();
+ hp_printk("load balancing trend = %lu min %lu\n",
+ balance, max_unbalance);
+
+ irqs_per_s = get_num_interrupts_per_s();
+
+ /* Dont let configuration change in the middle of our calculations. */
+ mutex_lock(&usecase_mutex);
+
+ /* detect "instant" load increase */
+ if (load > max_instant || irqs_per_s > exit_irq_per_s) {
+ inc_perf = true;
+ } else if (!usecase_conf[UX500_UC_USER].enable &&
+ usecase_conf[UX500_UC_AUTO].enable) {
+ /* detect high loadavg use case */
+ if (avg > upper_threshold)
+ inc_perf = true;
+ /* detect idle use case */
+ else if (trend < min_trend)
+ dec_perf = true;
+ /* detect unbalanced low cpu load use case */
+ else if ((balance > max_unbalance) && (trend < trend_unbalance))
+ dec_perf = true;
+ /* detect low loadavg use case */
+ else if (avg < lower_threshold)
+ dec_perf = true;
+ /* All user use cases disabled, current load not triggering
+ * any change.
+ */
+ else if (user_config_updated)
+ dec_perf = true;
+ } else {
+ dec_perf = true;
+ }
+
+ /*
+ * set_cpu_config() will not update the config unless it has been
+ * changed.
+ */
+ if (dec_perf) {
+ if (usecase_conf[UX500_UC_USER].enable)
+ set_cpu_config(UX500_UC_USER);
+ else if (usecase_conf[UX500_UC_AUTO].enable)
+ set_cpu_config(UX500_UC_AUTO);
+ } else if (inc_perf) {
+ set_cpu_config(UX500_UC_NORMAL);
+ }
+
+ mutex_unlock(&usecase_mutex);
+
+ /* reprogramm scheduled work */
+ schedule_delayed_work_on(0, &work_usecase,
+ msecs_to_jiffies(CPULOAD_MEAS_DELAY));
+
+}
+
+static struct dentry *usecase_dir;
+
+#ifdef CONFIG_DEBUG_FS
+#define define_set(_name) \
+static ssize_t set_##_name(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos) \
+{ \
+ int err; \
+ long unsigned i; \
+ \
+ err = kstrtoul_from_user(user_buf, count, 0, &i); \
+ \
+ if (err) \
+ return err; \
+ \
+ _name = i; \
+ hp_printk("New value : %lu\n", _name); \
+ \
+ return count; \
+}
+
+define_set(upper_threshold);
+define_set(lower_threshold);
+define_set(max_unbalance);
+define_set(trend_unbalance);
+define_set(min_trend);
+define_set(max_instant);
+define_set(debug);
+
+#define define_print(_name) \
+static ssize_t print_##_name(struct seq_file *s, void *p) \
+{ \
+ return seq_printf(s, "%lu\n", _name); \
+}
+
+define_print(upper_threshold);
+define_print(lower_threshold);
+define_print(max_unbalance);
+define_print(trend_unbalance);
+define_print(min_trend);
+define_print(max_instant);
+define_print(debug);
+
+#define define_open(_name) \
+static ssize_t open_##_name(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, print_##_name, inode->i_private); \
+}
+
+define_open(upper_threshold);
+define_open(lower_threshold);
+define_open(max_unbalance);
+define_open(trend_unbalance);
+define_open(min_trend);
+define_open(max_instant);
+define_open(debug);
+
+#define define_dbg_file(_name) \
+static const struct file_operations fops_##_name = { \
+ .open = open_##_name, \
+ .write = set_##_name, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+ .owner = THIS_MODULE, \
+}; \
+static struct dentry *file_##_name;
+
+define_dbg_file(upper_threshold);
+define_dbg_file(lower_threshold);
+define_dbg_file(max_unbalance);
+define_dbg_file(trend_unbalance);
+define_dbg_file(min_trend);
+define_dbg_file(max_instant);
+define_dbg_file(debug);
+
+struct dbg_file {
+ struct dentry **file;
+ const struct file_operations *fops;
+ const char *name;
+};
+
+#define define_dbg_entry(_name) \
+{ \
+ .file = &file_##_name, \
+ .fops = &fops_##_name, \
+ .name = #_name \
+}
+
+static struct dbg_file debug_entry[] = {
+ define_dbg_entry(upper_threshold),
+ define_dbg_entry(lower_threshold),
+ define_dbg_entry(max_unbalance),
+ define_dbg_entry(trend_unbalance),
+ define_dbg_entry(min_trend),
+ define_dbg_entry(max_instant),
+ define_dbg_entry(debug),
+};
+
+static int setup_debugfs(void)
+{
+ int i;
+ usecase_dir = debugfs_create_dir("usecase", NULL);
+
+ if (IS_ERR_OR_NULL(usecase_dir))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(debug_entry); i++) {
+ if (IS_ERR_OR_NULL(debugfs_create_file(debug_entry[i].name,
+ S_IWUGO | S_IRUGO,
+ usecase_dir,
+ NULL,
+ debug_entry[i].fops)))
+ goto fail;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_u32("exit_irq_per_s",
+ S_IWUGO | S_IRUGO, usecase_dir,
+ &exit_irq_per_s)))
+ goto fail;
+ return 0;
+fail:
+ debugfs_remove_recursive(usecase_dir);
+ return -EINVAL;
+}
+#else
+static int setup_debugfs(void)
+{
+ return 0;
+}
+#endif
+
+static void usecase_update_user_config(void)
+{
+ int i;
+ bool config_enable = false;
+ struct usecase_config *user_conf = &usecase_conf[UX500_UC_USER];
+
+ mutex_lock(&usecase_mutex);
+
+ user_conf->min_arm_opp = 25;
+ user_conf->max_arm_opp = 0;
+ user_conf->cpuidle_multiplier = 0;
+ user_conf->second_cpu_online = false;
+ user_conf->l2_prefetch_en = false;
+ user_conf->forced_state = cpuidle_deepest_state;
+ user_conf->vc_override = true; /* A single false will clear it. */
+
+ /* Dont include Auto and Normal modes in this */
+ for (i = (UX500_UC_AUTO + 1); i < UX500_UC_USER; i++) {
+ if (!usecase_conf[i].enable)
+ continue;
+
+ config_enable = true;
+
+ /* It's the highest arm opp requirement that should be used */
+ if (usecase_conf[i].min_arm_opp > user_conf->min_arm_opp)
+ user_conf->min_arm_opp = usecase_conf[i].min_arm_opp;
+
+ if (usecase_conf[i].max_arm_opp > user_conf->max_arm_opp)
+ user_conf->max_arm_opp = usecase_conf[i].max_arm_opp;
+
+ if (usecase_conf[i].cpuidle_multiplier >
+ user_conf->cpuidle_multiplier)
+ user_conf->cpuidle_multiplier =
+ usecase_conf[i].cpuidle_multiplier;
+
+ user_conf->second_cpu_online |=
+ usecase_conf[i].second_cpu_online;
+
+ user_conf->l2_prefetch_en |=
+ usecase_conf[i].l2_prefetch_en;
+
+ /* Take the shallowest state. */
+ if (usecase_conf[i].forced_state < user_conf->forced_state)
+ user_conf->forced_state = usecase_conf[i].forced_state;
+
+ /* Only override QOS if all enabled configurations are
+ * requesting it.
+ */
+ if (!usecase_conf[i].vc_override)
+ user_conf->vc_override = false;
+ }
+
+ user_conf->enable = config_enable;
+ user_config_updated = true;
+
+ mutex_unlock(&usecase_mutex);
+}
+
+struct usecase_devclass_attr {
+ struct sysdev_class_attribute class_attr;
+ u32 index;
+};
+
+/* One for each usecase except "user" + current + enable */
+#define UX500_NUM_SYSFS_NODES (UX500_UC_USER + 2)
+#define UX500_CURRENT_NODE_INDEX (UX500_NUM_SYSFS_NODES - 1)
+#define UX500_ENABLE_NODE_INDEX (UX500_NUM_SYSFS_NODES - 2)
+
+static struct usecase_devclass_attr usecase_dc_attr[UX500_NUM_SYSFS_NODES];
+
+static struct attribute *dbs_attributes[UX500_NUM_SYSFS_NODES + 1] = {NULL};
+
+static struct attribute_group dbs_attr_group = {
+ .attrs = dbs_attributes,
+ .name = "usecase",
+};
+
+static ssize_t show_current(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ enum ux500_uc display_uc = (current_uc == UX500_UC_MAX) ?
+ UX500_UC_NORMAL : current_uc;
+
+ return sprintf(buf, "min_arm_opp: %d\n"
+ "max_arm_opp: %d\n"
+ "cpuidle_multiplier: %ld\n"
+ "second_cpu_online: %s\n"
+ "l2_prefetch_en: %s\n"
+ "forced_state: %d\n"
+ "vc_override: %s\n",
+ usecase_conf[display_uc].min_arm_opp,
+ usecase_conf[display_uc].max_arm_opp,
+ usecase_conf[display_uc].cpuidle_multiplier,
+ usecase_conf[display_uc].second_cpu_online ? "true" : "false",
+ usecase_conf[display_uc].l2_prefetch_en ? "true" : "false",
+ usecase_conf[display_uc].forced_state,
+ usecase_conf[display_uc].vc_override ? "true" : "false");
+}
+
+static ssize_t show_enable(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", uc_master_enable);
+}
+
+static ssize_t store_enable(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+
+ ret = sscanf(buf, "%u", &input);
+ if (ret != 1)
+ return -EINVAL;
+
+ uc_master_enable = (bool) input;
+
+ usecase_update_governor_state();
+
+ return count;
+}
+
+static ssize_t show_dc_attr(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ struct usecase_devclass_attr *uattr =
+ container_of(attr, struct usecase_devclass_attr, class_attr);
+
+ return sprintf(buf, "%u\n",
+ usecase_conf[uattr->index].enable);
+}
+
+static ssize_t store_dc_attr(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+
+ struct usecase_devclass_attr *uattr =
+ container_of(attr, struct usecase_devclass_attr, class_attr);
+
+ ret = sscanf(buf, "%u", &input);
+
+ /* Normal mode cant be changed. */
+ if ((ret != 1) || (uattr->index == 0))
+ return -EINVAL;
+
+ usecase_conf[uattr->index].enable = (bool)input;
+
+ if (uattr->index == UX500_UC_VC)
+ prcmu_vc(usecase_conf[UX500_UC_VC].enable);
+
+ usecase_update_user_config();
+
+ usecase_update_governor_state();
+
+ return count;
+}
+
+static int usecase_sysfs_init(void)
+{
+ int err;
+ int i;
+
+ /* Last two nodes are not based on usecase configurations */
+ for (i = 0; i < (UX500_NUM_SYSFS_NODES - 2); i++) {
+ usecase_dc_attr[i].class_attr.attr.name = usecase_conf[i].name;
+ usecase_dc_attr[i].class_attr.attr.mode = 0644;
+ usecase_dc_attr[i].class_attr.show = show_dc_attr;
+ usecase_dc_attr[i].class_attr.store = store_dc_attr;
+ usecase_dc_attr[i].index = i;
+
+ dbs_attributes[i] = &(usecase_dc_attr[i].class_attr.attr);
+ }
+
+ /* sysfs current */
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr.name =
+ "current";
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr.mode =
+ 0644;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.show =
+ show_current;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.store =
+ NULL;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].index =
+ 0;
+ dbs_attributes[UX500_CURRENT_NODE_INDEX] =
+ &(usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr);
+
+ /* sysfs enable */
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr.name =
+ "enable";
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr.mode =
+ 0644;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.show =
+ show_enable;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.store =
+ store_enable;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].index =
+ 0;
+ dbs_attributes[UX500_ENABLE_NODE_INDEX] =
+ &(usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr);
+
+ err = sysfs_create_group(&(cpu_sysdev_class.kset.kobj),
+ &dbs_attr_group);
+ if (err)
+ pr_err("usecase-gov: sysfs_create_group"
+ " failed with error = %d\n", err);
+
+ return err;
+}
+
+static void usecase_cpuidle_init(void)
+{
+ int max_states;
+ int i;
+ struct cstate *state = ux500_ci_get_cstates(&max_states);
+
+ for (i = 0; i < max_states; i++)
+ if ((state[i].APE == APE_OFF) && (state[i].ARM == ARM_RET))
+ break;
+
+ usecase_conf[UX500_UC_LPA].forced_state = i;
+
+ cpuidle_deepest_state = max_states - 1;
+}
+
+/* initialize devices */
+static int __init init_usecase_devices(void)
+{
+ int err;
+
+ pr_info("Use-case governor initialized\n");
+
+ /* add early_suspend callback */
+ usecase_early_suspend.level = 200;
+ usecase_early_suspend.suspend = usecase_earlysuspend_callback;
+ usecase_early_suspend.resume = usecase_lateresume_callback;
+ register_early_suspend(&usecase_early_suspend);
+
+ /* register delayed queuework */
+ INIT_DELAYED_WORK_DEFERRABLE(&work_usecase,
+ delayed_usecase_work);
+
+ init_cpu_load_trend();
+
+ err = setup_debugfs();
+ if (err)
+ goto error;
+ err = usecase_sysfs_init();
+ if (err)
+ goto error2;
+
+ usecase_cpuidle_init();
+
+ prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "usecase", 25);
+
+ return 0;
+error2:
+ debugfs_remove_recursive(usecase_dir);
+error:
+ unregister_early_suspend(&usecase_early_suspend);
+ return err;
+}
+
+device_initcall(init_usecase_devices);
diff --git a/arch/arm/mach-ux500/prcc.h b/arch/arm/mach-ux500/prcc.h
new file mode 100644
index 00000000000..4224e478348
--- /dev/null
+++ b/arch/arm/mach-ux500/prcc.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009-2011 ST-Ericsson SA
+ *
+ * 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 __MACH_UX500_PRCC_H__
+
+#define PRCC_PCKEN 0x000
+#define PRCC_PCKDIS 0x004
+#define PRCC_KCKEN 0x008
+#define PRCC_KCKDIS 0x00C
+#define PRCC_PCKSR 0x010
+#define PRCC_KCKSR 0x014
+#define PRCC_K_SOFTRST_SET 0x018
+#define PRCC_K_SOFTRST_CLR 0x01C
+#define PRCC_K_RST_STATUS 0x020
+
+#endif
diff --git a/arch/arm/mach-ux500/prcmu-debug.c b/arch/arm/mach-ux500/prcmu-debug.c
new file mode 100644
index 00000000000..6842e4b68fe
--- /dev/null
+++ b/arch/arm/mach-ux500/prcmu-debug.c
@@ -0,0 +1,1041 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Martin Persson for ST-Ericsson
+ * Etienne Carriere <etienne.carriere@stericsson.com> for ST-Ericsson
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/hardware.h>
+
+#define MAX_STATES 5
+#define MAX_NAMELEN 16
+#define U8500_PRCMU_TCDM_SIZE 4096
+
+struct state_history {
+ ktime_t start;
+ u32 state;
+ u32 counter[MAX_STATES];
+ u8 opps[MAX_STATES];
+ int max_states;
+ int req;
+ bool reqs[MAX_STATES];
+ ktime_t time[MAX_STATES];
+ int state_names[MAX_STATES];
+ char prefix[MAX_NAMELEN];
+ spinlock_t lock;
+};
+
+static struct state_history ape_sh = {
+ .prefix = "APE",
+ .req = PRCMU_QOS_APE_OPP,
+ .opps = {APE_50_OPP, APE_100_OPP},
+ .state_names = {50, 100},
+ .max_states = 2,
+};
+
+static struct state_history ddr_sh = {
+ .prefix = "DDR",
+ .req = PRCMU_QOS_DDR_OPP,
+ .opps = {DDR_25_OPP, DDR_50_OPP, DDR_100_OPP},
+ .state_names = {25, 50, 100},
+ .max_states = 3,
+};
+
+static struct state_history arm_sh = {
+ .prefix = "ARM",
+ .req = PRCMU_QOS_ARM_OPP,
+ .opps = {ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP},
+ .state_names = {25, 50, 100, 125},
+ .max_states = 4,
+};
+
+static const u16 u8500_prcmu_dump_regs[] = {
+ /*ARMCLKFIX_MGT*/ 0x0, /*ACLK_MGT*/ 0x4,
+ /*SVAMMDSPCLK_MGT*/ 0x8, /*SIAMMDSPCLK_MGT*/ 0xc,
+ /*SGACLK_MGT*/ 0x14, /*UARTCLK_MGT*/ 0x18,
+ /*MSP02CLK_MGT*/ 0x1c, /*I2CCLK_MGT*/ 0x20,
+ /*SDMMCCLK_MGT*/ 0x24, /*SLIMCLK_MGT*/ 0x28,
+ /*PER1CLK_MGT*/ 0x2c, /*PER2CLK_MGT*/ 0x30,
+ /*PER3CLK_MGT*/ 0x34, /*PER5CLK_MGT*/ 0x38,
+ /*PER6CLK_MGT*/ 0x3c, /*PER7CLK_MGT*/ 0x40,
+ /*LCDCLK_MGT*/ 0x44, /*SPARE1CLK_MGT*/ 0x48,
+ /*BMLCLK_MGT*/ 0x4c, /*HSITXCLK_MGT*/ 0x50,
+ /*HSIRXCLK_MGT*/ 0x54, /*HDMICLK_MGT*/ 0x58,
+ /*APEATCLK_MGT*/ 0x5c, /*APETRACECLK_MGT*/ 0x60,
+ /*MCDECLK_MGT*/ 0x64, /*IPI2CCLK_MGT*/ 0x68,
+ /*DSIALTCLK_MGT*/ 0x6c, /*SPARE2CLK_MGT*/ 0x70,
+ /*DMACLK_MGT*/ 0x74, /*B2R2CLK_MGT*/ 0x78,
+ /*TVCLK_MGT*/ 0x7c, /*PLLSOC0_FREQ*/ 0x80,
+ /*PLLSOC1_FREQ*/ 0x84, /*PLLARM_FREQ*/ 0x88,
+ /*PLLDDR_FREQ*/ 0x8c, /*PLLSOC0_ENABLE*/ 0x90,
+ /*PLLSOC1_ENABLE*/ 0x94, /*PLLARM_ENABLE*/ 0x98,
+ /*PLLDDR_ENABLE*/ 0x9c, /*PLLSOC0_LOCKP*/ 0xa0,
+ /*PLLSOC1_LOCKP*/ 0xa4, /*PLLARM_LOCKP*/ 0xa8,
+ /*PLLDDR_LOCKP*/ 0xac, /*XP70CLK_MGT*/ 0xb0,
+ /*TIMER_0_REF*/ 0xb4, /*TIMER_0_DOWNCOUNT*/ 0xb8,
+ /*TIMER_0_MODE*/ 0xbc, /*TIMER_1_REF*/ 0xc0,
+ /*TIMER_1_DOWNCOUNT*/ 0xc4, /*TIMER_1_MODE*/ 0xc8,
+ /*TIMER_2_REF*/ 0xcc, /*TIMER_2_DOWNCOUNT*/ 0xd0,
+ /*TIMER_2_MODE*/ 0xd4, /*CLK009_MGT*/ 0xe4,
+ /*MODECLK*/ 0xe8, /*4500_CLK_REQ*/ 0xf8,
+ /*MBOX_CPU_VAL*/ 0xfc, /*PLL32K_ENABLE*/ 0x10c,
+ /*PLL32K_LOCKP*/ 0x110, /*ARM_CHGCLKREQ*/ 0x114,
+ /*ARM_PLLDIVPS*/ 0x118, /*ARMITMSK31TO0*/ 0x11c,
+ /*ARMITMSK63TO32*/ 0x120, /*ARMITMSK95TO64*/ 0x124,
+ /*ARMITMSK127TO96*/ 0x128, /*ARMSTANDBY_STATUS*/ 0x130,
+ /*CGATING_BYPASS*/ 0x134, /*GPIOCR*/ 0x138,
+ /*LEMI*/ 0x13c, /*COMPCR*/ 0x140,
+ /*COMPSTA*/ 0x144, /*ITSTATUS0*/ 0x148,
+ /*ITSTATUS1*/ 0x150, /*ITSTATUS2*/ 0x158,
+ /*ITSTATUS3*/ 0x160, /*ITSTATUS4*/ 0x168,
+ /*LINE_VALUE*/ 0x170, /*HOLD_EVT*/ 0x174,
+ /*EDGE_SENS_L*/ 0x178, /*EDGE_SENS_H*/ 0x17c,
+ /*DEBUG_CTRL_VAL*/ 0x190, /*DEBUG_NOPWRDOWN_VAL*/ 0x194,
+ /*DEBUG_CTRL_ACK*/ 0x198, /*A9PL_FORCE_CLKEN*/ 0x19c,
+ /*TPIU_FLUSHIN_REQ*/ 0x1a0, /*TPIU_FLUSHIN_ACK*/ 0x1a4,
+ /*STP_FLUSHIN_REQ*/ 0x1a8, /*STP_FLUSHIN_ACK*/ 0x1ac,
+ /*HWI2C_DIV*/ 0x1b0, /*HWI2C_CMD*/ 0x1b8,
+ /*HWI2C_DATA123*/ 0x1bc, /*HWI2C_SR*/ 0x1c0,
+ /*REMAPCR*/ 0x1c4, /*TCR*/ 0x1c8,
+ /*CLKOCR*/ 0x1cc, /*ITSTATUS_DBG*/ 0x1d0,
+ /*LINE_VALUE_DBG*/ 0x1d8, /*DBG_HOLD*/ 0x1dc,
+ /*EDGE_SENS_DBG*/ 0x1e0, /*APE_RESETN_VAL*/ 0x1ec,
+ /*A9_RESETN_SET*/ 0x1f0, /*A9_RESETN_VAL*/ 0x1f8,
+ /*MOD_RESETN_VAL*/ 0x204, /*GPIO_RESETN_VAL*/ 0x210,
+ /*4500_RESETN_VAL*/ 0x21c, /*SWD_RST_TEMPO*/ 0x238,
+ /*RST_4500_TEMPO*/ 0x23c, /*SVAMMDSP_IT*/ 0x240,
+ /*SIAMMDSP_IT*/ 0x248, /*POWER_STATE_VAL*/ 0x25c,
+ /*ARMITVALUE31TO0*/ 0x260, /*ARMITVALUE63TO32*/ 0x264,
+ /*ARMITVALUE95TO64*/ 0x268, /*ARMITVALUE127TO96*/ 0x26c,
+ /*REDUN_LOAD*/ 0x270, /*REDUN_STATUS*/ 0x274,
+ /*UNIPROCLK_MGT*/ 0x278, /*UICCCLK_MGT*/ 0x27c,
+ /*SSPCLK_MGT*/ 0x280, /*RNGCLK_MGT*/ 0x284,
+ /*MSP1CLK_MGT*/ 0x288, /*DAP_RESETN_SET*/ 0x2a0,
+ /*DAP_RESETN_VAL*/ 0x2a8, /*SRAM_DEDCSTOV*/ 0x300,
+ /*SRAM_LS_SLEEP*/ 0x304, /*SRAM_A9*/ 0x308,
+ /*ARM_LS_CLAMP*/ 0x30c, /*IOCR*/ 0x310,
+ /*MODEM_SYSCLKOK*/ 0x314, /*SYSCLKOK_DELAY*/ 0x318,
+ /*SYSCLKSTATUS*/ 0x31c, /*DSI_SW_RESET*/ 0x324,
+ /*A9_MASK_REQ*/ 0x328, /*A9_MASK_ACK*/ 0x32c,
+ /*HOSTACCESS_REQ*/ 0x334, /*TIMER_3_REF*/ 0x338,
+ /*TIMER_3_DOWNCOUNT*/ 0x33c, /*TIMER_3_MODE*/ 0x340,
+ /*PMB_SENS_CTRL*/ 0x344, /*PMB_REF_COUNTER*/ 0x348,
+ /*PMB_SENSOR_STATUS*/ 0x34c, /*APE_EPOD_CFG*/ 0x404,
+ /*DDR_EPOD_CFG*/ 0x408, /*EPOD_C_VAL*/ 0x418,
+ /*EPOD_VOK*/ 0x41c, /*MMIP_LS_CLAMP_VAL*/ 0x428,
+ /*VSAFE_LS_CLAMP_VAL*/ 0x434, /*DDRSUBSYS_APE_MINBW*/ 0x438,
+ /*DDRSUBSYS_STATUS*/ 0x43c, /*DDRSUBSYS_CONTROL*/ 0x440,
+ /*DDRSUBSYS_HIGH_LEAK_COND*/ 0x444, /*DDRSUBSYS_CONFIG*/ 0x448,
+ /*TIMER_4_REF*/ 0x450, /*TIMER_4_DOWNCOUNT*/ 0x454,
+ /*TIMER_4_MODE*/ 0x458, /*TIMER_5_REF*/ 0x45c,
+ /*TIMER_5_DOWNCOUNT*/ 0x460, /*TIMER_5_MODE*/ 0x464,
+ /*APE_MEM_REQ*/ 0x470, /*DBG_FRCS_APE_MEM_REQ*/ 0x474,
+ /*APE_MEM_WFX_EN*/ 0x478, /*APE_MEM_LATENCY*/ 0x47c,
+ /*APE_MEM_ACK*/ 0x480, /*ITSTATUS5*/ 0x484,
+ /*ARM_IT1_VAL*/ 0x494, /*MOD_PWR_OK*/ 0x498,
+ /*MOD_AUXCLKOK*/ 0x49c, /*MOD_AWAKE_STATUS*/ 0x4a0,
+ /*MOD_SWRESET_IRQ_ACK*/ 0x4a4, /*MOD_SWRESET_ACK*/ 0x4a8,
+ /*DBG_PWRCTL*/ 0x4ac, /*HWOBS_H*/ 0x4b0,
+ /*HWOBS_L*/ 0x4b4, /*PLLDSI_FREQ*/ 0x500,
+ /*PLLDSI_ENABLE*/ 0x504, /*PLLDSI_LOCKP*/ 0x508,
+ /*RNG_ENABLE*/ 0x50c, /*YYCLKEN0_MGT_SET*/ 0x510,
+ /*YYCLKEN0_MGT_VAL*/ 0x520, /*YYCLKEN1_MGT_VAL*/ 0x524,
+ /*XP70CLK_MGT2*/ 0x528, /*DSITVCLK_DIV*/ 0x52c,
+ /*DSI_PLLOUT_SEL*/ 0x530, /*DSI_GLITCHFREE_EN*/ 0x534,
+ /*CLKACTIV*/ 0x538, /*SIA_MMDSP_MEM_MGT*/ 0x53c,
+ /*SVA_MMDSP_MEM_MGT*/ 0x540, /*SXAMMDSP_FORCE_CLKEN*/ 0x544,
+ /*UICC_NANDTREE*/ 0x570, /*GPIOCR2*/ 0x574,
+ /*MDM_ACWAKE*/ 0x578, /*MOD_MEM_REQ*/ 0x5a4,
+ /*MOD_MEM_ACK*/ 0x5a8, /*ARM_PLLDIVPS_REQ*/ 0x5b0,
+ /*ARM_PLLDIVPS_ACK*/ 0x5b4, /*SRPTIMER_VAL*/ 0x5d0,
+};
+
+/* Offsets from secure base which is U8500_PRCMU_BASE + SZ_4K */
+static const u16 u8500_prcmu_dump_secure_regs[] = {
+ /*SECNONSEWM*/ 0x00, /*ESRAM0_INITN*/ 0x04,
+ /*ARMITMSKSEC_31TO0*/ 0x08, /*ARMITMSKSEC_63TO32*/ 0x0C,
+ /*ARMITMSKSEC_95TO64*/ 0x10, /*ARMITMSKSEC_127TO96*/ 0x14,
+ /*ARMIT_MASKXP70_IT*/ 0x18, /*ESRAM0_EPOD_CFG*/ 0x1C,
+ /*ESRAM0_EPOD_C_VAL*/ 0x20, /*ESRAM0_EPOD_VOK*/ 0x2C,
+ /*ESRAM0_LS_SLEEP*/ 0x30, /*SECURE_ONGOING*/ 0x34,
+ /*I2C_SECURE*/ 0x38, /*RESET_STATUS*/ 0x3C,
+ /*PERIPH4_RESETN_VAL*/ 0x48, /*SPAREOUT_SEC*/ 0x4C,
+ /*PIPELINEDCR*/ 0xD8,
+};
+
+static int ape_voltage_count;
+
+static void log_set(struct state_history *sh, u8 opp)
+{
+ ktime_t now;
+ ktime_t dtime;
+ unsigned long flags;
+ int state;
+
+ now = ktime_get();
+ spin_lock_irqsave(&sh->lock, flags);
+
+ for (state = 0 ; sh->opps[state] != opp; state++)
+ ;
+ BUG_ON(state >= sh->max_states);
+
+ dtime = ktime_sub(now, sh->start);
+ sh->time[sh->state] = ktime_add(sh->time[sh->state], dtime);
+ sh->start = now;
+ sh->counter[sh->state]++;
+ sh->state = state;
+
+ spin_unlock_irqrestore(&sh->lock, flags);
+}
+
+void prcmu_debug_ape_opp_log(u8 opp)
+{
+ if (opp == APE_50_PARTLY_25_OPP)
+ opp = APE_50_OPP;
+
+ log_set(&ape_sh, opp);
+}
+
+void prcmu_debug_ddr_opp_log(u8 opp)
+{
+ log_set(&ddr_sh, opp);
+}
+
+void prcmu_debug_arm_opp_log(u8 opp)
+{
+ log_set(&arm_sh, opp);
+}
+
+static void log_reset(struct state_history *sh)
+{
+ unsigned long flags;
+ int i;
+
+ pr_info("reset\n");
+
+ spin_lock_irqsave(&sh->lock, flags);
+ for (i = 0; i < sh->max_states; i++) {
+ sh->counter[i] = 0;
+ sh->time[i] = ktime_set(0, 0);
+ }
+
+ sh->start = ktime_get();
+ spin_unlock_irqrestore(&sh->lock, flags);
+
+}
+
+static ssize_t ape_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ log_reset(&ape_sh);
+ return count;
+}
+
+static ssize_t ddr_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ log_reset(&ddr_sh);
+ return count;
+}
+
+static ssize_t arm_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ log_reset(&arm_sh);
+ return count;
+}
+
+static int log_print(struct seq_file *s, struct state_history *sh)
+{
+ int i;
+ unsigned long flags;
+ ktime_t total;
+ ktime_t dtime;
+ s64 t_ms;
+ s64 perc;
+ s64 total_ms;
+
+ spin_lock_irqsave(&sh->lock, flags);
+
+ dtime = ktime_sub(ktime_get(), sh->start);
+
+ total = dtime;
+
+ for (i = 0; i < sh->max_states; i++)
+ total = ktime_add(total, sh->time[i]);
+ total_ms = ktime_to_ms(total);
+
+ for (i = 0; i < sh->max_states; i++) {
+ ktime_t t = sh->time[i];
+ if (sh->state == i)
+ t = ktime_add(t, dtime);
+
+ t_ms = ktime_to_ms(t);
+ perc = 100 * t_ms;
+ do_div(perc, total_ms);
+
+ seq_printf(s, "%s OPP %d: # %u in %lld ms %d%%\n",
+ sh->prefix, sh->state_names[i],
+ sh->counter[i] + (int)(sh->state == i),
+ t_ms, (u32)perc);
+
+ }
+ spin_unlock_irqrestore(&sh->lock, flags);
+ return 0;
+}
+
+static int ape_stats_print(struct seq_file *s, void *p)
+{
+ log_print(s, &ape_sh);
+ return 0;
+}
+
+static int ddr_stats_print(struct seq_file *s, void *p)
+{
+ log_print(s, &ddr_sh);
+ return 0;
+}
+
+static int arm_stats_print(struct seq_file *s, void *p)
+{
+ log_print(s, &arm_sh);
+ return 0;
+}
+
+static int opp_read(struct seq_file *s, void *p)
+{
+ int opp;
+
+ struct state_history *sh = (struct state_history *)s->private;
+
+ switch (sh->req) {
+ case PRCMU_QOS_DDR_OPP:
+ opp = prcmu_get_ddr_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == DDR_100_OPP) ? "100%" :
+ (opp == DDR_50_OPP) ? "50%" :
+ (opp == DDR_25_OPP) ? "25%" :
+ "unknown", opp);
+ break;
+ case PRCMU_QOS_APE_OPP:
+ opp = prcmu_get_ape_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == APE_100_OPP) ? "100%" :
+ (opp == APE_50_OPP) ? "50%" :
+ "unknown", opp);
+ break;
+ case PRCMU_QOS_ARM_OPP:
+ opp = prcmu_get_arm_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == ARM_MAX_OPP) ? "max" :
+ (opp == ARM_MAX_FREQ100OPP) ? "max-freq100" :
+ (opp == ARM_100_OPP) ? "100%" :
+ (opp == ARM_50_OPP) ? "50%" :
+ (opp == ARM_EXTCLK) ? "25% (extclk)" :
+ "unknown", opp);
+ break;
+ default:
+ break;
+ }
+ return 0;
+
+}
+
+static ssize_t opp_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ long unsigned i;
+ int err;
+ struct state_history *sh = (struct state_history *)
+ ((struct seq_file *)file->private_data)->private;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &i);
+
+ if (err)
+ return err;
+
+ prcmu_qos_force_opp(sh->req, i);
+
+ pr_info("prcmu debug: forced OPP for %s to %d\n", sh->prefix, (int)i);
+
+ return count;
+}
+
+static int cpufreq_delay_read(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "%lu\n", prcmu_qos_get_cpufreq_opp_delay());
+}
+
+static int ape_voltage_read(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "This reference count only includes "
+ "requests via debugfs.\nCount: %d\n",
+ ape_voltage_count);
+}
+
+static ssize_t ape_voltage_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ long unsigned i;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &i);
+
+ if (err)
+ return err;
+
+ switch (i) {
+ case 0:
+ if (ape_voltage_count == 0)
+ pr_info("prcmu debug: reference count is already 0\n");
+ else {
+ err = prcmu_request_ape_opp_100_voltage(false);
+ if (err)
+ pr_err("prcmu debug: drop request failed\n");
+ else
+ ape_voltage_count--;
+ }
+ break;
+ case 1:
+ err = prcmu_request_ape_opp_100_voltage(true);
+ if (err)
+ pr_err("prcmu debug: request failed\n");
+ else
+ ape_voltage_count++;
+ break;
+ default:
+ pr_info("prcmu debug: value not equal to 0 or 1\n");
+ }
+ return count;
+}
+
+static ssize_t cpufreq_delay_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err;
+ long unsigned i;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &i);
+
+ if (err)
+ return err;
+
+ prcmu_qos_set_cpufreq_opp_delay(i);
+
+ pr_info("prcmu debug: changed delay between cpufreq change and QoS "
+ "requirement to %lu.\n", i);
+
+ return count;
+}
+
+/* These are only for u8500 */
+#define PRCM_AVS_BASE 0x2FC
+#define AVS_VBB_RET 0x0
+#define AVS_VBB_MAX_OPP 0x1
+#define AVS_VBB_100_OPP 0x2
+#define AVS_VBB_50_OPP 0x3
+#define AVS_VARM_MAX_OPP 0x4
+#define AVS_VARM_100_OPP 0x5
+#define AVS_VARM_50_OPP 0x6
+#define AVS_VARM_RET 0x7
+#define AVS_VAPE_100_OPP 0x8
+#define AVS_VAPE_50_OPP 0x9
+#define AVS_VMOD_100_OPP 0xA
+#define AVS_VMOD_50_OPP 0xB
+#define AVS_VSAFE 0xC
+#define AVS_VSAFE_RET 0xD
+#define AVS_SIZE 14
+
+static int avs_read(struct seq_file *s, void *p)
+{
+
+ u8 avs[AVS_SIZE];
+ void __iomem *tcdm_base;
+
+ if (cpu_is_u8500()) {
+ tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+
+ memcpy_fromio(avs, tcdm_base + PRCM_AVS_BASE, AVS_SIZE);
+
+ seq_printf(s, "VBB_RET : 0x%02x\n", avs[AVS_VBB_RET]);
+ seq_printf(s, "VBB_MAX_OPP : 0x%02x\n", avs[AVS_VBB_MAX_OPP]);
+ seq_printf(s, "VBB_100_OPP : 0x%02x\n", avs[AVS_VBB_100_OPP]);
+ seq_printf(s, "VBB_50_OPP : 0x%02x\n", avs[AVS_VBB_50_OPP]);
+ seq_printf(s, "VARM_MAX_OPP : 0x%02x\n", avs[AVS_VARM_MAX_OPP]);
+ seq_printf(s, "VARM_100_OPP : 0x%02x\n", avs[AVS_VARM_100_OPP]);
+ seq_printf(s, "VARM_50_OPP : 0x%02x\n", avs[AVS_VARM_50_OPP]);
+ seq_printf(s, "VARM_RET : 0x%02x\n", avs[AVS_VARM_RET]);
+ seq_printf(s, "VAPE_100_OPP : 0x%02x\n", avs[AVS_VAPE_100_OPP]);
+ seq_printf(s, "VAPE_50_OPP : 0x%02x\n", avs[AVS_VAPE_50_OPP]);
+ seq_printf(s, "VMOD_100_OPP : 0x%02x\n", avs[AVS_VMOD_100_OPP]);
+ seq_printf(s, "VMOD_50_OPP : 0x%02x\n", avs[AVS_VMOD_50_OPP]);
+ seq_printf(s, "VSAFE : 0x%02x\n", avs[AVS_VSAFE]);
+ seq_printf(s, "VSAFE_RET : 0x%02x\n", avs[AVS_VSAFE_RET]);
+ } else {
+ seq_printf(s, "Only u8500 supported.\n");
+ }
+
+ return 0;
+}
+
+static void prcmu_data_mem_print(struct seq_file *s)
+{
+ int i;
+ int err;
+ void __iomem *tcdm_base;
+ u32 dmem[4];
+
+ if (cpu_is_u8500()) {
+ tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+
+ for (i = 0; i < U8500_PRCMU_TCDM_SIZE; i += 16) {
+ dmem[0] = readl(tcdm_base + i + 0);
+ dmem[1] = readl(tcdm_base + i + 4);
+ dmem[2] = readl(tcdm_base + i + 8);
+ dmem[3] = readl(tcdm_base + i + 12);
+
+ if (s) {
+ err = seq_printf(s,
+ "0x%x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ ((int)tcdm_base) + i, dmem[0],
+ dmem[1], dmem[2], dmem[3]);
+ if (err < 0) {
+ pr_err("%s: seq_printf overflow, addr=%x\n",
+ __func__, ((int)tcdm_base) + i);
+ /* Can't do much here */
+ return;
+ }
+ } else {
+ printk(KERN_INFO
+ "0x%x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ ((int)tcdm_base) + i, dmem[0],
+ dmem[1], dmem[2], dmem[3]);
+ }
+ }
+ }
+}
+
+void prcmu_debug_dump_data_mem(void)
+{
+ printk(KERN_INFO "PRCMU data memory dump:\n");
+ prcmu_data_mem_print(NULL);
+}
+
+static int prcmu_debugfs_data_mem_read(struct seq_file *s, void *p)
+{
+ seq_printf(s, "PRCMU data memory:\n");
+ prcmu_data_mem_print(s);
+
+ return 0;
+}
+
+static void prcmu_regs_print(struct seq_file *s)
+{
+ int i;
+ int err;
+ void __iomem *prcmu_base;
+ u32 reg_val;
+
+ if (cpu_is_u8500()) {
+ prcmu_base = __io_address(U8500_PRCMU_BASE);
+
+ for (i = 0; i < ARRAY_SIZE(u8500_prcmu_dump_regs); i++) {
+ reg_val = readl(prcmu_base +
+ u8500_prcmu_dump_regs[i]);
+
+ if (s) {
+ err = seq_printf(s, "0x%04x: 0x%08x\n",
+ u8500_prcmu_dump_regs[i], reg_val);
+ if (err < 0) {
+ pr_err("%s: seq_printf overflow,"
+ "offset=%x\n", __func__,
+ u8500_prcmu_dump_regs[i]);
+ /* Can't do much here */
+ return;
+ }
+ } else {
+ printk(KERN_INFO
+ "0x%04x: 0x%08x\n",
+ u8500_prcmu_dump_regs[i], reg_val);
+ }
+ }
+ }
+}
+
+static void prcmu_secure_regs_print(struct seq_file *s)
+{
+ int i;
+ int err;
+ void __iomem *prcmu_sec_base;
+ u32 reg_val;
+
+ if (cpu_is_u8500()) {
+ /* PRCMU secure base starts after SZ_4K */
+ prcmu_sec_base = ioremap(U8500_PRCMU_BASE + SZ_4K, SZ_4K);
+ if (!prcmu_sec_base) {
+ pr_err("%s: ioremap faild\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(u8500_prcmu_dump_secure_regs); i++) {
+ reg_val = readl(prcmu_sec_base +
+ u8500_prcmu_dump_secure_regs[i]);
+
+ if (s) {
+ err = seq_printf(s, "0x%04x: 0x%08x\n",
+ u8500_prcmu_dump_secure_regs[i] +
+ SZ_4K,
+ reg_val);
+ if (err < 0) {
+ pr_err("%s: seq_printf overflow,"
+ "offset=%x\n", __func__,
+ u8500_prcmu_dump_secure_regs[i] +
+ SZ_4K);
+ /* Can't do much here */
+ break;
+ }
+ } else {
+ printk(KERN_INFO
+ "0x%04x: 0x%08x\n",
+ u8500_prcmu_dump_secure_regs[i] +
+ SZ_4K,
+ reg_val);
+ }
+ }
+
+ iounmap(prcmu_sec_base);
+ }
+}
+
+void prcmu_debug_dump_regs(void)
+{
+ printk(KERN_INFO "PRCMU registers dump:\n");
+ prcmu_regs_print(NULL);
+ prcmu_secure_regs_print(NULL);
+}
+
+static int prcmu_debugfs_regs_read(struct seq_file *s, void *p)
+{
+ seq_printf(s, "PRCMU registers:\n");
+ prcmu_regs_print(s);
+ prcmu_secure_regs_print(s);
+ return 0;
+}
+
+/* Interrupt debugging */
+
+/* There are eight mailboxes */
+#define NUM_MAILBOXES 8
+#define NUM_MAILBOX0_EVENTS 32
+static u32 num_mailbox_interrupts[NUM_MAILBOXES];
+static u32 num_mailbox0_events[NUM_MAILBOX0_EVENTS];
+static u32 num_mailbox0_events_garbage[NUM_MAILBOX0_EVENTS];
+
+void prcmu_debug_register_interrupt(u32 mailbox)
+{
+ if (mailbox < NUM_MAILBOXES)
+ num_mailbox_interrupts[mailbox]++;
+}
+
+void prcmu_debug_register_mbox0_event(u32 ev, u32 mask)
+{
+ int i;
+
+ for (i = 0 ; i < NUM_MAILBOX0_EVENTS; i++)
+ if (ev & (1 << i)) {
+ if (mask & (1 << i))
+ num_mailbox0_events[i]++;
+ else
+ num_mailbox0_events_garbage[i]++;
+ }
+}
+
+static int interrupt_read(struct seq_file *s, void *p)
+{
+ int i;
+ char **mbox0names;
+
+ static char *mbox0names_u8500[] = {
+ "RTC",
+ "RTT0",
+ "RTT1",
+ "HSI0",
+ "HSI1",
+ "CA_WAKE",
+ "USB",
+ "ABB",
+ "ABB_FIFO",
+ "SYSCLK_OK",
+ "CA_SLEE",
+ "AC_WAKE_ACK",
+ "SIDE_TONE_OK",
+ "ANC_OK",
+ "SW_ERROR",
+ "AC_SLEEP_ACK",
+ NULL,
+ "ARM",
+ "HOTMON_LOW",
+ "HOTMON_HIGH",
+ "MODEM_SW_RESET_REQ",
+ NULL,
+ NULL,
+ "GPIO0",
+ "GPIO1",
+ "GPIO2",
+ "GPIO3",
+ "GPIO4",
+ "GPIO5",
+ "GPIO6",
+ "GPIO7",
+ "GPIO8"};
+ static char *mbox0names_u5500[] = {
+ "RTC",
+ "RTT0",
+ "RTT1",
+ "CD_IRQ",
+ "SRP_TIM",
+ "APE_REQ",
+ "USB",
+ "ABB",
+ "LOW_POWER_AUDIO",
+ "TEMP_SENSOR_LOW",
+ "ARM",
+ "AC_WAKE_ACK",
+ NULL,
+ "TEMP_SENSOR_HIGH",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "MODEM_SW_RESET_REQ",
+ NULL,
+ NULL,
+ "GPIO0",
+ "GPIO1",
+ "GPIO2",
+ "GPIO3",
+ "GPIO4",
+ "GPIO5",
+ "GPIO6",
+ "GPIO7",
+ "AC_REL_ACK"};
+
+ if (cpu_is_u8500()) {
+ mbox0names = mbox0names_u8500;
+ } else if (cpu_is_u5500()) {
+ mbox0names = mbox0names_u5500;
+ } else {
+ seq_printf(s, "Unknown ASIC!\n");
+ return 0;
+ }
+
+ seq_printf(s, "mailbox0: %d\n", num_mailbox_interrupts[0]);
+
+ for (i = 0; i < NUM_MAILBOX0_EVENTS; i++)
+ if (mbox0names[i]) {
+ seq_printf(s, " %20s %d ", mbox0names[i],
+ num_mailbox0_events[i]
+ );
+ if (num_mailbox0_events_garbage[i])
+ seq_printf(s, "unwanted: %d",
+ num_mailbox0_events_garbage[i]);
+ seq_printf(s, "\n");
+ } else if (num_mailbox0_events[i]) {
+ seq_printf(s, " unknown (%d) %d\n",
+ i, num_mailbox0_events[i]);
+ }
+
+ for (i = 1 ; i < NUM_MAILBOXES; i++)
+ seq_printf(s, "mailbox%d: %d\n", i, num_mailbox_interrupts[i]);
+ return 0;
+}
+
+static int opp_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, opp_read, inode->i_private);
+}
+
+static int ape_stats_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, ape_stats_print, inode->i_private);
+}
+
+static int ddr_stats_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, ddr_stats_print, inode->i_private);
+}
+
+static int arm_stats_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, arm_stats_print, inode->i_private);
+}
+
+static int cpufreq_delay_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, cpufreq_delay_read, inode->i_private);
+}
+
+static int ape_voltage_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, ape_voltage_read, inode->i_private);
+}
+
+static int avs_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, avs_read, inode->i_private);
+}
+
+static int prcmu_data_mem_open_file(struct inode *inode, struct file *file)
+{
+ int err;
+ struct seq_file *s;
+
+ err = single_open(file, prcmu_debugfs_data_mem_read, inode->i_private);
+ if (!err) {
+ /* Default buf size in seq_read is not enough */
+ s = (struct seq_file *)file->private_data;
+ s->size = (PAGE_SIZE * 4);
+ s->buf = kmalloc(s->size, GFP_KERNEL);
+ if (!s->buf) {
+ single_release(inode, file);
+ err = -ENOMEM;
+ }
+ }
+ return err;
+}
+
+static int prcmu_regs_open_file(struct inode *inode, struct file *file)
+{
+ int err;
+ struct seq_file *s;
+
+ err = single_open(file, prcmu_debugfs_regs_read, inode->i_private);
+ if (!err) {
+ /* Default buf size in seq_read is not enough */
+ s = (struct seq_file *)file->private_data;
+ s->size = (PAGE_SIZE * 2);
+ s->buf = kmalloc(s->size, GFP_KERNEL);
+ if (!s->buf) {
+ single_release(inode, file);
+ err = -ENOMEM;
+ }
+ }
+ return err;
+}
+
+static int interrupt_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, interrupt_read, inode->i_private);
+}
+
+static const struct file_operations opp_fops = {
+ .open = opp_open_file,
+ .write = opp_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ape_stats_fops = {
+ .open = ape_stats_open_file,
+ .write = ape_stats_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ddr_stats_fops = {
+ .open = ddr_stats_open_file,
+ .write = ddr_stats_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations arm_stats_fops = {
+ .open = arm_stats_open_file,
+ .write = arm_stats_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations cpufreq_delay_fops = {
+ .open = cpufreq_delay_open_file,
+ .write = cpufreq_delay_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ape_voltage_fops = {
+ .open = ape_voltage_open_file,
+ .write = ape_voltage_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations avs_fops = {
+ .open = avs_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations prcmu_data_mem_fops = {
+ .open = prcmu_data_mem_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations prcmu_regs_fops = {
+ .open = prcmu_regs_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations interrupts_fops = {
+ .open = interrupt_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int setup_debugfs(void)
+{
+ struct dentry *dir;
+ struct dentry *file;
+
+ dir = debugfs_create_dir("prcmu", NULL);
+ if (IS_ERR_OR_NULL(dir))
+ goto fail;
+
+ file = debugfs_create_file("ape_stats", (S_IRUGO | S_IWUGO),
+ dir, NULL, &ape_stats_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ddr_stats", (S_IRUGO | S_IWUGO),
+ dir, NULL, &ddr_stats_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("arm_stats", (S_IRUGO | S_IWUGO),
+ dir, NULL, &arm_stats_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ape_opp", (S_IRUGO),
+ dir, (void *)&ape_sh,
+ &opp_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ddr_opp", (S_IRUGO),
+ dir, (void *)&ddr_sh,
+ &opp_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("arm_opp", (S_IRUGO),
+ dir, (void *)&arm_sh,
+ &opp_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("opp_cpufreq_delay", (S_IRUGO),
+ dir, NULL, &cpufreq_delay_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ape_voltage", (S_IRUGO),
+ dir, NULL, &ape_voltage_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("avs",
+ (S_IRUGO),
+ dir, NULL, &avs_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("data_mem", (S_IRUGO),
+ dir, NULL,
+ &prcmu_data_mem_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("regs", (S_IRUGO),
+ dir, NULL,
+ &prcmu_regs_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("interrupts",
+ (S_IRUGO),
+ dir, NULL, &interrupts_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ return 0;
+fail:
+ if (!IS_ERR_OR_NULL(dir))
+ debugfs_remove_recursive(dir);
+
+ pr_err("prcmu debug: debugfs entry failed\n");
+ return -ENOMEM;
+}
+
+static __init int prcmu_debug_init(void)
+{
+ spin_lock_init(&ape_sh.lock);
+ spin_lock_init(&ddr_sh.lock);
+ spin_lock_init(&arm_sh.lock);
+ ape_sh.start = ktime_get();
+ ddr_sh.start = ktime_get();
+ arm_sh.start = ktime_get();
+ return 0;
+}
+arch_initcall(prcmu_debug_init);
+
+static __init int prcmu_debug_debugfs_init(void)
+{
+ setup_debugfs();
+ return 0;
+}
+late_initcall(prcmu_debug_debugfs_init);
diff --git a/arch/arm/mach-ux500/product.c b/arch/arm/mach-ux500/product.c
new file mode 100644
index 00000000000..f4647b3ab29
--- /dev/null
+++ b/arch/arm/mach-ux500/product.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jens Wiklander <jens.wiklander@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/tee.h>
+#include <linux/module.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+
+#define STATIC_TEE_TA_START_LOW 0xBC765EDE
+#define STATIC_TEE_TA_START_MID 0x6724
+#define STATIC_TEE_TA_START_HIGH 0x11DF
+#define STATIC_TEE_TA_START_CLOCKSEQ \
+ {0x8E, 0x12, 0xEC, 0xDB, 0xDF, 0xD7, 0x20, 0x85}
+
+#define U5500_PRCMU_DBG_PWRCTRL (U5500_PRCMU_BASE + 0x4AC)
+#define PRCMU_DBG_PWRCTRL_A9DBGCLKEN (1 << 4)
+
+static struct tee_product_config product_config;
+
+bool ux500_jtag_enabled(void)
+{
+#ifdef CONFIG_UX500_DEBUG_NO_LAUTERBACH
+ return false;
+#else
+ if (machine_is_snowball())
+ return true;
+ if (cpu_is_u5500())
+ return readl_relaxed(__io_address(U5500_PRCMU_DBG_PWRCTRL))
+ & PRCMU_DBG_PWRCTRL_A9DBGCLKEN;
+
+ if (cpu_is_u8500() || cpu_is_u9540())
+ return (product_config.rt_flags & TEE_RT_FLAGS_JTAG_ENABLED) ==
+ TEE_RT_FLAGS_JTAG_ENABLED;
+
+ return true;
+#endif
+}
+
+static int __init product_detect(void)
+{
+ int err;
+ int origin_err;
+ struct tee_operation operation = { { { 0 } } };
+ struct tee_context context;
+ struct tee_session session;
+
+ /* Selects trustzone application needed for the job. */
+ struct tee_uuid static_uuid = {
+ STATIC_TEE_TA_START_LOW,
+ STATIC_TEE_TA_START_MID,
+ STATIC_TEE_TA_START_HIGH,
+ STATIC_TEE_TA_START_CLOCKSEQ,
+ };
+
+ if (cpu_is_u5500())
+ return -ENODEV;
+
+ err = teec_initialize_context(NULL, &context);
+ if (err) {
+ pr_err("ux500-product: unable to initialize tee context,"
+ " err = %d\n", err);
+ err = -EINVAL;
+ goto error0;
+ }
+
+ err = teec_open_session(&context, &session, &static_uuid,
+ TEEC_LOGIN_PUBLIC, NULL, NULL, &origin_err);
+ if (err) {
+ pr_err("ux500-product: unable to open tee session,"
+ " tee error = %d, origin error = %d\n",
+ err, origin_err);
+ err = -EINVAL;
+ goto error1;
+ }
+
+ operation.shm[0].buffer = &product_config;
+ operation.shm[0].size = sizeof(product_config);
+ operation.shm[0].flags = TEEC_MEM_OUTPUT;
+ operation.flags = TEEC_MEMREF_0_USED;
+
+ err = teec_invoke_command(&session,
+ TEE_STA_GET_PRODUCT_CONFIG,
+ &operation, &origin_err);
+ if (err) {
+ pr_err("ux500-product: fetching product settings failed, err=%d",
+ err);
+ err = -EINVAL;
+ goto error1;
+ }
+
+ switch (product_config.product_id) {
+ case TEE_PRODUCT_ID_8400:
+ pr_info("ux500-product: u8400 detected\n");
+ break;
+ case TEE_PRODUCT_ID_8500:
+ pr_info("ux500-product: u8500 detected\n");
+ break;
+ case TEE_PRODUCT_ID_9500:
+ pr_info("ux500-product: u9500 detected\n");
+ break;
+ case TEE_PRODUCT_ID_5500:
+ pr_info("ux500-product: u5500 detected\n");
+ break;
+ case TEE_PRODUCT_ID_7400:
+ pr_info("ux500-product: u7400 detected\n");
+ break;
+ case TEE_PRODUCT_ID_8500C:
+ pr_info("ux500-product: u8500C detected\n");
+ break;
+ case TEE_PRODUCT_ID_UNKNOWN:
+ default:
+ pr_info("ux500-product: UNKNOWN! (0x%x) detected\n",
+ product_config.product_id);
+ break;
+ }
+ pr_info("ux500-product: JTAG is %s\n",
+ ux500_jtag_enabled() ? "enabled" : "disabled");
+error1:
+ (void) teec_finalize_context(&context);
+error0:
+ return err;
+}
+device_initcall(product_detect);
diff --git a/arch/arm/mach-ux500/product.h b/arch/arm/mach-ux500/product.h
new file mode 100644
index 00000000000..502eff4df14
--- /dev/null
+++ b/arch/arm/mach-ux500/product.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jens Wiklander <jens.wiklander@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#ifndef UX500_PRODUCT_H
+#define UX500_PRODUCT_H
+
+#ifdef CONFIG_TEE_UX500
+
+bool ux500_jtag_enabled(void);
+
+#else
+
+static inline bool ux500_jtag_enabled(void)
+{
+ return true;
+}
+
+#endif
+#endif
diff --git a/arch/arm/mach-ux500/regulator-u5500.h b/arch/arm/mach-ux500/regulator-u5500.h
new file mode 100644
index 00000000000..cf3eeed9366
--- /dev/null
+++ b/arch/arm/mach-ux500/regulator-u5500.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __REGULATOR_U5500_H
+#define __REGULATOR_U5500_H
+
+enum u5500_regulator_id {
+ U5500_REGULATOR_VAPE,
+ U5500_REGULATOR_SWITCH_SGA,
+ U5500_REGULATOR_SWITCH_HVA,
+ U5500_REGULATOR_SWITCH_SIA,
+ U5500_REGULATOR_SWITCH_DISP,
+ U5500_REGULATOR_SWITCH_ESRAM12,
+ U5500_NUM_REGULATORS
+};
+
+#endif
diff --git a/arch/arm/mach-ux500/test/Kconfig b/arch/arm/mach-ux500/test/Kconfig
new file mode 100644
index 00000000000..a071166d092
--- /dev/null
+++ b/arch/arm/mach-ux500/test/Kconfig
@@ -0,0 +1,6 @@
+
+config DB8500_PWR_TEST
+ bool "Power usage module test"
+ depends on (UX500_SOC_DB8500 && DEBUG_FS && REGULATOR_AB8500_DEBUG)
+ help
+ Add power module tests for idle
diff --git a/arch/arm/mach-ux500/test/Makefile b/arch/arm/mach-ux500/test/Makefile
new file mode 100644
index 00000000000..58f1f2f3be6
--- /dev/null
+++ b/arch/arm/mach-ux500/test/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DB8500_PWR_TEST) += pwr.o
diff --git a/arch/arm/mach-ux500/test/pwr.c b/arch/arm/mach-ux500/test/pwr.c
new file mode 100644
index 00000000000..5d5d24a38ab
--- /dev/null
+++ b/arch/arm/mach-ux500/test/pwr.c
@@ -0,0 +1,828 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * This is a module test for clocks and regulators.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/db8500-prcmu.h>
+
+#include <mach/hardware.h>
+#include <mach/pm.h>
+
+#include "../../../drivers/regulator/dbx500-prcmu.h"
+#include "../../../drivers/regulator/ab8500-debug.h"
+
+#define PRCC_PCKSR 0x010
+#define PRCC_KCKSR 0x014
+
+#define PRCM_PLLSOC0_ENABLE 0x090
+#define PRCM_PLLSOC1_ENABLE 0x094
+#define PRCM_PLL32K_ENABLE 0x10C
+#define PRCM_PLLARM_ENABLE 0x098
+#define PRCM_PLLDDR_ENABLE 0x09C
+#define PRCM_RNG_ENABLE 0x50C
+#define PRCM_PLLDSI_ENABLE 0x504
+
+#define PRCM_CLKOCR 0x1CC
+
+#define PRCM_ARMCLKFIX_MGT 0x000
+#define PRCM_ACLK_MGT 0x004
+#define PRCM_SGACLK_MGT 0x014
+#define PRCM_UARTCLK_MGT 0x018
+#define PRCM_MSP02CLK_MGT 0x01C
+#define PRCM_MSP1CLK_MGT 0x288
+#define PRCM_I2CCLK_MGT 0x020
+#define PRCM_SDMMCCLK_MGT 0x024
+#define PRCM_SLIMCLK_MGT 0x028
+#define PRCM_PER1CLK_MGT 0x02C
+#define PRCM_PER2CLK_MGT 0x030
+#define PRCM_PER3CLK_MGT 0x034
+#define PRCM_PER5CLK_MGT 0x038
+#define PRCM_PER6CLK_MGT 0x03C
+#define PRCM_PER7CLK_MGT 0x040
+#define PRCM_LCDCLK_MGT 0x044
+#define PRCM_BMLCLK_MGT 0x04C
+#define PRCM_HSITXCLK_MGT 0x050
+#define PRCM_HSIRXCLK_MGT 0x054
+#define PRCM_HDMICLK_MGT 0x058
+#define PRCM_APEATCLK_MGT 0x05C
+#define PRCM_APETRACECLK_MGT 0x060
+#define PRCM_MCDECLK_MGT 0x064
+#define PRCM_IPI2CCLK_MGT 0x068
+#define PRCM_DSIALTCLK_MGT 0x06C
+#define PRCM_SPARE2CLK_MGT 0x070
+#define PRCM_SPARE1CLK_MGT 0x048
+#define PRCM_DMACLK_MGT 0x074
+#define PRCM_B2R2CLK_MGT 0x078
+#define PRCM_TVCLK_MGT 0x07C
+#define PRCM_SSPCLK_MGT 0x280
+#define PRCM_RNGCLK_MGT 0x284
+#define PRCM_UICCCLK_MGT 0x27C
+
+#define PRCM_DSITVCLK_DIV 0x52C
+#define PRCM_DSI_PLLOUT_SEL 0x530
+
+#define PRCM_YYCLKEN0_MGT_VAL 0x520
+
+enum acc_type {
+ RAW = 0,
+ CLK_PRCMU,
+ CLK_PAR_PRCMU,
+ CLK_ABB_SYS,
+ REG_DB8500,
+ REG_AB8500,
+};
+
+struct pwr_test {
+ enum acc_type type;
+ u32 base;
+ u32 off;
+ u32 mask;
+ u32 val;
+
+ u32 par_off;
+ u32 par_mask;
+ u32 par_val;
+
+ char *txt;
+ char *txt2;
+
+ /* For AB8500 */
+ enum ab8500_regulator_mode mode;
+ enum ab8500_regulator_hwmode hwmode;
+ enum hwmode_auto hwmode_auto[4];
+ int volt_selected;
+ int alt_volt_selected;
+ int volt_len;
+ int volt[4];
+ int alt_volt[4];
+};
+
+#define RAW_TEST(_base, _off, _mask, _val) \
+ { \
+ .type = RAW, \
+ .base = _base, \
+ .off = _off, \
+ .mask = _mask, \
+ .val = _val, \
+ .txt = #_base, \
+ .txt2 = #_off \
+ }
+
+#define CLK_TEST_PAR_PRCMU(_base, _off, _mask, _val, _par_off, \
+ _par_mask, _par_val) \
+ { \
+ .type = CLK_PAR_PRCMU, \
+ .base = _base, \
+ .off = _off, \
+ .mask = _mask, \
+ .val = _val, \
+ .par_off = _par_off, \
+ .par_mask = _par_mask, \
+ .par_val = _par_val, \
+ .txt = #_base, \
+ .txt2 = #_off \
+ }
+
+#define CLK_TEST_PRCMU(_off, _mask, _val) \
+ { \
+ .type = CLK_PRCMU, \
+ .off = _off, \
+ .mask = _mask, \
+ .val = _val, \
+ .txt = #_off, \
+ }
+
+#define CLK_TEST_ABB_SYS(_off, _mask, _val) \
+ { \
+ .type = CLK_ABB_SYS, \
+ .off = _off, \
+ .mask = _mask, \
+ .val = _val, \
+ .txt = #_off, \
+ }
+#define REG_TEST_DB8500(_reg, _val) \
+ { \
+ .type = REG_DB8500, \
+ .off = _reg, \
+ .val = _val, \
+ }
+
+
+static struct u8500_regulators
+{
+ struct dbx500_regulator_info *db8500_reg;
+ int db8500_num;
+} u8500_reg;
+
+/*
+ * Idle - Note: this is not suspend.
+ * This test shall pass when the screen is black, before
+ * suspend is executed.
+ */
+
+static struct pwr_test idle_test[] = {
+
+ /* Test all periph kernel and bus clocks */
+ /* bus: gpioctrl */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST1_BASE, PRCC_PCKSR, 0x07ff, BIT(9),
+ PRCM_PER1CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST1_BASE, PRCC_KCKSR, 0x037f, 0x0000,
+ PRCM_PER1CLK_MGT, BIT(8), BIT(8)),
+ /* bus: gpioctrl */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST2_BASE, PRCC_PCKSR, 0x0fff, BIT(11),
+ PRCM_PER2CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST2_BASE, PRCC_KCKSR, 0x00ff, 0x0000,
+ PRCM_PER2CLK_MGT, BIT(8), BIT(8)),
+ /* bus: uart2, gpioctrl - users??, kernel: uart2 */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST3_BASE, PRCC_PCKSR, 0x01ff, BIT(8) | BIT(6),
+ PRCM_PER3CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST3_BASE, PRCC_KCKSR, 0x00fe, BIT(6),
+ PRCM_PER3CLK_MGT, BIT(8), BIT(8)),
+ /* No user on periph5 */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST5_BASE, PRCC_PCKSR, 0x0003, 0x0000,
+ PRCM_PER5CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST5_BASE, PRCC_KCKSR, 0x0000, 0x0000,
+ PRCM_PER5CLK_MGT, BIT(8), BIT(8)),
+ /* bus: MTU0 used for scheduling ApIdle */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST6_BASE, PRCC_PCKSR, 0x00ff, BIT(6),
+ PRCM_PER6CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST6_BASE, PRCC_KCKSR, 0x0001, 0x0000,
+ PRCM_PER6CLK_MGT, BIT(8), BIT(8)),
+
+ /* Test that clkout 1/2 is off */
+ CLK_TEST_PRCMU(PRCM_CLKOCR, 0x3f003f, 0x0000),
+
+ /* Test that ulp clock is off */
+ CLK_TEST_ABB_SYS(AB8500_SYSULPCLKCTRL1, 0xfc, 0x00),
+
+ /* Test that prcm clks are in proper state */
+ CLK_TEST_PRCMU(PRCM_ARMCLKFIX_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_ACLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_SGACLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_UARTCLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_MSP02CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_MSP1CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_I2CCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_SDMMCCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_SLIMCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_PER1CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_PER2CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_PER3CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_PER5CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_PER6CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_PER7CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_LCDCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_BMLCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_HSITXCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_HSIRXCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_HDMICLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_APEATCLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_APETRACECLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_MCDECLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_IPI2CCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_DSIALTCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_DMACLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_B2R2CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_TVCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_SSPCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_RNGCLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_UICCCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_DSITVCLK_DIV, BIT(24) | BIT(25) | BIT(26),
+ 0x000),
+ CLK_TEST_PRCMU(PRCM_DSI_PLLOUT_SEL, (BIT(0) | BIT(1) | BIT(2) |
+ BIT(8) | BIT(9) | BIT(10)),
+ 0x200), /* Will change */
+ CLK_TEST_PRCMU(PRCM_YYCLKEN0_MGT_VAL, 0xBFFFFFFF,
+ (BIT(27) | BIT(23) | BIT(22) | BIT(15) |
+ BIT(13) | BIT(12) | BIT(11) | BIT(5) |
+ BIT(1) | BIT(0))),
+
+ /* Check db8500 regulator settings - enable */
+ REG_TEST_DB8500(DB8500_REGULATOR_VAPE, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VARM, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VMODEM, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VPLL, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VSMPS1, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VSMPS2, 1),
+ REG_TEST_DB8500(DB8500_REGULATOR_VSMPS3, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VRF1, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAMMDSP, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAMMDSPRET, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAPIPE, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAMMDSP, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAMMDSPRET, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAPIPE, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SGA, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_B2R2_MCDE, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM12, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM12RET, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM34, 1),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM34RET, 0),
+
+ /* ab8500 regulators */
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VARM,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_OFF},
+ .volt_selected = 2,
+ /* Voltage and voltage selection depends on ARM_OPP */
+ .alt_volt_selected = 1,
+ .volt_len = 3,
+ .volt = {1350000, 1025000, 750000},
+ .alt_volt = {1250000, 1025000, 750000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VBBP,
+ .mode = AB8500_MODE_ON,
+ .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_INVAL},
+ .volt_selected = 1,
+ .volt_len = 2,
+ .volt = {-300000, 0},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VBBN,
+ .mode = AB8500_MODE_ON,
+ .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_INVAL},
+ .volt_selected = 1,
+ .volt_len = 2,
+ .volt = {300000, 0},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAPE,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_OFF},
+ .volt_selected = 2,
+ /* APE_OPP can get changes by cpufreq */
+ .alt_volt_selected = 1,
+ .volt_len = 3,
+ .volt = {1225000, 1000000, 1200000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VSMPS1,
+ .mode = AB8500_MODE_HW,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_selected = 2,
+ .volt_len = 3,
+ .volt = {1200000, 1200000, 1200000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VSMPS2,
+ .mode = AB8500_MODE_HW,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_ON, HWM_ON, HWM_OFF},
+ .volt_selected = 2,
+ .volt_len = 3,
+ .volt = {1800000, 1800000, 1800000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VSMPS3,
+ .mode = AB8500_MODE_HW,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_selected = 2,
+ .volt_len = 3,
+ .volt = {925000, 1212500, 1200000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VPLL,
+ .mode = AB8500_MODE_HW,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VREFDDR,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VMOD,
+ .mode = AB8500_MODE_OFF,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_selected = 2,
+ .volt_len = 2,
+ .volt = {1125000, 1025000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VEXTSUPPLY1,
+ .mode = AB8500_MODE_LP,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VEXTSUPPLY2,
+ .mode = AB8500_MODE_OFF,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VEXTSUPPLY3,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_OFF, HWM_ON, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VRF1,
+ .mode = AB8500_MODE_HW,
+ .volt_len = 1,
+ .volt = {2150000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VANA,
+ .mode = AB8500_MODE_OFF,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAUX1,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_len = 1,
+ .volt = {2800000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAUX2,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_len = 1,
+ .volt = {3300000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAUX3,
+ .mode = AB8500_MODE_OFF,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_len = 1,
+ .volt = {2910000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VINTCORE,
+ .mode = AB8500_MODE_OFF,
+ .volt_len = 1,
+ .volt = {1250000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VTVOUT,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAUDIO,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VANAMIC1,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VANAMIC2,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VDMIC,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VUSB,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VOTG,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VBUSBIS,
+ .mode = AB8500_MODE_OFF,
+ },
+};
+
+static int read_raw(struct pwr_test *t)
+{
+ u32 res;
+ int i;
+
+ for (i = 0 ; i < 50; i++) {
+ res = readl(__io_address(t->base) + t->off);
+ if ((res & t->mask) == t->val)
+ return 0;
+ msleep(10);
+ }
+
+ pr_err("pwr_test: ERROR: %s %s 0x%x, 0x%x reads 0x%x (masked: 0x%x), "
+ "expected 0x%x (mask: 0x%x)\n",
+ t->txt, t->txt2, t->base, t->off, res,
+ res & t->mask, t->val, t->mask);
+ return -EINVAL;
+}
+
+static int clk_read_par_prcmu(struct pwr_test *t)
+ {
+ u32 res;
+ int i;
+ bool parent_enabled = false;
+
+ /* check if parent clock is in expected configuration (usually PRCC) */
+ for (i = 0 ; i < 50; i++) {
+ res = prcmu_read(t->par_off);
+ if ((res & t->par_mask) == t->par_val) {
+ parent_enabled = true;
+ break;
+ }
+ msleep(10);
+ }
+
+ /* If parent clock is off, but clock is expected to be on --> fail */
+ if (!parent_enabled && t->val != 0) {
+ pr_err("pwr_test: ERROR: PRCMU parent clk of %s, "
+ "0x%x reads 0x%x (masked: 0x%x), "
+ "expected to be on 0x%x (mask: 0x%x)\n",
+ t->txt, t->par_off, res, res & t->par_mask,
+ t->par_val, t->par_mask);
+ return -EINVAL;
+ }
+
+ /* It's ok if parent clock is off and clock is off as well */
+ if (!parent_enabled && t->val == 0)
+ return 0;
+
+ return read_raw(t);
+}
+
+static int clk_read_prcmu(struct pwr_test *t)
+{
+ u32 res;
+ int i;
+
+ for (i = 0 ; i < 50; i++) {
+ res = prcmu_read(t->off);
+ if ((res & t->mask) == t->val)
+ return 0;
+ msleep(10);
+ }
+
+ pr_err("pwr_test: ERROR: %s PRCMU, 0x%x reads 0x%x (masked: 0x%x), "
+ "expected 0x%x (mask: 0x%x)\n",
+ t->txt, t->off, res, res & t->mask, t->val, t->mask);
+ return -EINVAL;
+}
+
+static int clk_read_abb_sys(struct pwr_test *t)
+{
+ int ret;
+ u8 val = 0;
+ int i;
+
+ for (i = 0; i < 50; i++) {
+ ret = ab8500_sysctrl_read(t->off, &val);
+ if (ret < 0) {
+ pr_err("pwr_test: AB8500 access error: %d of "
+ "reg: 0x%x\n", ret, t->off);
+ return ret;
+ }
+ if ((val & (u8)t->mask) == t->val)
+ return 0;
+ msleep(10);
+ }
+
+ pr_err("pwr_test: ERROR: AB8500 register 0x%x %s, reads 0x%x,"
+ " (masked 0x%x) expected 0x%x (mask: 0x%x)\n",
+ t->base, t->txt, val, val & (u8)t->mask, t->val, t->mask);
+
+ return -EINVAL;
+}
+
+static int reg_read_db8500(struct pwr_test *t)
+{
+ int i, j;
+
+ for (j = 0 ; j < u8500_reg.db8500_num ; j++)
+ if (u8500_reg.db8500_reg[j].desc.id == t->off)
+ break;
+
+ if (j == u8500_reg.db8500_num) {
+ pr_err("pwr_test: Invalid db8500 regulator\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 50; i++) {
+
+ /* powerstate is a special case */
+ if (t->off == DB8500_REGULATOR_VAPE) {
+ if ((u8500_reg.db8500_reg[j].is_enabled ||
+ power_state_active_is_enabled()) == t->val)
+ return 0;
+ } else {
+ if (u8500_reg.db8500_reg[j].is_enabled == t->val)
+ return 0;
+ }
+ msleep(10);
+ }
+
+ pr_err("pwr_test: ERROR: DB8500 regulator %s is 0x%x expected 0x%x\n",
+ u8500_reg.db8500_reg[j].desc.name,
+ t->off != DB8500_REGULATOR_VAPE ?
+ u8500_reg.db8500_reg[j].is_enabled :
+ u8500_reg.db8500_reg[j].is_enabled ||
+ power_state_active_is_enabled(),
+ t->val);
+
+ return -EINVAL;
+}
+
+static int reg_read_ab8500(struct pwr_test *t)
+{
+ int i, j;
+ int ret;
+ struct ab8500_debug_regulator_status s;
+
+ for (i = 0; i < 50; i++) {
+ ret = ab8500_regulator_debug_read(t->off, &s);
+
+ if (ret) {
+ pr_err("pwr_test: Fail to read ab8500 regulator\n");
+ return -EINVAL;
+ }
+
+ if (t->mode != s.mode)
+ goto repeat;
+
+ if (t->hwmode != s.hwmode)
+ goto repeat;
+
+ if (t->volt_selected != s.volt_selected &&
+ t->alt_volt_selected != s.volt_selected)
+ goto repeat;
+
+ if (t->volt_len != s.volt_len)
+ goto repeat;
+
+ for (j = 0; j < 4 && s.hwmode != AB8500_HWMODE_NONE; j++)
+ if (s.hwmode_auto[j] != t->hwmode_auto[j])
+ goto repeat;
+
+ for (j = 0; j < s.volt_len; j++)
+ if (s.volt[j] != t->volt[j] &&
+ s.volt[j] != t->alt_volt[j])
+ goto repeat;
+ return 0;
+
+repeat:
+ msleep(10);
+ }
+
+ pr_err("pwr test: ERROR: AB8500 regulator %s ", s.name);
+
+ if (t->mode != s.mode)
+ printk("mode is: %d expected: %d ",
+ s.mode, t->mode);
+
+ if (t->hwmode != s.hwmode)
+ printk("hwmode is: %d expected: %d ",
+ s.hwmode, t->hwmode);
+
+ if (t->volt_selected != s.volt_selected &&
+ t->alt_volt_selected != s.volt_selected) {
+ printk("volt selected is: %d expected: %d ",
+ s.volt_selected, t->volt_selected);
+ if (t->alt_volt_selected)
+ printk("(alt volt: %d) ", t->alt_volt_selected);
+ }
+
+ if (t->volt_len != s.volt_len)
+ printk("volt len is: %d expected: %d ",
+ s.volt_len, t->volt_len);
+
+ for (j = 0; j < 4; j++)
+ if (s.hwmode_auto[j] != t->hwmode_auto[j])
+ break;
+ if (j != 4 && s.hwmode != AB8500_HWMODE_NONE) {
+
+ printk("hwmode auto:: {");
+ for (j = 0; j < 4; j++) {
+ switch(s.hwmode_auto[j]) {
+ case HWM_OFF: { printk("OFF "); break; }
+ case HWM_ON: { printk("ON "); break; }
+ case HWM_INVAL: { printk("INVAL "); break; }
+ }
+ }
+ printk("}, expected: {");
+ for (j = 0; j < 4; j++) {
+ switch(t->hwmode_auto[j]) {
+ case HWM_OFF: { printk("OFF "); break; }
+ case HWM_ON: { printk("ON "); break; }
+ case HWM_INVAL: { printk("INVAL "); break; }
+ }
+ }
+ printk("} ");
+ }
+
+ if (s.volt_len == t->volt_len) {
+ for (j = 0; j < s.volt_len; j++)
+ if (s.volt[j] != t->volt[j] &&
+ t->alt_volt[j] != s.volt[j])
+ break;
+
+ } else {
+ j = 0;
+ }
+
+ if (j != s.volt_len) {
+ printk("voltage: {");
+ for (j = 0; j < s.volt_len; j++)
+ printk("%d ", s.volt[j]);
+
+ if (t->alt_volt) {
+ printk("} alt voltage: {");
+ for (j = 0; j < s.volt_len; j++)
+ printk("%d ", t->alt_volt[j]);
+ }
+ printk("} expected: {");
+ for (j = 0; j < t->volt_len; j++)
+ printk("%d ", t->volt[j]);
+ printk("} ");
+ }
+
+ printk("\n");
+
+ return -EINVAL;
+}
+
+
+static int test_execute(struct pwr_test *t, int len)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0 ; i < len ; i++) {
+ switch (t[i].type) {
+ case CLK_ABB_SYS:
+ err |= clk_read_abb_sys(&t[i]);
+ break;
+ case CLK_PRCMU:
+ err |= clk_read_prcmu(&t[i]);
+ break;
+ case CLK_PAR_PRCMU:
+ err |= clk_read_par_prcmu(&t[i]);
+ break;
+ case RAW:
+ err |= read_raw(&t[i]);
+ break;
+ case REG_DB8500:
+ err |= reg_read_db8500(&t[i]);
+ break;
+ case REG_AB8500:
+ err |= reg_read_ab8500(&t[i]);
+ break;
+ default:
+ break;
+ }
+
+ }
+ return err;
+}
+
+static int pwr_test_idle(struct seq_file *s, void *data)
+{
+ int err;
+
+ err = test_execute(idle_test, ARRAY_SIZE(idle_test));
+
+ seq_printf(s, "%s\n", err == 0 ? "PASS" : "FAIL");
+
+ return 0;
+}
+
+
+int dbx500_regulator_testcase(struct dbx500_regulator_info *regulator_info,
+ int num_regulators)
+{
+ u8500_reg.db8500_reg = regulator_info;
+ u8500_reg.db8500_num = num_regulators;
+ return 0;
+}
+
+static int pwr_test_debugfs_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file,
+ inode->i_private,
+ NULL);
+}
+
+static const struct file_operations pwr_test_debugfs_ops = {
+ .open = pwr_test_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *debugfs_dir;
+
+static int __init pwr_test_init(void)
+{
+ int err = 0;
+ void *err_ptr;
+
+ debugfs_dir = debugfs_create_dir("pwr_test", NULL);
+ if (IS_ERR(debugfs_dir))
+ return PTR_ERR(debugfs_dir);
+
+ err_ptr = debugfs_create_file("idle",
+ S_IFREG | S_IRUGO,
+ debugfs_dir, (void *)pwr_test_idle,
+ &pwr_test_debugfs_ops);
+ if (IS_ERR(err_ptr)) {
+ err = PTR_ERR(err_ptr);
+ goto out;
+ }
+ return 0;
+out:
+ debugfs_remove_recursive(debugfs_dir);
+ return err;
+}
+late_initcall(pwr_test_init);
diff --git a/arch/arm/mach-ux500/timer.c b/arch/arm/mach-ux500/timer.c
index d37df98b5c3..aa76dcec7a6 100644
--- a/arch/arm/mach-ux500/timer.c
+++ b/arch/arm/mach-ux500/timer.c
@@ -7,6 +7,7 @@
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/clksrc-dbx500-prcmu.h>
+#include <linux/clksrc-db5500-mtimer.h>
#include <linux/of.h>
#include <asm/smp_twd.h>
@@ -70,12 +71,18 @@ static void __init ux500_timer_init(void)
* depending on delay which is not yet calibrated. RTC-RTT is in the
* always-on powerdomain and is used as clockevent instead of twd when
* sleeping.
- * The PRCMU timer 4(3 for DB5500) register a clocksource and
- * sched_clock with higher rating then MTU since is always-on.
*
+ * The PRCMU timer 4 (3 for DB5500) registers a clocksource and
+ * sched_clock with higher rating than the MTU since it is
+ * always-on.
+ *
+ * On DB5500, the MTIMER is the best clocksource since, unlike the
+ * PRCMU timer, it doesn't occasionally go backwards.
*/
nmdk_timer_init(mtu_timer_base);
+ if (cpu_is_u5500())
+ db5500_mtimer_init(__io_address(U5500_MTIMER_BASE));
clksrc_dbx500_prcmu_init(prcmu_timer_base);
ux500_twd_init();
}
diff --git a/arch/arm/mach-ux500/uart-db8500.c b/arch/arm/mach-ux500/uart-db8500.c
new file mode 100644
index 00000000000..fad9b9a13df
--- /dev/null
+++ b/arch/arm/mach-ux500/uart-db8500.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/amba/serial.h>
+#include <mach/setup.h>
+#include <mach/hardware.h>
+#include <mach/context.h>
+
+#ifdef CONFIG_UX500_CONTEXT
+
+static struct {
+ struct clk *uart_clk;
+ void __iomem *base;
+ /* dr */
+ /* rsr_err */
+ u32 dma_wm;
+ u32 timeout;
+ /* fr */
+ u32 lcrh_rx;
+ u32 ilpr;
+ u32 ibrd;
+ u32 fbrd;
+ u32 lcrh_tx;
+ u32 cr;
+ u32 ifls;
+ u32 imsc;
+ /* ris */
+ /* mis */
+ /* icr */
+ u32 dmacr;
+ u32 xfcr;
+ u32 xon1;
+ u32 xon2;
+ u32 xoff1;
+ u32 xoff2;
+ /* itcr */
+ /* itip */
+ /* itop */
+ /* tdr */
+ u32 abcr;
+ /* absr */
+ /* abfmt */
+ /* abdr */
+ /* abdfr */
+ /* abmr */
+ u32 abimsc;
+ /* abris */
+ /* abmis */
+ /* abicr */
+ /* id_product_h_xy */
+ /* id_provider */
+ /* periphid0 */
+ /* periphid1 */
+ /* periphid2 */
+ /* periphid3 */
+ /* pcellid0 */
+ /* pcellid1 */
+ /* pcellid2 */
+ /* pcellid3 */
+} context_uart;
+
+static void save_uart(void)
+{
+ void __iomem *membase;
+
+ membase = context_uart.base;
+
+ clk_enable(context_uart.uart_clk);
+
+ context_uart.dma_wm = readl_relaxed(membase + ST_UART011_DMAWM);
+ context_uart.timeout = readl_relaxed(membase + ST_UART011_TIMEOUT);
+ context_uart.lcrh_rx = readl_relaxed(membase + ST_UART011_LCRH_RX);
+ context_uart.ilpr = readl_relaxed(membase + UART01x_ILPR);
+ context_uart.ibrd = readl_relaxed(membase + UART011_IBRD);
+ context_uart.fbrd = readl_relaxed(membase + UART011_FBRD);
+ context_uart.lcrh_tx = readl_relaxed(membase + ST_UART011_LCRH_TX);
+ context_uart.cr = readl_relaxed(membase + UART011_CR);
+ context_uart.ifls = readl_relaxed(membase + UART011_IFLS);
+ context_uart.imsc = readl_relaxed(membase + UART011_IMSC);
+ context_uart.dmacr = readl_relaxed(membase + UART011_DMACR);
+ context_uart.xfcr = readl_relaxed(membase + ST_UART011_XFCR);
+ context_uart.xon1 = readl_relaxed(membase + ST_UART011_XON1);
+ context_uart.xon2 = readl_relaxed(membase + ST_UART011_XON2);
+ context_uart.xoff1 = readl_relaxed(membase + ST_UART011_XOFF1);
+ context_uart.xoff2 = readl_relaxed(membase + ST_UART011_XOFF2);
+ context_uart.abcr = readl_relaxed(membase + ST_UART011_ABCR);
+ context_uart.abimsc = readl_relaxed(membase + ST_UART011_ABIMSC);
+
+ clk_disable(context_uart.uart_clk);
+}
+
+static void restore_uart(void)
+{
+ int cnt;
+ int retries = 100;
+ unsigned int cr;
+ void __iomem *membase;
+ u16 dummy;
+ bool show_warn = false;
+
+ membase = context_uart.base;
+ clk_enable(context_uart.uart_clk);
+
+ writew_relaxed(context_uart.ifls, membase + UART011_IFLS);
+ cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
+
+ writew_relaxed(cr, membase + UART011_CR);
+ writew_relaxed(0, membase + UART011_FBRD);
+ writew_relaxed(1, membase + UART011_IBRD);
+ writew_relaxed(0, membase + ST_UART011_LCRH_RX);
+ if (context_uart.lcrh_tx != ST_UART011_LCRH_RX) {
+ int i;
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX register,
+ * to get this delay write read only register 10 times
+ */
+ for (i = 0; i < 10; ++i)
+ dummy = readw(membase + ST_UART011_LCRH_RX);
+ writew_relaxed(0, membase + ST_UART011_LCRH_TX);
+ }
+ writew(0, membase + UART01x_DR);
+ do {
+ if (!(readw(membase + UART01x_FR) & UART01x_FR_BUSY))
+ break;
+ cpu_relax();
+ } while (retries-- > 0);
+ if (retries < 0)
+ /*
+ * We can't print out a warning here since the uart is
+ * not fully restored. Do it later.
+ */
+ show_warn = true;
+
+ writel_relaxed(context_uart.dma_wm, membase + ST_UART011_DMAWM);
+ writel_relaxed(context_uart.timeout, membase + ST_UART011_TIMEOUT);
+ writel_relaxed(context_uart.lcrh_rx, membase + ST_UART011_LCRH_RX);
+ writel_relaxed(context_uart.ilpr, membase + UART01x_ILPR);
+ writel_relaxed(context_uart.ibrd, membase + UART011_IBRD);
+ writel_relaxed(context_uart.fbrd, membase + UART011_FBRD);
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX register,
+ * to get this delay write read only register 10-3
+ * times, as already there are 3 writes after
+ * ST_UART011_LCRH_RX
+ */
+ for (cnt = 0; cnt < 7; cnt++)
+ dummy = readw(membase + ST_UART011_LCRH_RX);
+
+ writel_relaxed(context_uart.lcrh_tx, membase + ST_UART011_LCRH_TX);
+ writel_relaxed(context_uart.ifls, membase + UART011_IFLS);
+ writel_relaxed(context_uart.dmacr, membase + UART011_DMACR);
+ writel_relaxed(context_uart.xfcr, membase + ST_UART011_XFCR);
+ writel_relaxed(context_uart.xon1, membase + ST_UART011_XON1);
+ writel_relaxed(context_uart.xon2, membase + ST_UART011_XON2);
+ writel_relaxed(context_uart.xoff1, membase + ST_UART011_XOFF1);
+ writel_relaxed(context_uart.xoff2, membase + ST_UART011_XOFF2);
+ writel_relaxed(context_uart.abcr, membase + ST_UART011_ABCR);
+ writel_relaxed(context_uart.abimsc, membase + ST_UART011_ABIMSC);
+ writel_relaxed(context_uart.cr, membase + UART011_CR);
+ writel(context_uart.imsc, membase + UART011_IMSC);
+
+ clk_disable(context_uart.uart_clk);
+
+ if (show_warn)
+ pr_warning("%s:uart tx busy\n", __func__);
+}
+
+static int uart_context_notifier_call(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ switch (event) {
+ case CONTEXT_APE_SAVE:
+ save_uart();
+ break;
+
+ case CONTEXT_APE_RESTORE:
+ restore_uart();
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block uart_context_notifier = {
+ .notifier_call = uart_context_notifier_call,
+};
+
+#define __UART_BASE(soc, x) soc##_UART##x##_BASE
+#define UART_BASE(soc, x) __UART_BASE(soc, x)
+
+static int __init uart_context_notifier_init(void)
+{
+ unsigned long base;
+ static const char clkname[] __initconst
+ = "uart" __stringify(CONFIG_UX500_DEBUG_UART);
+
+ if (cpu_is_u8500())
+ base = UART_BASE(U8500, CONFIG_UX500_DEBUG_UART);
+ else if (cpu_is_u5500())
+ base = UART_BASE(U5500, CONFIG_UX500_DEBUG_UART);
+ else
+ ux500_unknown_soc();
+
+ context_uart.base = ioremap(base, SZ_4K);
+ context_uart.uart_clk = clk_get_sys(clkname, NULL);
+
+ if (IS_ERR(context_uart.uart_clk)) {
+ pr_err("%s:unable to get clk-uart%d\n", __func__,
+ CONFIG_UX500_DEBUG_UART);
+ return -EINVAL;
+ }
+
+ return WARN_ON(context_ape_notifier_register(&uart_context_notifier));
+}
+arch_initcall(uart_context_notifier_init);
+#endif
diff --git a/arch/arm/plat-nomadik/include/plat/mtu.h b/arch/arm/plat-nomadik/include/plat/mtu.h
index 582641f3dc0..489e92723b4 100644
--- a/arch/arm/plat-nomadik/include/plat/mtu.h
+++ b/arch/arm/plat-nomadik/include/plat/mtu.h
@@ -5,5 +5,7 @@ void nmdk_timer_init(void __iomem *base);
void nmdk_clkevt_reset(void);
void nmdk_clksrc_reset(void);
+struct clock_event_device *nmdk_clkevt_get(void);
+
#endif /* __PLAT_MTU_H */
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 5138927a416..6f86f8ca9b0 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -19,13 +19,27 @@ config DW_APB_TIMER
config CLKSRC_DBX500_PRCMU
bool "Clocksource PRCMU Timer"
depends on UX500_SOC_DB5500 || UX500_SOC_DB8500
- default y
+ default y if UX500_SOC_DB8500
help
Use the always on PRCMU Timer as clocksource
config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
- bool "Clocksource PRCMU Timer sched_clock"
- depends on (CLKSRC_DBX500_PRCMU && !NOMADIK_MTU_SCHED_CLOCK)
+ bool
+ depends on CLKSRC_DBX500_PRCMU
+ select HAVE_SCHED_CLOCK
+ help
+ Use the always on PRCMU Timer as sched_clock
+
+config CLKSRC_DB5500_MTIMER
+ bool "Clocksource MTIMER"
+ depends on UX500_SOC_DB5500
default y
help
+ Use the always on MTIMER as clocksource
+
+config CLKSRC_DB5500_MTIMER_SCHED_CLOCK
+ bool
+ depends on CLKSRC_DB5500_MTIMER
+ select HAVE_SCHED_CLOCK
+ help
Use the always on PRCMU Timer as sched_clock
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8d81a1d3265..9b10f6b7536 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
-obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o \ No newline at end of file
+obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
+obj-$(CONFIG_CLKSRC_DB5500_MTIMER) += db5500-mtimer.o
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
index c26c369eb9e..dc71e432dc5 100644
--- a/drivers/clocksource/clksrc-dbx500-prcmu.c
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -14,6 +14,9 @@
*/
#include <linux/clockchips.h>
#include <linux/clksrc-dbx500-prcmu.h>
+#ifdef CONFIG_BOOTTIME
+#include <linux/boottime.h>
+#endif
#include <asm/sched_clock.h>
@@ -68,6 +71,23 @@ static u32 notrace dbx500_prcmu_sched_clock_read(void)
#endif
+#ifdef CONFIG_BOOTTIME
+static unsigned long __init boottime_get_time(void)
+{
+ return div_s64(clocksource_cyc2ns(clocksource_dbx500_prcmu.read(
+ &clocksource_dbx500_prcmu),
+ clocksource_dbx500_prcmu.mult,
+ clocksource_dbx500_prcmu.shift),
+ 1000);
+}
+
+static struct boottime_timer __initdata boottime_timer = {
+ .init = NULL,
+ .get_time = boottime_get_time,
+ .finalize = NULL,
+};
+#endif
+
void __init clksrc_dbx500_prcmu_init(void __iomem *base)
{
clksrc_dbx500_timer_base = base;
@@ -90,4 +110,7 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base)
32, RATE_32K);
#endif
clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K);
+#ifdef CONFIG_BOOTTIME
+ boottime_activate(&boottime_timer);
+#endif
}
diff --git a/drivers/clocksource/db5500-mtimer.c b/drivers/clocksource/db5500-mtimer.c
new file mode 100644
index 00000000000..5e64da19e66
--- /dev/null
+++ b/drivers/clocksource/db5500-mtimer.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/clockchips.h>
+#include <linux/clksrc-db5500-mtimer.h>
+#include <linux/boottime.h>
+
+#include <asm/sched_clock.h>
+
+#define MTIMER_PRIMARY_COUNTER 0x18
+
+static void __iomem *db5500_mtimer_base;
+
+#ifdef CONFIG_CLKSRC_DB5500_MTIMER_SCHED_CLOCK
+static DEFINE_CLOCK_DATA(cd);
+
+unsigned long long notrace sched_clock(void)
+{
+ u32 cyc;
+
+ if (unlikely(!db5500_mtimer_base))
+ return 0;
+
+ cyc = readl_relaxed(db5500_mtimer_base + MTIMER_PRIMARY_COUNTER);
+
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
+
+static void notrace db5500_mtimer_update_sched_clock(void)
+{
+ u32 cyc = readl_relaxed(db5500_mtimer_base + MTIMER_PRIMARY_COUNTER);
+ update_sched_clock(&cd, cyc, (u32)~0);
+}
+#endif
+
+#ifdef CONFIG_BOOTTIME
+static unsigned long __init boottime_get_time(void)
+{
+ return sched_clock();
+}
+
+static struct boottime_timer __initdata boottime_timer = {
+ .init = NULL,
+ .get_time = boottime_get_time,
+ .finalize = NULL,
+};
+#endif
+
+void __init db5500_mtimer_init(void __iomem *base)
+{
+ db5500_mtimer_base = base;
+
+ clocksource_mmio_init(base + MTIMER_PRIMARY_COUNTER, "mtimer", 32768,
+ 400, 32, clocksource_mmio_readl_up);
+
+#ifdef CONFIG_CLKSRC_DB5500_MTIMER_SCHED_CLOCK
+ init_sched_clock(&cd, db5500_mtimer_update_sched_clock,
+ 32, 32768);
+#endif
+ boottime_activate(&boottime_timer);
+}
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 9531fc2eda2..44aa7093235 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -39,7 +39,8 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
##################################################################################
# ARM SoC drivers
-obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
+obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o
+obj-$(CONFIG_UX500_SOC_DB5500) += dbx500-cpufreq.o
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 7f2f149ae40..4de1fff790f 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -376,6 +376,27 @@ show_one(scaling_cur_freq, cur);
static int __cpufreq_set_policy(struct cpufreq_policy *data,
struct cpufreq_policy *policy);
+int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max)
+{
+ int ret;
+ struct cpufreq_policy new_policy;
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+
+ ret = cpufreq_get_policy(&new_policy, cpu);
+ if (ret)
+ return -EINVAL;
+
+ new_policy.min = min;
+ new_policy.max = max;
+
+ ret = __cpufreq_set_policy(policy, &new_policy);
+ policy->user_policy.min = policy->min;
+ policy->user_policy.max = policy->max;
+
+ return ret;
+}
+EXPORT_SYMBOL(cpufreq_update_freq);
+
/**
* cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access
*/
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 836e9b062e5..765256e4904 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -79,7 +79,6 @@ struct cpu_dbs_info_s {
cputime64_t prev_cpu_wall;
cputime64_t prev_cpu_nice;
struct cpufreq_policy *cur_policy;
- struct delayed_work work;
struct cpufreq_frequency_table *freq_table;
unsigned int freq_lo;
unsigned int freq_lo_jiffies;
@@ -95,8 +94,10 @@ struct cpu_dbs_info_s {
struct mutex timer_mutex;
};
static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info);
+static DEFINE_PER_CPU(struct delayed_work, ondemand_work);
static unsigned int dbs_enable; /* number of CPUs using this policy */
+static ktime_t time_stamp;
/*
* dbs_mutex protects dbs_enable in governor start/stop.
@@ -290,22 +291,23 @@ static void update_sampling_rate(unsigned int new_rate)
mutex_lock(&dbs_info->timer_mutex);
- if (!delayed_work_pending(&dbs_info->work)) {
+ if (!delayed_work_pending(&per_cpu(ondemand_work, cpu))) {
mutex_unlock(&dbs_info->timer_mutex);
continue;
}
next_sampling = jiffies + usecs_to_jiffies(new_rate);
- appointed_at = dbs_info->work.timer.expires;
+ appointed_at = per_cpu(ondemand_work, cpu).timer.expires;
if (time_before(next_sampling, appointed_at)) {
mutex_unlock(&dbs_info->timer_mutex);
- cancel_delayed_work_sync(&dbs_info->work);
+ cancel_delayed_work_sync(&per_cpu(ondemand_work, cpu));
mutex_lock(&dbs_info->timer_mutex);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work,
+ schedule_delayed_work_on(dbs_info->cpu,
+ &per_cpu(ondemand_work, cpu),
usecs_to_jiffies(new_rate));
}
@@ -449,6 +451,26 @@ static struct attribute_group dbs_attr_group = {
/************************** sysfs end ************************/
+static bool dbs_sw_coordinated_cpus(void)
+{
+ struct cpu_dbs_info_s *dbs_info;
+ struct cpufreq_policy *policy;
+ int i = 0;
+ int j;
+
+ dbs_info = &per_cpu(od_cpu_dbs_info, 0);
+ policy = dbs_info->cur_policy;
+
+ for_each_cpu(j, policy->cpus) {
+ i++;
+ }
+
+ if (i > 1)
+ return true; /* Dependant CPUs */
+ else
+ return false;
+}
+
static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq)
{
if (dbs_tuners_ins.powersave_bias)
@@ -463,7 +485,6 @@ static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq)
static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
{
unsigned int max_load_freq;
-
struct cpufreq_policy *policy;
unsigned int j;
@@ -598,20 +619,42 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
static void do_dbs_timer(struct work_struct *work)
{
- struct cpu_dbs_info_s *dbs_info =
- container_of(work, struct cpu_dbs_info_s, work.work);
- unsigned int cpu = dbs_info->cpu;
- int sample_type = dbs_info->sample_type;
-
+ struct cpu_dbs_info_s *dbs_info;
+ unsigned int cpu = smp_processor_id();
+ int sample_type;
int delay;
+ bool sample = true;
+
+ /* If SW dependant CPUs, use CPU 0 as leader */
+ if (dbs_sw_coordinated_cpus()) {
+
+ ktime_t time_now;
+ s64 delta_us;
- mutex_lock(&dbs_info->timer_mutex);
+ dbs_info = &per_cpu(od_cpu_dbs_info, 0);
+ mutex_lock(&dbs_info->timer_mutex);
+
+ time_now = ktime_get();
+ delta_us = ktime_us_delta(time_now, time_stamp);
+
+ /* Do nothing if we recently have sampled */
+ if (delta_us < (s64)(dbs_tuners_ins.sampling_rate / 2))
+ sample = false;
+ else
+ time_stamp = time_now;
+ } else {
+ dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+ mutex_lock(&dbs_info->timer_mutex);
+ }
+
+ sample_type = dbs_info->sample_type;
/* Common NORMAL_SAMPLE setup */
dbs_info->sample_type = DBS_NORMAL_SAMPLE;
if (!dbs_tuners_ins.powersave_bias ||
sample_type == DBS_NORMAL_SAMPLE) {
- dbs_check_cpu(dbs_info);
+ if (sample)
+ dbs_check_cpu(dbs_info);
if (dbs_info->freq_lo) {
/* Setup timer for SUB_SAMPLE */
dbs_info->sample_type = DBS_SUB_SAMPLE;
@@ -627,15 +670,17 @@ static void do_dbs_timer(struct work_struct *work)
delay -= jiffies % delay;
}
} else {
- __cpufreq_driver_target(dbs_info->cur_policy,
- dbs_info->freq_lo, CPUFREQ_RELATION_H);
+ if (sample)
+ __cpufreq_driver_target(dbs_info->cur_policy,
+ dbs_info->freq_lo,
+ CPUFREQ_RELATION_H);
delay = dbs_info->freq_lo_jiffies;
}
- schedule_delayed_work_on(cpu, &dbs_info->work, delay);
+ schedule_delayed_work_on(cpu, &per_cpu(ondemand_work, cpu), delay);
mutex_unlock(&dbs_info->timer_mutex);
}
-static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
+static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info, int cpu)
{
/* We want all CPUs to do sampling nearly on same jiffy */
int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
@@ -644,13 +689,18 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
delay -= jiffies % delay;
dbs_info->sample_type = DBS_NORMAL_SAMPLE;
- INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay);
+ cancel_delayed_work_sync(&per_cpu(ondemand_work, cpu));
+ schedule_delayed_work_on(cpu, &per_cpu(ondemand_work, cpu), delay);
+}
+
+static inline void dbs_timer_exit(int cpu)
+{
+ cancel_delayed_work_sync(&per_cpu(ondemand_work, cpu));
}
-static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
+static void dbs_timer_exit_per_cpu(struct work_struct *dummy)
{
- cancel_delayed_work_sync(&dbs_info->work);
+ dbs_timer_exit(smp_processor_id());
}
/*
@@ -676,6 +726,42 @@ static int should_io_be_busy(void)
return 0;
}
+static int __cpuinit cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+ struct device *cpu_dev;
+ struct cpu_dbs_info_s *dbs_info;
+
+ if (dbs_sw_coordinated_cpus())
+ dbs_info = &per_cpu(od_cpu_dbs_info, 0);
+ else
+ dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+
+ cpu_dev = get_cpu_device(cpu);
+ if (cpu_dev) {
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ dbs_timer_init(dbs_info, cpu);
+ break;
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ dbs_timer_exit(cpu);
+ break;
+ case CPU_DOWN_FAILED:
+ case CPU_DOWN_FAILED_FROZEN:
+ dbs_timer_init(dbs_info, cpu);
+ break;
+ }
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata ondemand_cpu_notifier = {
+ .notifier_call = cpu_callback,
+};
+
static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
unsigned int event)
{
@@ -704,9 +790,13 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
if (dbs_tuners_ins.ignore_nice)
j_dbs_info->prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ mutex_init(&j_dbs_info->timer_mutex);
+ INIT_DELAYED_WORK_DEFERRABLE(&per_cpu(ondemand_work, j),
+ do_dbs_timer);
+
+ j_dbs_info->rate_mult = 1;
}
this_dbs_info->cpu = cpu;
- this_dbs_info->rate_mult = 1;
ondemand_powersave_bias_init_cpu(cpu);
/*
* Start the timerschedule work, when this governor
@@ -736,21 +826,46 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
}
mutex_unlock(&dbs_mutex);
- mutex_init(&this_dbs_info->timer_mutex);
- dbs_timer_init(this_dbs_info);
+ /* If SW coordinated CPUs then register notifier */
+ if (dbs_sw_coordinated_cpus()) {
+ register_hotcpu_notifier(&ondemand_cpu_notifier);
+
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_info_s *j_dbs_info;
+
+ j_dbs_info = &per_cpu(od_cpu_dbs_info, 0);
+ dbs_timer_init(j_dbs_info, j);
+ }
+
+ /* Initiate timer time stamp */
+ time_stamp = ktime_get();
+
+
+ } else
+ dbs_timer_init(this_dbs_info, cpu);
break;
case CPUFREQ_GOV_STOP:
- dbs_timer_exit(this_dbs_info);
+
+ dbs_timer_exit(cpu);
mutex_lock(&dbs_mutex);
mutex_destroy(&this_dbs_info->timer_mutex);
dbs_enable--;
mutex_unlock(&dbs_mutex);
- if (!dbs_enable)
+ if (!dbs_enable) {
sysfs_remove_group(cpufreq_global_kobject,
&dbs_attr_group);
+ if (dbs_sw_coordinated_cpus()) {
+ /*
+ * Make sure all pending timers/works are
+ * stopped.
+ */
+ schedule_on_each_cpu(dbs_timer_exit_per_cpu);
+ unregister_hotcpu_notifier(&ondemand_cpu_notifier);
+ }
+ }
break;
case CPUFREQ_GOV_LIMITS:
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c
deleted file mode 100644
index 0bf1b8910ee..00000000000
--- a/drivers/cpufreq/db8500-cpufreq.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) STMicroelectronics 2009
- * Copyright (C) ST-Ericsson SA 2010
- *
- * License Terms: GNU General Public License v2
- * Author: Sundar Iyer <sundar.iyer@stericsson.com>
- * Author: Martin Persson <martin.persson@stericsson.com>
- * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
- *
- */
-#include <linux/kernel.h>
-#include <linux/cpufreq.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/mfd/dbx500-prcmu.h>
-#include <mach/id.h>
-
-static struct cpufreq_frequency_table freq_table[] = {
- [0] = {
- .index = 0,
- .frequency = 200000,
- },
- [1] = {
- .index = 1,
- .frequency = 400000,
- },
- [2] = {
- .index = 2,
- .frequency = 800000,
- },
- [3] = {
- /* Used for MAX_OPP, if available */
- .index = 3,
- .frequency = CPUFREQ_TABLE_END,
- },
- [4] = {
- .index = 4,
- .frequency = CPUFREQ_TABLE_END,
- },
-};
-
-static enum arm_opp idx2opp[] = {
- ARM_EXTCLK,
- ARM_50_OPP,
- ARM_100_OPP,
- ARM_MAX_OPP
-};
-
-static struct freq_attr *db8500_cpufreq_attr[] = {
- &cpufreq_freq_attr_scaling_available_freqs,
- NULL,
-};
-
-static int db8500_cpufreq_verify_speed(struct cpufreq_policy *policy)
-{
- return cpufreq_frequency_table_verify(policy, freq_table);
-}
-
-static int db8500_cpufreq_target(struct cpufreq_policy *policy,
- unsigned int target_freq,
- unsigned int relation)
-{
- struct cpufreq_freqs freqs;
- unsigned int idx;
-
- /* scale the target frequency to one of the extremes supported */
- if (target_freq < policy->cpuinfo.min_freq)
- target_freq = policy->cpuinfo.min_freq;
- if (target_freq > policy->cpuinfo.max_freq)
- target_freq = policy->cpuinfo.max_freq;
-
- /* Lookup the next frequency */
- if (cpufreq_frequency_table_target
- (policy, freq_table, target_freq, relation, &idx)) {
- return -EINVAL;
- }
-
- freqs.old = policy->cur;
- freqs.new = freq_table[idx].frequency;
-
- if (freqs.old == freqs.new)
- return 0;
-
- /* pre-change notification */
- for_each_cpu(freqs.cpu, policy->cpus)
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
-
- /* request the PRCM unit for opp change */
- if (prcmu_set_arm_opp(idx2opp[idx])) {
- pr_err("db8500-cpufreq: Failed to set OPP level\n");
- return -EINVAL;
- }
-
- /* post change notification */
- for_each_cpu(freqs.cpu, policy->cpus)
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
-
- return 0;
-}
-
-static unsigned int db8500_cpufreq_getspeed(unsigned int cpu)
-{
- int i;
- /* request the prcm to get the current ARM opp */
- for (i = 0; prcmu_get_arm_opp() != idx2opp[i]; i++)
- ;
- return freq_table[i].frequency;
-}
-
-static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
-{
- int i, res;
-
- BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
-
- if (prcmu_has_arm_maxopp())
- freq_table[3].frequency = 1000000;
-
- pr_info("db8500-cpufreq : Available frequencies:\n");
- for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
- pr_info(" %d Mhz\n", freq_table[i].frequency/1000);
-
- /* get policy fields based on the table */
- res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
- if (!res)
- cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
- else {
- pr_err("db8500-cpufreq : Failed to read policy table\n");
- return res;
- }
-
- policy->min = policy->cpuinfo.min_freq;
- policy->max = policy->cpuinfo.max_freq;
- policy->cur = db8500_cpufreq_getspeed(policy->cpu);
- policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
-
- /*
- * FIXME : Need to take time measurement across the target()
- * function with no/some/all drivers in the notification
- * list.
- */
- policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */
-
- /* policy sharing between dual CPUs */
- cpumask_copy(policy->cpus, cpu_present_mask);
-
- policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
-
- return 0;
-}
-
-static struct cpufreq_driver db8500_cpufreq_driver = {
- .flags = CPUFREQ_STICKY,
- .verify = db8500_cpufreq_verify_speed,
- .target = db8500_cpufreq_target,
- .get = db8500_cpufreq_getspeed,
- .init = db8500_cpufreq_init,
- .name = "DB8500",
- .attr = db8500_cpufreq_attr,
-};
-
-static int __init db8500_cpufreq_register(void)
-{
- if (!cpu_is_u8500v20_or_later())
- return -ENODEV;
-
- pr_info("cpufreq for DB8500 started\n");
- return cpufreq_register_driver(&db8500_cpufreq_driver);
-}
-device_initcall(db8500_cpufreq_register);
diff --git a/drivers/cpufreq/dbx500-cpufreq.c b/drivers/cpufreq/dbx500-cpufreq.c
new file mode 100644
index 00000000000..a6f991e2fb6
--- /dev/null
+++ b/drivers/cpufreq/dbx500-cpufreq.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <mach/id.h>
+
+static struct cpufreq_frequency_table db8500_freq_table[] = {
+ [0] = {
+ .index = 0,
+ .frequency = 200000,
+ },
+ [1] = {
+ .index = 1,
+ .frequency = 400000,
+ },
+ [2] = {
+ .index = 2,
+ .frequency = 800000,
+ },
+ [3] = {
+ /* Used for MAX_OPP, if available */
+ .index = 3,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+ [4] = {
+ .index = 4,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+};
+
+static struct cpufreq_frequency_table db5500_freq_table[] = {
+ [0] = {
+ .index = 0,
+ .frequency = 200000,
+ },
+ [1] = {
+ .index = 1,
+ .frequency = 396500,
+ },
+ [2] = {
+ .index = 2,
+ .frequency = 793000,
+ },
+ [3] = {
+ .index = 3,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+};
+
+static struct cpufreq_frequency_table *freq_table;
+static int freq_table_len;
+
+static enum arm_opp db8500_idx2opp[] = {
+ ARM_EXTCLK,
+ ARM_50_OPP,
+ ARM_100_OPP,
+ ARM_MAX_OPP
+};
+
+static enum arm_opp db5500_idx2opp[] = {
+ ARM_EXTCLK,
+ ARM_50_OPP,
+ ARM_100_OPP,
+};
+
+static enum arm_opp *idx2opp;
+
+static struct freq_attr *dbx500_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static int dbx500_cpufreq_verify_speed(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, freq_table);
+}
+
+static int dbx500_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct cpufreq_freqs freqs;
+ unsigned int idx;
+
+ /* scale the target frequency to one of the extremes supported */
+ if (target_freq < policy->cpuinfo.min_freq)
+ target_freq = policy->cpuinfo.min_freq;
+ if (target_freq > policy->cpuinfo.max_freq)
+ target_freq = policy->cpuinfo.max_freq;
+
+ /* Lookup the next frequency */
+ if (cpufreq_frequency_table_target
+ (policy, freq_table, target_freq, relation, &idx)) {
+ return -EINVAL;
+ }
+
+ freqs.old = policy->cur;
+ freqs.new = freq_table[idx].frequency;
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ /* pre-change notification */
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ BUG_ON(idx >= freq_table_len);
+
+ /* request the PRCM unit for opp change */
+ if (prcmu_set_arm_opp(idx2opp[idx])) {
+ pr_err("ux500-cpufreq: Failed to set OPP level\n");
+ return -EINVAL;
+ }
+
+ /* post change notification */
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return 0;
+}
+
+static unsigned int dbx500_cpufreq_getspeed(unsigned int cpu)
+{
+ int i;
+ enum arm_opp current_opp;
+
+ current_opp = prcmu_get_arm_opp();
+
+ /* request the prcm to get the current ARM opp */
+ for (i = 0; i < freq_table_len; i++) {
+ if (current_opp == idx2opp[i])
+ return freq_table[i].frequency;
+ }
+
+ pr_err("cpufreq: ERROR: unknown opp %d given from prcmufw!\n",
+ current_opp);
+ BUG_ON(1);
+
+ /*
+ * Better to return something that might be correct than
+ * errno or zero, since clk_get_rate() won't do well with an errno.
+ */
+ return freq_table[0].frequency;
+}
+
+static void __init dbx500_cpufreq_init_maxopp_freq(void)
+{
+ struct prcmu_fw_version *fw_version = prcmu_get_fw_version();
+
+ if ((fw_version == NULL) || !prcmu_has_arm_maxopp())
+ return;
+
+ switch (fw_version->project) {
+ case PRCMU_FW_PROJECT_U8500:
+ case PRCMU_FW_PROJECT_U9500:
+ case PRCMU_FW_PROJECT_U8420:
+ freq_table[3].frequency = 1000000;
+ break;
+ case PRCMU_FW_PROJECT_U8500_C2:
+ case PRCMU_FW_PROJECT_U9500_C2:
+ case PRCMU_FW_PROJECT_U8520:
+ freq_table[3].frequency = 1150000;
+ break;
+ default:
+ break;
+ }
+}
+
+static bool initialized;
+
+static void __init dbx500_cpufreq_early_init(void)
+{
+ if (cpu_is_u5500()) {
+ freq_table = db5500_freq_table;
+ idx2opp = db5500_idx2opp;
+ freq_table_len = ARRAY_SIZE(db5500_freq_table);
+ } else if (cpu_is_u8500()) {
+ freq_table = db8500_freq_table;
+ idx2opp = db8500_idx2opp;
+ dbx500_cpufreq_init_maxopp_freq();
+ freq_table_len = ARRAY_SIZE(db8500_freq_table);
+ if (!prcmu_has_arm_maxopp())
+ freq_table_len--;
+ } else {
+ ux500_unknown_soc();
+ }
+ initialized = true;
+}
+
+/*
+ * This is called from localtimer initialization, via the clk_get_rate() for
+ * the smp_twd clock. This is way before cpufreq is initialized.
+ */
+unsigned long dbx500_cpufreq_getfreq(void)
+{
+ if (!initialized)
+ dbx500_cpufreq_early_init();
+
+ return dbx500_cpufreq_getspeed(0) * 1000;
+}
+
+int dbx500_cpufreq_percent2freq(int percent)
+{
+ int op;
+ int i;
+
+ switch (percent) {
+ case 0:
+ /* Fall through */
+ case 25:
+ op = ARM_EXTCLK;
+ break;
+ case 50:
+ op = ARM_50_OPP;
+ break;
+ case 100:
+ op = ARM_100_OPP;
+ break;
+ case 125:
+ if (cpu_is_u8500() && prcmu_has_arm_maxopp())
+ op = ARM_MAX_OPP;
+ else
+ op = ARM_100_OPP;
+ break;
+ default:
+ pr_err("cpufreq-dbx500: Incorrect arm target value (%d).\n",
+ percent);;
+ return -EINVAL;
+ break;
+ }
+
+ for (i = 0; idx2opp[i] != op && i < freq_table_len; i++)
+ ;
+
+ if (freq_table[i].frequency == CPUFREQ_TABLE_END) {
+ pr_err("cpufreq-dbx500: Matching frequency does not exist!\n");
+ return -EINVAL;
+ }
+
+ return freq_table[i].frequency;
+}
+
+int dbx500_cpufreq_get_limits(int cpu, int r,
+ unsigned int *min, unsigned int *max)
+{
+ int freq;
+ int ret;
+ static int old_freq;
+ struct cpufreq_policy p;
+
+ freq = dbx500_cpufreq_percent2freq(r);
+
+ if (freq < 0)
+ return -EINVAL;
+
+ if (freq != old_freq)
+ pr_debug("cpufreq-dbx500: set min arm freq to %d\n",
+ freq);
+
+ (*min) = freq;
+
+ ret = cpufreq_get_policy(&p, cpu);
+ if (ret) {
+ pr_err("cpufreq-dbx500: Failed to get policy.\n");
+ return -EINVAL;
+ }
+
+ (*max) = p.max;
+ return 0;
+}
+
+static int __cpuinit dbx500_cpufreq_init(struct cpufreq_policy *policy)
+{
+ int res;
+
+ /* get policy fields based on the table */
+ res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+ if (!res)
+ cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
+ else {
+ pr_err("dbx500-cpufreq : Failed to read policy table\n");
+ return res;
+ }
+
+ policy->min = policy->cpuinfo.min_freq;
+ policy->max = policy->cpuinfo.max_freq;
+ policy->cur = dbx500_cpufreq_getspeed(policy->cpu);
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+ /*
+ * FIXME : Need to take time measurement across the target()
+ * function with no/some/all drivers in the notification
+ * list.
+ */
+ policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */
+
+ /* policy sharing between dual CPUs */
+ cpumask_copy(policy->cpus, cpu_present_mask);
+
+ policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+
+ return 0;
+}
+
+static struct cpufreq_driver dbx500_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = dbx500_cpufreq_verify_speed,
+ .target = dbx500_cpufreq_target,
+ .get = dbx500_cpufreq_getspeed,
+ .init = dbx500_cpufreq_init,
+ .name = "DBX500",
+ .attr = dbx500_cpufreq_attr,
+};
+
+static int __init dbx500_cpufreq_register(void)
+{
+ int i;
+
+ if (!initialized)
+ dbx500_cpufreq_early_init();
+
+ pr_info("dbx500-cpufreq : Available frequencies:\n");
+
+ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
+ pr_info(" %d Mhz\n", freq_table[i].frequency / 1000);
+
+ return cpufreq_register_driver(&dbx500_cpufreq_driver);
+}
+device_initcall(dbx500_cpufreq_register);
diff --git a/drivers/mfd/db5500-prcmu-regs.h b/drivers/mfd/db5500-prcmu-regs.h
new file mode 100644
index 00000000000..0428b5e95ae
--- /dev/null
+++ b/drivers/mfd/db5500-prcmu-regs.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __MACH_PRCMU_REGS_DB5500_H
+#define __MACH_PRCMU_REGS_DB5500_H
+
+#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
+
+#define PRCM_TCR 0x1C8
+#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
+#define PRCM_TCR_STOP_TIMERS BIT(16)
+#define PRCM_TCR_DOZE_MODE BIT(17)
+
+/* PRCMU HW semaphore */
+#define PRCM_SEM 0x400
+#define PRCM_SEM_PRCM_SEM BIT(0)
+
+#define DB5500_PRCM_ACLK_MGT 0x004
+#define DB5500_PRCM_SVACLK_MGT 0x008
+#define DB5500_PRCM_SIACLK_MGT 0x00C
+#define DB5500_PRCM_SGACLK_MGT 0x014
+#define DB5500_PRCM_UARTCLK_MGT 0x018
+#define DB5500_PRCM_MSP02CLK_MGT 0x01C
+#define DB5500_PRCM_I2CCLK_MGT 0x020
+#define DB5500_PRCM_SDMMCCLK_MGT 0x024
+#define DB5500_PRCM_PER1CLK_MGT 0x02C
+#define DB5500_PRCM_PER2CLK_MGT 0x030
+#define DB5500_PRCM_PER3CLK_MGT 0x034
+#define DB5500_PRCM_PER5CLK_MGT 0x038
+#define DB5500_PRCM_PER6CLK_MGT 0x03C
+#define DB5500_PRCM_IRDACLK_MGT 0x040
+#define DB5500_PRCM_PWMCLK_MGT 0x044
+#define DB5500_PRCM_SPARE1CLK_MGT 0x048
+#define DB5500_PRCM_IRRCCLK_MGT 0x04C
+#define DB5500_PRCM_HDMICLK_MGT 0x058
+#define DB5500_PRCM_APEATCLK_MGT 0x05C
+#define DB5500_PRCM_APETRACECLK_MGT 0x060
+#define DB5500_PRCM_MCDECLK_MGT 0x064
+#define DB5500_PRCM_DSIALTCLK_MGT 0x06C
+#define DB5500_PRCM_DMACLK_MGT 0x074
+#define DB5500_PRCM_B2R2CLK_MGT 0x078
+#define DB5500_PRCM_TVCLK_MGT 0x07C
+#define DB5500_PRCM_RNGCLK_MGT 0x284
+
+#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLDIV_SHIFT 0
+#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
+#define PRCM_CLK_MGT_CLKEN BIT(8)
+
+#define PRCM_ARM_IT1_CLEAR 0x48C
+#define PRCM_ARM_IT1_VAL 0x494
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL 0x0FC
+#define PRCM_MBOX_CPU_SET 0x100
+
+/* System reset register */
+#define PRCM_APE_SOFTRST 0x228
+
+/* PRCMU clock/PLL/reset registers */
+#define PRCM_PLLDSI_FREQ 0x500
+#define PRCM_PLLDSI_ENABLE 0x504
+#define PRCM_PLLDSI_LOCKP 0x508
+#define PRCM_DSI_PLLOUT_SEL 0x530
+#define PRCM_DSITVCLK_DIV 0x52C
+#define PRCM_APE_RESETN_SET 0x1E4
+#define PRCM_APE_RESETN_CLR 0x1E8
+
+/* CLKOUTx SEL0 settings */
+#define CLKOUT_SEL0_REF_CLK 0x01 /* 0b 0001 */
+#define CLKOUT_SEL0_RTC_CLK0 0x02 /* 0b 0010 */
+#define CLKOUT_SEL0_ULP_CLK 0x04 /* 0b 0100 */
+#define CLKOUT_SEL0_SEL_CLK 0x08 /* 0b 1000 */
+
+/* CLKOUTx SEL settings */
+#define CLKOUT_SEL_STATIC0 0x0001 /* 0b 00 0000 0001 */
+#define CLKOUT_SEL_REFCLK 0x0002 /* 0b 00 0000 0010 */
+#define CLKOUT_SEL_ULPCLK 0x0004 /* 0b 00 0000 0100 */
+#define CLKOUT_SEL_ARMCLK 0x0008 /* 0b 00 0000 1000 */
+#define CLKOUT_SEL_SYSACC0CLK 0x0010 /* 0b 00 0001 0000 */
+#define CLKOUT_SEL_SOC0PLLCLK 0x0020 /* 0b 00 0010 0000 */
+#define CLKOUT_SEL_SOC1PLLCLK 0x0040 /* 0b 00 0100 0000 */
+#define CLKOUT_SEL_DDRPLLCLK 0x0080 /* 0b 00 1000 0000 */
+#define CLKOUT_SEL_TVCLK 0x0100 /* 0b 01 0000 0000 */
+#define CLKOUT_SEL_IRDACLK 0x0200 /* 0b 10 0000 0000 */
+
+/* CLKOUTx dividers */
+#define CLKOUT_DIV_2 0x00 /* 0b 000 */
+#define CLKOUT_DIV_4 0x01 /* 0b 001 */
+#define CLKOUT_DIV_8 0x02 /* 0b 010 */
+#define CLKOUT_DIV_16 0x03 /* 0b 011 */
+#define CLKOUT_DIV_32 0x04 /* 0b 100 */
+#define CLKOUT_DIV_64 0x05 /* 0b 101 */
+/* Values 0x06 and 0x07 will also set the CLKOUTx divider to 64. */
+
+/* PRCM_CLKOCR CLKOUTx Control registers */
+#define PRCM_CLKOCR 0x1CC
+#define PRCM_CLKOCR_CLKOUT0_SEL0_SHIFT 0
+#define PRCM_CLKOCR_CLKOUT0_SEL0_MASK BITS(0, 3)
+#define PRCM_CLKOCR_CLKOUT0_SEL_SHIFT 4
+#define PRCM_CLKOCR_CLKOUT0_SEL_MASK BITS(4, 13)
+#define PRCM_CLKOCR_CLKOUT1_SEL0_SHIFT 16
+#define PRCM_CLKOCR_CLKOUT1_SEL0_MASK BITS(16, 19)
+#define PRCM_CLKOCR_CLKOUT1_SEL_SHIFT 20
+#define PRCM_CLKOCR_CLKOUT1_SEL_MASK BITS(20, 29)
+
+/* PRCM_CLKODIV CLKOUTx Dividers */
+#define PRCM_CLKODIV 0x188
+#define PRCM_CLKODIV_CLKOUT0_DIV_SHIFT 0
+#define PRCM_CLKODIV_CLKOUT0_DIV_MASK BITS(0, 2)
+#define PRCM_CLKODIV_CLKOUT1_DIV_SHIFT 16
+#define PRCM_CLKODIV_CLKOUT1_DIV_MASK BITS(16, 18)
+
+#define PRCM_MMIP_LS_CLAMP_SET 0x420
+#define PRCM_MMIP_LS_CLAMP_CLR 0x424
+#define PRCM_DDR_SUBSYS_APE_MINBW 0x438
+
+/* Miscellaneous unit registers */
+#define PRCM_DSI_SW_RESET 0x324
+#define PRCM_RESOUTN_SET_OFFSET 0x214
+#define PRCM_RESOUTN_CLR_OFFSET 0x218
+
+/* APE - Modem Registers */
+#define PRCM_HOSTACCESS_REQ 0x334
+/* APE - Modem register bit maipulation */
+#define PRCM_HOSTACCESS_REQ_BIT BIT(0)
+#define PRCM_APE_ACK 0x49c
+#define PRCM_APE_ACK_BIT 0x01
+
+/* Watchdog - mtimer registers */
+#define PRCM_TIMER0_RTOS_COMP1_OFFSET 0x4C
+#define PRCM_TIMER0_RTOS_COUNTER_OFFSET 0x40
+#define PRCM_TIMER0_IRQ_EN_SET_OFFSET 0x70
+#define PRCM_TIMER0_IRQ_EN_CLR_OFFSET 0x6C
+#define PRCM_TIMER0_IRQ_RTOS1_SET 0x08
+#define PRCM_TIMER0_IRQ_RTOS1_CLR 0x08
+
+#endif
diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c
index bb115b2f04e..b106632d03c 100644
--- a/drivers/mfd/db5500-prcmu.c
+++ b/drivers/mfd/db5500-prcmu.c
@@ -19,12 +19,21 @@
#include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/regulator/db5500-prcmu.h>
+#include <linux/regulator/machine.h>
#include <linux/interrupt.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db5500-regs.h>
-#include "dbx500-prcmu-regs.h"
+#include <mach/prcmu-debug.h>
+
+#include "db5500-prcmu-regs.h"
+
+#define PRCMU_FW_VERSION_OFFSET 0xA4
+#define PRCM_SW_RST_REASON (tcdm_base + 0xFF8) /* 2 bytes */
#define _PRCM_MB_HEADER (tcdm_base + 0xFE8)
#define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0)
@@ -64,6 +73,52 @@
#define PRCM_ACK_MB6 (tcdm_base + 0xF0C)
#define PRCM_ACK_MB7 (tcdm_base + 0xF08)
+/* Share info */
+#define PRCM_SHARE_INFO (tcdm_base + 0xEC8)
+
+#define PRCM_SHARE_INFO_HOTDOG (PRCM_SHARE_INFO + 62)
+
+/* Mailbox 0 REQs */
+#define PRCM_REQ_MB0_AP_POWER_STATE (PRCM_REQ_MB0 + 0x0)
+#define PRCM_REQ_MB0_ULP_CLOCK_STATE (PRCM_REQ_MB0 + 0x1)
+#define PRCM_REQ_MB0_AP_PLL_STATE (PRCM_REQ_MB0 + 0x2)
+#define PRCM_REQ_MB0_DDR_STATE (PRCM_REQ_MB0 + 0x3)
+#define PRCM_REQ_MB0_ESRAM0_STATE (PRCM_REQ_MB0 + 0x4)
+#define PRCM_REQ_MB0_WAKEUP_DBB (PRCM_REQ_MB0 + 0x8)
+#define PRCM_REQ_MB0_WAKEUP_ABB (PRCM_REQ_MB0 + 0xC)
+
+/* Mailbox 0 ACKs */
+#define PRCM_ACK_MB0_AP_PWRSTTR_STATUS (PRCM_ACK_MB0 + 0x0)
+#define PRCM_ACK_MB0_READ_POINTER (PRCM_ACK_MB0 + 0x1)
+#define PRCM_ACK_MB0_WAKEUP_0_DBB (PRCM_ACK_MB0 + 0x4)
+#define PRCM_ACK_MB0_WAKEUP_0_ABB (PRCM_ACK_MB0 + 0x8)
+#define PRCM_ACK_MB0_WAKEUP_1_DBB (PRCM_ACK_MB0 + 0x28)
+#define PRCM_ACK_MB0_WAKEUP_1_ABB (PRCM_ACK_MB0 + 0x2C)
+#define PRCM_ACK_MB0_EVENT_ABB_NUMBERS 20
+
+/* Request mailbox 1 fields. */
+#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0)
+#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1)
+
+/* Mailbox 1 ACKs */
+#define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0)
+#define PRCM_ACK_MB1_CURRENT_APE_OPP (PRCM_ACK_MB1 + 0x1)
+#define PRCM_ACK_MB1_ARM_VOLT_STATUS (PRCM_ACK_MB1 + 0x2)
+#define PRCM_ACK_MB1_APE_VOLT_STATUS (PRCM_ACK_MB1 + 0x3)
+
+/* Mailbox 2 REQs */
+#define PRCM_REQ_MB2_EPOD_CLIENT (PRCM_REQ_MB2 + 0x0)
+#define PRCM_REQ_MB2_EPOD_STATE (PRCM_REQ_MB2 + 0x1)
+#define PRCM_REQ_MB2_CLK_CLIENT (PRCM_REQ_MB2 + 0x2)
+#define PRCM_REQ_MB2_CLK_STATE (PRCM_REQ_MB2 + 0x3)
+#define PRCM_REQ_MB2_PLL_CLIENT (PRCM_REQ_MB2 + 0x4)
+#define PRCM_REQ_MB2_PLL_STATE (PRCM_REQ_MB2 + 0x5)
+
+/* Mailbox 2 ACKs */
+#define PRCM_ACK_MB2_EPOD_STATUS (PRCM_ACK_MB2 + 0x2)
+#define PRCM_ACK_MB2_CLK_STATUS (PRCM_ACK_MB2 + 0x6)
+#define PRCM_ACK_MB2_PLL_STATUS (PRCM_ACK_MB2 + 0xA)
+
enum mb_return_code {
RC_SUCCESS,
RC_FAIL,
@@ -71,12 +126,58 @@ enum mb_return_code {
/* Mailbox 0 headers. */
enum mb0_header {
- /* request */
- RMB0H_PWR_STATE_TRANS = 1,
- RMB0H_WAKE_UP_CFG,
- RMB0H_RD_WAKE_UP_ACK,
/* acknowledge */
- AMB0H_WAKE_UP = 1,
+ MB0H_WAKE_UP = 0,
+ /* request */
+ MB0H_PWR_STATE_TRANS,
+ MB0H_WAKE_UP_CFG,
+ MB0H_RD_WAKE_UP_ACK,
+};
+
+/* Mailbox 1 headers.*/
+enum mb1_header {
+ MB1H_ARM_OPP = 1,
+ MB1H_APE_OPP,
+ MB1H_ARM_APE_OPP,
+};
+
+/* Mailbox 2 headers. */
+enum mb2_header {
+ MB2H_EPOD_REQUEST = 1,
+ MB2H_CLK_REQUEST,
+ MB2H_PLL_REQUEST,
+};
+
+/* Mailbox 3 headers. */
+enum mb3_header {
+ MB3H_REFCLK_REQUEST = 1,
+};
+
+enum sysclk_state {
+ SYSCLK_OFF,
+ SYSCLK_ON,
+};
+
+/* Mailbox 4 headers */
+enum mb4_header {
+ MB4H_CFG_HOTDOG = 7,
+ MB4H_CFG_HOTMON = 8,
+ MB4H_CFG_HOTPERIOD = 10,
+ MB4H_CGF_MODEM_RESET = 13,
+ MB4H_CGF_A9WDOG_EN_PREBARK = 14,
+ MB4H_CGF_A9WDOG_EN_NOPREBARK = 15,
+ MB4H_CGF_A9WDOG_DIS = 16,
+};
+
+/* Mailbox 4 ACK headers */
+enum mb4_ack_header {
+ MB4H_ACK_CFG_HOTDOG = 5,
+ MB4H_ACK_CFG_HOTMON = 6,
+ MB4H_ACK_CFG_HOTPERIOD = 8,
+ MB4H_ACK_CFG_MODEM_RESET = 11,
+ MB4H_ACK_CGF_A9WDOG_EN_PREBARK = 12,
+ MB4H_ACK_CGF_A9WDOG_EN_NOPREBARK = 13,
+ MB4H_ACK_CGF_A9WDOG_DIS = 14,
};
/* Mailbox 5 headers. */
@@ -85,6 +186,69 @@ enum mb5_header {
MB5H_I2C_READ,
};
+enum db5500_arm_opp {
+ DB5500_ARM_100_OPP = 1,
+ DB5500_ARM_50_OPP,
+ DB5500_ARM_EXT_OPP,
+};
+
+enum db5500_ape_opp {
+ DB5500_APE_100_OPP = 1,
+ DB5500_APE_50_OPP
+};
+
+enum epod_state {
+ EPOD_OFF,
+ EPOD_ON,
+};
+enum epod_onoffret_state {
+ EPOD_OOR_OFF,
+ EPOD_OOR_RET,
+ EPOD_OOR_ON,
+};
+enum db5500_prcmu_pll {
+ DB5500_PLL_SOC0,
+ DB5500_PLL_SOC1,
+ DB5500_PLL_DDR,
+ DB5500_NUM_PLL_ID,
+};
+
+enum db5500_prcmu_clk {
+ DB5500_MSP1CLK,
+ DB5500_CDCLK,
+ DB5500_IRDACLK,
+ DB5500_TVCLK,
+ DB5500_NUM_CLK_CLIENTS,
+};
+
+enum on_off_ret {
+ OFF_ST,
+ RET_ST,
+ ON_ST,
+};
+
+enum db5500_ap_pwr_state {
+ DB5500_AP_SLEEP = 2,
+ DB5500_AP_DEEP_SLEEP,
+ DB5500_AP_IDLE,
+};
+
+/* Request mailbox 3 fields */
+#define PRCM_REQ_MB3_REFCLK_MGT (PRCM_REQ_MB3 + 0x0)
+
+/* Ack. mailbox 3 fields */
+#define PRCM_ACK_MB3_REFCLK_REQ (PRCM_ACK_MB3 + 0x0)
+
+
+/* Request mailbox 4 fields */
+#define PRCM_REQ_MB4_HOTDOG_THRESHOLD (PRCM_REQ_MB4 + 32)
+#define PRCM_REQ_MB4_HOT_PERIOD (PRCM_REQ_MB4 + 34)
+#define PRCM_REQ_MB4_HOTMON_LOW (PRCM_REQ_MB4 + 36)
+#define PRCM_REQ_MB4_HOTMON_HIGH (PRCM_REQ_MB4 + 38)
+
+/* Ack. mailbox 4 field */
+#define PRCM_ACK_MB4_REQUESTS (PRCM_ACK_MB4 + 0x0)
+
/* Request mailbox 5 fields. */
#define PRCM_REQ_MB5_I2C_SLAVE (PRCM_REQ_MB5 + 0)
#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 1)
@@ -105,11 +269,12 @@ enum mb5_header {
#define PRCMU_RESET_DSIPLL 0x00004000
#define PRCMU_UNCLAMP_DSIPLL 0x00400800
-/* HDMI CLK MGT PLLSW=001 (PLLSOC0), PLLDIV=0x8, = 50 Mhz*/
-#define PRCMU_DSI_CLOCK_SETTING 0x00000128
+/* HDMI CLK MGT PLLSW=001 (PLLSOC0), PLLDIV=0xC, = 33.33 Mhz*/
+#define PRCMU_DSI_CLOCK_SETTING 0x0000012C
/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135
-#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121
+/* PRCM_PLLDSI_FREQ R=4, N=1, D= 0x65 */
+#define PRCMU_PLLDSI_FREQ_SETTING 0x00040165
#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002
#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201
#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101
@@ -125,13 +290,176 @@ enum mb5_header {
#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
/*
+ * Wakeups/IRQs
+ */
+
+#define WAKEUP_BIT_RTC BIT(0)
+#define WAKEUP_BIT_RTT0 BIT(1)
+#define WAKEUP_BIT_RTT1 BIT(2)
+#define WAKEUP_BIT_CD_IRQ BIT(3)
+#define WAKEUP_BIT_SRP_TIM BIT(4)
+#define WAKEUP_BIT_APE_REQ BIT(5)
+#define WAKEUP_BIT_USB BIT(6)
+#define WAKEUP_BIT_ABB BIT(7)
+#define WAKEUP_BIT_LOW_POWER_AUDIO BIT(8)
+#define WAKEUP_BIT_TEMP_SENSOR_LOW BIT(9)
+#define WAKEUP_BIT_ARM BIT(10)
+#define WAKEUP_BIT_AC_WAKE_ACK BIT(11)
+#define WAKEUP_BIT_TEMP_SENSOR_HIGH BIT(12)
+#define WAKEUP_BIT_MODEM_SW_RESET_REQ BIT(20)
+#define WAKEUP_BIT_GPIO0 BIT(23)
+#define WAKEUP_BIT_GPIO1 BIT(24)
+#define WAKEUP_BIT_GPIO2 BIT(25)
+#define WAKEUP_BIT_GPIO3 BIT(26)
+#define WAKEUP_BIT_GPIO4 BIT(27)
+#define WAKEUP_BIT_GPIO5 BIT(28)
+#define WAKEUP_BIT_GPIO6 BIT(29)
+#define WAKEUP_BIT_GPIO7 BIT(30)
+#define WAKEUP_BIT_AC_REL_ACK BIT(30)
+
+/*
+ * This vector maps irq numbers to the bits in the bit field used in
+ * communication with the PRCMU firmware.
+ *
+ * The reason for having this is to keep the irq numbers contiguous even though
+ * the bits in the bit field are not. (The bits also have a tendency to move
+ * around, to further complicate matters.)
+ */
+#define IRQ_INDEX(_name) ((IRQ_DB5500_PRCMU_##_name) - IRQ_DB5500_PRCMU_BASE)
+#define IRQ_ENTRY(_name)[IRQ_INDEX(_name)] = (WAKEUP_BIT_##_name)
+static u32 prcmu_irq_bit[NUM_DB5500_PRCMU_WAKEUPS] = {
+ IRQ_ENTRY(RTC),
+ IRQ_ENTRY(RTT0),
+ IRQ_ENTRY(RTT1),
+ IRQ_ENTRY(CD_IRQ),
+ IRQ_ENTRY(SRP_TIM),
+ IRQ_ENTRY(APE_REQ),
+ IRQ_ENTRY(USB),
+ IRQ_ENTRY(ABB),
+ IRQ_ENTRY(LOW_POWER_AUDIO),
+ IRQ_ENTRY(TEMP_SENSOR_LOW),
+ IRQ_ENTRY(TEMP_SENSOR_HIGH),
+ IRQ_ENTRY(ARM),
+ IRQ_ENTRY(AC_WAKE_ACK),
+ IRQ_ENTRY(MODEM_SW_RESET_REQ),
+ IRQ_ENTRY(GPIO0),
+ IRQ_ENTRY(GPIO1),
+ IRQ_ENTRY(GPIO2),
+ IRQ_ENTRY(GPIO3),
+ IRQ_ENTRY(GPIO4),
+ IRQ_ENTRY(GPIO5),
+ IRQ_ENTRY(GPIO6),
+ IRQ_ENTRY(GPIO7),
+ IRQ_ENTRY(AC_REL_ACK),
+};
+
+#define VALID_WAKEUPS (BIT(NUM_PRCMU_WAKEUP_INDICES) - 1)
+#define WAKEUP_ENTRY(_name)[PRCMU_WAKEUP_INDEX_##_name] = (WAKEUP_BIT_##_name)
+static u32 prcmu_wakeup_bit[NUM_PRCMU_WAKEUP_INDICES] = {
+ WAKEUP_ENTRY(RTC),
+ WAKEUP_ENTRY(RTT0),
+ WAKEUP_ENTRY(RTT1),
+ WAKEUP_ENTRY(CD_IRQ),
+ WAKEUP_ENTRY(USB),
+ WAKEUP_ENTRY(ABB),
+ WAKEUP_ENTRY(ARM)
+};
+
+/*
* mb0_transfer - state needed for mailbox 0 communication.
- * @lock: The transaction lock.
+ * @lock The transaction lock.
+ * @dbb_irqs_lock lock used for (un)masking DBB wakeup interrupts
+ * @mask_work: Work structure used for (un)masking wakeup interrupts.
+ * @ac_wake_lock: mutex to lock modem_req and modem_rel
+ * @req: Request data that need to persist between requests.
*/
static struct {
spinlock_t lock;
+ spinlock_t dbb_irqs_lock;
+ struct work_struct mask_work;
+ struct mutex ac_wake_lock;
+ struct {
+ u32 dbb_irqs;
+ u32 dbb_wakeups;
+ u32 abb_events;
+ } req;
} mb0_transfer;
+
+/*
+ * mb1_transfer - state needed for mailbox 1 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @req_arm_opp Requested arm opp
+ * @req_ape_opp Requested ape opp
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ u8 req_arm_opp;
+ u8 req_ape_opp;
+ struct {
+ u8 header;
+ u8 arm_opp;
+ u8 ape_opp;
+ u8 arm_voltage_st;
+ u8 ape_voltage_st;
+ } ack;
+} mb1_transfer;
+
+/*
+ * mb2_transfer - state needed for mailbox 2 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @req: Request data that need to persist between requests.
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ struct {
+ u8 epod_st[DB5500_NUM_EPOD_ID];
+ u8 pll_st[DB5500_NUM_PLL_ID];
+ } req;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb2_transfer;
+
+/*
+ * mb3_transfer - state needed for mailbox 3 communication.
+ * @sysclk_lock: A lock used to handle concurrent sysclk requests.
+ * @sysclk_work: Work structure used for sysclk requests.
+ * @req_st: Requested clock state.
+ * @ack: Acknowledgement data
+ */
+static struct {
+ struct mutex sysclk_lock;
+ struct completion sysclk_work;
+ enum sysclk_state req_st;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb3_transfer;
+
+/*
+ * mb4_transfer - state needed for mailbox 4 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @ack: Acknowledgement data
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb4_transfer;
+
/*
* mb5_transfer - state needed for mailbox 5 communication.
* @lock: The transaction lock.
@@ -148,9 +476,825 @@ static struct {
} ack;
} mb5_transfer;
-/* PRCMU TCDM base IO address. */
+/* Spinlocks */
+static DEFINE_SPINLOCK(clkout_lock);
+
+/* PRCMU TCDM base IO address */
static __iomem void *tcdm_base;
+/* PRCMU MTIMER base IO address */
+static __iomem void *mtimer_base;
+
+struct clk_mgt {
+ unsigned int offset;
+ u32 pllsw;
+ u32 div;
+ bool scalable;
+ bool force50;
+};
+
+/* PRCMU Firmware Details */
+static struct {
+ u16 board;
+ u8 fw_version;
+ u8 api_version;
+} prcmu_version;
+
+static struct {
+ u32 timeout;
+ bool enabled;
+} a9wdog_timer;
+
+static DEFINE_SPINLOCK(clk_mgt_lock);
+
+#define CLK_MGT_ENTRY(_name, _scalable)[PRCMU_##_name] = { \
+ .offset = DB5500_PRCM_##_name##_MGT, \
+ .scalable = _scalable, \
+}
+
+static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
+ CLK_MGT_ENTRY(SGACLK, true),
+ CLK_MGT_ENTRY(UARTCLK, false),
+ CLK_MGT_ENTRY(MSP02CLK, false),
+ CLK_MGT_ENTRY(I2CCLK, false),
+ [PRCMU_SDMMCCLK] {
+ .offset = DB5500_PRCM_SDMMCCLK_MGT,
+ .force50 = true,
+ .scalable = false,
+
+ },
+ [PRCMU_SPARE1CLK] {
+ .offset = DB5500_PRCM_SPARE1CLK_MGT,
+ .force50 = true,
+ .scalable = false,
+
+ },
+ CLK_MGT_ENTRY(PER1CLK, false),
+ CLK_MGT_ENTRY(PER2CLK, true),
+ CLK_MGT_ENTRY(PER3CLK, true),
+ CLK_MGT_ENTRY(PER5CLK, false), /* used for SPI */
+ CLK_MGT_ENTRY(PER6CLK, true),
+ CLK_MGT_ENTRY(PWMCLK, false),
+ CLK_MGT_ENTRY(IRDACLK, false),
+ CLK_MGT_ENTRY(IRRCCLK, false),
+ CLK_MGT_ENTRY(HDMICLK, false),
+ CLK_MGT_ENTRY(APEATCLK, false),
+ CLK_MGT_ENTRY(APETRACECLK, true),
+ CLK_MGT_ENTRY(MCDECLK, true),
+ CLK_MGT_ENTRY(DSIALTCLK, false),
+ CLK_MGT_ENTRY(DMACLK, true),
+ CLK_MGT_ENTRY(B2R2CLK, true),
+ CLK_MGT_ENTRY(TVCLK, false),
+ CLK_MGT_ENTRY(RNGCLK, false),
+ CLK_MGT_ENTRY(SIACLK, false),
+ CLK_MGT_ENTRY(SVACLK, false),
+ CLK_MGT_ENTRY(ACLK, true),
+};
+
+static atomic_t modem_req_state = ATOMIC_INIT(0);
+
+bool db5500_prcmu_is_modem_requested(void)
+{
+ return (atomic_read(&modem_req_state) != 0);
+}
+
+/**
+ * prcmu_modem_req - APE requests Modem to wake up
+ *
+ * Whenever APE wants to send message to the modem, it will have to call this
+ * function to make sure that modem is awake.
+ */
+void prcmu_modem_req(void)
+{
+ u32 val;
+
+ mutex_lock(&mb0_transfer.ac_wake_lock);
+
+ val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ if (val & PRCM_HOSTACCESS_REQ_BIT)
+ goto unlock_and_return;
+
+ writel((val | PRCM_HOSTACCESS_REQ_BIT),
+ (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+ atomic_set(&modem_req_state, 1);
+
+unlock_and_return:
+ mutex_unlock(&mb0_transfer.ac_wake_lock);
+
+}
+
+/**
+ * prcmu_modem_rel - APE has no more messages to send and hence releases modem.
+ *
+ * APE to Modem communication is initiated by modem_req and once the
+ * communication is completed, APE sends modem_rel to complete the protocol.
+ */
+void prcmu_modem_rel(void)
+{
+ u32 val;
+
+ mutex_lock(&mb0_transfer.ac_wake_lock);
+
+ val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ if (!(val & PRCM_HOSTACCESS_REQ_BIT))
+ goto unlock_and_return;
+
+ writel((val & ~PRCM_HOSTACCESS_REQ_BIT),
+ (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+
+ atomic_set(&modem_req_state, 0);
+
+unlock_and_return:
+ mutex_unlock(&mb0_transfer.ac_wake_lock);
+}
+
+/**
+ * prcm_ape_ack - send an acknowledgement to modem
+ *
+ * On ape receiving ape_req, APE will have to acknowledge for the interrupt
+ * received. This function will send the acknowledgement by writing to the
+ * prcmu register and an interrupt is trigerred to modem.
+ */
+void prcmu_ape_ack(void)
+{
+ writel(PRCM_APE_ACK_BIT, (_PRCMU_BASE + PRCM_APE_ACK));
+}
+
+/**
+ * db5500_prcmu_modem_reset - Assert a Reset on modem
+ *
+ * This function will assert a reset request to the modem. Prior to that
+ * PRCM_HOSTACCESS_REQ must be '0'.
+ */
+void db5500_prcmu_modem_reset(void)
+{
+ mutex_lock(&mb4_transfer.lock);
+
+ /* PRCM_HOSTACCESS_REQ = 0, before asserting a reset */
+ prcmu_modem_rel();
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writeb(MB4H_CGF_MODEM_RESET, PRCM_REQ_MB4_HEADER);
+ writel(MBOX_BIT(4), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ wait_for_completion(&mb4_transfer.work);
+ if (mb4_transfer.ack.status != RC_SUCCESS ||
+ mb4_transfer.ack.header != MB4H_CGF_MODEM_RESET)
+ printk(KERN_ERR,
+ "ACK not received for modem reset interrupt\n");
+ mutex_unlock(&mb4_transfer.lock);
+}
+
+/**
+ * prcmu_config_clkout - Configure one of the programmable clock outputs.
+ * @clkout: The CLKOUT number (0 or 1).
+ * @source: Clock source.
+ * @div: The divider to be applied.
+ *
+ * Configures one of the programmable clock outputs (CLKOUTs).
+ */
+int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
+{
+ static bool configured[2] = {false, false};
+ int r = 0;
+ unsigned long flags;
+ u32 sel_val;
+ u32 div_val;
+ u32 sel_bits;
+ u32 div_bits;
+ u32 sel_mask;
+ u32 div_mask;
+ u8 sel0 = CLKOUT_SEL0_SEL_CLK;
+ u16 sel = 0;
+
+ BUG_ON(clkout > DB5500_CLKOUT1);
+ BUG_ON(source > DB5500_CLKOUT_IRDACLK);
+ BUG_ON(div > 7);
+
+ switch (source) {
+ case DB5500_CLKOUT_REF_CLK_SEL0:
+ sel0 = CLKOUT_SEL0_REF_CLK;
+ break;
+ case DB5500_CLKOUT_RTC_CLK0_SEL0:
+ sel0 = CLKOUT_SEL0_RTC_CLK0;
+ break;
+ case DB5500_CLKOUT_ULP_CLK_SEL0:
+ sel0 = CLKOUT_SEL0_ULP_CLK;
+ break;
+ case DB5500_CLKOUT_STATIC0:
+ sel = CLKOUT_SEL_STATIC0;
+ break;
+ case DB5500_CLKOUT_REFCLK:
+ sel = CLKOUT_SEL_REFCLK;
+ break;
+ case DB5500_CLKOUT_ULPCLK:
+ sel = CLKOUT_SEL_ULPCLK;
+ break;
+ case DB5500_CLKOUT_ARMCLK:
+ sel = CLKOUT_SEL_ARMCLK;
+ break;
+ case DB5500_CLKOUT_SYSACC0CLK:
+ sel = CLKOUT_SEL_SYSACC0CLK;
+ break;
+ case DB5500_CLKOUT_SOC0PLLCLK:
+ sel = CLKOUT_SEL_SOC0PLLCLK;
+ break;
+ case DB5500_CLKOUT_SOC1PLLCLK:
+ sel = CLKOUT_SEL_SOC1PLLCLK;
+ break;
+ case DB5500_CLKOUT_DDRPLLCLK:
+ sel = CLKOUT_SEL_DDRPLLCLK;
+ break;
+ case DB5500_CLKOUT_TVCLK:
+ sel = CLKOUT_SEL_TVCLK;
+ break;
+ case DB5500_CLKOUT_IRDACLK:
+ sel = CLKOUT_SEL_IRDACLK;
+ break;
+ }
+
+ switch (clkout) {
+ case DB5500_CLKOUT0:
+ sel_mask = PRCM_CLKOCR_CLKOUT0_SEL0_MASK |
+ PRCM_CLKOCR_CLKOUT0_SEL_MASK;
+ sel_bits = ((sel0 << PRCM_CLKOCR_CLKOUT0_SEL0_SHIFT) |
+ (sel << PRCM_CLKOCR_CLKOUT0_SEL_SHIFT));
+ div_mask = PRCM_CLKODIV_CLKOUT0_DIV_MASK;
+ div_bits = div << PRCM_CLKODIV_CLKOUT0_DIV_SHIFT;
+ break;
+ case DB5500_CLKOUT1:
+ sel_mask = PRCM_CLKOCR_CLKOUT1_SEL0_MASK |
+ PRCM_CLKOCR_CLKOUT1_SEL_MASK;
+ sel_bits = ((sel0 << PRCM_CLKOCR_CLKOUT1_SEL0_SHIFT) |
+ (sel << PRCM_CLKOCR_CLKOUT1_SEL_SHIFT));
+ div_mask = PRCM_CLKODIV_CLKOUT1_DIV_MASK;
+ div_bits = div << PRCM_CLKODIV_CLKOUT1_DIV_SHIFT;
+ break;
+ }
+
+ spin_lock_irqsave(&clkout_lock, flags);
+
+ if (configured[clkout]) {
+ r = -EINVAL;
+ goto unlock_and_return;
+ }
+
+ sel_val = readl(_PRCMU_BASE + PRCM_CLKOCR);
+ writel((sel_bits | (sel_val & ~sel_mask)),
+ (_PRCMU_BASE + PRCM_CLKOCR));
+
+ div_val = readl(_PRCMU_BASE + PRCM_CLKODIV);
+ writel((div_bits | (div_val & ~div_mask)),
+ (_PRCMU_BASE + PRCM_CLKODIV));
+
+ configured[clkout] = true;
+
+unlock_and_return:
+ spin_unlock_irqrestore(&clkout_lock, flags);
+
+ return r;
+}
+
+static int request_sysclk(bool enable)
+{
+ int r;
+
+ r = 0;
+ mutex_lock(&mb3_transfer.sysclk_lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
+ cpu_relax();
+
+ if (enable)
+ mb3_transfer.req_st = SYSCLK_ON;
+ else
+ mb3_transfer.req_st = SYSCLK_OFF;
+
+ writeb(mb3_transfer.req_st, (PRCM_REQ_MB3_REFCLK_MGT));
+
+ writeb(MB3H_REFCLK_REQUEST, (PRCM_REQ_MB3_HEADER));
+ writel(MBOX_BIT(3), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ /*
+ * The firmware only sends an ACK if we want to enable the
+ * SysClk, and it succeeds.
+ */
+ if (!wait_for_completion_timeout(&mb3_transfer.sysclk_work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ __func__);
+ r = -EIO;
+ WARN(1, "Failed to set sysclk");
+ goto unlock_and_return;
+ }
+
+ if ((mb3_transfer.ack.header != MB3H_REFCLK_REQUEST) ||
+ (mb3_transfer.ack.status != mb3_transfer.req_st)) {
+ r = -EIO;
+ }
+
+unlock_and_return:
+ mutex_unlock(&mb3_transfer.sysclk_lock);
+
+ return r;
+}
+
+static int request_timclk(bool enable)
+{
+ u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
+
+ if (!enable)
+ val |= PRCM_TCR_STOP_TIMERS;
+ writel(val, _PRCMU_BASE + PRCM_TCR);
+
+ return 0;
+}
+
+static int request_clk(u8 clock, bool enable)
+{
+ int r = 0;
+
+ BUG_ON(clock >= DB5500_NUM_CLK_CLIENTS);
+
+ mutex_lock(&mb2_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ /* fill in mailbox */
+ writeb(clock, PRCM_REQ_MB2_CLK_CLIENT);
+ writeb(enable, PRCM_REQ_MB2_CLK_STATE);
+
+ writeb(MB2H_CLK_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: request_clk() failed.\n");
+ r = -EIO;
+ WARN(1, "Failed in request_clk");
+ goto unlock_and_return;
+ }
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_CLK_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+ return r;
+}
+
+static int request_reg_clock(u8 clock, bool enable)
+{
+ u32 val;
+ unsigned long flags;
+
+ WARN_ON(!clk_mgt[clock].offset);
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
+ if (enable) {
+ val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
+ } else {
+ clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
+ val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
+ }
+ writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
+
+ /* Release the HW semaphore. */
+ writel(0, _PRCMU_BASE + PRCM_SEM);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ return 0;
+}
+
+/*
+ * request_pll() - Request for a pll to be enabled or disabled.
+ * @pll: The pll for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+static int request_pll(u8 pll, bool enable)
+{
+ int r = 0;
+
+ BUG_ON(pll >= DB5500_NUM_PLL_ID);
+ mutex_lock(&mb2_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ mb2_transfer.req.pll_st[pll] = enable;
+
+ /* fill in mailbox */
+ writeb(pll, PRCM_REQ_MB2_PLL_CLIENT);
+ writeb(mb2_transfer.req.pll_st[pll], PRCM_REQ_MB2_PLL_STATE);
+
+ writeb(MB2H_PLL_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: set_pll() failed.\n");
+ r = -EIO;
+ WARN(1, "Failed to set pll");
+ goto unlock_and_return;
+ }
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_PLL_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+
+ return r;
+}
+
+/**
+ * db5500_prcmu_request_clock() - Request for a clock to be enabled or disabled.
+ * @clock: The clock for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+int db5500_prcmu_request_clock(u8 clock, bool enable)
+{
+ /* MSP1 & CD clocks are handled by FW */
+ if (clock == PRCMU_MSP1CLK)
+ return request_clk(DB5500_MSP1CLK, enable);
+ else if (clock == PRCMU_CDCLK)
+ return request_clk(DB5500_CDCLK, enable);
+ else if (clock == PRCMU_IRDACLK)
+ return request_clk(DB5500_IRDACLK, enable);
+ else if (clock < PRCMU_NUM_REG_CLOCKS)
+ return request_reg_clock(clock, enable);
+ else if (clock == PRCMU_TIMCLK)
+ return request_timclk(enable);
+ else if (clock == PRCMU_PLLSOC0)
+ return request_pll(DB5500_PLL_SOC0, enable);
+ else if (clock == PRCMU_PLLSOC1)
+ return request_pll(DB5500_PLL_SOC1, enable);
+ else if (clock == PRCMU_PLLDDR)
+ return request_pll(DB5500_PLL_DDR, enable);
+ else if (clock == PRCMU_SYSCLK)
+ return request_sysclk(enable);
+ else
+ return -EINVAL;
+}
+
+/* This function should only be called while mb0_transfer.lock is held. */
+static void config_wakeups(void)
+{
+ static u32 last_dbb_events;
+ static u32 last_abb_events;
+ u32 dbb_events;
+ u32 abb_events;
+
+ dbb_events = mb0_transfer.req.dbb_irqs | mb0_transfer.req.dbb_wakeups;
+
+ abb_events = mb0_transfer.req.abb_events;
+
+ if ((dbb_events == last_dbb_events) && (abb_events == last_abb_events))
+ return;
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ writel(dbb_events, PRCM_REQ_MB0_WAKEUP_DBB);
+ writel(abb_events, PRCM_REQ_MB0_WAKEUP_ABB);
+ writeb(MB0H_WAKE_UP_CFG, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ last_dbb_events = dbb_events;
+ last_abb_events = abb_events;
+}
+
+int db5500_prcmu_config_esram0_deep_sleep(u8 state)
+{
+ unsigned long flags;
+
+ if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) ||
+ (state < ESRAM0_DEEP_SLEEP_STATE_OFF))
+ return -EINVAL;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ if (state == ESRAM0_DEEP_SLEEP_STATE_RET)
+ writeb(RET_ST, PRCM_REQ_MB0_ESRAM0_STATE);
+ else
+ writeb(OFF_ST, PRCM_REQ_MB0_ESRAM0_STATE);
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+
+ return 0;
+}
+
+int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
+{
+ int r = 0;
+ unsigned long flags;
+
+ /* Deep Idle is not supported in DB5500 */
+ BUG_ON((state < PRCMU_AP_SLEEP) || (state >= PRCMU_AP_DEEP_IDLE));
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ switch (state) {
+ case PRCMU_AP_IDLE:
+ writeb(DB5500_AP_IDLE, PRCM_REQ_MB0_AP_POWER_STATE);
+ /* TODO: Can be high latency */
+ writeb(DDR_PWR_STATE_UNCHANGED, PRCM_REQ_MB0_DDR_STATE);
+ break;
+ case PRCMU_AP_SLEEP:
+ writeb(DB5500_AP_SLEEP, PRCM_REQ_MB0_AP_POWER_STATE);
+ break;
+ case PRCMU_AP_DEEP_SLEEP:
+ writeb(DB5500_AP_DEEP_SLEEP, PRCM_REQ_MB0_AP_POWER_STATE);
+ break;
+ default:
+ r = -EINVAL;
+ goto unlock_return;
+ }
+ writeb((keep_ap_pll ? 1 : 0), PRCM_REQ_MB0_AP_PLL_STATE);
+ writeb((keep_ulp_clk ? 1 : 0), PRCM_REQ_MB0_ULP_CLOCK_STATE);
+
+ writeb(MB0H_PWR_STATE_TRANS, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+unlock_return:
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+
+ return r;
+}
+
+u8 db5500_prcmu_get_power_state_result(void)
+{
+ u8 status = readb_relaxed(PRCM_ACK_MB0_AP_PWRSTTR_STATUS);
+
+ /*
+ * Callers expect all the status values to match 8500. Adjust for
+ * PendingReq_Er (0x2b).
+ */
+ if (status == 0x2b)
+ status = PRCMU_PRCMU2ARMPENDINGIT_ER;
+
+ return status;
+}
+
+void db5500_prcmu_enable_wakeups(u32 wakeups)
+{
+ unsigned long flags;
+ u32 bits;
+ int i;
+
+ BUG_ON(wakeups != (wakeups & VALID_WAKEUPS));
+
+ for (i = 0, bits = 0; i < NUM_PRCMU_WAKEUP_INDICES; i++) {
+ if (wakeups & BIT(i)) {
+ if (prcmu_wakeup_bit[i] == 0)
+ WARN(1, "WAKEUP NOT SUPPORTED");
+ else
+ bits |= prcmu_wakeup_bit[i];
+ }
+ }
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ mb0_transfer.req.dbb_wakeups = bits;
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void db5500_prcmu_config_abb_event_readout(u32 abb_events)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ mb0_transfer.req.abb_events = abb_events;
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void db5500_prcmu_get_abb_event_buffer(void __iomem **buf)
+{
+ if (readb(PRCM_ACK_MB0_READ_POINTER) & 1)
+ *buf = (PRCM_ACK_MB0_WAKEUP_1_ABB);
+ else
+ *buf = (PRCM_ACK_MB0_WAKEUP_0_ABB);
+}
+
+/* This function should be called with lock */
+static int mailbox4_request(u8 mb4_request, u8 ack_request)
+{
+ int ret = 0;
+
+ writeb(mb4_request, PRCM_REQ_MB4_HEADER);
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ if (!wait_for_completion_timeout(&mb4_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: MB4 request %d failed", mb4_request);
+ ret = -EIO;
+ WARN(1, "prcmu: failed mb4 request");
+ goto failed;
+ }
+
+ if (mb4_transfer.ack.header != ack_request ||
+ mb4_transfer.ack.status != RC_SUCCESS)
+ ret = -EIO;
+failed:
+ return ret;
+}
+
+int db5500_prcmu_get_hotdog(void)
+{
+ return readw(PRCM_SHARE_INFO_HOTDOG);
+}
+
+int db5500_prcmu_config_hotdog(u8 threshold)
+{
+ int r = 0;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writew(threshold, PRCM_REQ_MB4_HOTDOG_THRESHOLD);
+ r = mailbox4_request(MB4H_CFG_HOTDOG, MB4H_ACK_CFG_HOTDOG);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return r;
+}
+
+int db5500_prcmu_config_hotmon(u8 low, u8 high)
+{
+ int r = 0;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writew(low, PRCM_REQ_MB4_HOTMON_LOW);
+ writew(high, PRCM_REQ_MB4_HOTMON_HIGH);
+
+ r = mailbox4_request(MB4H_CFG_HOTMON, MB4H_ACK_CFG_HOTMON);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return r;
+}
+
+static int config_hot_period(u16 val)
+{
+ int r = 0;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writew(val, PRCM_REQ_MB4_HOT_PERIOD);
+ r = mailbox4_request(MB4H_CFG_HOTPERIOD, MB4H_ACK_CFG_HOTPERIOD);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return r;
+}
+
+/*
+ * period in milli seconds
+ */
+int db5500_prcmu_start_temp_sense(u16 period)
+{
+ if (period == 0xFFFF)
+ return -EINVAL;
+
+ return config_hot_period(period);
+}
+
+int db5500_prcmu_stop_temp_sense(void)
+{
+ return config_hot_period(0xFFFF);
+}
+
+static int prcmu_a9wdog(u8 req, u8 ack)
+{
+ int r = 0;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ r = mailbox4_request(req, ack);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return r;
+}
+
+static void prcmu_a9wdog_set_interrupt(bool enable)
+{
+ if (enable) {
+ writel(PRCM_TIMER0_IRQ_RTOS1_SET,
+ (mtimer_base + PRCM_TIMER0_IRQ_EN_SET_OFFSET));
+ } else {
+ writel(PRCM_TIMER0_IRQ_RTOS1_CLR,
+ (mtimer_base + PRCM_TIMER0_IRQ_EN_CLR_OFFSET));
+ }
+}
+
+static void prcmu_a9wdog_set_timeout(u32 timeout)
+{
+ u32 comp_timeout;
+
+ comp_timeout = readl(mtimer_base + PRCM_TIMER0_RTOS_COUNTER_OFFSET) +
+ timeout;
+ writel(comp_timeout, mtimer_base + PRCM_TIMER0_RTOS_COMP1_OFFSET);
+}
+
+int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+{
+ /*
+ * Sleep auto off feature is not supported. Resume and
+ * suspend will be handled by watchdog driver.
+ */
+ return 0;
+}
+
+int db5500_prcmu_enable_a9wdog(u8 id)
+{
+ int r = 0;
+
+ if (a9wdog_timer.enabled)
+ return -EPERM;
+
+ prcmu_a9wdog_set_interrupt(true);
+
+ r = prcmu_a9wdog(MB4H_CGF_A9WDOG_EN_PREBARK,
+ MB4H_ACK_CGF_A9WDOG_EN_PREBARK);
+ if (!r)
+ a9wdog_timer.enabled = true;
+ else
+ prcmu_a9wdog_set_interrupt(false);
+
+ return r;
+}
+
+int db5500_prcmu_disable_a9wdog(u8 id)
+{
+ if (!a9wdog_timer.enabled)
+ return -EPERM;
+
+ prcmu_a9wdog_set_interrupt(false);
+
+ a9wdog_timer.enabled = false;
+
+ return prcmu_a9wdog(MB4H_CGF_A9WDOG_DIS,
+ MB4H_ACK_CGF_A9WDOG_DIS);
+}
+
+int db5500_prcmu_kick_a9wdog(u8 id)
+{
+ int r = 0;
+
+ if (a9wdog_timer.enabled)
+ prcmu_a9wdog_set_timeout(a9wdog_timer.timeout);
+ else
+ r = -EPERM;
+
+ return r;
+}
+
+int db5500_prcmu_load_a9wdog(u8 id, u32 timeout)
+{
+ if (a9wdog_timer.enabled)
+ return -EPERM;
+
+ prcmu_a9wdog_set_timeout(timeout);
+ a9wdog_timer.timeout = timeout;
+
+ return 0;
+}
+
/**
* db5500_prcmu_abb_read() - Read register value(s) from the ABB.
* @slave: The I2C slave address.
@@ -170,14 +1314,14 @@ int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
mutex_lock(&mb5_transfer.lock);
- while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
writeb(slave, PRCM_REQ_MB5_I2C_SLAVE);
writeb(reg, PRCM_REQ_MB5_I2C_REG);
writeb(size, PRCM_REQ_MB5_I2C_SIZE);
writeb(MB5H_I2C_READ, PRCM_REQ_MB5_HEADER);
- writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
wait_for_completion(&mb5_transfer.work);
r = 0;
@@ -211,7 +1355,7 @@ int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
mutex_lock(&mb5_transfer.lock);
- while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
writeb(slave, PRCM_REQ_MB5_I2C_SLAVE);
writeb(reg, PRCM_REQ_MB5_I2C_REG);
@@ -219,7 +1363,7 @@ int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
memcpy_toio(PRCM_REQ_MB5_I2C_DATA, value, size);
writeb(MB5H_I2C_WRITE, PRCM_REQ_MB5_HEADER);
- writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
wait_for_completion(&mb5_transfer.work);
if ((mb5_transfer.ack.header == MB5H_I2C_WRITE) &&
@@ -233,42 +1377,385 @@ int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
return r;
}
+/**
+ * db5500_prcmu_set_arm_opp - set the appropriate ARM OPP
+ * @opp: The new ARM operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the the operating point of the ARM.
+ */
+int db5500_prcmu_set_arm_opp(u8 opp)
+{
+ int r;
+ u8 db5500_opp;
+
+ r = 0;
+
+ switch (opp) {
+ case ARM_EXTCLK:
+ db5500_opp = DB5500_ARM_EXT_OPP;
+ break;
+ case ARM_50_OPP:
+ db5500_opp = DB5500_ARM_50_OPP;
+ break;
+ case ARM_100_OPP:
+ db5500_opp = DB5500_ARM_100_OPP;
+ break;
+ default:
+ pr_err("prcmu: %s() received wrong opp value: %d\n",
+ __func__, opp);
+ r = -EINVAL;
+ goto bailout;
+ }
+
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_ARM_OPP, PRCM_REQ_MB1_HEADER);
+
+ writeb(db5500_opp, PRCM_REQ_MB1_ARM_OPP);
+ writel(MBOX_BIT(1), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ if (!wait_for_completion_timeout(&mb1_transfer.work,
+ msecs_to_jiffies(20000))) {
+ r = -EIO;
+ WARN(1, "prcmu: failed to set arm opp");
+ goto unlock_and_return;
+ }
+
+ if (mb1_transfer.ack.header != MB1H_ARM_OPP ||
+ (mb1_transfer.ack.arm_opp != db5500_opp) ||
+ (mb1_transfer.ack.arm_voltage_st != RC_SUCCESS))
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb1_transfer.lock);
+bailout:
+ if (!r)
+ prcmu_debug_arm_opp_log(opp);
+ return r;
+}
+
+static void __init prcmu_ape_clocks_init(void)
+{
+ u8 opp = db5500_prcmu_get_ape_opp();
+ unsigned long flags;
+ int i;
+
+ WARN(opp != APE_100_OPP, "%s: Initial APE OPP (%u) not 100%%?\n",
+ __func__, opp);
+
+ for (i = 0; i < PRCMU_NUM_REG_CLOCKS; i++) {
+ struct clk_mgt *clkmgt = &clk_mgt[i];
+ u32 clkval;
+ u32 div;
+
+ if (!clkmgt->scalable && !clkmgt->force50)
+ continue;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ clkval = readl(_PRCMU_BASE + clkmgt->offset);
+ div = clkval & PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ div >>= PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+
+ if (clkmgt->force50) {
+ div *= 2;
+
+ clkval &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ clkval |= div << PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+ writel(clkval, _PRCMU_BASE + clkmgt->offset);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+ continue;
+ }
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ clkmgt->div = div;
+ if (!div)
+ pr_err("%s: scalable clock at offset %#x has zero divisor\n",
+ __func__, clkmgt->offset);
+ }
+}
+
+static void prcmu_ape_clocks_scale(u8 opp)
+{
+ unsigned long irqflags;
+ unsigned int i;
+ u32 clkval;
+
+ /*
+ * Note: calling printk() under the following lock can cause lock
+ * recursion via clk_enable() for the console UART!
+ */
+ spin_lock_irqsave(&clk_mgt_lock, irqflags);
+
+ /* take a lock on HW (HWSEM)*/
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < PRCMU_NUM_REG_CLOCKS; i++) {
+ u32 divval;
+
+ if (!clk_mgt[i].scalable)
+ continue;
+
+ clkval = readl(_PRCMU_BASE + clk_mgt[i].offset);
+ divval = clk_mgt[i].div;
+
+ pr_debug("PRCMU: reg %#x prev clk = 0x%x stored div = 0x%x\n",
+ clk_mgt[i].offset, clkval, divval);
+
+ if (opp == DB5500_APE_50_OPP)
+ divval *= 2;
+
+ clkval &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ clkval |= divval << PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+
+ pr_debug("PRCMU: wr 0x%x in reg 0x%x\n",
+ clkval, clk_mgt[i].offset);
+
+ writel(clkval, _PRCMU_BASE + clk_mgt[i].offset);
+ }
+
+ /* release lock */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, irqflags);
+}
+/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
+static void request_even_slower_clocks(bool enable)
+{
+ void __iomem *clock_reg[] = {
+ (_PRCMU_BASE + DB5500_PRCM_ACLK_MGT),
+ (_PRCMU_BASE + DB5500_PRCM_DMACLK_MGT)
+ };
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < ARRAY_SIZE(clock_reg); i++) {
+ u32 val;
+ u32 div;
+
+ val = readl(clock_reg[i]);
+ div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
+ if (enable) {
+ if ((div <= 1) || (div > 15)) {
+ pr_err("prcmu: Bad clock divider %d in %s\n",
+ div, __func__);
+ goto unlock_and_return;
+ }
+ div <<= 1;
+ } else {
+ if (div <= 2)
+ goto unlock_and_return;
+ div >>= 1;
+ }
+ val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
+ (div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
+ writel(val, clock_reg[i]);
+ }
+
+unlock_and_return:
+ /* Release the HW semaphore. */
+ writel(0, _PRCMU_BASE + PRCM_SEM);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+int db5500_prcmu_set_ape_opp(u8 opp)
+{
+ int ret = 0;
+ u8 db5500_opp;
+ if (opp == mb1_transfer.req_ape_opp)
+ return 0;
+
+ switch (opp) {
+ case APE_100_OPP:
+ db5500_opp = DB5500_APE_100_OPP;
+ break;
+ case APE_50_OPP:
+ case APE_50_PARTLY_25_OPP:
+ db5500_opp = DB5500_APE_50_OPP;
+ break;
+ default:
+ pr_err("prcmu: %s() received wrong opp value: %d\n",
+ __func__, opp);
+ ret = -EINVAL;
+ goto bailout;
+ }
+
+ mutex_lock(&mb1_transfer.lock);
+ if (mb1_transfer.req_ape_opp == APE_50_PARTLY_25_OPP)
+ request_even_slower_clocks(false);
+ if ((opp != APE_100_OPP) && (mb1_transfer.req_ape_opp != APE_100_OPP))
+ goto skip_message;
+
+ prcmu_ape_clocks_scale(db5500_opp);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_APE_OPP, PRCM_REQ_MB1_HEADER);
+ writeb(db5500_opp, PRCM_REQ_MB1_APE_OPP);
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ if (!wait_for_completion_timeout(&mb1_transfer.work,
+ msecs_to_jiffies(20000))) {
+ ret = -EIO;
+ WARN(1, "prcmu: failed to set ape opp to %u", opp);
+ goto unlock_and_return;
+ }
+
+ if (mb1_transfer.ack.header != MB1H_APE_OPP ||
+ (mb1_transfer.ack.ape_opp != db5500_opp) ||
+ (mb1_transfer.ack.arm_voltage_st != RC_SUCCESS))
+ ret = -EIO;
+
+skip_message:
+ if ((!ret && (opp == APE_50_PARTLY_25_OPP)) ||
+ (ret && (mb1_transfer.req_ape_opp == APE_50_PARTLY_25_OPP)))
+ request_even_slower_clocks(true);
+ if (!ret)
+ mb1_transfer.req_ape_opp = opp;
+unlock_and_return:
+ mutex_unlock(&mb1_transfer.lock);
+bailout:
+ return ret;
+}
+
+int db5500_prcmu_get_ape_opp(void)
+{
+ u8 opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+
+ switch (opp) {
+ case DB5500_APE_100_OPP:
+ return APE_100_OPP;
+ case DB5500_APE_50_OPP:
+ return APE_50_OPP;
+ default:
+ pr_err("prcmu: %s() read unknown opp value: %d\n",
+ __func__, opp);
+ return APE_100_OPP;
+ }
+}
+
+int db5500_prcmu_get_ddr_opp(void)
+{
+ return readb(_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW);
+}
+
+int db5500_prcmu_set_ddr_opp(u8 opp)
+{
+ if (opp != DDR_100_OPP && opp != DDR_50_OPP)
+ return -EINVAL;
+
+ writeb(opp, _PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW);
+
+ return 0;
+}
+
+/**
+ * db5500_prcmu_get_arm_opp - get the current ARM OPP
+ *
+ * Returns: the current ARM OPP
+ */
+int db5500_prcmu_get_arm_opp(void)
+{
+ u8 opp = readb(PRCM_ACK_MB1_CURRENT_ARM_OPP);
+
+ switch (opp) {
+ case DB5500_ARM_EXT_OPP:
+ return ARM_EXTCLK;
+ case DB5500_ARM_50_OPP:
+ return ARM_50_OPP;
+ case DB5500_ARM_100_OPP:
+ return ARM_100_OPP;
+ default:
+ pr_err("prcmu: %s() read unknown opp value: %d\n",
+ __func__, opp);
+ return ARM_100_OPP;
+ }
+}
+
+int prcmu_resetout(u8 resoutn, u8 state)
+{
+ int offset;
+ int pin = -1;
+
+ offset = state > 0 ? PRCM_RESOUTN_SET_OFFSET : PRCM_RESOUTN_CLR_OFFSET;
+
+ switch (resoutn) {
+ case 0:
+ pin = PRCMU_RESOUTN0_PIN;
+ break;
+ case 1:
+ pin = PRCMU_RESOUTN1_PIN;
+ break;
+ case 2:
+ pin = PRCMU_RESOUTN2_PIN;
+ default:
+ break;
+ }
+
+ if (pin > 0)
+ writel(pin, _PRCMU_BASE + offset);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
int db5500_prcmu_enable_dsipll(void)
{
int i;
+ int ret = 0;
/* Enable DSIPLL_RESETN resets */
- writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
+ writel(PRCMU_RESET_DSIPLL, _PRCMU_BASE + PRCM_APE_RESETN_CLR);
/* Unclamp DSIPLL in/out */
- writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
+ writel(PRCMU_UNCLAMP_DSIPLL, _PRCMU_BASE + PRCM_MMIP_LS_CLAMP_CLR);
/* Set DSI PLL FREQ */
- writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ);
+ writel(PRCMU_PLLDSI_FREQ_SETTING, _PRCMU_BASE + PRCM_PLLDSI_FREQ);
writel(PRCMU_DSI_PLLOUT_SEL_SETTING,
- PRCM_DSI_PLLOUT_SEL);
+ _PRCMU_BASE + PRCM_DSI_PLLOUT_SEL);
/* Enable Escape clocks */
- writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
+ writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, _PRCMU_BASE + PRCM_DSITVCLK_DIV);
/* Start DSI PLL */
- writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
+ writel(PRCMU_ENABLE_PLLDSI, _PRCMU_BASE + PRCM_PLLDSI_ENABLE);
/* Reset DSI PLL */
- writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET);
+ writel(PRCMU_DSI_RESET_SW, _PRCMU_BASE + PRCM_DSI_SW_RESET);
for (i = 0; i < 10; i++) {
- if ((readl(PRCM_PLLDSI_LOCKP) &
+ if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) &
PRCMU_PLLDSI_LOCKP_LOCKED) == PRCMU_PLLDSI_LOCKP_LOCKED)
break;
udelay(100);
}
+
+ if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) &
+ PRCMU_PLLDSI_LOCKP_LOCKED)
+ != PRCMU_PLLDSI_LOCKP_LOCKED)
+ ret = -EIO;
/* Release DSIPLL_RESETN */
- writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET);
- return 0;
+ writel(PRCMU_RESET_DSIPLL, _PRCMU_BASE + PRCM_APE_RESETN_SET);
+ return ret;
}
int db5500_prcmu_disable_dsipll(void)
{
/* Disable dsi pll */
- writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
+ writel(PRCMU_DISABLE_PLLDSI, _PRCMU_BASE + PRCM_PLLDSI_ENABLE);
/* Disable escapeclock */
- writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
+ writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, _PRCMU_BASE + PRCM_DSITVCLK_DIV);
return 0;
}
@@ -276,27 +1763,150 @@ int db5500_prcmu_set_display_clocks(void)
{
/* HDMI and TVCLK Should be handled somewhere else */
/* PLLDIV=8, PLLSW=2, CLKEN=1 */
- writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT);
+ writel(PRCMU_DSI_CLOCK_SETTING, _PRCMU_BASE + DB5500_PRCM_HDMICLK_MGT);
/* PLLDIV=14, PLLSW=2, CLKEN=1 */
- writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);
+ writel(PRCMU_DSI_LP_CLOCK_SETTING, _PRCMU_BASE + DB5500_PRCM_TVCLK_MGT);
return 0;
}
+u32 db5500_prcmu_read(unsigned int reg)
+{
+ return readl_relaxed(_PRCMU_BASE + reg);
+}
+
+void db5500_prcmu_write(unsigned int reg, u32 value)
+{
+ writel_relaxed(value, _PRCMU_BASE + reg);
+}
+
+void db5500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
+{
+ u32 val;
+
+ val = readl_relaxed(_PRCMU_BASE + reg);
+ val = (val & ~mask) | (value & mask);
+ writel_relaxed(val, _PRCMU_BASE + reg);
+}
+
+/**
+ * db5500_prcmu_system_reset - System reset
+ *
+ * Saves the reset reason code and then sets the APE_SOFTRST register which
+ * fires an interrupt to fw
+ */
+void db5500_prcmu_system_reset(u16 reset_code)
+{
+ writew(reset_code, PRCM_SW_RST_REASON);
+ writel(1, _PRCMU_BASE + PRCM_APE_SOFTRST);
+}
+
+/**
+ * db5500_prcmu_get_reset_code - Retrieve SW reset reason code
+ *
+ * Retrieves the reset reason code stored by prcmu_system_reset() before
+ * last restart.
+ */
+u16 db5500_prcmu_get_reset_code(void)
+{
+ return readw(PRCM_SW_RST_REASON);
+}
+
static void ack_dbb_wakeup(void)
{
unsigned long flags;
spin_lock_irqsave(&mb0_transfer.lock, flags);
- while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
cpu_relax();
- writeb(RMB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER);
- writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
+ writeb(MB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
+int db5500_prcmu_set_epod(u16 epod, u8 epod_state)
+{
+ int r = 0;
+ bool ram_retention = false;
+
+ /* check argument */
+ BUG_ON(epod < DB5500_EPOD_ID_BASE);
+ BUG_ON(epod_state > EPOD_STATE_ON);
+ BUG_ON((epod - DB5500_EPOD_ID_BASE) >= DB5500_NUM_EPOD_ID);
+
+ if (epod == DB5500_EPOD_ID_ESRAM12)
+ ram_retention = true;
+
+ /* check argument */
+ BUG_ON(epod_state == EPOD_STATE_RAMRET && !ram_retention);
+
+ /* get lock */
+ mutex_lock(&mb2_transfer.lock);
+
+ /* wait for mailbox */
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ /* Retention is allowed only for ESRAM12 */
+ if (epod == DB5500_EPOD_ID_ESRAM12) {
+ switch (epod_state) {
+ case EPOD_STATE_ON:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_ON;
+ break;
+ case EPOD_STATE_OFF:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_OFF;
+ break;
+ case EPOD_STATE_RAMRET:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_RET;
+ break;
+ default:
+ r = -EINVAL;
+ goto unlock_and_return;
+ break;
+ }
+ } else {
+ if (epod_state == EPOD_STATE_ON)
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_ON;
+ else if (epod_state == EPOD_STATE_OFF)
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OFF;
+ else {
+ r = -EINVAL;
+ goto unlock_and_return;
+ }
+ }
+ /* fill in mailbox */
+ writeb((epod - DB5500_EPOD_ID_BASE), PRCM_REQ_MB2_EPOD_CLIENT);
+ writeb(mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE],
+ PRCM_REQ_MB2_EPOD_STATE);
+
+ writeb(MB2H_EPOD_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: set_epod() failed.\n");
+ r = -EIO;
+ WARN(1, "Failed to set epod");
+ goto unlock_and_return;
+ }
+
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_EPOD_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+ return r;
+}
+
static inline void print_unknown_header_warning(u8 n, u8 header)
{
pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n",
@@ -306,11 +1916,31 @@ static inline void print_unknown_header_warning(u8 n, u8 header)
static bool read_mailbox_0(void)
{
bool r;
+ u32 ev;
+ unsigned int n;
+
u8 header;
header = readb(PRCM_ACK_MB0_HEADER);
switch (header) {
- case AMB0H_WAKE_UP:
+ case MB0H_WAKE_UP:
+ if (readb(PRCM_ACK_MB0_READ_POINTER) & 1)
+ ev = readl(PRCM_ACK_MB0_WAKEUP_1_DBB);
+ else
+ ev = readl(PRCM_ACK_MB0_WAKEUP_0_DBB);
+
+ prcmu_debug_register_mbox0_event(ev,
+ (mb0_transfer.req.dbb_irqs |
+ mb0_transfer.req.dbb_wakeups));
+
+ ev &= mb0_transfer.req.dbb_irqs;
+
+ for (n = 0; n < NUM_DB5500_PRCMU_WAKEUPS; n++) {
+ if (ev & prcmu_irq_bit[n]) {
+ if (n != IRQ_INDEX(ABB))
+ generic_handle_irq(IRQ_DB5500_PRCMU_BASE + n);
+ }
+ }
r = true;
break;
default:
@@ -318,31 +1948,123 @@ static bool read_mailbox_0(void)
r = false;
break;
}
- writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
return r;
}
static bool read_mailbox_1(void)
{
- writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
+ u8 header;
+ bool do_complete = true;
+
+ header = mb1_transfer.ack.header = readb(PRCM_ACK_MB1_HEADER);
+
+ switch (header) {
+ case MB1H_ARM_OPP:
+ mb1_transfer.ack.arm_opp = readb(PRCM_ACK_MB1_CURRENT_ARM_OPP);
+ mb1_transfer.ack.arm_voltage_st =
+ readb(PRCM_ACK_MB1_ARM_VOLT_STATUS);
+ break;
+ case MB1H_APE_OPP:
+ mb1_transfer.ack.ape_opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+ mb1_transfer.ack.ape_voltage_st =
+ readb(PRCM_ACK_MB1_APE_VOLT_STATUS);
+ break;
+ case MB1H_ARM_APE_OPP:
+ mb1_transfer.ack.ape_opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+ mb1_transfer.ack.ape_voltage_st =
+ readb(PRCM_ACK_MB1_APE_VOLT_STATUS);
+ break;
+ default:
+ print_unknown_header_warning(1, header);
+ do_complete = false;
+ break;
+ }
+
+ writel(MBOX_BIT(1), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+
+ if (do_complete)
+ complete(&mb1_transfer.work);
+
return false;
}
static bool read_mailbox_2(void)
{
- writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
+ u8 header;
+
+ header = readb(PRCM_ACK_MB2_HEADER);
+ mb2_transfer.ack.header = header;
+ switch (header) {
+ case MB2H_EPOD_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_EPOD_STATUS);
+ break;
+ case MB2H_CLK_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_CLK_STATUS);
+ break;
+ case MB2H_PLL_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_PLL_STATUS);
+ break;
+ default:
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ pr_err("prcmu: Wrong ACK received for MB2 request \n");
+ return false;
+ break;
+ }
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ complete(&mb2_transfer.work);
return false;
}
static bool read_mailbox_3(void)
{
- writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
+ u8 header;
+
+ header = readb(PRCM_ACK_MB3_HEADER);
+ mb3_transfer.ack.header = header;
+ switch (header) {
+ case MB3H_REFCLK_REQUEST:
+ mb3_transfer.ack.status = readb(PRCM_ACK_MB3_REFCLK_REQ);
+ writel(MBOX_BIT(3), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ complete(&mb3_transfer.sysclk_work);
+ break;
+ default:
+ writel(MBOX_BIT(3), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ pr_err("prcmu: wrong MB3 header\n");
+ break;
+ }
+
return false;
}
static bool read_mailbox_4(void)
{
- writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
+ u8 header;
+ bool do_complete = true;
+
+ header = readb(PRCM_ACK_MB4_HEADER);
+ mb4_transfer.ack.header = header;
+ switch (header) {
+ case MB4H_ACK_CFG_HOTDOG:
+ case MB4H_ACK_CFG_HOTMON:
+ case MB4H_ACK_CFG_HOTPERIOD:
+ case MB4H_ACK_CFG_MODEM_RESET:
+ case MB4H_ACK_CGF_A9WDOG_EN_PREBARK:
+ case MB4H_ACK_CGF_A9WDOG_EN_NOPREBARK:
+ case MB4H_ACK_CGF_A9WDOG_DIS:
+ mb4_transfer.ack.status = readb(PRCM_ACK_MB4_REQUESTS);
+ break;
+ default:
+ print_unknown_header_warning(4, header);
+ do_complete = false;
+ break;
+ }
+
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_ARM_IT1_CLEAR));
+
+ if (do_complete)
+ complete(&mb4_transfer.work);
+
return false;
}
@@ -363,19 +2085,19 @@ static bool read_mailbox_5(void)
print_unknown_header_warning(5, header);
break;
}
- writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
return false;
}
static bool read_mailbox_6(void)
{
- writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
+ writel(MBOX_BIT(6), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
return false;
}
static bool read_mailbox_7(void)
{
- writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
+ writel(MBOX_BIT(7), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
return false;
}
@@ -396,7 +2118,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
u8 n;
irqreturn_t r;
- bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ bits = (readl(_PRCMU_BASE + PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
if (unlikely(!bits))
return IRQ_NONE;
@@ -406,6 +2128,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
bits -= MBOX_BIT(n);
if (read_mailbox[n]())
r = IRQ_WAKE_THREAD;
+ prcmu_debug_register_interrupt(n);
}
}
return r;
@@ -413,39 +2136,271 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
static irqreturn_t prcmu_irq_thread_fn(int irq, void *data)
{
+ u32 ev;
+
+ /*
+ * ABB needs to be handled before the wakeup because
+ * the ping/pong buffers for ABB events could change
+ * after we acknowledge the wakeup.
+ */
+ if (readb(PRCM_ACK_MB0_READ_POINTER) & 1)
+ ev = readl(PRCM_ACK_MB0_WAKEUP_1_DBB);
+ else
+ ev = readl(PRCM_ACK_MB0_WAKEUP_0_DBB);
+
+ ev &= mb0_transfer.req.dbb_irqs;
+ if (ev & WAKEUP_BIT_ABB)
+ handle_nested_irq(IRQ_DB5500_PRCMU_ABB);
+
ack_dbb_wakeup();
+
return IRQ_HANDLED;
}
+static void prcmu_mask_work(struct work_struct *work)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+static void prcmu_irq_mask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+ mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[d->irq - IRQ_DB5500_PRCMU_BASE];
+
+ spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+ schedule_work(&mb0_transfer.mask_work);
+}
+
+static void prcmu_irq_unmask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+ mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[d->irq - IRQ_DB5500_PRCMU_BASE];
+
+ spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+ schedule_work(&mb0_transfer.mask_work);
+}
+
+static void noop(struct irq_data *d)
+{
+}
+
+static struct irq_chip prcmu_irq_chip = {
+ .name = "prcmu",
+ .irq_disable = prcmu_irq_mask,
+ .irq_ack = noop,
+ .irq_mask = prcmu_irq_mask,
+ .irq_unmask = prcmu_irq_unmask,
+};
+
void __init db5500_prcmu_early_init(void)
{
+ unsigned int i;
+ void *tcpm_base = ioremap_nocache(U5500_PRCMU_TCPM_BASE, SZ_4K);
+
+ if (tcpm_base != NULL) {
+ int version_high, version_low;
+
+ version_high = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
+ version_low = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET + 4);
+ prcmu_version.board = (version_high >> 24) & 0xFF;
+ prcmu_version.fw_version = version_high & 0xFF;
+ prcmu_version.api_version = version_low & 0xFF;
+
+ pr_info("PRCMU Firmware Version: 0x%x\n",
+ prcmu_version.fw_version);
+ pr_info("PRCMU API Version: 0x%x\n",
+ prcmu_version.api_version);
+
+ iounmap(tcpm_base);
+ }
+
tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE);
+ mtimer_base = __io_address(U5500_MTIMER_BASE);
spin_lock_init(&mb0_transfer.lock);
+ spin_lock_init(&mb0_transfer.dbb_irqs_lock);
+ mutex_init(&mb0_transfer.ac_wake_lock);
+ mutex_init(&mb1_transfer.lock);
+ init_completion(&mb1_transfer.work);
+ mutex_init(&mb2_transfer.lock);
+ init_completion(&mb2_transfer.work);
+ mutex_init(&mb3_transfer.sysclk_lock);
+ init_completion(&mb3_transfer.sysclk_work);
+ mutex_init(&mb4_transfer.lock);
+ init_completion(&mb4_transfer.work);
mutex_init(&mb5_transfer.lock);
init_completion(&mb5_transfer.work);
+
+ INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
+
+ /* Initalize irqs. */
+ for (i = 0; i < NUM_DB5500_PRCMU_WAKEUPS; i++) {
+ unsigned int irq;
+
+ irq = IRQ_DB5500_PRCMU_BASE + i;
+ irq_set_chip_and_handler(irq, &prcmu_irq_chip,
+ handle_simple_irq);
+ if (irq == IRQ_DB5500_PRCMU_ABB)
+ irq_set_nested_thread(irq, true);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+ prcmu_ape_clocks_init();
+}
+
+/*
+ * Power domain switches (ePODs) modeled as regulators for the DB5500 SoC
+ */
+static struct regulator_consumer_supply db5500_vape_consumers[] = {
+ REGULATOR_SUPPLY("v-ape", NULL),
+ REGULATOR_SUPPLY("v-i2c", "nmk-i2c.0"),
+ REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"),
+ REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"),
+ REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"),
+ REGULATOR_SUPPLY("vcore", "sdi0"),
+ REGULATOR_SUPPLY("vcore", "sdi1"),
+ REGULATOR_SUPPLY("vcore", "sdi2"),
+ REGULATOR_SUPPLY("vcore", "sdi3"),
+ REGULATOR_SUPPLY("vcore", "sdi4"),
+ REGULATOR_SUPPLY("v-uart", "uart0"),
+ REGULATOR_SUPPLY("v-uart", "uart1"),
+ REGULATOR_SUPPLY("v-uart", "uart2"),
+ REGULATOR_SUPPLY("v-uart", "uart3"),
+ REGULATOR_SUPPLY("v-ape", "db5500-keypad"),
+};
+
+static struct regulator_consumer_supply db5500_sga_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.0"),
+ REGULATOR_SUPPLY("v-mali", NULL),
+};
+
+static struct regulator_consumer_supply db5500_hva_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.1"),
+ REGULATOR_SUPPLY("v-hva", NULL),
+};
+
+static struct regulator_consumer_supply db5500_sia_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.2"),
+ REGULATOR_SUPPLY("v-sia", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply db5500_disp_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.3"),
+ REGULATOR_SUPPLY("vsupply", "b2r2_bus"),
+ REGULATOR_SUPPLY("vsupply", "mcde"),
+};
+
+static struct regulator_consumer_supply db5500_esram12_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.4"),
+ REGULATOR_SUPPLY("v-esram12", "mcde"),
+ REGULATOR_SUPPLY("esram12", "hva"),
+};
+
+#define DB5500_REGULATOR_SWITCH(lower, upper) \
+[DB5500_REGULATOR_SWITCH_##upper] = { \
+ .constraints = { \
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, \
+ }, \
+ .consumer_supplies = db5500_##lower##_consumers, \
+ .num_consumer_supplies = ARRAY_SIZE(db5500_##lower##_consumers),\
}
+#define DB5500_REGULATOR_SWITCH_VAPE(lower, upper) \
+[DB5500_REGULATOR_SWITCH_##upper] = { \
+ .supply_regulator = "db5500-vape", \
+ .constraints = { \
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, \
+ }, \
+ .consumer_supplies = db5500_##lower##_consumers, \
+ .num_consumer_supplies = ARRAY_SIZE(db5500_##lower##_consumers),\
+} \
+
+static struct regulator_init_data db5500_regulators[DB5500_NUM_REGULATORS] = {
+ [DB5500_REGULATOR_VAPE] = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = db5500_vape_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db5500_vape_consumers),
+ },
+ DB5500_REGULATOR_SWITCH_VAPE(sga, SGA),
+ DB5500_REGULATOR_SWITCH_VAPE(hva, HVA),
+ DB5500_REGULATOR_SWITCH_VAPE(sia, SIA),
+ DB5500_REGULATOR_SWITCH_VAPE(disp, DISP),
+ /*
+ * ESRAM12 is put in retention by the firmware when VAPE is
+ * turned off so there's no need to hold VAPE.
+ */
+ DB5500_REGULATOR_SWITCH(esram12, ESRAM12),
+};
+
+static struct mfd_cell db5500_prcmu_devs[] = {
+ {
+ .name = "db5500-prcmu-regulators",
+ .platform_data = &db5500_regulators,
+ .pdata_size = sizeof(db5500_regulators),
+ },
+ {
+ .name = "cpufreq-u5500",
+ },
+};
+
/**
* prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
*
*/
-int __init db5500_prcmu_init(void)
+static int __init db5500_prcmu_probe(struct platform_device *pdev)
{
- int r = 0;
+ int err = 0;
if (ux500_is_svp() || !cpu_is_u5500())
return -ENODEV;
/* Clean up the mailbox interrupts after pre-kernel code. */
- writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
+ writel(ALL_MBOX_BITS, _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
- r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
- prcmu_irq_thread_fn, 0, "prcmu", NULL);
- if (r < 0) {
+ err = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
+ prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
+ if (err < 0) {
pr_err("prcmu: Failed to allocate IRQ_DB5500_PRCMU1.\n");
- return -EBUSY;
+ err = -EBUSY;
+ goto no_irq_return;
}
- return 0;
+
+ err = mfd_add_devices(&pdev->dev, 0, db5500_prcmu_devs,
+ ARRAY_SIZE(db5500_prcmu_devs), NULL,
+ 0);
+
+ if (err)
+ pr_err("prcmu: Failed to add subdevices\n");
+ else
+ pr_info("DB5500 PRCMU initialized\n");
+
+no_irq_return:
+ return err;
+
+}
+
+static struct platform_driver db5500_prcmu_driver = {
+ .driver = {
+ .name = "db5500-prcmu",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init db5500_prcmu_init(void)
+{
+ return platform_driver_probe(&db5500_prcmu_driver, db5500_prcmu_probe);
}
arch_initcall(db5500_prcmu_init);
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 5be32489714..7c26c41a7ef 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -30,11 +30,13 @@
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
+#include <linux/mfd/abx500.h>
#include <asm/hardware/gic.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db8500-regs.h>
#include <mach/id.h>
+#include <mach/prcmu-debug.h>
#include "dbx500-prcmu-regs.h"
/* Offset for the firmware version within the TCPM */
@@ -70,6 +72,8 @@
#define PRCM_SW_RST_REASON 0xFF8 /* 2 bytes */
+#define PRCM_TCDM_VOICE_CALL_FLAG 0xDD4 /* 4 bytes */
+
#define _PRCM_MBOX_HEADER 0xFE8 /* 16 bytes */
#define PRCM_MBOX_HEADER_REQ_MB0 (_PRCM_MBOX_HEADER + 0x0)
#define PRCM_MBOX_HEADER_REQ_MB1 (_PRCM_MBOX_HEADER + 0x1)
@@ -214,10 +218,8 @@
#define PRCM_REQ_MB5_I2C_HW_BITS (PRCM_REQ_MB5 + 0x1)
#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 0x2)
#define PRCM_REQ_MB5_I2C_VAL (PRCM_REQ_MB5 + 0x3)
-#define PRCMU_I2C_WRITE(slave) \
- (((slave) << 1) | (cpu_is_u8500v2() ? BIT(6) : 0))
-#define PRCMU_I2C_READ(slave) \
- (((slave) << 1) | BIT(0) | (cpu_is_u8500v2() ? BIT(6) : 0))
+#define PRCMU_I2C_WRITE(slave) (((slave) << 1) | BIT(6))
+#define PRCMU_I2C_READ(slave) (((slave) << 1) | BIT(0) | BIT(6))
#define PRCMU_I2C_STOP_EN BIT(3)
/* Mailbox 5 ACKs */
@@ -424,6 +426,13 @@ static DEFINE_SPINLOCK(clkout_lock);
/* Global var to runtime determine TCDM base for v2 or v1 */
static __iomem void *tcdm_base;
+/*
+ * Copies of the startup values of the reset status register and the SW reset
+ * code.
+ */
+static u32 reset_status_copy;
+static u16 reset_code_copy;
+
struct clk_mgt {
void __iomem *reg;
u32 pllsw;
@@ -637,6 +646,26 @@ void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
spin_unlock_irqrestore(&prcmu_lock, flags);
}
+/*
+ * Dump AB8500 registers, PRCMU registers and PRCMU data memory
+ * on critical errors.
+ */
+static void db8500_prcmu_debug_dump(const char *func,
+ bool dump_prcmu, bool dump_abb)
+{
+ printk(KERN_DEBUG"%s: timeout\n", func);
+
+ /* Dump AB8500 registers */
+ if (dump_abb)
+ abx500_dump_all_banks();
+
+ /* Dump prcmu registers and data memory */
+ if (dump_prcmu) {
+ prcmu_debug_dump_regs();
+ prcmu_debug_dump_data_mem();
+ }
+}
+
struct prcmu_fw_version *prcmu_get_fw_version(void)
{
return fw_info.valid ? &fw_info.version : NULL;
@@ -648,6 +677,11 @@ bool prcmu_has_arm_maxopp(void)
PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK;
}
+void db8500_prcmu_vc(bool enable)
+{
+ writel((enable ? 0xF : 0), (tcdm_base + PRCM_TCDM_VOICE_CALL_FLAG));
+}
+
/**
* prcmu_get_boot_status - PRCMU boot status checking
* Returns: the current PRCMU boot status
@@ -1049,7 +1083,7 @@ int db8500_prcmu_set_ddr_opp(u8 opp)
if (opp < DDR_100_OPP || opp > DDR_25_OPP)
return -EINVAL;
/* Changing the DDR OPP can hang the hardware pre-v21 */
- if (cpu_is_u8500v20_or_later() && !cpu_is_u8500v20())
+ if (!cpu_is_u8500v20())
writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW);
return 0;
@@ -1111,12 +1145,14 @@ unlock_and_return:
int db8500_prcmu_set_ape_opp(u8 opp)
{
int r = 0;
+ u8 prcmu_opp_req;
if (opp == mb1_transfer.ape_opp)
return 0;
mutex_lock(&mb1_transfer.lock);
+ /* Exit APE_50_PARTLY_25_OPP */
if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
request_even_slower_clocks(false);
@@ -1126,20 +1162,22 @@ int db8500_prcmu_set_ape_opp(u8 opp)
while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
+ prcmu_opp_req = (opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp;
+
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
- writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
- (tcdm_base + PRCM_REQ_MB1_APE_OPP));
+ writeb(prcmu_opp_req, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
- (mb1_transfer.ack.ape_opp != opp))
+ (mb1_transfer.ack.ape_opp != prcmu_opp_req))
r = -EIO;
skip_message:
if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
+ /* Set APE_50_PARTLY_25_OPP back in case new opp failed */
(r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
request_even_slower_clocks(true);
if (!r)
@@ -1322,6 +1360,7 @@ int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state)
pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
__func__);
r = -EIO;
+ db8500_prcmu_debug_dump(__func__, true, true);
goto unlock_and_return;
}
@@ -1416,6 +1455,7 @@ static int request_sysclk(bool enable)
pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
__func__);
r = -EIO;
+ db8500_prcmu_debug_dump(__func__, true, true);
}
mutex_unlock(&mb3_transfer.sysclk_lock);
@@ -2190,6 +2230,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
__func__);
r = -EIO;
+ db8500_prcmu_debug_dump(__func__, true, false);
} else {
r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO);
}
@@ -2240,6 +2281,7 @@ int prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size)
pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
__func__);
r = -EIO;
+ db8500_prcmu_debug_dump(__func__, true, false);
} else {
r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO);
}
@@ -2287,6 +2329,7 @@ retry:
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000))) {
+ db8500_prcmu_debug_dump(__func__, true, true);
pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__);
goto unlock_and_return;
@@ -2309,6 +2352,7 @@ retry:
if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000)))
goto retry;
+ db8500_prcmu_debug_dump(__func__, true, true);
pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
__func__);
}
@@ -2335,6 +2379,7 @@ void prcmu_ac_sleep_req()
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000))) {
+ db8500_prcmu_debug_dump(__func__, true, true);
pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__);
}
@@ -2370,7 +2415,17 @@ void db8500_prcmu_system_reset(u16 reset_code)
*/
u16 db8500_prcmu_get_reset_code(void)
{
- return readw(tcdm_base + PRCM_SW_RST_REASON);
+ return reset_code_copy;
+}
+
+/**
+ * db8500_prcmu_get_reset_status - Retrieve reset status
+ *
+ * Retrieves the value of the reset status register as read at startup.
+ */
+u32 db8500_prcmu_get_reset_status(void)
+{
+ return reset_status_copy;
}
/**
@@ -2437,6 +2492,13 @@ static bool read_mailbox_0(void)
if (ev & WAKEUP_BIT_SYSCLK_OK)
complete(&mb3_transfer.sysclk_work);
+ prcmu_debug_register_mbox0_event(ev,
+ (mb0_transfer.req.dbb_irqs |
+ mb0_transfer.req.dbb_wakeups |
+ WAKEUP_BIT_AC_WAKE_ACK |
+ WAKEUP_BIT_AC_SLEEP_ACK |
+ WAKEUP_BIT_SYSCLK_OK));
+
ev &= mb0_transfer.req.dbb_irqs;
for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) {
@@ -2561,6 +2623,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
bits -= MBOX_BIT(n);
if (read_mailbox[n]())
r = IRQ_WAKE_THREAD;
+ prcmu_debug_register_interrupt(n);
}
}
return r;
@@ -2646,29 +2709,38 @@ static char *fw_project_name(u8 project)
void __init db8500_prcmu_early_init(void)
{
unsigned int i;
- if (cpu_is_u8500v2()) {
- void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
-
- if (tcpm_base != NULL) {
- u32 version;
- version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
- fw_info.version.project = version & 0xFF;
- fw_info.version.api_version = (version >> 8) & 0xFF;
- fw_info.version.func_version = (version >> 16) & 0xFF;
- fw_info.version.errata = (version >> 24) & 0xFF;
- fw_info.valid = true;
- pr_info("PRCMU firmware: %s, version %d.%d.%d\n",
- fw_project_name(fw_info.version.project),
- (version >> 8) & 0xFF, (version >> 16) & 0xFF,
- (version >> 24) & 0xFF);
- iounmap(tcpm_base);
- }
+ void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
+ void __iomem *sec_base;
+
+ if (tcpm_base != NULL) {
+ u32 version;
+ version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
+ fw_info.version.project = version & 0xFF;
+ fw_info.version.api_version = (version >> 8) & 0xFF;
+ fw_info.version.func_version = (version >> 16) & 0xFF;
+ fw_info.version.errata = (version >> 24) & 0xFF;
+ fw_info.valid = true;
+ pr_info("PRCMU firmware: %s, version %d.%d.%d\n",
+ fw_project_name(fw_info.version.project),
+ (version >> 8) & 0xFF, (version >> 16) & 0xFF,
+ (version >> 24) & 0xFF);
+ iounmap(tcpm_base);
+ }
- tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
- } else {
- pr_err("prcmu: Unsupported chip version\n");
- BUG();
+ tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+
+ /*
+ * Copy the value of the reset status register and if needed also
+ * the software reset code.
+ */
+ sec_base = ioremap_nocache(U8500_PRCMU_SEC_BASE, SZ_4K);
+ if (sec_base != NULL) {
+ reset_status_copy = readl(sec_base +
+ DB8500_SEC_PRCM_RESET_STATUS);
+ iounmap(sec_base);
}
+ if (reset_status_copy & DB8500_SEC_PRCM_RESET_STATUS_APE_SOFTWARE_RESET)
+ reset_code_copy = readw(tcdm_base + PRCM_SW_RST_REASON);
spin_lock_init(&mb0_transfer.lock);
spin_lock_init(&mb0_transfer.dbb_irqs_lock);
@@ -2734,6 +2806,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = {
REGULATOR_SUPPLY("vcore", "uart2"),
REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"),
REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"),
+ REGULATOR_SUPPLY("vddvario", "smsc911x.0"),
};
static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
@@ -2743,7 +2816,7 @@ static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
};
static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = {
- REGULATOR_SUPPLY("vsupply", "b2r2_bus"),
+ REGULATOR_SUPPLY("vsupply", "b2r2_core"),
REGULATOR_SUPPLY("vsupply", "mcde"),
};
@@ -2962,9 +3035,6 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
{
int err = 0;
- if (ux500_is_svp())
- return -ENODEV;
-
init_prcm_registers();
/* Clean up the mailbox interrupts after pre-kernel code. */
@@ -2978,8 +3048,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
goto no_irq_return;
}
- if (cpu_is_u8500v20_or_later())
- prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+ prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
ARRAY_SIZE(db8500_prcmu_devs), NULL,
diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index 3a0bf91d778..0835a57dac1 100644
--- a/drivers/mfd/dbx500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -34,6 +34,9 @@
#define PRCM_PER5CLK_MGT PRCM_CLK_MGT(0x038)
#define PRCM_PER6CLK_MGT PRCM_CLK_MGT(0x03C)
#define PRCM_PER7CLK_MGT PRCM_CLK_MGT(0x040)
+#define PRCM_PWMCLK_MGT PRCM_CLK_MGT(0x044) /* for DB5500 */
+#define PRCM_IRDACLK_MGT PRCM_CLK_MGT(0x048) /* for DB5500 */
+#define PRCM_IRRCCLK_MGT PRCM_CLK_MGT(0x04C) /* for DB5500 */
#define PRCM_LCDCLK_MGT PRCM_CLK_MGT(0x044)
#define PRCM_BMLCLK_MGT PRCM_CLK_MGT(0x04C)
#define PRCM_HSITXCLK_MGT PRCM_CLK_MGT(0x050)
@@ -124,7 +127,6 @@
#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168)
#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484)
#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488)
-#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018)
/* System reset register */
#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
@@ -247,4 +249,17 @@
/* System reset register */
#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
+/* Secure read-only registers */
+#define DB8500_SEC_PRCM_RESET_STATUS 0x03C
+#define DB8500_SEC_PRCM_RESET_STATUS_A9_CPU0_WATCHDOG_RESET BIT(0)
+#define DB8500_SEC_PRCM_RESET_STATUS_A9_CPU1_WATCHDOG_RESET BIT(1)
+#define DB8500_SEC_PRCM_RESET_STATUS_APE_SOFTWARE_RESET BIT(2)
+#define DB8500_SEC_PRCM_RESET_STATUS_APE_RESET BIT(3)
+#define DB8500_SEC_PRCM_RESET_STATUS_SECURE_WATCHDOG BIT(4)
+#define DB8500_SEC_PRCM_RESET_STATUS_POWER_ON_RESET BIT(5)
+#define DB8500_SEC_PRCM_RESET_STATUS_A9_RESTART BIT(6)
+#define DB8500_SEC_PRCM_RESET_STATUS_APE_RESTART BIT(7)
+#define DB8500_SEC_PRCM_RESET_STATUS_MODEM_SOFTWARE_RESET BIT(8)
+#define DB8500_SEC_PRCM_RESET_STATUS_BOOT_ENGI BIT(16)
+
#endif /* __DB8500_PRCMU_REGS_H */
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 36db5a441eb..7278f1e4141 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -247,6 +247,14 @@ config REGULATOR_AB8500
This driver supports the regulators found on the ST-Ericsson mixed
signal AB8500 PMIC
+config REGULATOR_AB8500_EXT
+ bool "ST-Ericsson AB8500 External Regulators"
+ depends on REGULATOR_AB8500
+ default y if REGULATOR_AB8500
+ help
+ This driver supports the external regulator controls found on the
+ ST-Ericsson mixed signal AB8500 PMIC
+
config REGULATOR_DBX500_PRCMU
bool
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 94b52745e95..b5b90fb232b 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
+obj-$(CONFIG_REGULATOR_AB8500_EXT) += ab8500-ext.o
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
diff --git a/drivers/regulator/ab5500.c b/drivers/regulator/ab5500.c
new file mode 100644
index 00000000000..99676f7ad8e
--- /dev/null
+++ b/drivers/regulator/ab5500.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Based on ab3100.c.
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/regulator/ab5500.h>
+
+#define AB5500_LDO_VDIGMIC_ST 0x50
+
+#define AB5500_LDO_G_ST 0x78
+#define AB5500_LDO_G_PWR1 0x79
+#define AB5500_LDO_G_PWR0 0x7a
+
+#define AB5500_LDO_H_ST 0x7b
+#define AB5500_LDO_H_PWR1 0x7c
+#define AB5500_LDO_H_PWR0 0x7d
+
+#define AB5500_LDO_K_ST 0x7e
+#define AB5500_LDO_K_PWR1 0x7f
+#define AB5500_LDO_K_PWR0 0x80
+
+#define AB5500_LDO_L_ST 0x81
+#define AB5500_LDO_L_PWR1 0x82
+#define AB5500_LDO_L_PWR0 0x83
+
+/* In SIM bank */
+#define AB5500_SIM_SUP 0x14
+
+#define AB5500_MBIAS1 0x00
+#define AB5500_MBIAS2 0x01
+
+#define AB5500_LDO_MODE_MASK (0x3 << 4)
+#define AB5500_LDO_MODE_FULLPOWER (0x3 << 4)
+#define AB5500_LDO_MODE_PWRCTRL (0x2 << 4)
+#define AB5500_LDO_MODE_LOWPOWER (0x1 << 4)
+#define AB5500_LDO_MODE_OFF (0x0 << 4)
+#define AB5500_LDO_VOLT_MASK 0x07
+
+#define AB5500_MBIAS1_ENABLE (0x1 << 1)
+#define AB5500_MBIAS1_MODE_MASK (0x1 << 1)
+#define AB5500_MBIAS2_ENABLE (0x1 << 1)
+#define AB5500_MBIAS2_VOLT_MASK (0x1 << 2)
+#define AB5500_MBIAS2_MODE_MASK (0x1 << 1)
+
+struct ab5500_regulator {
+ struct regulator_desc desc;
+ const int *voltages;
+ int num_holes;
+ bool off_is_lowpower;
+ bool enabled;
+ int enable_time;
+ int load_lp_uA;
+ u8 bank;
+ u8 reg;
+ u8 mode;
+ u8 update_mask;
+ u8 update_val_idle;
+ u8 update_val_normal;
+ u8 voltage_mask;
+};
+
+struct ab5500_regulators {
+ struct device *dev;
+ struct ab5500_regulator *regulator[AB5500_NUM_REGULATORS];
+ struct regulator_dev *rdev[AB5500_NUM_REGULATORS];
+};
+
+static int ab5500_regulator_enable_time(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ return r->enable_time; /* microseconds */
+}
+
+static int ab5500_regulator_enable(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ int ret;
+
+ ret = abx500_mask_and_set(ab5500->dev, r->bank, r->reg,
+ r->update_mask, r->mode);
+ if (ret < 0)
+ return ret;
+
+ r->enabled = true;
+
+ return 0;
+}
+
+static int ab5500_regulator_disable(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval;
+ int ret;
+
+ if (r->off_is_lowpower)
+ regval = AB5500_LDO_MODE_LOWPOWER;
+ else
+ regval = AB5500_LDO_MODE_OFF;
+
+ ret = abx500_mask_and_set(ab5500->dev, r->bank, r->reg,
+ r->update_mask, regval);
+ if (ret < 0)
+ return ret;
+
+ r->enabled = false;
+
+ return 0;
+}
+
+static unsigned int ab5500_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ if (r->mode == r->update_val_idle)
+ return REGULATOR_MODE_IDLE;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static unsigned int
+ab5500_regulator_get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ unsigned int mode;
+
+ if (load_uA <= r->load_lp_uA)
+ mode = REGULATOR_MODE_IDLE;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static int ab5500_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ r->mode = r->update_val_normal;
+ break;
+ case REGULATOR_MODE_IDLE:
+ r->mode = r->update_val_idle;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (r->enabled)
+ return ab5500_regulator_enable(rdev);
+
+ return 0;
+}
+
+static int ab5500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval;
+ int err;
+
+ err = abx500_get_register_interruptible(ab5500->dev,
+ r->bank, r->reg, &regval);
+ if (err) {
+ dev_err(rdev_get_dev(rdev), "unable to get register 0x%x\n",
+ r->reg);
+ return err;
+ }
+
+ switch (regval & r->update_mask) {
+ case AB5500_LDO_MODE_PWRCTRL:
+ case AB5500_LDO_MODE_OFF:
+ r->enabled = false;
+ break;
+ case AB5500_LDO_MODE_LOWPOWER:
+ if (r->off_is_lowpower) {
+ r->enabled = false;
+ break;
+ }
+ /* fall through */
+ default:
+ r->enabled = true;
+ break;
+ }
+
+ return r->enabled;
+}
+
+static int
+ab5500_regulator_list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ unsigned n_voltages = r->desc.n_voltages;
+ int selindex;
+ int i;
+
+ for (i = 0, selindex = 0; selindex < n_voltages; i++) {
+ int voltage = r->voltages[i];
+
+ if (!voltage)
+ continue;
+
+ if (selindex == selector)
+ return voltage;
+
+ selindex++;
+ }
+
+ return -EINVAL;
+}
+
+static int ab5500_regulator_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ return r->voltages[0];
+}
+
+static int ab5500_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval;
+ int ret;
+
+ ret = abx500_get_register_interruptible(ab5500->dev,
+ r->bank, r->reg, &regval);
+ if (ret) {
+ dev_warn(rdev_get_dev(rdev),
+ "failed to get regulator value in register "
+ "%02x\n", r->reg);
+ return ret;
+ }
+
+ regval &= r->voltage_mask;
+ if (regval >= r->desc.n_voltages + r->num_holes)
+ return -EINVAL;
+
+ if (!r->voltages[regval])
+ return -EINVAL;
+
+ return r->voltages[regval];
+}
+
+static int ab5500_get_best_voltage_index(struct ab5500_regulator *r,
+ int min_uV, int max_uV)
+{
+ unsigned n_voltages = r->desc.n_voltages;
+ int bestmatch = INT_MAX;
+ int bestindex = -EINVAL;
+ int selindex;
+ int i;
+
+ /*
+ * Locate the minimum voltage fitting the criteria on
+ * this regulator. The switchable voltages are not
+ * in strict falling order so we need to check them
+ * all for the best match.
+ */
+ for (i = 0, selindex = 0; selindex < n_voltages; i++) {
+ int voltage = r->voltages[i];
+
+ if (!voltage)
+ continue;
+
+ if (voltage <= max_uV &&
+ voltage >= min_uV &&
+ voltage < bestmatch) {
+ bestmatch = voltage;
+ bestindex = i;
+ }
+
+ selindex++;
+ }
+
+ return bestindex;
+}
+
+static int ab5500_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV,
+ unsigned *selector)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ int bestindex;
+
+ bestindex = ab5500_get_best_voltage_index(r, min_uV, max_uV);
+ if (bestindex < 0) {
+ dev_warn(rdev_get_dev(rdev),
+ "requested %d<=x<=%d uV, out of range!\n",
+ min_uV, max_uV);
+ return bestindex;
+ }
+
+ *selector = bestindex;
+
+ return abx500_mask_and_set_register_interruptible(ab5500->dev,
+ r->bank, r->reg, r->voltage_mask, bestindex);
+
+}
+
+static struct regulator_ops ab5500_regulator_variable_ops = {
+ .enable = ab5500_regulator_enable,
+ .disable = ab5500_regulator_disable,
+ .is_enabled = ab5500_regulator_is_enabled,
+ .enable_time = ab5500_regulator_enable_time,
+ .get_voltage = ab5500_regulator_get_voltage,
+ .set_voltage = ab5500_regulator_set_voltage,
+ .list_voltage = ab5500_regulator_list_voltage,
+ .set_mode = ab5500_regulator_set_mode,
+ .get_mode = ab5500_regulator_get_mode,
+ .get_optimum_mode = ab5500_regulator_get_optimum_mode,
+};
+
+static struct regulator_ops ab5500_regulator_fixed_ops = {
+ .enable = ab5500_regulator_enable,
+ .disable = ab5500_regulator_disable,
+ .is_enabled = ab5500_regulator_is_enabled,
+ .enable_time = ab5500_regulator_enable_time,
+ .get_voltage = ab5500_regulator_fixed_get_voltage,
+ .list_voltage = ab5500_regulator_list_voltage,
+ .set_mode = ab5500_regulator_set_mode,
+ .get_mode = ab5500_regulator_get_mode,
+ .get_optimum_mode = ab5500_regulator_get_optimum_mode,
+};
+
+static const int ab5500_ldo_lg_voltages[] = {
+ [0x00] = 1200000,
+ [0x01] = 0, /* not used */
+ [0x02] = 1500000,
+ [0x03] = 1800000,
+ [0x04] = 0, /* not used */
+ [0x05] = 2500000,
+ [0x06] = 2730000,
+ [0x07] = 2910000,
+};
+
+static const int ab5500_ldo_kh_voltages[] = {
+ [0x00] = 1200000,
+ [0x01] = 1500000,
+ [0x02] = 1800000,
+ [0x03] = 2100000,
+ [0x04] = 2500000,
+ [0x05] = 2750000,
+ [0x06] = 2790000,
+ [0x07] = 2910000,
+};
+
+static const int ab5500_ldo_vdigmic_voltages[] = {
+ [0x00] = 2100000,
+};
+
+static const int ab5500_ldo_sim_voltages[] = {
+ [0x00] = 1875000,
+ [0x01] = 2800000,
+ [0x02] = 2900000,
+};
+
+static const int ab5500_bias2_voltages[] = {
+ [0x00] = 2000000,
+ [0x01] = 2200000,
+};
+
+static const int ab5500_bias1_voltages[] = {
+ [0x00] = 2000000,
+};
+
+static struct ab5500_regulator ab5500_regulators[] = {
+ [AB5500_LDO_L] = {
+ .desc = {
+ .name = "LDO_L",
+ .id = AB5500_LDO_L,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) -
+ 2,
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_L_ST,
+ .voltages = ab5500_ldo_lg_voltages,
+ .num_holes = 2, /* 2 register values unused */
+ .enable_time = 400,
+ .load_lp_uA = 20000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_G] = {
+ .desc = {
+ .name = "LDO_G",
+ .id = AB5500_LDO_G,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) -
+ 2,
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_G_ST,
+ .voltages = ab5500_ldo_lg_voltages,
+ .num_holes = 2, /* 2 register values unused */
+ .enable_time = 400,
+ .load_lp_uA = 20000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_K] = {
+ .desc = {
+ .name = "LDO_K",
+ .id = AB5500_LDO_K,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_K_ST,
+ .voltages = ab5500_ldo_kh_voltages,
+ .enable_time = 400,
+ .load_lp_uA = 20000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_H] = {
+ .desc = {
+ .name = "LDO_H",
+ .id = AB5500_LDO_H,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_H_ST,
+ .voltages = ab5500_ldo_kh_voltages,
+ .enable_time = 400,
+ .load_lp_uA = 20000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_VDIGMIC] = {
+ .desc = {
+ .name = "LDO_VDIGMIC",
+ .id = AB5500_LDO_VDIGMIC,
+ .ops = &ab5500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages =
+ ARRAY_SIZE(ab5500_ldo_vdigmic_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_VDIGMIC_ST,
+ .voltages = ab5500_ldo_vdigmic_voltages,
+ .enable_time = 450,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_SIM] = {
+ .desc = {
+ .name = "LDO_SIM",
+ .id = AB5500_LDO_SIM,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_sim_voltages),
+ },
+ .bank = AB5500_BANK_SIM_USBSIM,
+ .reg = AB5500_SIM_SUP,
+ .voltages = ab5500_ldo_sim_voltages,
+ .enable_time = 1000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_BIAS2] = {
+ .desc = {
+ .name = "MBIAS2",
+ .id = AB5500_BIAS2,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_bias2_voltages),
+ },
+ .bank = AB5500_BANK_AUDIO_HEADSETUSB,
+ .reg = AB5500_MBIAS2,
+ .voltages = ab5500_bias2_voltages,
+ .enable_time = 1000,
+ .mode = AB5500_MBIAS2_ENABLE,
+ .update_mask = AB5500_MBIAS2_MODE_MASK,
+ .update_val_normal = AB5500_MBIAS2_ENABLE,
+ .update_val_idle = AB5500_MBIAS2_ENABLE,
+ .voltage_mask = AB5500_MBIAS2_VOLT_MASK,
+ },
+ [AB5500_BIAS1] = {
+ .desc = {
+ .name = "MBIAS1",
+ .id = AB5500_BIAS1,
+ .ops = &ab5500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_bias1_voltages),
+ },
+ .bank = AB5500_BANK_AUDIO_HEADSETUSB,
+ .reg = AB5500_MBIAS1,
+ .voltages = ab5500_bias1_voltages,
+ .enable_time = 1000,
+ .mode = AB5500_MBIAS1_ENABLE,
+ .update_mask = AB5500_MBIAS1_MODE_MASK,
+ .update_val_normal = AB5500_MBIAS1_ENABLE,
+ .update_val_idle = AB5500_MBIAS1_ENABLE,
+ },
+};
+
+
+static int __devinit ab5500_regulator_probe(struct platform_device *pdev)
+{
+ struct ab5500_platform_data *ppdata = pdev->dev.parent->platform_data;
+ struct ab5500_regulator_platform_data *pdata = ppdata->regulator;
+ struct ab5500_regulator_data *regdata;
+ struct ab5500_regulators *ab5500;
+ int err = 0;
+ int i;
+
+ if (!pdata || !pdata->regulator)
+ return -EINVAL;
+
+ ab5500 = kzalloc(sizeof(*ab5500), GFP_KERNEL);
+ if (!ab5500)
+ return -ENOMEM;
+
+ ab5500->dev = &pdev->dev;
+ regdata = pdata->data;
+
+ platform_set_drvdata(pdev, ab5500);
+
+ for (i = 0; i < AB5500_NUM_REGULATORS; i++) {
+ struct ab5500_regulator *regulator = &ab5500_regulators[i];
+ struct regulator_dev *rdev;
+
+ if (regdata)
+ regulator->off_is_lowpower = regdata[i].off_is_lowpower;
+
+ ab5500->regulator[i] = regulator;
+
+ rdev = regulator_register(&regulator->desc, &pdev->dev,
+ &pdata->regulator[i], ab5500);
+ if (IS_ERR(rdev)) {
+ err = PTR_ERR(rdev);
+ dev_err(&pdev->dev, "failed to register regulator %s err %d\n",
+ regulator->desc.name, err);
+ goto err_unregister;
+ }
+
+ ab5500->rdev[i] = rdev;
+ }
+
+ return 0;
+
+err_unregister:
+ /* remove the already registered regulators */
+ while (--i >= 0)
+ regulator_unregister(ab5500->rdev[i]);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(ab5500);
+
+ return err;
+}
+
+static int __devexit ab5500_regulators_remove(struct platform_device *pdev)
+{
+ struct ab5500_regulators *ab5500 = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < AB5500_NUM_REGULATORS; i++)
+ regulator_unregister(ab5500->rdev[i]);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(ab5500);
+
+ return 0;
+}
+
+static struct platform_driver ab5500_regulator_driver = {
+ .driver = {
+ .name = "ab5500-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab5500_regulator_probe,
+ .remove = __devexit_p(ab5500_regulators_remove),
+};
+
+static __init int ab5500_regulator_init(void)
+{
+ return platform_driver_register(&ab5500_regulator_driver);
+}
+
+static __exit void ab5500_regulator_exit(void)
+{
+ platform_driver_unregister(&ab5500_regulator_driver);
+}
+
+subsys_initcall(ab5500_regulator_init);
+module_exit(ab5500_regulator_exit);
+
+MODULE_DESCRIPTION("AB5500 Regulator Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ab5500-regulator");
diff --git a/drivers/regulator/ab8500-debug.c b/drivers/regulator/ab8500-debug.c
new file mode 100644
index 00000000000..f71cc26c135
--- /dev/null
+++ b/drivers/regulator/ab8500-debug.c
@@ -0,0 +1,2083 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson.
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/regulator/ab8500-debug.h>
+#include <linux/io.h>
+
+#include <mach/db8500-regs.h> /* U8500_BACKUPRAM1_BASE */
+#include <mach/hardware.h>
+
+#include "ab8500-debug.h"
+
+/* board profile address - to determine if suspend-force is default */
+#define BOOT_INFO_BACKUPRAM1 (U8500_BACKUPRAM1_BASE + 0xffc)
+#define BOARD_PROFILE_BACKUPRAM1 (0x3)
+
+/* board profile option */
+#define OPTION_BOARD_VERSION_V5X 50
+
+/* for error prints */
+struct device *dev;
+struct platform_device *pdev;
+
+/* setting for suspend force (disabled by default) */
+static bool setting_suspend_force;
+
+/*
+ * regulator states
+ */
+enum ab8500_regulator_state_id {
+ AB8500_REGULATOR_STATE_INIT,
+ AB8500_REGULATOR_STATE_SUSPEND,
+ AB8500_REGULATOR_STATE_SUSPEND_CORE,
+ AB8500_REGULATOR_STATE_RESUME_CORE,
+ AB8500_REGULATOR_STATE_RESUME,
+ AB8500_REGULATOR_STATE_CURRENT,
+ NUM_REGULATOR_STATE
+};
+
+static const char *regulator_state_name[NUM_REGULATOR_STATE] = {
+ [AB8500_REGULATOR_STATE_INIT] = "init",
+ [AB8500_REGULATOR_STATE_SUSPEND] = "suspend",
+ [AB8500_REGULATOR_STATE_SUSPEND_CORE] = "suspend-core",
+ [AB8500_REGULATOR_STATE_RESUME_CORE] = "resume-core",
+ [AB8500_REGULATOR_STATE_RESUME] = "resume",
+ [AB8500_REGULATOR_STATE_CURRENT] = "current",
+};
+
+/*
+ * regulator register definitions
+ */
+enum ab8500_register_id {
+ AB8500_REGU_NOUSE, /* if not defined */
+ AB8500_REGU_REQUEST_CTRL1,
+ AB8500_REGU_REQUEST_CTRL2,
+ AB8500_REGU_REQUEST_CTRL3,
+ AB8500_REGU_REQUEST_CTRL4,
+ AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ AB8500_REGU_HW_HP_REQ1_VALID1,
+ AB8500_REGU_HW_HP_REQ1_VALID2,
+ AB8500_REGU_HW_HP_REQ2_VALID1,
+ AB8500_REGU_HW_HP_REQ2_VALID2,
+ AB8500_REGU_SW_HP_REQ_VALID1,
+ AB8500_REGU_SW_HP_REQ_VALID2,
+ AB8500_REGU_SYSCLK_REQ1_VALID,
+ AB8500_REGU_SYSCLK_REQ2_VALID,
+ AB9540_REGU_VAUX4_REQ_VALID,
+ AB8500_REGU_MISC1,
+ AB8500_REGU_OTG_SUPPLY_CTRL,
+ AB8500_REGU_VUSB_CTRL,
+ AB8500_REGU_VAUDIO_SUPPLY,
+ AB8500_REGU_CTRL1_VAMIC,
+ AB8500_REGU_ARM_REGU1,
+ AB8500_REGU_ARM_REGU2,
+ AB8500_REGU_VAPE_REGU,
+ AB8500_REGU_VSMPS1_REGU,
+ AB8500_REGU_VSMPS2_REGU,
+ AB8500_REGU_VSMPS3_REGU,
+ AB8500_REGU_VPLL_VANA_REGU,
+ AB8500_REGU_VREF_DDR,
+ AB8500_REGU_EXT_SUPPLY_REGU,
+ AB8500_REGU_VAUX12_REGU,
+ AB8500_REGU_VRF1_VAUX3_REGU,
+ AB8500_REGU_VARM_SEL1,
+ AB8500_REGU_VARM_SEL2,
+ AB8500_REGU_VARM_SEL3,
+ AB8500_REGU_VAPE_SEL1,
+ AB8500_REGU_VAPE_SEL2,
+ AB8500_REGU_VAPE_SEL3,
+ AB9540_REGU_VAUX4_REQ_CTRL,
+ AB9540_REGU_VAUX4_REGU,
+ AB9540_REGU_VAUX4_SEL,
+ AB8500_REGU_VBB_SEL1,
+ AB8500_REGU_VBB_SEL2,
+ AB8500_REGU_VSMPS1_SEL1,
+ AB8500_REGU_VSMPS1_SEL2,
+ AB8500_REGU_VSMPS1_SEL3,
+ AB8500_REGU_VSMPS2_SEL1,
+ AB8500_REGU_VSMPS2_SEL2,
+ AB8500_REGU_VSMPS2_SEL3,
+ AB8500_REGU_VSMPS3_SEL1,
+ AB8500_REGU_VSMPS3_SEL2,
+ AB8500_REGU_VSMPS3_SEL3,
+ AB8500_REGU_VAUX1_SEL,
+ AB8500_REGU_VAUX2_SEL,
+ AB8500_REGU_VRF1_VAUX3_SEL,
+ AB8500_REGU_CTRL_EXT_SUP,
+ AB8500_REGU_VMOD_REGU,
+ AB8500_REGU_VMOD_SEL1,
+ AB8500_REGU_VMOD_SEL2,
+ AB8500_REGU_CTRL_DISCH,
+ AB8500_REGU_CTRL_DISCH2,
+ AB9540_REGU_CTRL_DISCH3,
+ AB8500_OTHER_SYSCLK_CTRL, /* Other */
+ AB8500_OTHER_VSIM_SYSCLK_CTRL, /* Other */
+ AB8500_OTHER_SYSULPCLK_CTRL1, /* Other */
+ NUM_AB8500_REGISTER
+};
+
+struct ab8500_register {
+ const char *name;
+ u8 bank;
+ u8 addr;
+ u8 unavailable; /* Used to flag when AB doesn't support a register */
+};
+
+static struct ab8500_register
+ ab8500_register[NUM_AB8500_REGISTER] = {
+ [AB8500_REGU_REQUEST_CTRL1] = {
+ .name = "ReguRequestCtrl1",
+ .bank = 0x03,
+ .addr = 0x03,
+ },
+ [AB8500_REGU_REQUEST_CTRL2] = {
+ .name = "ReguRequestCtrl2",
+ .bank = 0x03,
+ .addr = 0x04,
+ },
+ [AB8500_REGU_REQUEST_CTRL3] = {
+ .name = "ReguRequestCtrl3",
+ .bank = 0x03,
+ .addr = 0x05,
+ },
+ [AB8500_REGU_REQUEST_CTRL4] = {
+ .name = "ReguRequestCtrl4",
+ .bank = 0x03,
+ .addr = 0x06,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_HP_VALID1] = {
+ .name = "ReguSysClkReq1HPValid",
+ .bank = 0x03,
+ .addr = 0x07,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_HP_VALID2] = {
+ .name = "ReguSysClkReq1HPValid2",
+ .bank = 0x03,
+ .addr = 0x08,
+ },
+ [AB8500_REGU_HW_HP_REQ1_VALID1] = {
+ .name = "ReguHwHPReq1Valid1",
+ .bank = 0x03,
+ .addr = 0x09,
+ },
+ [AB8500_REGU_HW_HP_REQ1_VALID2] = {
+ .name = "ReguHwHPReq1Valid2",
+ .bank = 0x03,
+ .addr = 0x0a,
+ },
+ [AB8500_REGU_HW_HP_REQ2_VALID1] = {
+ .name = "ReguHwHPReq2Valid1",
+ .bank = 0x03,
+ .addr = 0x0b,
+ },
+ [AB8500_REGU_HW_HP_REQ2_VALID2] = {
+ .name = "ReguHwHPReq2Valid2",
+ .bank = 0x03,
+ .addr = 0x0c,
+ },
+ [AB8500_REGU_SW_HP_REQ_VALID1] = {
+ .name = "ReguSwHPReqValid1",
+ .bank = 0x03,
+ .addr = 0x0d,
+ },
+ [AB8500_REGU_SW_HP_REQ_VALID2] = {
+ .name = "ReguSwHPReqValid2",
+ .bank = 0x03,
+ .addr = 0x0e,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_VALID] = {
+ .name = "ReguSysClkReqValid1",
+ .bank = 0x03,
+ .addr = 0x0f,
+ },
+ [AB8500_REGU_SYSCLK_REQ2_VALID] = {
+ .name = "ReguSysClkReqValid2",
+ .bank = 0x03,
+ .addr = 0x10,
+ },
+ [AB9540_REGU_VAUX4_REQ_VALID] = {
+ .name = "ReguVaux4ReqValid",
+ .bank = 0x03,
+ .addr = 0x11,
+ .unavailable = true, /* ab9540 register */
+ },
+ [AB8500_REGU_MISC1] = {
+ .name = "ReguMisc1",
+ .bank = 0x03,
+ .addr = 0x80,
+ },
+ [AB8500_REGU_OTG_SUPPLY_CTRL] = {
+ .name = "OTGSupplyCtrl",
+ .bank = 0x03,
+ .addr = 0x81,
+ },
+ [AB8500_REGU_VUSB_CTRL] = {
+ .name = "VusbCtrl",
+ .bank = 0x03,
+ .addr = 0x82,
+ },
+ [AB8500_REGU_VAUDIO_SUPPLY] = {
+ .name = "VaudioSupply",
+ .bank = 0x03,
+ .addr = 0x83,
+ },
+ [AB8500_REGU_CTRL1_VAMIC] = {
+ .name = "ReguCtrl1VAmic",
+ .bank = 0x03,
+ .addr = 0x84,
+ },
+ [AB8500_REGU_ARM_REGU1] = {
+ .name = "ArmRegu1",
+ .bank = 0x04,
+ .addr = 0x00,
+ },
+ [AB8500_REGU_ARM_REGU2] = {
+ .name = "ArmRegu2",
+ .bank = 0x04,
+ .addr = 0x01,
+ },
+ [AB8500_REGU_VAPE_REGU] = {
+ .name = "VapeRegu",
+ .bank = 0x04,
+ .addr = 0x02,
+ },
+ [AB8500_REGU_VSMPS1_REGU] = {
+ .name = "Vsmps1Regu",
+ .bank = 0x04,
+ .addr = 0x03,
+ },
+ [AB8500_REGU_VSMPS2_REGU] = {
+ .name = "Vsmps2Regu",
+ .bank = 0x04,
+ .addr = 0x04,
+ },
+ [AB8500_REGU_VSMPS3_REGU] = {
+ .name = "Vsmps3Regu",
+ .bank = 0x04,
+ .addr = 0x05,
+ },
+ [AB8500_REGU_VPLL_VANA_REGU] = {
+ .name = "VpllVanaRegu",
+ .bank = 0x04,
+ .addr = 0x06,
+ },
+ [AB8500_REGU_VREF_DDR] = {
+ .name = "VrefDDR",
+ .bank = 0x04,
+ .addr = 0x07,
+ },
+ [AB8500_REGU_EXT_SUPPLY_REGU] = {
+ .name = "ExtSupplyRegu",
+ .bank = 0x04,
+ .addr = 0x08,
+ },
+ [AB8500_REGU_VAUX12_REGU] = {
+ .name = "Vaux12Regu",
+ .bank = 0x04,
+ .addr = 0x09,
+ },
+ [AB8500_REGU_VRF1_VAUX3_REGU] = {
+ .name = "VRF1Vaux3Regu",
+ .bank = 0x04,
+ .addr = 0x0a,
+ },
+ [AB8500_REGU_VARM_SEL1] = {
+ .name = "VarmSel1",
+ .bank = 0x04,
+ .addr = 0x0b,
+ },
+ [AB8500_REGU_VARM_SEL2] = {
+ .name = "VarmSel2",
+ .bank = 0x04,
+ .addr = 0x0c,
+ },
+ [AB8500_REGU_VARM_SEL3] = {
+ .name = "VarmSel3",
+ .bank = 0x04,
+ .addr = 0x0d,
+ },
+ [AB8500_REGU_VAPE_SEL1] = {
+ .name = "VapeSel1",
+ .bank = 0x04,
+ .addr = 0x0e,
+ },
+ [AB8500_REGU_VAPE_SEL2] = {
+ .name = "VapeSel2",
+ .bank = 0x04,
+ .addr = 0x0f,
+ },
+ [AB8500_REGU_VAPE_SEL3] = {
+ .name = "VapeSel3",
+ .bank = 0x04,
+ .addr = 0x10,
+ },
+ [AB9540_REGU_VAUX4_REQ_CTRL] = {
+ .name = "Vaux4ReqCtrl",
+ .bank = 0x04,
+ .addr = 0x2d,
+ .unavailable = true, /* ab9540 register */
+ },
+ [AB9540_REGU_VAUX4_REGU] = {
+ .name = "Vaux4Regu",
+ .bank = 0x04,
+ .addr = 0x2e,
+ .unavailable = true, /* ab9540 register */
+ },
+ [AB9540_REGU_VAUX4_SEL] = {
+ .name = "Vaux4Sel",
+ .bank = 0x04,
+ .addr = 0x2f,
+ .unavailable = true, /* ab9540 register */
+ },
+ [AB8500_REGU_VBB_SEL1] = {
+ .name = "VBBSel1",
+ .bank = 0x04,
+ .addr = 0x11,
+ },
+ [AB8500_REGU_VBB_SEL2] = {
+ .name = "VBBSel2",
+ .bank = 0x04,
+ .addr = 0x12,
+ },
+ [AB8500_REGU_VSMPS1_SEL1] = {
+ .name = "Vsmps1Sel1",
+ .bank = 0x04,
+ .addr = 0x13,
+ },
+ [AB8500_REGU_VSMPS1_SEL2] = {
+ .name = "Vsmps1Sel2",
+ .bank = 0x04,
+ .addr = 0x14,
+ },
+ [AB8500_REGU_VSMPS1_SEL3] = {
+ .name = "Vsmps1Sel3",
+ .bank = 0x04,
+ .addr = 0x15,
+ },
+ [AB8500_REGU_VSMPS2_SEL1] = {
+ .name = "Vsmps2Sel1",
+ .bank = 0x04,
+ .addr = 0x17,
+ },
+ [AB8500_REGU_VSMPS2_SEL2] = {
+ .name = "Vsmps2Sel2",
+ .bank = 0x04,
+ .addr = 0x18,
+ },
+ [AB8500_REGU_VSMPS2_SEL3] = {
+ .name = "Vsmps2Sel3",
+ .bank = 0x04,
+ .addr = 0x19,
+ },
+ [AB8500_REGU_VSMPS3_SEL1] = {
+ .name = "Vsmps3Sel1",
+ .bank = 0x04,
+ .addr = 0x1b,
+ },
+ [AB8500_REGU_VSMPS3_SEL2] = {
+ .name = "Vsmps3Sel2",
+ .bank = 0x04,
+ .addr = 0x1c,
+ },
+ [AB8500_REGU_VSMPS3_SEL3] = {
+ .name = "Vsmps3Sel3",
+ .bank = 0x04,
+ .addr = 0x1d,
+ },
+ [AB8500_REGU_VAUX1_SEL] = {
+ .name = "Vaux1Sel",
+ .bank = 0x04,
+ .addr = 0x1f,
+ },
+ [AB8500_REGU_VAUX2_SEL] = {
+ .name = "Vaux2Sel",
+ .bank = 0x04,
+ .addr = 0x20,
+ },
+ [AB8500_REGU_VRF1_VAUX3_SEL] = {
+ .name = "VRF1Vaux3Sel",
+ .bank = 0x04,
+ .addr = 0x21,
+ },
+ [AB8500_REGU_CTRL_EXT_SUP] = {
+ .name = "ReguCtrlExtSup",
+ .bank = 0x04,
+ .addr = 0x22,
+ },
+ [AB8500_REGU_VMOD_REGU] = {
+ .name = "VmodRegu",
+ .bank = 0x04,
+ .addr = 0x40,
+ },
+ [AB8500_REGU_VMOD_SEL1] = {
+ .name = "VmodSel1",
+ .bank = 0x04,
+ .addr = 0x41,
+ },
+ [AB8500_REGU_VMOD_SEL2] = {
+ .name = "VmodSel2",
+ .bank = 0x04,
+ .addr = 0x42,
+ },
+ [AB8500_REGU_CTRL_DISCH] = {
+ .name = "ReguCtrlDisch",
+ .bank = 0x04,
+ .addr = 0x43,
+ },
+ [AB8500_REGU_CTRL_DISCH2] = {
+ .name = "ReguCtrlDisch2",
+ .bank = 0x04,
+ .addr = 0x44,
+ },
+ [AB9540_REGU_CTRL_DISCH3] = {
+ .name = "ReguCtrlDisch3",
+ .bank = 0x04,
+ .addr = 0x48,
+ .unavailable = true, /* ab9540 register */
+ },
+ /* Outside regulator banks */
+ [AB8500_OTHER_SYSCLK_CTRL] = {
+ .name = "SysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x0c,
+ },
+ [AB8500_OTHER_VSIM_SYSCLK_CTRL] = {
+ .name = "VsimSysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x33,
+ },
+ [AB8500_OTHER_SYSULPCLK_CTRL1] = {
+ .name = "SysUlpClkCtrl1",
+ .bank = 0x02,
+ .addr = 0x0b,
+ },
+};
+
+struct ab9540_register_update {
+ /* Identity of register to be updated */
+ u8 bank;
+ u8 addr;
+ /* New value for unavailable flag */
+ u8 unavailable;
+};
+
+static const struct ab9540_register_update ab9540_update[] = {
+ /* AB8500 register which is unavailable to AB9540 */
+ /* AB8500_REGU_VREF_DDR */
+ {
+ .bank = 0x04,
+ .addr = 0x07,
+ .unavailable = true,
+ },
+
+ /* Registers which were not available to AB8500 but are on the
+ * AB9540. */
+ /* AB9540_REGU_VAUX4_REQ_VALID */
+ {
+ .bank = 0x03,
+ .addr = 0x11,
+ },
+ /* AB9540_REGU_VAUX4_REQ_CTRL */
+ {
+ .bank = 0x04,
+ .addr = 0x2d,
+ },
+ /* AB9540_REGU_VAUX4_REGU */
+ {
+ .bank = 0x04,
+ .addr = 0x2e,
+ },
+ /* AB9540_REGU_VAUX4_SEL */
+ {
+ .bank = 0x04,
+ .addr = 0x2f,
+ },
+ /* AB9540_REGU_CTRL_DISCH3 */
+ {
+ .bank = 0x04,
+ .addr = 0x48,
+ },
+};
+
+static void ab9540_registers_update(void)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < NUM_AB8500_REGISTER; i++)
+ for (j = 0; j < ARRAY_SIZE(ab9540_update); j++)
+ if (ab8500_register[i].bank == ab9540_update[j].bank &&
+ ab8500_register[i].addr == ab9540_update[j].addr) {
+ ab8500_register[i].unavailable =
+ ab9540_update[j].unavailable;
+ break;
+ }
+}
+
+static u8 ab8500_register_state[NUM_REGULATOR_STATE][NUM_AB8500_REGISTER];
+static bool ab8500_register_state_saved[NUM_REGULATOR_STATE];
+static bool ab8500_register_state_save = true;
+
+static int ab8500_regulator_record_state(int state)
+{
+ u8 val;
+ int i;
+ int ret;
+
+ /* check arguments */
+ if ((state > NUM_REGULATOR_STATE) || (state < 0)) {
+ dev_err(dev, "Wrong state specified\n");
+ return -EINVAL;
+ }
+
+ /* record */
+ if (!ab8500_register_state_save)
+ goto exit;
+
+ ab8500_register_state_saved[state] = true;
+
+ for (i = 1; i < NUM_AB8500_REGISTER; i++) {
+ if (ab8500_register[i].unavailable)
+ continue;
+
+ ret = abx500_get_register_interruptible(dev,
+ ab8500_register[i].bank,
+ ab8500_register[i].addr,
+ &val);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ ab8500_register_state[state][i] = val;
+ }
+exit:
+ return 0;
+}
+
+/*
+ * regulator register dump
+ */
+static int ab8500_regulator_dump_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ int state, reg_id, i;
+ int err;
+
+ /* record current state */
+ ab8500_regulator_record_state(AB8500_REGULATOR_STATE_CURRENT);
+
+ /* print dump header */
+ err = seq_printf(s, "ab8500-regulator dump:\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+
+ /* print states */
+ for (state = NUM_REGULATOR_STATE - 1; state >= 0; state--) {
+ if (ab8500_register_state_saved[state])
+ err = seq_printf(s, "%16s saved -------",
+ regulator_state_name[state]);
+ else
+ err = seq_printf(s, "%12s not saved -------",
+ regulator_state_name[state]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ for (i = 0; i < NUM_REGULATOR_STATE; i++) {
+ if (i < state)
+ err = seq_printf(s, "-----");
+ else if (i == state)
+ err = seq_printf(s, "----+");
+ else
+ err = seq_printf(s, " |");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n",
+ __LINE__);
+ }
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ }
+
+ /* print labels */
+ err = seq_printf(s, "\n addr\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ /* dump registers */
+ for (reg_id = 1; reg_id < NUM_AB8500_REGISTER; reg_id++) {
+ if (ab8500_register[reg_id].unavailable)
+ continue;
+
+ err = seq_printf(s, "%22s 0x%02x%02x:",
+ ab8500_register[reg_id].name,
+ ab8500_register[reg_id].bank,
+ ab8500_register[reg_id].addr);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+
+ for (state = 0; state < NUM_REGULATOR_STATE; state++) {
+ err = seq_printf(s, " 0x%02x",
+ ab8500_register_state[state][reg_id]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+ }
+
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+ }
+
+ return 0;
+}
+
+static int ab8500_regulator_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_regulator_dump_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_dump_fops = {
+ .open = ab8500_regulator_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * regulator_voltage
+ */
+struct regulator_volt {
+ u8 value;
+ int volt;
+};
+
+struct regulator_volt_range {
+ struct regulator_volt start;
+ struct regulator_volt step;
+ struct regulator_volt end;
+};
+
+/*
+ * ab8500_regulator
+ * @name
+ * @update_regid
+ * @update_mask
+ * @update_val[4] {off, on, hw, lp}
+ * @hw_mode_regid
+ * @hw_mode_mask
+ * @hw_mode_val[4] {hp/lp, hp/off, hp, hp}
+ * @hw_valid_regid[4] {sysclkreq1, hw1, hw2, sw}
+ * @hw_valid_mask[4] {sysclkreq1, hw1, hw2, sw}
+ * @vsel_sel_regid
+ * @vsel_sel_mask
+ * @vsel_val[333] {sel1, sel2, sel3, sel3}
+ * @vsel_regid
+ * @vsel_mask
+ * @vsel_range
+ * @vsel_range_len
+ * @unavailable {true/false depending on whether AB supports the regulator}
+ */
+struct ab8500_regulator {
+ const char *name;
+ int update_regid;
+ u8 update_mask;
+ u8 update_val[4];
+ int hw_mode_regid;
+ u8 hw_mode_mask;
+ u8 hw_mode_val[4];
+ int hw_valid_regid[4];
+ u8 hw_valid_mask[4];
+ int vsel_sel_regid;
+ u8 vsel_sel_mask;
+ u8 vsel_sel_val[4];
+ int vsel_regid[3];
+ u8 vsel_mask[3];
+ struct regulator_volt_range const *vsel_range[3];
+ int vsel_range_len[3];
+ u8 unavailable;
+};
+
+static const char *update_val_name[] = {
+ "off",
+ "on ",
+ "hw ",
+ "lp ",
+ " - " /* undefined value */
+};
+
+static const char *hw_mode_val_name[] = {
+ "hp/lp ",
+ "hp/off",
+ "hp ",
+ "hp ",
+ "-/- ", /* undefined value */
+};
+
+/* voltage selection */
+/* AB8500 device - Varm_vsel in 12.5mV steps */
+#define AB8500_VARM_VSEL_MASK 0x3f
+static const struct regulator_volt_range ab8500_varm_vsel[] = {
+ { {0x00, 700000}, {0x01, 12500}, {0x35, 1362500} },
+ { {0x36, 1362500}, {0x01, 0}, {0x3f, 1362500} },
+};
+
+/* AB9540 device - Varm_vsel in 6.25mV steps */
+#define AB9540_VARM_VSEL_MASK 0x7f
+static const struct regulator_volt_range ab9540_varm_vsel[] = {
+ { {0x00, 600000}, {0x01, 6250}, {0x7f, 1393750} },
+};
+
+static const struct regulator_volt_range vape_vmod_vsel[] = {
+ { {0x00, 700000}, {0x01, 12500}, {0x35, 1362500} },
+ { {0x36, 1362500}, {0x01, 0}, {0x3f, 1362500} },
+};
+
+/* AB8500 device - Vbbp_vsel and Vbbn_sel in 100mV steps */
+static const struct regulator_volt_range ab8500_vbbp_vsel[] = {
+ { {0x00, 0}, {0x10, 100000}, {0x40, 400000} },
+ { {0x50, 400000}, {0x10, 0}, {0x70, 400000} },
+ { {0x80, -400000}, {0x10, 0}, {0xb0, -400000} },
+ { {0xc0, -400000}, {0x10, 100000}, {0xf0, -100000} },
+};
+
+static const struct regulator_volt_range ab8500_vbbn_vsel[] = {
+ { {0x00, 0}, {0x01, -100000}, {0x04, -400000} },
+ { {0x05, -400000}, {0x01, 0}, {0x07, -400000} },
+ { {0x08, 0}, {0x01, 100000}, {0x0c, 400000} },
+ { {0x0d, 400000}, {0x01, 0}, {0x0f, 400000} },
+};
+
+/* AB9540 device - Vbbp_vsel and Vbbn_sel in 50mV steps */
+static const struct regulator_volt_range ab9540_vbbp_vsel[] = {
+ { {0x00, 0}, {0x10, -50000}, {0x70, -350000} },
+ { {0x80, 50000}, {0x10, 50000}, {0xf0, 400000} },
+};
+
+static const struct regulator_volt_range ab9540_vbbn_vsel[] = {
+ { {0x00, 0}, {0x01, -50000}, {0x07, -350000} },
+ { {0x08, 50000}, {0x01, 50000}, {0x0f, 400000} },
+};
+
+static const struct regulator_volt_range vsmps1_vsel[] = {
+ { {0x00, 1100000}, {0x01, 0}, {0x1f, 1100000} },
+ { {0x20, 1100000}, {0x01, 12500}, {0x30, 1300000} },
+ { {0x31, 1300000}, {0x01, 0}, {0x3f, 1300000} },
+};
+
+static const struct regulator_volt_range vsmps2_vsel[] = {
+ { {0x00, 1800000}, {0x01, 0}, {0x38, 1800000} },
+ { {0x39, 1800000}, {0x01, 12500}, {0x7f, 1875000} },
+};
+
+static const struct regulator_volt_range vsmps3_vsel[] = {
+ { {0x00, 700000}, {0x01, 12500}, {0x35, 1363500} },
+ { {0x36, 1363500}, {0x01, 0}, {0x7f, 1363500} },
+};
+
+/* for Vaux1, Vaux2 and Vaux4 */
+static const struct regulator_volt_range vauxn_vsel[] = {
+ { {0x00, 1100000}, {0x01, 100000}, {0x04, 1500000} },
+ { {0x05, 1800000}, {0x01, 50000}, {0x07, 1900000} },
+ { {0x08, 2500000}, {0x01, 0}, {0x08, 2500000} },
+ { {0x09, 2650000}, {0x01, 50000}, {0x0c, 2800000} },
+ { {0x0d, 2900000}, {0x01, 100000}, {0x0e, 3000000} },
+ { {0x0f, 3300000}, {0x01, 0}, {0x0f, 3300000} },
+};
+
+static const struct regulator_volt_range vaux3_vsel[] = {
+ { {0x00, 1200000}, {0x01, 300000}, {0x03, 2100000} },
+ { {0x04, 2500000}, {0x01, 250000}, {0x05, 2750000} },
+ { {0x06, 2790000}, {0x01, 0}, {0x06, 2790000} },
+ { {0x07, 2910000}, {0x01, 0}, {0x07, 2910000} },
+};
+
+static const struct regulator_volt_range vrf1_vsel[] = {
+ { {0x00, 1800000}, {0x10, 200000}, {0x10, 2000000} },
+ { {0x20, 2150000}, {0x10, 0}, {0x20, 2150000} },
+ { {0x30, 2500000}, {0x10, 0}, {0x30, 2500000} },
+};
+
+static const struct regulator_volt_range vintcore12_vsel[] = {
+ { {0x00, 1200000}, {0x08, 25000}, {0x30, 1350000} },
+ { {0x38, 1350000}, {0x01, 0}, {0x38, 1350000} },
+};
+
+/* regulators */
+static struct ab8500_regulator ab8500_regulator[AB8500_NUM_REGULATORS] = {
+ [AB8500_VARM] = {
+ .name = "Varm",
+ .update_regid = AB8500_REGU_ARM_REGU1,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x02,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VARM_SEL1,
+ .vsel_mask[0] = AB8500_VARM_VSEL_MASK,
+ .vsel_range[0] = ab8500_varm_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(ab8500_varm_vsel),
+ .vsel_regid[1] = AB8500_REGU_VARM_SEL2,
+ .vsel_mask[1] = AB8500_VARM_VSEL_MASK,
+ .vsel_range[1] = ab8500_varm_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(ab8500_varm_vsel),
+ .vsel_regid[2] = AB8500_REGU_VARM_SEL3,
+ .vsel_mask[2] = AB8500_VARM_VSEL_MASK,
+ .vsel_range[2] = ab8500_varm_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(ab8500_varm_vsel),
+ },
+ [AB8500_VBBP] = {
+ .name = "Vbbp",
+ .update_regid = AB8500_REGU_ARM_REGU2,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x00},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x10,
+ .vsel_sel_val = {0x00, 0x10, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VBB_SEL1,
+ .vsel_mask[0] = 0xf0,
+ .vsel_range[0] = ab8500_vbbp_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(ab8500_vbbp_vsel),
+ .vsel_regid[1] = AB8500_REGU_VBB_SEL2,
+ .vsel_mask[1] = 0xf0,
+ .vsel_range[1] = ab8500_vbbp_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(ab8500_vbbp_vsel),
+ },
+ [AB8500_VBBN] = {
+ .name = "Vbbn",
+ .update_regid = AB8500_REGU_ARM_REGU2,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x00},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x20,
+ .vsel_sel_val = {0x00, 0x20, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VBB_SEL1,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = ab8500_vbbn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(ab8500_vbbn_vsel),
+ .vsel_regid[1] = AB8500_REGU_VBB_SEL2,
+ .vsel_mask[1] = 0x0f,
+ .vsel_range[1] = ab8500_vbbn_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(ab8500_vbbn_vsel),
+ },
+ [AB8500_VAPE] = {
+ .name = "Vape",
+ .update_regid = AB8500_REGU_VAPE_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x01,
+ .vsel_sel_regid = AB8500_REGU_VAPE_REGU,
+ .vsel_sel_mask = 0x24,
+ .vsel_sel_val = {0x00, 0x04, 0x20, 0x24},
+ .vsel_regid[0] = AB8500_REGU_VAPE_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vape_vmod_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vape_vmod_vsel),
+ .vsel_regid[1] = AB8500_REGU_VAPE_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vape_vmod_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vape_vmod_vsel),
+ .vsel_regid[2] = AB8500_REGU_VAPE_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = vape_vmod_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vape_vmod_vsel),
+ },
+ [AB8500_VSMPS1] = {
+ .name = "Vsmps1",
+ .update_regid = AB8500_REGU_VSMPS1_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x01,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x01,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_VSMPS1_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS1_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vsmps1_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps1_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS1_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vsmps1_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps1_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS1_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = vsmps1_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps1_vsel),
+ },
+ [AB8500_VSMPS2] = {
+ .name = "Vsmps2",
+ .update_regid = AB8500_REGU_VSMPS2_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x02,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x02,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x08,
+ .vsel_sel_regid = AB8500_REGU_VSMPS2_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS2_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vsmps2_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps2_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS2_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vsmps2_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps2_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS2_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = vsmps2_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps2_vsel),
+ },
+ [AB8500_VSMPS3] = {
+ .name = "Vsmps3",
+ .update_regid = AB8500_REGU_VSMPS3_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x04,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x04,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x04,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x10,
+ .vsel_sel_regid = AB8500_REGU_VSMPS3_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS3_SEL1,
+ .vsel_mask[0] = 0x7f,
+ .vsel_range[0] = vsmps3_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps3_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS3_SEL2,
+ .vsel_mask[1] = 0x7f,
+ .vsel_range[1] = vsmps3_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps3_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS3_SEL3,
+ .vsel_mask[2] = 0x7f,
+ .vsel_range[2] = vsmps3_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps3_vsel),
+ },
+ [AB8500_VPLL] = {
+ .name = "Vpll",
+ .update_regid = AB8500_REGU_VPLL_VANA_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x10,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x10,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x10,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x40,
+ },
+ [AB8500_VREFDDR] = {
+ .name = "VrefDDR",
+ .update_regid = AB8500_REGU_VREF_DDR,
+ .update_mask = 0x01,
+ .update_val = {0x00, 0x01, 0x00, 0x00},
+ },
+ [AB8500_VMOD] = {
+ .name = "Vmod",
+ .update_regid = AB8500_REGU_VMOD_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_VMOD_REGU,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x08,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x08,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x08,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x20,
+ .vsel_sel_regid = AB8500_REGU_VMOD_REGU,
+ .vsel_sel_mask = 0x04,
+ .vsel_sel_val = {0x00, 0x04, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VMOD_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vape_vmod_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vape_vmod_vsel),
+ .vsel_regid[1] = AB8500_REGU_VMOD_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vape_vmod_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vape_vmod_vsel),
+ },
+ [AB8500_VEXTSUPPLY1] = {
+ .name = "Vextsupply1",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x10,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x01,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x04,
+ },
+ [AB8500_VEXTSUPPLY2] = {
+ .name = "VextSupply2",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x20,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x02,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x08,
+ },
+ [AB8500_VEXTSUPPLY3] = {
+ .name = "VextSupply3",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x30,
+ .update_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x40,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x04,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x04,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x10,
+ },
+ [AB8500_VRF1] = {
+ .name = "Vrf1",
+ .update_regid = AB8500_REGU_VRF1_VAUX3_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VRF1_VAUX3_SEL,
+ .vsel_mask[0] = 0x30,
+ .vsel_range[0] = vrf1_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vrf1_vsel),
+ },
+ [AB8500_VANA] = {
+ .name = "Vana",
+ .update_regid = AB8500_REGU_VPLL_VANA_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x08,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x08,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x08,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x20,
+ },
+ [AB8500_VAUX1] = {
+ .name = "Vaux1",
+ .update_regid = AB8500_REGU_VAUX12_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x20,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x20,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x20,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x80,
+ .vsel_regid[0] = AB8500_REGU_VAUX1_SEL,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vauxn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vauxn_vsel),
+ },
+ [AB8500_VAUX2] = {
+ .name = "Vaux2",
+ .update_regid = AB8500_REGU_VAUX12_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x40,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x40,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x40,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x01,
+ .vsel_regid[0] = AB8500_REGU_VAUX2_SEL,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vauxn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vauxn_vsel),
+ },
+ [AB8500_VAUX3] = {
+ .name = "Vaux3",
+ .update_regid = AB8500_REGU_VRF1_VAUX3_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL4,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x80,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x80,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x80,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x02,
+ .vsel_regid[0] = AB8500_REGU_VRF1_VAUX3_SEL,
+ .vsel_mask[0] = 0x07,
+ .vsel_range[0] = vaux3_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vaux3_vsel),
+ },
+ [AB9540_VAUX4] = {
+ .name = "Vaux4",
+ .update_regid = AB9540_REGU_VAUX4_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB9540_REGU_VAUX4_REQ_CTRL,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB9540_REGU_VAUX4_REQ_VALID,
+ .hw_valid_mask[0] = 0x08,
+ .hw_valid_regid[1] = AB9540_REGU_VAUX4_REQ_VALID,
+ .hw_valid_mask[1] = 0x04,
+ .hw_valid_regid[2] = AB9540_REGU_VAUX4_REQ_VALID,
+ .hw_valid_mask[2] = 0x02,
+ .hw_valid_regid[3] = AB9540_REGU_VAUX4_REQ_VALID,
+ .hw_valid_mask[3] = 0x01,
+ .vsel_regid[0] = AB9540_REGU_VAUX4_SEL,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vauxn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vauxn_vsel),
+ .unavailable = true, /* AB9540 regulator */
+ },
+ [AB8500_VINTCORE] = {
+ .name = "VintCore12",
+ .update_regid = AB8500_REGU_MISC1,
+ .update_mask = 0x44,
+ .update_val = {0x00, 0x04, 0x00, 0x44},
+ .vsel_regid[0] = AB8500_REGU_MISC1,
+ .vsel_mask[0] = 0x38,
+ .vsel_range[0] = vintcore12_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vintcore12_vsel),
+ },
+ [AB8500_VTVOUT] = {
+ .name = "VTVout",
+ .update_regid = AB8500_REGU_MISC1,
+ .update_mask = 0x82,
+ .update_val = {0x00, 0x02, 0x00, 0x82},
+ },
+ [AB8500_VAUDIO] = {
+ .name = "Vaudio",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x02,
+ .update_val = {0x00, 0x02, 0x00, 0x00},
+ },
+ [AB8500_VANAMIC1] = {
+ .name = "Vanamic1",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x08,
+ .update_val = {0x00, 0x08, 0x00, 0x00},
+ },
+ [AB8500_VANAMIC2] = {
+ .name = "Vanamic2",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x10,
+ .update_val = {0x00, 0x10, 0x00, 0x00},
+ },
+ [AB8500_VDMIC] = {
+ .name = "Vdmic",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x04,
+ .update_val = {0x00, 0x04, 0x00, 0x00},
+ },
+ [AB8500_VUSB] = {
+ .name = "Vusb",
+ .update_regid = AB8500_REGU_VUSB_CTRL,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x00, 0x03},
+ },
+ [AB8500_VOTG] = {
+ .name = "VOTG",
+ .update_regid = AB8500_REGU_OTG_SUPPLY_CTRL,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x00, 0x03},
+ },
+ [AB8500_VBUSBIS] = {
+ .name = "Vbusbis",
+ .update_regid = AB8500_REGU_OTG_SUPPLY_CTRL,
+ .update_mask = 0x08,
+ .update_val = {0x00, 0x08, 0x00, 0x00},
+ },
+};
+
+static void ab9540_regulators_update(void)
+{
+ /* Update unavailable regulators */
+ ab8500_regulator[AB8500_VREFDDR].unavailable = true;
+ ab8500_regulator[AB9540_VAUX4].unavailable = false;
+
+ /* Update regulator characteristics for AB9540 */
+ ab8500_regulator[AB8500_VARM].vsel_mask[0] = AB9540_VARM_VSEL_MASK;
+ ab8500_regulator[AB8500_VARM].vsel_range[0] = ab9540_varm_vsel;
+ ab8500_regulator[AB8500_VARM].vsel_range_len[0] =
+ ARRAY_SIZE(ab9540_varm_vsel);
+ ab8500_regulator[AB8500_VARM].vsel_mask[1] = AB9540_VARM_VSEL_MASK;
+ ab8500_regulator[AB8500_VARM].vsel_range[1] = ab9540_varm_vsel;
+ ab8500_regulator[AB8500_VARM].vsel_range_len[1] =
+ ARRAY_SIZE(ab9540_varm_vsel);
+ ab8500_regulator[AB8500_VARM].vsel_mask[2] = AB9540_VARM_VSEL_MASK;
+ ab8500_regulator[AB8500_VARM].vsel_range[2] = ab9540_varm_vsel;
+ ab8500_regulator[AB8500_VARM].vsel_range_len[2] =
+ ARRAY_SIZE(ab9540_varm_vsel);
+
+ ab8500_regulator[AB8500_VBBP].vsel_range[0] = ab9540_vbbp_vsel;
+ ab8500_regulator[AB8500_VBBP].vsel_range_len[0] =
+ ARRAY_SIZE(ab9540_vbbp_vsel);
+ ab8500_regulator[AB8500_VBBP].vsel_range[1] = ab9540_vbbp_vsel;
+ ab8500_regulator[AB8500_VBBP].vsel_range_len[1] =
+ ARRAY_SIZE(ab9540_vbbp_vsel);
+
+ ab8500_regulator[AB8500_VBBN].vsel_range[0] = ab9540_vbbn_vsel;
+ ab8500_regulator[AB8500_VBBN].vsel_range_len[0] =
+ ARRAY_SIZE(ab9540_vbbn_vsel);
+ ab8500_regulator[AB8500_VBBN].vsel_range[1] = ab9540_vbbn_vsel;
+ ab8500_regulator[AB8500_VBBN].vsel_range_len[1] =
+ ARRAY_SIZE(ab9540_vbbn_vsel);
+}
+
+static int status_state = AB8500_REGULATOR_STATE_CURRENT;
+
+static int _get_voltage(struct regulator_volt_range const *volt_range,
+ u8 value, int *volt)
+{
+ u8 start = volt_range->start.value;
+ u8 end = volt_range->end.value;
+ u8 step = volt_range->step.value;
+
+ /* Check if witin range */
+ if (step == 0) {
+ if (value == start) {
+ *volt = volt_range->start.volt;
+ return 1;
+ }
+ } else {
+ if ((start <= value) && (value <= end)) {
+ if ((value - start) % step != 0)
+ return -EINVAL; /* invalid setting */
+ *volt = volt_range->start.volt
+ + volt_range->step.volt
+ *((value - start) / step);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int get_voltage(struct regulator_volt_range const *volt_range,
+ int volt_range_len,
+ u8 value)
+{
+ int volt;
+ int i, ret;
+
+ for (i = 0; i < volt_range_len; i++) {
+ ret = _get_voltage(&volt_range[i], value, &volt);
+ if (ret < 0)
+ break; /* invalid setting */
+ if (ret == 1)
+ return volt; /* successful */
+ }
+
+ return -EINVAL;
+}
+
+static bool get_reg_and_mask(int regid, u8 mask, u8 *val)
+{
+ int ret;
+ u8 t;
+
+ if (!regid)
+ return false;
+
+ ret = abx500_get_register_interruptible(dev,
+ ab8500_register[regid].bank,
+ ab8500_register[regid].addr,
+ &t);
+ if (ret < 0)
+ return false;
+
+ (*val) = t & mask;
+
+ return true;
+}
+
+/* Convert regulator register value to index */
+static bool val2idx(u8 val, u8 *v, int len, int *idx)
+{
+ int i;
+
+ for (i = 0; i < len && v[i] != val; i++);
+
+ if (i == len)
+ return false;
+
+ (*idx) = i;
+ return true;
+}
+
+int ab8500_regulator_debug_read(enum ab8500_regulator_id id,
+ struct ab8500_debug_regulator_status *s)
+{
+ int i;
+ u8 val;
+ bool found;
+ int idx = 0;
+
+ if (id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ s->name = (char *)ab8500_regulator[id].name;
+
+ /* read mode */
+ (void) get_reg_and_mask(ab8500_regulator[id].update_regid,
+ ab8500_regulator[id].update_mask,
+ &val);
+
+ (void) val2idx(val, ab8500_regulator[id].update_val,
+ 4, &idx);
+
+ s->mode = (u8) idx;
+
+ /* read hw mode */
+ found = get_reg_and_mask(ab8500_regulator[id].hw_mode_regid,
+ ab8500_regulator[id].hw_mode_mask,
+ &val);
+
+ if (found)
+ found = val2idx(val, ab8500_regulator[id].hw_mode_val, 4, &idx);
+
+ if (found)
+ /* +1 since 0 = HWMODE_NONE */
+ s->hwmode = idx + 1;
+ else
+ s->hwmode = AB8500_HWMODE_NONE;
+
+ for (i = 0; i < 4 && found; i++) {
+
+ bool f = get_reg_and_mask(ab8500_regulator[id].hw_valid_regid[i],
+ ab8500_regulator[id].hw_valid_mask[i],
+ &val);
+ if (f)
+ s->hwmode_auto[i] = !!val;
+ else
+ s->hwmode_auto[i] = HWM_INVAL;
+ }
+
+ /* read voltage */
+ found = get_reg_and_mask(ab8500_regulator[id].vsel_sel_regid,
+ ab8500_regulator[id].vsel_sel_mask,
+ &val);
+ if (found)
+ found = val2idx(val, ab8500_regulator[id].vsel_sel_val,
+ 3, &idx);
+
+ if (found && idx < 3)
+ s->volt_selected = idx + 1;
+ else
+ s->volt_selected = 0;
+
+ for (s->volt_len = 0; s->volt_len < 3; s->volt_len++) {
+ int volt;
+ int i = s->volt_len;
+
+ found = get_reg_and_mask(ab8500_regulator[id].vsel_regid[i],
+ ab8500_regulator[id].vsel_mask[i],
+ &val);
+ if (!found)
+ break;
+
+ volt = get_voltage(ab8500_regulator[id].vsel_range[i],
+ ab8500_regulator[id].vsel_range_len[i],
+ val);
+ s->volt[i] = volt;
+ }
+ return 0;
+}
+
+static int ab8500_regulator_status_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ int id, regid;
+ int i;
+ u8 val;
+ int err;
+
+ /* record current state */
+ ab8500_regulator_record_state(AB8500_REGULATOR_STATE_CURRENT);
+
+ /* check if chosen state is recorded */
+ if (!ab8500_register_state_saved[status_state]) {
+ seq_printf(s, "ab8500-regulator status is not recorded.\n");
+ goto exit;
+ }
+
+ /* print dump header */
+ err = seq_printf(s, "ab8500-regulator status:\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+
+ /* print state */
+ for (i = 0; i < NUM_REGULATOR_STATE; i++) {
+ if (i == status_state)
+ err = seq_printf(s, "-> %i. %12s\n",
+ i, regulator_state_name[i]);
+ else
+ err = seq_printf(s, " %i. %12s\n",
+ i, regulator_state_name[i]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+ }
+
+ /* print labels */
+ err = seq_printf(s,
+ "+-----------+----+--------------+-------------------------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "| name|man |auto |voltage |\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "+-----------+----+--------------+ +-----------------------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "| |mode|mode |0|1|2|3| | 1 | 2 | 3 |\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "+-----------+----+------+-+-+-+-+-+-------+-------+-------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ /* dump registers */
+ for (id = 0; id < AB8500_NUM_REGULATORS; id++) {
+ if (ab8500_register[id].unavailable ||
+ ab8500_regulator[id].unavailable)
+ continue;
+
+ /* print name */
+ err = seq_printf(s, "|%11s|",
+ ab8500_regulator[id].name);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print manual mode */
+ regid = ab8500_regulator[id].update_regid;
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].update_mask;
+ for (i = 0; i < 4; i++) {
+ if (val == ab8500_regulator[id].update_val[i])
+ break;
+ }
+ err = seq_printf(s, "%4s|",
+ update_val_name[i]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print auto mode */
+ regid = ab8500_regulator[id].hw_mode_regid;
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].hw_mode_mask;
+ for (i = 0; i < 4; i++) {
+ if (val == ab8500_regulator[id].hw_mode_val[i])
+ break;
+ }
+ err = seq_printf(s, "%6s|",
+ hw_mode_val_name[i]);
+ } else {
+ err = seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print valid bits */
+ for (i = 0; i < 4; i++) {
+ regid = ab8500_regulator[id].hw_valid_regid[i];
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].hw_valid_mask[i];
+ if (val)
+ err = seq_printf(s, "1|");
+ else
+ err = seq_printf(s, "0|");
+ } else {
+ err = seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+ }
+
+ /* print voltage selection */
+ regid = ab8500_regulator[id].vsel_sel_regid;
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].vsel_sel_mask;
+ for (i = 0; i < 3; i++) {
+ if (val == ab8500_regulator[id].vsel_sel_val[i])
+ break;
+ }
+ if (i < 3)
+ seq_printf(s, "%i|", i + 1);
+ else
+ seq_printf(s, "-|");
+ } else {
+ seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+
+ for (i = 0; i < 3; i++) {
+ int volt;
+
+ regid = ab8500_regulator[id].vsel_regid[i];
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].vsel_mask[i];
+ volt = get_voltage(
+ ab8500_regulator[id].vsel_range[i],
+ ab8500_regulator[id].vsel_range_len[i],
+ val);
+ seq_printf(s, "%7i|", volt);
+ } else {
+ seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+ }
+
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+
+ }
+ err = seq_printf(s,
+ "+-----------+----+------+-+-+-+-+-+-------+-------+-------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "Note! In HW mode, voltage selection is controlled by HW.\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+
+exit:
+ return 0;
+}
+
+static int ab8500_regulator_status_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+
+ /* copy user data */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* convert */
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+
+ /* set suspend force setting */
+ if (user_val > NUM_REGULATOR_STATE) {
+ dev_err(dev, "debugfs error input > number of states\n");
+ return -EINVAL;
+ }
+
+ status_state = user_val;
+
+ return buf_size;
+}
+
+
+static int ab8500_regulator_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_regulator_status_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_status_fops = {
+ .open = ab8500_regulator_status_open,
+ .write = ab8500_regulator_status_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+#ifdef CONFIG_PM
+
+struct ab8500_force_reg {
+ char *name;
+ u8 bank;
+ u8 addr;
+ u8 mask;
+ u8 val;
+ bool restore;
+ u8 restore_val;
+ u8 unavailable;
+};
+
+static struct ab8500_force_reg ab8500_force_reg[] = {
+ {
+ /*
+ * SysClkCtrl
+ * OTP: 0x00, HSI: 0x06, suspend: 0x00/0x07 (value/mask)
+ * [ 2] USBClkEna = disable SysClk path to USB block
+ * [ 1] TVoutClkEna = disable 27Mhz clock to TVout block
+ * [ 0] TVoutPllEna = disable TVout pll
+ * (generate 27Mhz from SysClk)
+ */
+ .name = "SysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x0c,
+ .mask = 0x07,
+ .val = 0x00,
+ },
+ {
+ /*
+ * VsimSysClkCtrl
+ * OTP: 0x01, HSI: 0x21, suspend: 0x01/0xff (value/mask)
+ * [ 7] VsimSysClkReq8Valid = no connection
+ * [ 6] VsimSysClkReq7Valid = no connection
+ * [ 5] VsimSysClkReq6Valid = no connection
+ * [ 4] VsimSysClkReq5Valid = no connection
+ * [ 3] VsimSysClkReq4Valid = no connection
+ * [ 2] VsimSysClkReq3Valid = no connection
+ * [ 1] VsimSysClkReq2Valid = no connection
+ * [ 0] VsimSysClkReq1Valid = Vsim set by SysClkReq1
+ */
+ .name = "VsimSysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x33,
+ .mask = 0xff,
+ .val = 0x01,
+ },
+ {
+ /*
+ * SysUlpClkCtrl1
+ * OTP: 0x00, HSI: 0x00, suspend: 0x00/0x0f (value/mask)
+ * [ 3] 4500SysClkReq = inactive
+ * [ 2] UlpClkReq = inactive
+ * [1:0] SysUlpClkIntSel[1:0] = no internal clock switching.
+ * Internal clock is SysClk.
+ */
+ .name = "SysUlpClkCtrl1",
+ .bank = 0x02,
+ .addr = 0x0b,
+ .mask = 0x0f,
+ .val = 0x00,
+ },
+ {
+ /*
+ * TVoutCtrl
+ * OTP: N/A, HSI: N/A, suspend: 0x00/0x03 (value/mask)
+ * [ 2] PlugTvOn = plug/unplug detection disabled
+ * [1:0] TvoutDacCtrl[1:0] = "0" forced on DAC input (test)
+ */
+ .name = "TVoutCtrl",
+ .bank = 0x06,
+ .addr = 0x80,
+ .mask = 0x03,
+ .val = 0x00,
+ },
+};
+
+static void ab9540_force_reg_update(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_force_reg); i++) {
+ if (ab8500_force_reg[i].bank == 0x02 &&
+ ab8500_force_reg[i].addr == 0x0C) {
+ /*
+ * SysClkCtrl
+ * OTP: 0x00, HSI: 0x06, suspend: 0x00/0x07 (value/mask)
+ * [ 2] USBClkEna = disable SysClk path to USB block
+ */
+ ab8500_force_reg[i].mask = 0x04;
+ ab8500_force_reg[i].val = 0x00;
+ } else if (ab8500_force_reg[i].bank == 0x06 &&
+ ab8500_force_reg[i].addr == 0x80) {
+ /* TVoutCtrl not supported by AB9540 */
+ ab8500_force_reg[i].unavailable = true;
+ }
+ }
+}
+
+void ab8500_regulator_debug_force(void)
+{
+ int ret, i;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_SUSPEND);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record suspend state.\n");
+
+ /* check if registers should be forced */
+ if (!setting_suspend_force)
+ goto exit;
+
+ /*
+ * Optimize href v2_v50_pwr board for ApSleep/ApDeepSleep
+ * power consumption measurements
+ */
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_force_reg); i++) {
+ if (ab8500_force_reg[i].unavailable)
+ continue;
+
+ dev_vdbg(&pdev->dev, "Save and set %s: "
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x.\n",
+ ab8500_force_reg[i].name,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].val);
+
+ /* assume that register should be restored */
+ ab8500_force_reg[i].restore = true;
+
+ /* get register value before forcing it */
+ ret = abx500_get_register_interruptible(&pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ &ab8500_force_reg[i].restore_val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read %s.\n",
+ ab8500_force_reg[i].name);
+ ab8500_force_reg[i].restore = false;
+ break;
+ }
+
+ /* force register value */
+ ret = abx500_mask_and_set_register_interruptible(&pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].val);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to write %s.\n",
+ ab8500_force_reg[i].name);
+ ab8500_force_reg[i].restore = false;
+ }
+ }
+
+exit:
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(
+ AB8500_REGULATOR_STATE_SUSPEND_CORE);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record suspend state.\n");
+
+ return;
+}
+
+void ab8500_regulator_debug_restore(void)
+{
+ int ret, i;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_RESUME_CORE);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record resume state.\n");
+ for (i = ARRAY_SIZE(ab8500_force_reg) - 1; i >= 0; i--) {
+ if (ab8500_force_reg[i].unavailable)
+ continue;
+
+ /* restore register value */
+ if (ab8500_force_reg[i].restore) {
+ ret = abx500_mask_and_set_register_interruptible(
+ &pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].restore_val);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to restore %s.\n",
+ ab8500_force_reg[i].name);
+ dev_vdbg(&pdev->dev, "Restore %s: "
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ ab8500_force_reg[i].name,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].restore_val);
+ }
+ }
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_RESUME);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record resume state.\n");
+
+ return;
+}
+
+#endif
+
+static int ab8500_regulator_suspend_force_show(struct seq_file *s, void *p)
+{
+ /* print suspend standby status */
+ if (setting_suspend_force)
+ return seq_printf(s, "suspend force enabled\n");
+ else
+ return seq_printf(s, "no suspend force\n");
+}
+
+static int ab8500_regulator_suspend_force_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+
+ /* copy user data */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* convert */
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+
+ /* set suspend force setting */
+ if (user_val > 1) {
+ dev_err(dev, "debugfs error input > 1\n");
+ return -EINVAL;
+ }
+
+ if (user_val)
+ setting_suspend_force = true;
+ else
+ setting_suspend_force = false;
+
+ return buf_size;
+}
+
+static int ab8500_regulator_suspend_force_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_regulator_suspend_force_show,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_suspend_force_fops = {
+ .open = ab8500_regulator_suspend_force_open,
+ .write = ab8500_regulator_suspend_force_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab8500_regulator_dir;
+static struct dentry *ab8500_regulator_dump_file;
+static struct dentry *ab8500_regulator_status_file;
+static struct dentry *ab8500_regulator_suspend_force_file;
+
+int __devinit ab8500_regulator_debug_init(struct platform_device *plf)
+{
+ void __iomem *boot_info_backupram;
+ int ret;
+ struct ab8500 *ab8500;
+
+ /* setup dev pointers */
+ dev = &plf->dev;
+ pdev = plf;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_INIT);
+ if (ret < 0)
+ dev_err(&plf->dev, "Failed to record init state.\n");
+
+ ab8500 = dev_get_drvdata(plf->dev.parent);
+ /* Update data structures for AB9540 */
+ if (is_ab9540(ab8500)) {
+ ab9540_registers_update();
+ ab9540_regulators_update();
+ ab9540_force_reg_update();
+ }
+ /* make suspend-force default if board profile is v5x-power */
+ boot_info_backupram = ioremap(BOOT_INFO_BACKUPRAM1, 0x4);
+
+ if (boot_info_backupram) {
+ u8 board_profile;
+ board_profile = readb(
+ boot_info_backupram + BOARD_PROFILE_BACKUPRAM1);
+ dev_dbg(dev, "Board profile is 0x%02x\n", board_profile);
+
+ if (board_profile >= OPTION_BOARD_VERSION_V5X)
+ setting_suspend_force = true;
+
+ iounmap(boot_info_backupram);
+ } else {
+ dev_err(dev, "Failed to read backupram.\n");
+ }
+
+ /* create directory */
+ ab8500_regulator_dir = debugfs_create_dir("ab8500-regulator", NULL);
+ if (!ab8500_regulator_dir)
+ goto exit_no_debugfs;
+
+ /* create "dump" file */
+ ab8500_regulator_dump_file = debugfs_create_file("dump",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_dump_fops);
+ if (!ab8500_regulator_dump_file)
+ goto exit_destroy_dir;
+
+ /* create "status" file */
+ ab8500_regulator_status_file = debugfs_create_file("status",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_status_fops);
+ if (!ab8500_regulator_status_file)
+ goto exit_destroy_dump_file;
+
+ /*
+ * create "suspend-force-v5x" file. As indicated by the name, this is
+ * only applicable for v2_v5x hardware versions.
+ */
+ ab8500_regulator_suspend_force_file = debugfs_create_file(
+ "suspend-force-v5x",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_suspend_force_fops);
+ if (!ab8500_regulator_suspend_force_file)
+ goto exit_destroy_status_file;
+
+ return 0;
+
+exit_destroy_status_file:
+ debugfs_remove(ab8500_regulator_status_file);
+exit_destroy_dump_file:
+ debugfs_remove(ab8500_regulator_dump_file);
+exit_destroy_dir:
+ debugfs_remove(ab8500_regulator_dir);
+exit_no_debugfs:
+ dev_err(&plf->dev, "failed to create debugfs entries.\n");
+ return -ENOMEM;
+}
+
+int __devexit ab8500_regulator_debug_exit(struct platform_device *plf)
+{
+ debugfs_remove(ab8500_regulator_suspend_force_file);
+ debugfs_remove(ab8500_regulator_status_file);
+ debugfs_remove(ab8500_regulator_dump_file);
+ debugfs_remove(ab8500_regulator_dir);
+
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com");
+MODULE_DESCRIPTION("AB8500 Regulator Debug");
+MODULE_ALIAS("platform:ab8500-regulator-debug");
diff --git a/drivers/regulator/ab8500-debug.h b/drivers/regulator/ab8500-debug.h
new file mode 100644
index 00000000000..2b59e556a3f
--- /dev/null
+++ b/drivers/regulator/ab8500-debug.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson.
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __AB8500_DEBUG_H__
+#define __AB8500_DEBUG_H__
+
+/*
+ * regulator status print
+ */
+enum ab8500_regulator_id {
+ AB8500_VARM,
+ AB8500_VBBP,
+ AB8500_VBBN,
+ AB8500_VAPE,
+ AB8500_VSMPS1,
+ AB8500_VSMPS2,
+ AB8500_VSMPS3,
+ AB8500_VPLL,
+ AB8500_VREFDDR,
+ AB8500_VMOD,
+ AB8500_VEXTSUPPLY1,
+ AB8500_VEXTSUPPLY2,
+ AB8500_VEXTSUPPLY3,
+ AB8500_VRF1,
+ AB8500_VANA,
+ AB8500_VAUX1,
+ AB8500_VAUX2,
+ AB8500_VAUX3,
+ AB9540_VAUX4, /* Note: AB9540 only */
+ AB8500_VINTCORE,
+ AB8500_VTVOUT,
+ AB8500_VAUDIO,
+ AB8500_VANAMIC1,
+ AB8500_VANAMIC2,
+ AB8500_VDMIC,
+ AB8500_VUSB,
+ AB8500_VOTG,
+ AB8500_VBUSBIS,
+ AB8500_NUM_REGULATORS,
+};
+
+enum ab8500_regulator_mode {
+ AB8500_MODE_OFF = 0,
+ AB8500_MODE_ON,
+ AB8500_MODE_HW,
+ AB8500_MODE_LP
+};
+
+enum ab8500_regulator_hwmode {
+ AB8500_HWMODE_NONE = 0,
+ AB8500_HWMODE_HPLP,
+ AB8500_HWMODE_HPOFF,
+ AB8500_HWMODE_HP,
+ AB8500_HWMODE_HP2,
+};
+
+enum hwmode_auto {
+ HWM_OFF = 0,
+ HWM_ON = 1,
+ HWM_INVAL = 2,
+};
+
+struct ab8500_debug_regulator_status {
+ char *name;
+ enum ab8500_regulator_mode mode;
+ enum ab8500_regulator_hwmode hwmode;
+ enum hwmode_auto hwmode_auto[4];
+ int volt_selected;
+ int volt_len;
+ int volt[4];
+};
+
+int ab8500_regulator_debug_read(enum ab8500_regulator_id id,
+ struct ab8500_debug_regulator_status *s);
+#endif /* __AB8500_DEBUG_H__ */
diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c
new file mode 100644
index 00000000000..8a5064c07fb
--- /dev/null
+++ b/drivers/regulator/ab8500-ext.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ *
+ * This file is based on drivers/regulator/ab8500.c
+ *
+ * AB8500 external regulators
+ *
+ * ab8500-ext supports the following regulators:
+ * - VextSupply3
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/regulator/ab8500.h>
+
+/**
+ * struct ab8500_ext_regulator_info - ab8500 regulator information
+ * @dev: device pointer
+ * @desc: regulator description
+ * @rdev: regulator device
+ * @cfg: regulator configuration (extension of regulator FW configuration)
+ * @is_enabled: status of regulator (on/off)
+ * @fixed_uV: typical voltage (for fixed voltage supplies)
+ * @update_bank: bank to control on/off
+ * @update_reg: register to control on/off
+ * @update_mask: mask to enable/disable and set mode of regulator
+ * @update_val: bits holding the regulator current mode
+ * @update_val_hp: bits to set EN pin active (LPn pin deactive)
+ * normally this means high power mode
+ * @update_val_lp: bits to set EN pin active and LPn pin active
+ * normally this means low power mode
+ * @update_val_hw: bits to set regulator pins in HW control
+ * SysClkReq pins and logic will choose mode
+ */
+struct ab8500_ext_regulator_info {
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct ab8500_ext_regulator_cfg *cfg;
+ bool is_enabled;
+ int fixed_uV;
+ u8 update_bank;
+ u8 update_reg;
+ u8 update_mask;
+ u8 update_val;
+ u8 update_val_hp;
+ u8 update_val_lp;
+ u8 update_val_hw;
+};
+
+static int enable(struct ab8500_ext_regulator_info *info, u8 *regval)
+{
+ int ret;
+
+ *regval = info->update_val;
+
+ /*
+ * To satisfy both HW high power request and SW request, the regulator
+ * must be on in high power.
+ */
+ if (info->cfg && info->cfg->hwreq)
+ *regval = info->update_val_hp;
+
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, *regval);
+ if (ret < 0)
+ dev_err(rdev_get_dev(info->rdev),
+ "couldn't set enable bits for regulator\n");
+
+ info->is_enabled = true;
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_enable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = enable(info, &regval);
+
+ dev_dbg(rdev_get_dev(rdev), "%s-enable (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_set_suspend_enable(struct regulator_dev *rdev)
+{
+ dev_dbg(rdev_get_dev(rdev), "suspend: ");
+
+ return ab8500_ext_regulator_enable(rdev);
+}
+
+static int disable(struct ab8500_ext_regulator_info *info, u8 *regval)
+{
+ int ret;
+
+ *regval = 0x0;
+
+ /*
+ * Set the regulator in HW request mode if configured
+ */
+ if (info->cfg && info->cfg->hwreq)
+ *regval = info->update_val_hw;
+
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, *regval);
+ if (ret < 0)
+ dev_err(rdev_get_dev(info->rdev),
+ "couldn't set disable bits for regulator\n");
+
+ info->is_enabled = false;
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_disable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = disable(info, &regval);
+
+ dev_dbg(rdev_get_dev(rdev), "%s-disable (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_set_suspend_disable(struct regulator_dev *rdev)
+{
+ dev_dbg(rdev_get_dev(rdev), "suspend: ");
+
+ return ab8500_ext_regulator_disable(rdev);
+}
+
+static int ab8500_ext_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_get_register_interruptible(info->dev,
+ info->update_bank, info->update_reg, &regval);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read 0x%x register\n", info->update_reg);
+ return ret;
+ }
+
+ dev_dbg(rdev_get_dev(rdev), "%s-is_enabled (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ if (((regval & info->update_mask) == info->update_val_lp) ||
+ ((regval & info->update_mask) == info->update_val_hp))
+ info->is_enabled = true;
+ else
+ info->is_enabled = false;
+
+ return info->is_enabled;
+}
+
+static int ab8500_ext_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int ret = 0;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ info->update_val = info->update_val_hp;
+ break;
+ case REGULATOR_MODE_IDLE:
+ info->update_val = info->update_val_lp;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (info->is_enabled) {
+ u8 regval;
+
+ ret = enable(info, &regval);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "Could not set regulator mode.\n");
+
+ dev_dbg(rdev_get_dev(rdev),
+ "%s-set_mode (bank, reg, mask, value): "
+ "0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+ }
+
+ return ret;
+}
+
+static unsigned int ab8500_ext_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ if (info->update_val == info->update_val_hp)
+ ret = REGULATOR_MODE_NORMAL;
+ else if (info->update_val == info->update_val_lp)
+ ret = REGULATOR_MODE_IDLE;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int ab8500_ext_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ return info->fixed_uV;
+}
+
+static int ab8500_ext_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ /* return the uV for the fixed regulators */
+ if (info->fixed_uV)
+ return info->fixed_uV;
+
+ return -EINVAL;
+}
+
+static struct regulator_ops ab8500_ext_regulator_ops = {
+ .enable = ab8500_ext_regulator_enable,
+ .set_suspend_enable = ab8500_ext_regulator_set_suspend_enable,
+ .disable = ab8500_ext_regulator_disable,
+ .set_suspend_disable = ab8500_ext_regulator_set_suspend_disable,
+ .is_enabled = ab8500_ext_regulator_is_enabled,
+ .set_mode = ab8500_ext_regulator_set_mode,
+ .get_mode = ab8500_ext_regulator_get_mode,
+ .get_voltage = ab8500_ext_fixed_get_voltage,
+ .list_voltage = ab8500_ext_list_voltage,
+};
+
+static struct ab8500_ext_regulator_info
+ ab8500_ext_regulator_info[AB8500_NUM_EXT_REGULATORS] = {
+ [AB8500_EXT_SUPPLY1] = {
+ .desc = {
+ .name = "VEXTSUPPLY1",
+ .ops = &ab8500_ext_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_EXT_SUPPLY1,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1800000,
+ .update_bank = 0x04,
+ .update_reg = 0x08,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_hp = 0x01,
+ .update_val_lp = 0x03,
+ .update_val_hw = 0x02,
+ },
+ [AB8500_EXT_SUPPLY2] = {
+ .desc = {
+ .name = "VEXTSUPPLY2",
+ .ops = &ab8500_ext_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_EXT_SUPPLY2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1360000,
+ .update_bank = 0x04,
+ .update_reg = 0x08,
+ .update_mask = 0x0c,
+ .update_val = 0x04,
+ .update_val_hp = 0x04,
+ .update_val_lp = 0x0c,
+ .update_val_hw = 0x08,
+ },
+ [AB8500_EXT_SUPPLY3] = {
+ .desc = {
+ .name = "VEXTSUPPLY3",
+ .ops = &ab8500_ext_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_EXT_SUPPLY3,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 3400000,
+ .update_bank = 0x04,
+ .update_reg = 0x08,
+ .update_mask = 0x30,
+ .update_val = 0x10,
+ .update_val_hp = 0x10,
+ .update_val_lp = 0x30,
+ .update_val_hw = 0x20,
+ },
+};
+
+__devinit int ab8500_ext_regulator_init(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *ppdata;
+ struct ab8500_regulator_platform_data *pdata;
+ int i, err;
+
+ if (!ab8500) {
+ dev_err(&pdev->dev, "null mfd parent\n");
+ return -EINVAL;
+ }
+ ppdata = dev_get_platdata(ab8500->dev);
+ if (!ppdata) {
+ dev_err(&pdev->dev, "null parent pdata\n");
+ return -EINVAL;
+ }
+
+ pdata = ppdata->regulator;
+ if (!pdata) {
+ dev_err(&pdev->dev, "null pdata\n");
+ return -EINVAL;
+ }
+
+ /* make sure the platform data has the correct size */
+ if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) {
+ dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
+ return -EINVAL;
+ }
+
+ /* check for AB8500 2.x */
+ if (is_ab8500_2p0_or_earlier(ab8500)) {
+ struct ab8500_ext_regulator_info *info;
+
+ /* VextSupply3LPn is inverted on AB8500 2.x */
+ info = &ab8500_ext_regulator_info[AB8500_EXT_SUPPLY3];
+ info->update_val = 0x30;
+ info->update_val_hp = 0x30;
+ info->update_val_lp = 0x10;
+ }
+
+ /* register all regulators */
+ for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) {
+ struct ab8500_ext_regulator_info *info = NULL;
+
+ /* assign per-regulator data */
+ info = &ab8500_ext_regulator_info[i];
+ info->dev = &pdev->dev;
+ info->cfg = (struct ab8500_ext_regulator_cfg *)
+ pdata->ext_regulator[i].driver_data;
+
+ /* register regulator with framework */
+ info->rdev = regulator_register(&info->desc, &pdev->dev,
+ &pdata->ext_regulator[i], info, NULL);
+ if (IS_ERR(info->rdev)) {
+ err = PTR_ERR(info->rdev);
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ info->desc.name);
+ /* when we fail, un-register all earlier regulators */
+ while (--i >= 0) {
+ info = &ab8500_ext_regulator_info[i];
+ regulator_unregister(info->rdev);
+ }
+ return err;
+ }
+
+ dev_dbg(rdev_get_dev(info->rdev),
+ "%s-probed\n", info->desc.name);
+ }
+
+ return 0;
+}
+
+__devexit int ab8500_ext_regulator_exit(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) {
+ struct ab8500_ext_regulator_info *info = NULL;
+ info = &ab8500_ext_regulator_info[i];
+
+ dev_vdbg(rdev_get_dev(info->rdev),
+ "%s-remove\n", info->desc.name);
+
+ regulator_unregister(info->rdev);
+ }
+
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 external regulator driver");
+MODULE_ALIAS("platform:ab8500-ext-regulator");
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index c7ee4c15d6f..71328249659 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -21,43 +21,55 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpio.h> /* for sysclkreq pins */
+#include <mach/id.h>
/**
* struct ab8500_regulator_info - ab8500 regulator information
* @dev: device pointer
* @desc: regulator description
* @regulator_dev: regulator device
+ * @is_enabled: status of regulator (on/off)
* @max_uV: maximum voltage (for variable voltage supplies)
* @min_uV: minimum voltage (for variable voltage supplies)
* @fixed_uV: typical voltage (for fixed voltage supplies)
+ * @load_lp_uA: maximum load in idle (low power) mode
* @update_bank: bank to control on/off
* @update_reg: register to control on/off
- * @update_mask: mask to enable/disable regulator
- * @update_val_enable: bits to enable the regulator in normal (high power) mode
+ * @update_mask: mask to enable/disable and set mode of regulator
+ * @update_val: bits holding the regulator current mode
+ * @update_val_idle: bits to enable the regulator in idle (low power) mode
+ * @update_val_normal: bits to enable the regulator in normal (high power) mode
* @voltage_bank: bank to control regulator voltage
* @voltage_reg: register to control regulator voltage
* @voltage_mask: mask to control regulator voltage
* @voltages: supported voltage table
* @voltages_len: number of supported voltages for the regulator
* @delay: startup/set voltage delay in us
+ * @gpio_pin: ab8500 gpio pin offset number (for sysclkreq regulator only)
*/
struct ab8500_regulator_info {
struct device *dev;
struct regulator_desc desc;
struct regulator_dev *regulator;
+ bool is_enabled;
int max_uV;
int min_uV;
int fixed_uV;
+ int load_lp_uA;
u8 update_bank;
u8 update_reg;
u8 update_mask;
- u8 update_val_enable;
+ u8 update_val;
+ u8 update_val_idle;
+ u8 update_val_normal;
u8 voltage_bank;
u8 voltage_reg;
u8 voltage_mask;
int const *voltages;
int voltages_len;
unsigned int delay;
+ enum ab8500_pin gpio_pin;
};
/* voltage tables for the vauxn/vintcore supplies */
@@ -113,15 +125,17 @@ static int ab8500_regulator_enable(struct regulator_dev *rdev)
ret = abx500_mask_and_set_register_interruptible(info->dev,
info->update_bank, info->update_reg,
- info->update_mask, info->update_val_enable);
+ info->update_mask, info->update_val);
if (ret < 0)
dev_err(rdev_get_dev(rdev),
"couldn't set enable bits for regulator\n");
+ info->is_enabled = true;
+
dev_vdbg(rdev_get_dev(rdev),
"%s-enable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
info->desc.name, info->update_bank, info->update_reg,
- info->update_mask, info->update_val_enable);
+ info->update_mask, info->update_val);
return ret;
}
@@ -143,6 +157,8 @@ static int ab8500_regulator_disable(struct regulator_dev *rdev)
dev_err(rdev_get_dev(rdev),
"couldn't set disable bits for regulator\n");
+ info->is_enabled = false;
+
dev_vdbg(rdev_get_dev(rdev),
"%s-disable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
info->desc.name, info->update_bank, info->update_reg,
@@ -151,6 +167,88 @@ static int ab8500_regulator_disable(struct regulator_dev *rdev)
return ret;
}
+static unsigned int ab8500_regulator_get_optimum_mode(
+ struct regulator_dev *rdev, int input_uV,
+ int output_uV, int load_uA)
+{
+ unsigned int mode;
+
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ if (load_uA <= info->load_lp_uA)
+ mode = REGULATOR_MODE_IDLE;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int ret = 0;
+
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ info->update_val = info->update_val_normal;
+ break;
+ case REGULATOR_MODE_IDLE:
+ info->update_val = info->update_val_idle;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (info->is_enabled) {
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set regulator mode\n");
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-set_mode (bank, reg, mask, value): "
+ "0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+ }
+
+ return ret;
+}
+
+static unsigned int ab8500_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ if (info->update_val == info->update_val_normal)
+ ret = REGULATOR_MODE_NORMAL;
+ else if (info->update_val == info->update_val_idle)
+ ret = REGULATOR_MODE_IDLE;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
{
int ret;
@@ -177,9 +275,11 @@ static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
info->update_mask, regval);
if (regval & info->update_mask)
- return true;
+ info->is_enabled = true;
else
- return false;
+ info->is_enabled = false;
+
+ return info->is_enabled;
}
static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector)
@@ -273,8 +373,13 @@ static int ab8500_regulator_set_voltage(struct regulator_dev *rdev,
*selector = ret;
+ /* vintcore register has a different layout */
+ if (info->desc.id == AB8500_LDO_INTCORE)
+ regval = ((u8)ret) << 3;
+ else
+ regval = (u8)ret;
+
/* set the registers for the request */
- regval = (u8)ret;
ret = abx500_mask_and_set_register_interruptible(info->dev,
info->voltage_bank, info->voltage_reg,
info->voltage_mask, regval);
@@ -314,9 +419,12 @@ static int ab8500_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
return info->delay;
}
-static struct regulator_ops ab8500_regulator_ops = {
+static struct regulator_ops ab8500_regulator_volt_mode_ops = {
.enable = ab8500_regulator_enable,
.disable = ab8500_regulator_disable,
+ .get_optimum_mode = ab8500_regulator_get_optimum_mode,
+ .set_mode = ab8500_regulator_set_mode,
+ .get_mode = ab8500_regulator_get_mode,
.is_enabled = ab8500_regulator_is_enabled,
.get_voltage_sel = ab8500_regulator_get_voltage_sel,
.set_voltage = ab8500_regulator_set_voltage,
@@ -337,16 +445,116 @@ static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
return info->fixed_uV;
}
-static struct regulator_ops ab8500_regulator_fixed_ops = {
+static struct regulator_ops ab8500_regulator_mode_ops = {
.enable = ab8500_regulator_enable,
.disable = ab8500_regulator_disable,
.is_enabled = ab8500_regulator_is_enabled,
+ .get_optimum_mode = ab8500_regulator_get_optimum_mode,
+ .set_mode = ab8500_regulator_set_mode,
+ .get_mode = ab8500_regulator_get_mode,
.get_voltage = ab8500_fixed_get_voltage,
.list_voltage = ab8500_list_voltage,
.enable_time = ab8500_regulator_enable_time,
.set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel,
};
+static struct regulator_ops ab8500_regulator_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+static int ab8500_sysclkreq_enable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_select(info->dev, info->gpio_pin, false);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set sysclkreq pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = true;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-enable (gpio_pin, gpio_select): %i, false\n",
+ info->desc.name, info->gpio_pin);
+
+ return ret;
+}
+
+static int ab8500_sysclkreq_disable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_select(info->dev, info->gpio_pin, true);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set gpio pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = false;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-disable (gpio_pin, gpio_select): %i, true\n",
+ info->desc.name, info->gpio_pin);
+
+ return ret;
+}
+
+static int ab8500_sysclkreq_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ bool gpio_select;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_get_select(info->dev, info->gpio_pin,
+ &gpio_select);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read gpio pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = !gpio_select;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-is_enabled (gpio_pin, is_enabled): %i, %i\n",
+ info->desc.name, info->gpio_pin, !gpio_select);
+
+ return info->is_enabled;
+}
+
+static struct regulator_ops ab8500_sysclkreq_ops = {
+ .enable = ab8500_sysclkreq_enable,
+ .disable = ab8500_sysclkreq_disable,
+ .is_enabled = ab8500_sysclkreq_is_enabled,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+/* AB8500 regulator information */
static struct ab8500_regulator_info
ab8500_regulator_info[AB8500_NUM_REGULATORS] = {
/*
@@ -358,7 +566,7 @@ static struct ab8500_regulator_info
[AB8500_LDO_AUX1] = {
.desc = {
.name = "LDO-AUX1",
- .ops = &ab8500_regulator_ops,
+ .ops = &ab8500_regulator_volt_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_AUX1,
.owner = THIS_MODULE,
@@ -366,10 +574,13 @@ static struct ab8500_regulator_info
},
.min_uV = 1100000,
.max_uV = 3300000,
+ .load_lp_uA = 5000,
.update_bank = 0x04,
.update_reg = 0x09,
.update_mask = 0x03,
- .update_val_enable = 0x01,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
.voltage_bank = 0x04,
.voltage_reg = 0x1f,
.voltage_mask = 0x0f,
@@ -379,7 +590,7 @@ static struct ab8500_regulator_info
[AB8500_LDO_AUX2] = {
.desc = {
.name = "LDO-AUX2",
- .ops = &ab8500_regulator_ops,
+ .ops = &ab8500_regulator_volt_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_AUX2,
.owner = THIS_MODULE,
@@ -387,10 +598,13 @@ static struct ab8500_regulator_info
},
.min_uV = 1100000,
.max_uV = 3300000,
+ .load_lp_uA = 5000,
.update_bank = 0x04,
.update_reg = 0x09,
.update_mask = 0x0c,
- .update_val_enable = 0x04,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
.voltage_bank = 0x04,
.voltage_reg = 0x20,
.voltage_mask = 0x0f,
@@ -400,7 +614,7 @@ static struct ab8500_regulator_info
[AB8500_LDO_AUX3] = {
.desc = {
.name = "LDO-AUX3",
- .ops = &ab8500_regulator_ops,
+ .ops = &ab8500_regulator_volt_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_AUX3,
.owner = THIS_MODULE,
@@ -408,10 +622,13 @@ static struct ab8500_regulator_info
},
.min_uV = 1100000,
.max_uV = 3300000,
+ .load_lp_uA = 5000,
.update_bank = 0x04,
.update_reg = 0x0a,
.update_mask = 0x03,
- .update_val_enable = 0x01,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
.voltage_bank = 0x04,
.voltage_reg = 0x21,
.voltage_mask = 0x07,
@@ -421,7 +638,7 @@ static struct ab8500_regulator_info
[AB8500_LDO_INTCORE] = {
.desc = {
.name = "LDO-INTCORE",
- .ops = &ab8500_regulator_ops,
+ .ops = &ab8500_regulator_volt_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_INTCORE,
.owner = THIS_MODULE,
@@ -429,10 +646,13 @@ static struct ab8500_regulator_info
},
.min_uV = 1100000,
.max_uV = 3300000,
+ .load_lp_uA = 5000,
.update_bank = 0x03,
.update_reg = 0x80,
.update_mask = 0x44,
- .update_val_enable = 0x04,
+ .update_val = 0x44,
+ .update_val_idle = 0x44,
+ .update_val_normal = 0x04,
.voltage_bank = 0x03,
.voltage_reg = 0x80,
.voltage_mask = 0x38,
@@ -448,7 +668,275 @@ static struct ab8500_regulator_info
[AB8500_LDO_TVOUT] = {
.desc = {
.name = "LDO-TVOUT",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_TVOUT,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .delay = 500,
+ .fixed_uV = 2000000,
+ .load_lp_uA = 1000,
+ .update_bank = 0x03,
+ .update_reg = 0x80,
+ .update_mask = 0x82,
+ .update_val = 0x02,
+ .update_val_idle = 0x82,
+ .update_val_normal = 0x02,
+ },
+ [AB8500_LDO_AUDIO] = {
+ .desc = {
+ .name = "LDO-AUDIO",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUDIO,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2000000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x02,
+ .update_val = 0x02,
+ },
+ [AB8500_LDO_ANAMIC1] = {
+ .desc = {
+ .name = "LDO-ANAMIC1",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANAMIC1,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2050000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x08,
+ .update_val = 0x08,
+ },
+ [AB8500_LDO_ANAMIC2] = {
+ .desc = {
+ .name = "LDO-ANAMIC2",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANAMIC2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2050000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x10,
+ .update_val = 0x10,
+ },
+ [AB8500_LDO_DMIC] = {
+ .desc = {
+ .name = "LDO-DMIC",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_DMIC,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1800000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x04,
+ .update_val = 0x04,
+ },
+
+ /*
+ * Regulators with fixed voltage and normal/idle modes
+ */
+ [AB8500_LDO_ANA] = {
+ .desc = {
+ .name = "LDO-ANA",
+ .ops = &ab8500_regulator_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANA,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1200000,
+ .load_lp_uA = 1000,
+ .update_bank = 0x04,
+ .update_reg = 0x06,
+ .update_mask = 0x0c,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
+ },
+
+ /*
+ * SysClkReq regulators
+ */
+ [AB8500_SYSCLKREQ_2] = {
+ .desc = {
+ .name = "SYSCLKREQ-2",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO1,
+ },
+ [AB8500_SYSCLKREQ_4] = {
+ .desc = {
+ .name = "SYSCLKREQ-4",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_4,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO3,
+ },
+};
+
+/* AB9540 regulator information */
+static struct ab8500_regulator_info
+ ab9540_regulator_info[AB9540_NUM_REGULATORS] = {
+ /*
+ * Variable Voltage Regulators
+ * name, min mV, max mV,
+ * update bank, reg, mask, enable val
+ * volt bank, reg, mask, table, table length
+ */
+ [AB9540_LDO_AUX1] = {
+ .desc = {
+ .name = "LDO-AUX1",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX1,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x09,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x1f,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB9540_LDO_AUX2] = {
+ .desc = {
+ .name = "LDO-AUX2",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX2,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x09,
+ .update_mask = 0x0c,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x20,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB9540_LDO_AUX3] = {
+ .desc = {
+ .name = "LDO-AUX3",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX3,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x0a,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x21,
+ .voltage_mask = 0x07,
+ .voltages = ldo_vaux3_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vaux3_voltages),
+ },
+ [AB9540_LDO_AUX4] = {
+ .desc = {
+ .name = "LDO-AUX4",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB9540_LDO_AUX4,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ /* values for Vaux4Regu register */
+ .update_bank = 0x04,
+ .update_reg = 0x2e,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
+ /* values for Vaux4SEL register */
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x2f,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB9540_LDO_INTCORE] = {
+ .desc = {
+ .name = "LDO-INTCORE",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_INTCORE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x03,
+ .update_reg = 0x80,
+ .update_mask = 0x44,
+ .update_val = 0x44,
+ .update_val_idle = 0x44,
+ .update_val_normal = 0x04,
+ .voltage_bank = 0x03,
+ .voltage_reg = 0x80,
+ .voltage_mask = 0x38,
+ .voltages = ldo_vintcore_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vintcore_voltages),
+ },
+
+ /*
+ * Fixed Voltage Regulators
+ * name, fixed mV,
+ * update bank, reg, mask, enable val
+ */
+ [AB9540_LDO_TVOUT] = {
+ .desc = {
+ .name = "LDO-TVOUT",
+ .ops = &ab8500_regulator_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_TVOUT,
.owner = THIS_MODULE,
@@ -456,17 +944,20 @@ static struct ab8500_regulator_info
},
.delay = 10000,
.fixed_uV = 2000000,
+ .load_lp_uA = 1000,
.update_bank = 0x03,
.update_reg = 0x80,
.update_mask = 0x82,
- .update_val_enable = 0x02,
+ .update_val = 0x02,
+ .update_val_idle = 0x82,
+ .update_val_normal = 0x02,
},
- [AB8500_LDO_USB] = {
+ [AB9540_LDO_USB] = {
.desc = {
.name = "LDO-USB",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
- .id = AB8500_LDO_USB,
+ .id = AB9540_LDO_USB,
.owner = THIS_MODULE,
.n_voltages = 1,
},
@@ -474,12 +965,14 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x82,
.update_mask = 0x03,
- .update_val_enable = 0x01,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
},
- [AB8500_LDO_AUDIO] = {
+ [AB9540_LDO_AUDIO] = {
.desc = {
.name = "LDO-AUDIO",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_AUDIO,
.owner = THIS_MODULE,
@@ -489,12 +982,12 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x83,
.update_mask = 0x02,
- .update_val_enable = 0x02,
+ .update_val = 0x02,
},
- [AB8500_LDO_ANAMIC1] = {
+ [AB9540_LDO_ANAMIC1] = {
.desc = {
.name = "LDO-ANAMIC1",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_ANAMIC1,
.owner = THIS_MODULE,
@@ -504,12 +997,12 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x83,
.update_mask = 0x08,
- .update_val_enable = 0x08,
+ .update_val = 0x08,
},
- [AB8500_LDO_ANAMIC2] = {
+ [AB9540_LDO_ANAMIC2] = {
.desc = {
.name = "LDO-ANAMIC2",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_ANAMIC2,
.owner = THIS_MODULE,
@@ -519,12 +1012,12 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x83,
.update_mask = 0x10,
- .update_val_enable = 0x10,
+ .update_val = 0x10,
},
- [AB8500_LDO_DMIC] = {
+ [AB9540_LDO_DMIC] = {
.desc = {
.name = "LDO-DMIC",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_DMIC,
.owner = THIS_MODULE,
@@ -534,25 +1027,58 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x83,
.update_mask = 0x04,
- .update_val_enable = 0x04,
+ .update_val = 0x04,
},
- [AB8500_LDO_ANA] = {
+
+ /*
+ * Regulators with fixed voltage and normal/idle modes
+ */
+ [AB9540_LDO_ANA] = {
.desc = {
.name = "LDO-ANA",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_ANA,
.owner = THIS_MODULE,
.n_voltages = 1,
},
.fixed_uV = 1200000,
+ .load_lp_uA = 1000,
.update_bank = 0x04,
.update_reg = 0x06,
.update_mask = 0x0c,
- .update_val_enable = 0x04,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
},
-
+ /*
+ * SysClkReq regulators
+ */
+ [AB9540_SYSCLKREQ_2] = {
+ .desc = {
+ .name = "SYSCLKREQ-2",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO1,
+ },
+ [AB9540_SYSCLKREQ_4] = {
+ .desc = {
+ .name = "SYSCLKREQ-4",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_4,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO3,
+ },
};
struct ab8500_reg_init {
@@ -568,13 +1094,13 @@ struct ab8500_reg_init {
.mask = _mask, \
}
+/* AB8500 register init */
static struct ab8500_reg_init ab8500_reg_init[] = {
/*
* 0x30, VanaRequestCtrl
- * 0x0C, VpllRequestCtrl
* 0xc0, VextSupply1RequestCtrl
*/
- REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xfc),
+ REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xf0),
/*
* 0x03, VextSupply2RequestCtrl
* 0x0c, VextSupply3RequestCtrl
@@ -641,13 +1167,21 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
REG_INIT(AB8500_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f),
/*
* 0x02, SysClkReq2Valid1
- * ...
+ * 0x04, SysClkReq3Valid1
+ * 0x08, SysClkReq4Valid1
+ * 0x10, SysClkReq5Valid1
+ * 0x20, SysClkReq6Valid1
+ * 0x40, SysClkReq7Valid1
* 0x80, SysClkReq8Valid1
*/
REG_INIT(AB8500_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe),
/*
* 0x02, SysClkReq2Valid2
- * ...
+ * 0x04, SysClkReq3Valid2
+ * 0x08, SysClkReq4Valid2
+ * 0x10, SysClkReq5Valid2
+ * 0x20, SysClkReq6Valid2
+ * 0x40, SysClkReq7Valid2
* 0x80, SysClkReq8Valid2
*/
REG_INIT(AB8500_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe),
@@ -672,8 +1206,8 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
*/
REG_INIT(AB8500_REGUCTRL1VAMIC, 0x03, 0x84, 0x03),
/*
+ * 0x03, VpllRegu (NOTE! PRCMU register bits)
* 0x0c, VanaRegu
- * 0x03, VpllRegu
*/
REG_INIT(AB8500_VPLLVANAREGU, 0x04, 0x06, 0x0f),
/*
@@ -699,10 +1233,6 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
*/
REG_INIT(AB8500_VRF1VAUX3REGU, 0x04, 0x0a, 0x03),
/*
- * 0x3f, Vsmps1Sel1
- */
- REG_INIT(AB8500_VSMPS1SEL1, 0x04, 0x13, 0x3f),
- /*
* 0x0f, Vaux1Sel
*/
REG_INIT(AB8500_VAUX1SEL, 0x04, 0x1f, 0x0f),
@@ -735,79 +1265,412 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
REG_INIT(AB8500_REGUCTRLDISCH2, 0x04, 0x44, 0x16),
};
+/* Possibility to add debug */
+int __attribute__((weak)) ab8500_regulator_debug_init(
+ struct platform_device *pdev)
+{
+ return 0;
+}
+
+int __attribute__((weak)) ab8500_regulator_debug_exit(
+ struct platform_device *pdev)
+{
+ return 0;
+}
+
+/* AB9540 register init */
+static struct ab8500_reg_init ab9540_reg_init[] = {
+ /*
+ * 0x03, VarmRequestCtrl
+ * 0x0c, VapeRequestCtrl
+ * 0x30, Vsmps1RequestCtrl
+ * 0xc0, Vsmps2RequestCtrl
+ */
+ REG_INIT(AB9540_REGUREQUESTCTRL1, 0x03, 0x03, 0xff),
+ /*
+ * 0x03, Vsmps3RequestCtrl
+ * 0x0c, VpllRequestCtrl
+ * 0x30, VanaRequestCtrl
+ * 0xc0, VextSupply1RequestCtrl
+ */
+ REG_INIT(AB9540_REGUREQUESTCTRL2, 0x03, 0x04, 0xff),
+ /*
+ * 0x03, VextSupply2RequestCtrl
+ * 0x0c, VextSupply3RequestCtrl
+ * 0x30, Vaux1RequestCtrl
+ * 0xc0, Vaux2RequestCtrl
+ */
+ REG_INIT(AB9540_REGUREQUESTCTRL3, 0x03, 0x05, 0xff),
+ /*
+ * 0x03, Vaux3RequestCtrl
+ * 0x04, SwHPReq
+ */
+ REG_INIT(AB9540_REGUREQUESTCTRL4, 0x03, 0x06, 0x07),
+ /*
+ * 0x01, Vsmps1SysClkReq1HPValid
+ * 0x02, Vsmps2SysClkReq1HPValid
+ * 0x04, Vsmps3SysClkReq1HPValid
+ * 0x08, VanaSysClkReq1HPValid
+ * 0x10, VpllSysClkReq1HPValid
+ * 0x20, Vaux1SysClkReq1HPValid
+ * 0x40, Vaux2SysClkReq1HPValid
+ * 0x80, Vaux3SysClkReq1HPValid
+ */
+ REG_INIT(AB9540_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff),
+ /*
+ * 0x01, VapeSysClkReq1HPValid
+ * 0x02, VarmSysClkReq1HPValid
+ * 0x04, VbbSysClkReq1HPValid
+ * 0x08, VmodSysClkReq1HPValid
+ * 0x10, VextSupply1SysClkReq1HPValid
+ * 0x20, VextSupply2SysClkReq1HPValid
+ * 0x40, VextSupply3SysClkReq1HPValid
+ */
+ REG_INIT(AB9540_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x7f),
+ /*
+ * 0x01, Vsmps1HwHPReq1Valid
+ * 0x02, Vsmps2HwHPReq1Valid
+ * 0x04, Vsmps3HwHPReq1Valid
+ * 0x08, VanaHwHPReq1Valid
+ * 0x10, VpllHwHPReq1Valid
+ * 0x20, Vaux1HwHPReq1Valid
+ * 0x40, Vaux2HwHPReq1Valid
+ * 0x80, Vaux3HwHPReq1Valid
+ */
+ REG_INIT(AB9540_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff),
+ /*
+ * 0x01, VextSupply1HwHPReq1Valid
+ * 0x02, VextSupply2HwHPReq1Valid
+ * 0x04, VextSupply3HwHPReq1Valid
+ * 0x08, VmodHwHPReq1Valid
+ */
+ REG_INIT(AB9540_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x0f),
+ /*
+ * 0x01, Vsmps1HwHPReq2Valid
+ * 0x02, Vsmps2HwHPReq2Valid
+ * 0x03, Vsmps3HwHPReq2Valid
+ * 0x08, VanaHwHPReq2Valid
+ * 0x10, VpllHwHPReq2Valid
+ * 0x20, Vaux1HwHPReq2Valid
+ * 0x40, Vaux2HwHPReq2Valid
+ * 0x80, Vaux3HwHPReq2Valid
+ */
+ REG_INIT(AB9540_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff),
+ /*
+ * 0x01, VextSupply1HwHPReq2Valid
+ * 0x02, VextSupply2HwHPReq2Valid
+ * 0x04, VextSupply3HwHPReq2Valid
+ * 0x08, VmodHwHPReq2Valid
+ */
+ REG_INIT(AB9540_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x0f),
+ /*
+ * 0x01, VapeSwHPReqValid
+ * 0x02, VarmSwHPReqValid
+ * 0x04, Vsmps1SwHPReqValid
+ * 0x08, Vsmps2SwHPReqValid
+ * 0x10, Vsmps3SwHPReqValid
+ * 0x20, VanaSwHPReqValid
+ * 0x40, VpllSwHPReqValid
+ * 0x80, Vaux1SwHPReqValid
+ */
+ REG_INIT(AB9540_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff),
+ /*
+ * 0x01, Vaux2SwHPReqValid
+ * 0x02, Vaux3SwHPReqValid
+ * 0x04, VextSupply1SwHPReqValid
+ * 0x08, VextSupply2SwHPReqValid
+ * 0x10, VextSupply3SwHPReqValid
+ * 0x20, VmodSwHPReqValid
+ */
+ REG_INIT(AB9540_REGUSWHPREQVALID2, 0x03, 0x0e, 0x3f),
+ /*
+ * 0x02, SysClkReq2Valid1
+ * ...
+ * 0x80, SysClkReq8Valid1
+ */
+ REG_INIT(AB9540_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe),
+ /*
+ * 0x02, SysClkReq2Valid2
+ * ...
+ * 0x80, SysClkReq8Valid2
+ */
+ REG_INIT(AB9540_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe),
+ /*
+ * 0x01, Vaux4SwHPReqValid
+ * 0x02, Vaux4HwHPReq2Valid
+ * 0x04, Vaux4HwHPReq1Valid
+ * 0x08, Vaux4SysClkReq1HPValid
+ */
+ REG_INIT(AB9540_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f),
+ /*
+ * 0x02, VTVoutEna
+ * 0x04, Vintcore12Ena
+ * 0x38, Vintcore12Sel
+ * 0x40, Vintcore12LP
+ * 0x80, VTVoutLP
+ */
+ REG_INIT(AB9540_REGUMISC1, 0x03, 0x80, 0xfe),
+ /*
+ * 0x02, VaudioEna
+ * 0x04, VdmicEna
+ * 0x08, Vamic1Ena
+ * 0x10, Vamic2Ena
+ */
+ REG_INIT(AB9540_VAUDIOSUPPLY, 0x03, 0x83, 0x1e),
+ /*
+ * 0x01, Vamic1_dzout
+ * 0x02, Vamic2_dzout
+ */
+ REG_INIT(AB9540_REGUCTRL1VAMIC, 0x03, 0x84, 0x03),
+ /*
+ * 0x03, Vsmps1Regu
+ * 0x0c, Vsmps1SelCtrl
+ * 0x10, Vsmps1AutoMode
+ * 0x20, Vsmps1PWMMode
+ */
+ REG_INIT(AB9540_VSMPS1REGU, 0x04, 0x03, 0x3f),
+ /*
+ * 0x03, Vsmps2Regu
+ * 0x0c, Vsmps2SelCtrl
+ * 0x10, Vsmps2AutoMode
+ * 0x20, Vsmps2PWMMode
+ */
+ REG_INIT(AB9540_VSMPS2REGU, 0x04, 0x04, 0x3f),
+ /*
+ * 0x03, Vsmps3Regu
+ * 0x0c, Vsmps3SelCtrl
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB9540_VSMPS3REGU, 0x04, 0x05, 0x0f),
+ /*
+ * 0x03, VpllRegu
+ * 0x0c, VanaRegu
+ */
+ REG_INIT(AB9540_VPLLVANAREGU, 0x04, 0x06, 0x0f),
+ /*
+ * 0x03, VextSupply1Regu
+ * 0x0c, VextSupply2Regu
+ * 0x30, VextSupply3Regu
+ * 0x40, ExtSupply2Bypass
+ * 0x80, ExtSupply3Bypass
+ */
+ REG_INIT(AB9540_EXTSUPPLYREGU, 0x04, 0x08, 0xff),
+ /*
+ * 0x03, Vaux1Regu
+ * 0x0c, Vaux2Regu
+ */
+ REG_INIT(AB9540_VAUX12REGU, 0x04, 0x09, 0x0f),
+ /*
+ * 0x0c, Vrf1Regu
+ * 0x03, Vaux3Regu
+ */
+ REG_INIT(AB9540_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f),
+ /*
+ * 0x3f, Vsmps1Sel1
+ */
+ REG_INIT(AB9540_VSMPS1SEL1, 0x04, 0x13, 0x3f),
+ /*
+ * 0x3f, Vsmps1Sel2
+ */
+ REG_INIT(AB9540_VSMPS1SEL2, 0x04, 0x14, 0x3f),
+ /*
+ * 0x3f, Vsmps1Sel3
+ */
+ REG_INIT(AB9540_VSMPS1SEL3, 0x04, 0x15, 0x3f),
+ /*
+ * 0x3f, Vsmps2Sel1
+ */
+ REG_INIT(AB9540_VSMPS2SEL1, 0x04, 0x17, 0x3f),
+ /*
+ * 0x3f, Vsmps2Sel2
+ */
+ REG_INIT(AB9540_VSMPS2SEL2, 0x04, 0x18, 0x3f),
+ /*
+ * 0x3f, Vsmps2Sel3
+ */
+ REG_INIT(AB9540_VSMPS2SEL3, 0x04, 0x19, 0x3f),
+ /*
+ * 0x7f, Vsmps3Sel1
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB9540_VSMPS3SEL1, 0x04, 0x1b, 0x7f),
+ /*
+ * 0x7f, Vsmps3Sel2
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB9540_VSMPS3SEL2, 0x04, 0x1c, 0x7f),
+ /*
+ * 0x0f, Vaux1Sel
+ */
+ REG_INIT(AB9540_VAUX1SEL, 0x04, 0x1f, 0x0f),
+ /*
+ * 0x0f, Vaux2Sel
+ */
+ REG_INIT(AB9540_VAUX2SEL, 0x04, 0x20, 0x0f),
+ /*
+ * 0x07, Vaux3Sel
+ * 0x30, Vrf1Sel
+ */
+ REG_INIT(AB9540_VRF1VAUX3SEL, 0x04, 0x21, 0x37),
+ /*
+ * 0x01, VextSupply12LP
+ */
+ REG_INIT(AB9540_REGUCTRL2SPARE, 0x04, 0x22, 0x01),
+ /*
+ * 0x03, Vaux4RequestCtrl
+ */
+ REG_INIT(AB9540_VAUX4REQCTRL, 0x04, 0x2d, 0x03),
+ /*
+ * 0x03, Vaux4Regu
+ */
+ REG_INIT(AB9540_VAUX4REGU, 0x04, 0x2e, 0x03),
+ /*
+ * 0x08, Vaux4Sel
+ */
+ REG_INIT(AB9540_VAUX4SEL, 0x04, 0x2f, 0x0f),
+ /*
+ * 0x01, VpllDisch
+ * 0x02, Vrf1Disch
+ * 0x04, Vaux1Disch
+ * 0x08, Vaux2Disch
+ * 0x10, Vaux3Disch
+ * 0x20, Vintcore12Disch
+ * 0x40, VTVoutDisch
+ * 0x80, VaudioDisch
+ */
+ REG_INIT(AB9540_REGUCTRLDISCH, 0x04, 0x43, 0xff),
+ /*
+ * 0x01, VsimDisch
+ * 0x02, VanaDisch
+ * 0x04, VdmicPullDownEna
+ * 0x08, VpllPullDownEna
+ * 0x10, VdmicDisch
+ */
+ REG_INIT(AB9540_REGUCTRLDISCH2, 0x04, 0x44, 0x1f),
+ /*
+ * 0x01, Vaux4Disch
+ */
+ REG_INIT(AB9540_REGUCTRLDISCH3, 0x04, 0x48, 0x01),
+};
+
static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
{
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
- struct ab8500_platform_data *pdata;
+ struct ab8500_platform_data *ppdata;
+ struct ab8500_regulator_platform_data *pdata;
int i, err;
+ struct ab8500_regulator_info *regulator_info;
+ int regulator_info_size;
+ struct ab8500_reg_init *reg_init;
+ int reg_init_size;
+ /* cache values needed repeatedly inside for-loops */
if (!ab8500) {
dev_err(&pdev->dev, "null mfd parent\n");
return -EINVAL;
}
- pdata = dev_get_platdata(ab8500->dev);
+
+ ppdata = dev_get_platdata(ab8500->dev);
+ if (!ppdata) {
+ dev_err(&pdev->dev, "null parent pdata\n");
+ return -EINVAL;
+ }
+
+ pdata = ppdata->regulator;
if (!pdata) {
dev_err(&pdev->dev, "null pdata\n");
return -EINVAL;
}
+ if (is_ab9540(ab8500)) {
+ regulator_info = ab9540_regulator_info;
+ regulator_info_size = ARRAY_SIZE(ab9540_regulator_info);
+ reg_init = ab9540_reg_init;
+ reg_init_size = AB9540_NUM_REGULATOR_REGISTERS;
+ } else {
+ regulator_info = ab8500_regulator_info;
+ regulator_info_size = ARRAY_SIZE(ab8500_regulator_info);
+ reg_init = ab8500_reg_init;
+ reg_init_size = AB8500_NUM_REGULATOR_REGISTERS;
+ }
+
/* make sure the platform data has the correct size */
- if (pdata->num_regulator != ARRAY_SIZE(ab8500_regulator_info)) {
+ if (pdata->num_regulator != regulator_info_size) {
dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
return -EINVAL;
}
+ /* initialize debug (initial state is recorded with this call) */
+ err = ab8500_regulator_debug_init(pdev);
+ if (err)
+ return err;
+
/* initialize registers */
- for (i = 0; i < pdata->num_regulator_reg_init; i++) {
+ for (i = 0; i < pdata->num_reg_init; i++) {
int id;
- u8 value;
+ u8 mask, value;
- id = pdata->regulator_reg_init[i].id;
- value = pdata->regulator_reg_init[i].value;
+ id = pdata->reg_init[i].id;
+ mask = pdata->reg_init[i].mask;
+ value = pdata->reg_init[i].value;
/* check for configuration errors */
- if (id >= AB8500_NUM_REGULATOR_REGISTERS) {
- dev_err(&pdev->dev,
- "Configuration error: id outside range.\n");
- return -EINVAL;
- }
- if (value & ~ab8500_reg_init[id].mask) {
- dev_err(&pdev->dev,
- "Configuration error: value outside mask.\n");
- return -EINVAL;
- }
+ BUG_ON(id >= reg_init_size);
+ BUG_ON(value & ~mask);
+ BUG_ON(mask & ~reg_init[id].mask);
/* initialize register */
err = abx500_mask_and_set_register_interruptible(&pdev->dev,
- ab8500_reg_init[id].bank,
- ab8500_reg_init[id].addr,
- ab8500_reg_init[id].mask,
- value);
+ reg_init[id].bank,
+ reg_init[id].addr,
+ mask, value);
if (err < 0) {
dev_err(&pdev->dev,
"Failed to initialize 0x%02x, 0x%02x.\n",
- ab8500_reg_init[id].bank,
- ab8500_reg_init[id].addr);
+ reg_init[id].bank,
+ reg_init[id].addr);
return err;
}
dev_vdbg(&pdev->dev,
" init: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
- ab8500_reg_init[id].bank,
- ab8500_reg_init[id].addr,
- ab8500_reg_init[id].mask,
- value);
+ reg_init[id].bank,
+ reg_init[id].addr,
+ mask, value);
+ }
+
+ /*
+ * This changes the default setting for VextSupply3Regu to low power.
+ * Active high or low is depending on OTP which is changed from ab8500v3.0.
+ * Remove this when ab8500v2.0 is no longer important.
+ * This only affects power consumption and it depends on the
+ * HREF OTP configurations.
+ */
+ if (is_ab8500_2p0_or_earlier(ab8500)) {
+ err = abx500_mask_and_set_register_interruptible(&pdev->dev,
+ AB8500_REGU_CTRL2, 0x08, 0x30, 0x30);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "Failed to override 0x%02x, 0x%02x.\n",
+ AB8500_REGU_CTRL2, 0x08);
+ return err;
+ }
}
+ /* register external regulators (before Vaux1, 2 and 3) */
+ err = ab8500_ext_regulator_init(pdev);
+ if (err)
+ return err;
+
/* register all regulators */
- for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ for (i = 0; i < regulator_info_size; i++) {
struct ab8500_regulator_info *info = NULL;
/* assign per-regulator data */
- info = &ab8500_regulator_info[i];
+ info = &regulator_info[i];
info->dev = &pdev->dev;
/* fix for hardware before ab8500v2.0 */
- if (abx500_get_chip_id(info->dev) < 0x20) {
+ if (is_ab8500_1p1_or_earlier(ab8500)) {
if (info->desc.id == AB8500_LDO_AUX3) {
info->desc.n_voltages =
ARRAY_SIZE(ldo_vauxn_voltages);
@@ -827,7 +1690,7 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
info->desc.name);
/* when we fail, un-register all earlier regulators */
while (--i >= 0) {
- info = &ab8500_regulator_info[i];
+ info = &regulator_info[i];
regulator_unregister(info->regulator);
}
return err;
@@ -842,11 +1705,23 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
{
- int i;
+ int i, err;
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_regulator_info *regulator_info;
+ int regulator_info_size;
+
- for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ if (is_ab9540(ab8500)) {
+ regulator_info = ab9540_regulator_info;
+ regulator_info_size = ARRAY_SIZE(ab9540_regulator_info);
+ } else {
+ regulator_info = ab8500_regulator_info;
+ regulator_info_size = ARRAY_SIZE(ab8500_regulator_info);
+ }
+
+ for (i = 0; i < regulator_info_size; i++) {
struct ab8500_regulator_info *info = NULL;
- info = &ab8500_regulator_info[i];
+ info = &regulator_info[i];
dev_vdbg(rdev_get_dev(info->regulator),
"%s-remove\n", info->desc.name);
@@ -854,6 +1729,16 @@ static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
regulator_unregister(info->regulator);
}
+ /* remove external regulators (after Vaux1, 2 and 3) */
+ err = ab8500_ext_regulator_exit(pdev);
+ if (err)
+ return err;
+
+ /* remove regulator debug */
+ err = ab8500_regulator_debug_exit(pdev);
+ if (err)
+ return err;
+
return 0;
}
@@ -886,5 +1771,6 @@ module_exit(ab8500_regulator_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC");
MODULE_ALIAS("platform:ab8500-regulator");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 046fb1bd861..bc6c12c8a76 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -81,6 +81,7 @@ struct regulator {
struct device_attribute dev_attr;
struct regulator_dev *rdev;
struct dentry *debugfs;
+ int use;
};
static int _regulator_is_enabled(struct regulator_dev *rdev);
@@ -199,11 +200,13 @@ static int regulator_check_consumers(struct regulator_dev *rdev,
*/
if (!regulator->min_uV && !regulator->max_uV)
continue;
-
- if (*max_uV > regulator->max_uV)
- *max_uV = regulator->max_uV;
- if (*min_uV < regulator->min_uV)
- *min_uV = regulator->min_uV;
+
+ if (regulator->use) {
+ if (*max_uV > regulator->max_uV)
+ *max_uV = regulator->max_uV;
+ if (*min_uV < regulator->min_uV)
+ *min_uV = regulator->min_uV;
+ }
}
if (*min_uV > *max_uV)
@@ -602,6 +605,32 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
static DEVICE_ATTR(suspend_standby_state, 0444,
regulator_suspend_standby_state_show, NULL);
+static ssize_t regulator_use_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ struct regulator *reg;
+ size_t size = 0;
+
+ if (rdev->use_count == 0)
+ return sprintf(buf, "no users\n");
+
+ list_for_each_entry(reg, &rdev->consumer_list, list) {
+ if (!reg->use)
+ continue;
+
+ if (reg->dev != NULL)
+ size += sprintf((buf + size), "%s (%d) ",
+ dev_name(reg->dev), reg->use);
+ else
+ size += sprintf((buf + size), "unknown (%d) ",
+ reg->use);
+ }
+ size += sprintf((buf + size), "\n");
+
+ return size;
+}
+static DEVICE_ATTR(use, 0444, regulator_use_show, NULL);
/*
* These are the only attributes are present for all regulators.
@@ -1491,12 +1520,8 @@ static int _regulator_enable(struct regulator_dev *rdev)
trace_regulator_enable_delay(rdev_get_name(rdev));
- if (delay >= 1000) {
- mdelay(delay / 1000);
- udelay(delay % 1000);
- } else if (delay) {
- udelay(delay);
- }
+ if (delay)
+ usleep_range(delay, delay);
trace_regulator_enable_complete(rdev_get_name(rdev));
@@ -1540,6 +1565,8 @@ int regulator_enable(struct regulator *regulator)
if (ret != 0 && rdev->supply)
regulator_disable(rdev->supply);
+ else
+ regulator->use++;
return ret;
}
@@ -1613,6 +1640,9 @@ int regulator_disable(struct regulator *regulator)
if (ret == 0 && rdev->supply)
regulator_disable(rdev->supply);
+ if (ret == 0)
+ regulator->use--;
+
return ret;
}
EXPORT_SYMBOL_GPL(regulator_disable);
@@ -2699,6 +2729,10 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
struct regulator_ops *ops = rdev->desc->ops;
int status = 0;
+ status = device_create_file(dev, &dev_attr_use);
+ if (status < 0)
+ dev_warn(dev, "Create sysfs file \"use\" failed");
+
/* some attributes need specific methods to be displayed */
if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
(ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0)) {
diff --git a/drivers/regulator/db5500-prcmu.c b/drivers/regulator/db5500-prcmu.c
new file mode 100644
index 00000000000..189362ab8e0
--- /dev/null
+++ b/drivers/regulator/db5500-prcmu.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * Power domain regulators on DB5500
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/db5500-prcmu.h>
+
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include "dbx500-prcmu.h"
+static int db5500_regulator_enable(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-enable\n",
+ info->desc.name);
+
+ if (!info->is_enabled) {
+ info->is_enabled = true;
+ if (!info->exclude_from_power_state)
+ power_state_active_enable();
+ }
+
+ return 0;
+}
+
+static int db5500_regulator_disable(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-disable\n",
+ info->desc.name);
+
+ if (info->is_enabled) {
+ info->is_enabled = false;
+ if (!info->exclude_from_power_state)
+ ret = power_state_active_disable();
+ }
+
+ return ret;
+}
+
+static int db5500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-is_enabled (is_enabled):"
+ " %i\n", info->desc.name, info->is_enabled);
+
+ return info->is_enabled;
+}
+
+/* db5500 regulator operations */
+static struct regulator_ops db5500_regulator_ops = {
+ .enable = db5500_regulator_enable,
+ .disable = db5500_regulator_disable,
+ .is_enabled = db5500_regulator_is_enabled,
+};
+
+/*
+ * EPOD control
+ */
+static bool epod_on[NUM_EPOD_ID];
+static bool epod_ramret[NUM_EPOD_ID];
+
+static inline int epod_id_to_index(u16 epod_id)
+{
+ return epod_id - DB5500_EPOD_ID_BASE;
+}
+
+static int enable_epod(u16 epod_id, bool ramret)
+{
+ int idx = epod_id_to_index(epod_id);
+ int ret;
+
+ if (ramret) {
+ if (!epod_on[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
+ if (ret < 0)
+ return ret;
+ }
+ epod_ramret[idx] = true;
+ } else {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_ON);
+ if (ret < 0)
+ return ret;
+ epod_on[idx] = true;
+ }
+
+ return 0;
+}
+
+static int disable_epod(u16 epod_id, bool ramret)
+{
+ int idx = epod_id_to_index(epod_id);
+ int ret;
+
+ if (ramret) {
+ if (!epod_on[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF);
+ if (ret < 0)
+ return ret;
+ }
+ epod_ramret[idx] = false;
+ } else {
+ if (epod_ramret[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF);
+ if (ret < 0)
+ return ret;
+ }
+ epod_on[idx] = false;
+ }
+
+ return 0;
+}
+
+/*
+ * Regulator switch
+ */
+static int db5500_regulator_switch_enable(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-switch-%s-enable\n",
+ info->desc.name);
+
+ ret = enable_epod(info->epod_id, info->is_ramret);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "regulator-switch-%s-enable: prcmu call failed\n",
+ info->desc.name);
+ goto out;
+ }
+
+ info->is_enabled = true;
+out:
+ return ret;
+}
+
+static int db5500_regulator_switch_disable(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-switch-%s-disable\n",
+ info->desc.name);
+
+ ret = disable_epod(info->epod_id, info->is_ramret);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "regulator_switch-%s-disable: prcmu call failed\n",
+ info->desc.name);
+ goto out;
+ }
+
+ info->is_enabled = 0;
+out:
+ return ret;
+}
+
+static int db5500_regulator_switch_is_enabled(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "regulator-switch-%s-is_enabled (is_enabled): %i\n",
+ info->desc.name, info->is_enabled);
+
+ return info->is_enabled;
+}
+
+static struct regulator_ops db5500_regulator_switch_ops = {
+ .enable = db5500_regulator_switch_enable,
+ .disable = db5500_regulator_switch_disable,
+ .is_enabled = db5500_regulator_switch_is_enabled,
+};
+
+/*
+ * Regulator information
+ */
+#define DB5500_REGULATOR_SWITCH(_name, reg) \
+ [DB5500_REGULATOR_SWITCH_##reg] = { \
+ .desc = { \
+ .name = _name, \
+ .id = DB5500_REGULATOR_SWITCH_##reg, \
+ .ops = &db5500_regulator_switch_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ .epod_id = DB5500_EPOD_ID_##reg, \
+}
+
+static struct dbx500_regulator_info
+ dbx500_regulator_info[DB5500_NUM_REGULATORS] = {
+ [DB5500_REGULATOR_VAPE] = {
+ .desc = {
+ .name = "db5500-vape",
+ .id = DB5500_REGULATOR_VAPE,
+ .ops = &db5500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ DB5500_REGULATOR_SWITCH("db5500-sga", SGA),
+ DB5500_REGULATOR_SWITCH("db5500-hva", HVA),
+ DB5500_REGULATOR_SWITCH("db5500-sia", SIA),
+ DB5500_REGULATOR_SWITCH("db5500-disp", DISP),
+ DB5500_REGULATOR_SWITCH("db5500-esram12", ESRAM12),
+};
+
+static int __devinit db5500_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_init_data *db5500_init_data =
+ dev_get_platdata(&pdev->dev);
+ int i, err;
+
+ /* register all regulators */
+ for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) {
+ struct dbx500_regulator_info *info;
+ struct regulator_init_data *init_data = &db5500_init_data[i];
+
+ /* assign per-regulator data */
+ info = &dbx500_regulator_info[i];
+ info->dev = &pdev->dev;
+
+ /* register with the regulator framework */
+ info->rdev = regulator_register(&info->desc, &pdev->dev,
+ init_data, info);
+ if (IS_ERR(info->rdev)) {
+ err = PTR_ERR(info->rdev);
+ dev_err(&pdev->dev, "failed to register %s: err %i\n",
+ info->desc.name, err);
+
+ /* if failing, unregister all earlier regulators */
+ i--;
+ while (i >= 0) {
+ info = &dbx500_regulator_info[i];
+ regulator_unregister(info->rdev);
+ i--;
+ }
+ return err;
+ }
+
+ dev_dbg(rdev_get_dev(info->rdev),
+ "regulator-%s-probed\n", info->desc.name);
+ }
+
+ return 0;
+}
+
+static int __exit db5500_regulator_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) {
+ struct dbx500_regulator_info *info;
+ info = &dbx500_regulator_info[i];
+
+ dev_vdbg(rdev_get_dev(info->rdev),
+ "regulator-%s-remove\n", info->desc.name);
+
+ regulator_unregister(info->rdev);
+ }
+
+ return 0;
+}
+
+static struct platform_driver db5500_regulator_driver = {
+ .driver = {
+ .name = "db5500-prcmu-regulators",
+ .owner = THIS_MODULE,
+ },
+ .probe = db5500_regulator_probe,
+ .remove = __exit_p(db5500_regulator_remove),
+};
+
+static int __init db5500_regulator_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&db5500_regulator_driver);
+ if (ret < 0)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void __exit db5500_regulator_exit(void)
+{
+ platform_driver_unregister(&db5500_regulator_driver);
+}
+
+arch_initcall(db5500_regulator_init);
+module_exit(db5500_regulator_exit);
+
+MODULE_AUTHOR("STMicroelectronics/ST-Ericsson");
+MODULE_DESCRIPTION("DB5500 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c
index f2e5ecdc586..bee4f7be93b 100644
--- a/drivers/regulator/dbx500-prcmu.c
+++ b/drivers/regulator/dbx500-prcmu.c
@@ -9,6 +9,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/err.h>
#include <linux/regulator/driver.h>
#include <linux/debugfs.h>
@@ -62,12 +63,105 @@ out:
return ret;
}
+struct ux500_regulator {
+ char *name;
+ void (*enable)(void);
+ int (*disable)(void);
+ int count;
+};
+
+static struct ux500_regulator ux500_atomic_regulators[] = {
+ {
+ .name = "dma40.0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "ssp0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "ssp1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi2",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi3",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "cryp1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "hash1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+};
+
+struct ux500_regulator *__must_check ux500_regulator_get(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ux500_atomic_regulators); i++) {
+ if (!strcmp(dev_name(dev), ux500_atomic_regulators[i].name))
+ return &ux500_atomic_regulators[i];
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_get);
+
+int ux500_regulator_atomic_enable(struct ux500_regulator *regulator)
+{
+ if (regulator) {
+ regulator->count++;
+ regulator->enable();
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_atomic_enable);
+
+int ux500_regulator_atomic_disable(struct ux500_regulator *regulator)
+{
+ if (regulator) {
+ regulator->count--;
+ return regulator->disable();
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_atomic_disable);
+
+void ux500_regulator_put(struct ux500_regulator *regulator)
+{
+ /* Here for symetric reasons and for possible future use */
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_put);
+
#ifdef CONFIG_REGULATOR_DEBUG
static struct ux500_regulator_debug {
struct dentry *dir;
- struct dentry *status_file;
- struct dentry *power_state_cnt_file;
struct dbx500_regulator_info *regulator_array;
int num_regulators;
u8 *state_before_suspend;
@@ -119,6 +213,35 @@ static const struct file_operations ux500_regulator_power_state_cnt_fops = {
.owner = THIS_MODULE,
};
+static int ux500_regulator_power_state_use_print(struct seq_file *s, void *p)
+{
+ int i;
+
+ seq_printf(s, "\nPower state usage:\n\n");
+
+ for (i = 0; i < ARRAY_SIZE(ux500_atomic_regulators); i++) {
+ seq_printf(s, "%s\t : %d\n",
+ ux500_atomic_regulators[i].name,
+ ux500_atomic_regulators[i].count);
+ }
+ return 0;
+}
+
+static int ux500_regulator_power_state_use_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ux500_regulator_power_state_use_print,
+ inode->i_private);
+}
+
+static const struct file_operations ux500_regulator_power_state_use_fops = {
+ .open = ux500_regulator_power_state_use_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static int ux500_regulator_status_print(struct seq_file *s, void *p)
{
struct device *dev = s->private;
@@ -180,22 +303,27 @@ ux500_regulator_debug_init(struct platform_device *pdev,
{
/* create directory */
rdebug.dir = debugfs_create_dir("ux500-regulator", NULL);
- if (!rdebug.dir)
+ if (IS_ERR_OR_NULL(rdebug.dir))
goto exit_no_debugfs;
/* create "status" file */
- rdebug.status_file = debugfs_create_file("status",
- S_IRUGO, rdebug.dir, &pdev->dev,
- &ux500_regulator_status_fops);
- if (!rdebug.status_file)
- goto exit_destroy_dir;
+ if (IS_ERR_OR_NULL(debugfs_create_file("status",
+ S_IRUGO, rdebug.dir, &pdev->dev,
+ &ux500_regulator_status_fops)))
+ goto exit_fail;
+
+ /* create "power-state-count" file */
+ if (IS_ERR_OR_NULL(debugfs_create_file("power-state-count",
+ S_IRUGO, rdebug.dir, &pdev->dev,
+ &ux500_regulator_power_state_cnt_fops)))
+ goto exit_fail;
/* create "power-state-count" file */
- rdebug.power_state_cnt_file = debugfs_create_file("power-state-count",
- S_IRUGO, rdebug.dir, &pdev->dev,
- &ux500_regulator_power_state_cnt_fops);
- if (!rdebug.power_state_cnt_file)
- goto exit_destroy_status;
+ if (IS_ERR_OR_NULL(debugfs_create_file("power-state-usage",
+ S_IRUGO, rdebug.dir, &pdev->dev,
+ &ux500_regulator_power_state_use_fops)))
+ goto exit_fail;
+
rdebug.regulator_array = regulator_info;
rdebug.num_regulators = num_regulators;
@@ -204,27 +332,22 @@ ux500_regulator_debug_init(struct platform_device *pdev,
if (!rdebug.state_before_suspend) {
dev_err(&pdev->dev,
"could not allocate memory for saving state\n");
- goto exit_destroy_power_state;
+ goto exit_fail;
}
rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL);
if (!rdebug.state_after_suspend) {
dev_err(&pdev->dev,
"could not allocate memory for saving state\n");
- goto exit_free;
+ goto exit_fail;
}
dbx500_regulator_testcase(regulator_info, num_regulators);
return 0;
-exit_free:
+exit_fail:
kfree(rdebug.state_before_suspend);
-exit_destroy_power_state:
- debugfs_remove(rdebug.power_state_cnt_file);
-exit_destroy_status:
- debugfs_remove(rdebug.status_file);
-exit_destroy_dir:
- debugfs_remove(rdebug.dir);
+ debugfs_remove_recursive(rdebug.dir);
exit_no_debugfs:
dev_err(&pdev->dev, "failed to create debugfs entries.\n");
return -ENOMEM;
diff --git a/include/linux/clksrc-db5500-mtimer.h b/include/linux/clksrc-db5500-mtimer.h
new file mode 100644
index 00000000000..3112c7f2709
--- /dev/null
+++ b/include/linux/clksrc-db5500-mtimer.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+#ifndef __CLKSRC_DB5500_MTIMER_H
+#define __CLKSRC_DB5500_MTIMER_H
+
+#include <linux/io.h>
+
+#ifdef CONFIG_CLKSRC_DB5500_MTIMER
+void db5500_mtimer_init(void __iomem *base);
+#else
+static inline void db5500_mtimer_init(void __iomem *base) {}
+#endif
+
+#endif
diff --git a/include/linux/cpufreq-dbx500.h b/include/linux/cpufreq-dbx500.h
new file mode 100644
index 00000000000..15c59c3b1c8
--- /dev/null
+++ b/include/linux/cpufreq-dbx500.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+#ifndef __CPUFREQ_DBX500_H
+#define __CPUFREQ_DBX500_H
+
+#include <linux/cpufreq.h>
+
+int dbx500_cpufreq_get_limits(int cpu, int r,
+ unsigned int *min, unsigned int *max);
+
+int dbx500_cpufreq_percent2freq(int percent);
+
+#endif
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index b60f6ba01d0..faf1d179032 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -200,6 +200,7 @@ extern int __cpufreq_driver_getavg(struct cpufreq_policy *policy,
int cpufreq_register_governor(struct cpufreq_governor *governor);
void cpufreq_unregister_governor(struct cpufreq_governor *governor);
+int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max);
/*********************************************************************
* CPUFREQ DRIVER INTERFACE *
@@ -337,6 +338,7 @@ static inline unsigned int cpufreq_quick_get_max(unsigned int cpu)
}
#endif
+int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max);
/*********************************************************************
* CPUFREQ DEFAULT GOVERNOR *
diff --git a/include/linux/mfd/db5500-prcmu.h b/include/linux/mfd/db5500-prcmu.h
index 5a049dfaf15..681c8f99bf1 100644
--- a/include/linux/mfd/db5500-prcmu.h
+++ b/include/linux/mfd/db5500-prcmu.h
@@ -8,42 +8,169 @@
#ifndef __MFD_DB5500_PRCMU_H
#define __MFD_DB5500_PRCMU_H
-static inline int prcmu_resetout(u8 resoutn, u8 state)
+#ifdef CONFIG_MFD_DB5500_PRCMU
+
+void db5500_prcmu_early_init(void);
+int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state);
+int db5500_prcmu_set_display_clocks(void);
+int db5500_prcmu_disable_dsipll(void);
+int db5500_prcmu_enable_dsipll(void);
+int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
+int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
+void db5500_prcmu_enable_wakeups(u32 wakeups);
+int db5500_prcmu_request_clock(u8 clock, bool enable);
+void db5500_prcmu_config_abb_event_readout(u32 abb_events);
+void db5500_prcmu_get_abb_event_buffer(void __iomem **buf);
+int prcmu_resetout(u8 resoutn, u8 state);
+int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk,
+ bool keep_ap_pll);
+u8 db5500_prcmu_get_power_state_result(void);
+int db5500_prcmu_config_esram0_deep_sleep(u8 state);
+void db5500_prcmu_system_reset(u16 reset_code);
+u16 db5500_prcmu_get_reset_code(void);
+#ifdef CONFIG_UX500_SOC_DB5500
+void prcmu_modem_req(void);
+void prcmu_modem_rel(void);
+void prcmu_ape_ack(void);
+#endif
+bool db5500_prcmu_is_modem_requested(void);
+void db5500_prcmu_modem_reset(void);
+int db5500_prcmu_set_arm_opp(u8 opp);
+int db5500_prcmu_get_arm_opp(void);
+int db5500_prcmu_set_ape_opp(u8 opp);
+int db5500_prcmu_get_ape_opp(void);
+int db5500_prcmu_set_ddr_opp(u8 opp);
+int db5500_prcmu_get_ddr_opp(void);
+
+u32 db5500_prcmu_read(unsigned int reg);
+void db5500_prcmu_write(unsigned int reg, u32 value);
+void db5500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value);
+
+static inline unsigned long prcmu_clock_rate(u8 clock)
{
return 0;
}
-static inline int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state)
+static inline long prcmu_round_clock_rate(u8 clock, unsigned long rate)
+{
+ return 0;
+}
+
+static inline int prcmu_set_clock_rate(u8 clock, unsigned long rate)
{
return 0;
}
+int db5500_prcmu_get_hotdog(void);
+int db5500_prcmu_config_hotdog(u8 threshold);
+int db5500_prcmu_config_hotmon(u8 low, u8 high);
+int db5500_prcmu_start_temp_sense(u16 cycles32k);
+int db5500_prcmu_stop_temp_sense(void);
+
+int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off);
+int db5500_prcmu_enable_a9wdog(u8 id);
+int db5500_prcmu_disable_a9wdog(u8 id);
+int db5500_prcmu_kick_a9wdog(u8 id);
+int db5500_prcmu_load_a9wdog(u8 id, u32 timeout);
+
+#else /* !CONFIG_UX500_SOC_DB5500 */
+
+static inline void db5500_prcmu_early_init(void) {}
+
+static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ return -ENOSYS;
+}
+
+static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ return -ENOSYS;
+}
static inline int db5500_prcmu_request_clock(u8 clock, bool enable)
{
return 0;
}
+static inline unsigned long db5500_prcmu_clock_rate(u8 clock)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_set_display_clocks(void)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_disable_dsipll(void)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_enable_dsipll(void)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_config_esram0_deep_sleep(u8 state)
+{
+ return 0;
+}
+
+static inline void db5500_prcmu_enable_wakeups(u32 wakeups) {}
+
+static inline long db5500_prcmu_round_clock_rate(u8 clock, unsigned long rate)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_set_clock_rate(u8 clock, unsigned long rate)
+{
+ return 0;
+}
+
+static inline int prcmu_resetout(u8 resoutn, u8 state)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state)
+{
+ return 0;
+}
+
+static inline void db5500_prcmu_get_abb_event_buffer(void __iomem **buf) {}
+static inline void db5500_prcmu_config_abb_event_readout(u32 abb_events) {}
+
static inline int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk,
bool keep_ap_pll)
{
return 0;
}
-static inline int db5500_prcmu_config_esram0_deep_sleep(u8 state)
+static inline u8 db5500_prcmu_get_power_state_result(void)
{
return 0;
}
+static inline void db5500_prcmu_system_reset(u16 reset_code) {}
+
static inline u16 db5500_prcmu_get_reset_code(void)
{
return 0;
}
-static inline bool db5500_prcmu_is_ac_wake_requested(void)
+static inline void db5500_prcmu_modem_reset(void) {}
+static inline bool db5500_prcmu_is_modem_requested(void)
{
return 0;
}
+#ifdef CONFIG_UX500_SOC_DB5500
+static void prcmu_ape_ack(void) {}
+static void prcmu_modem_req(void) {}
+static void prcmu_modem_rel(void) {}
+#endif
+
static inline int db5500_prcmu_set_arm_opp(u8 opp)
{
return 0;
@@ -54,48 +181,80 @@ static inline int db5500_prcmu_get_arm_opp(void)
return 0;
}
-static inline void db5500_prcmu_config_abb_event_readout(u32 abb_events) {}
-
-static inline void db5500_prcmu_get_abb_event_buffer(void __iomem **buf) {}
+static inline int db5500_prcmu_set_ape_opp(u8 opp)
+{
+ return 0;
+}
-static inline void db5500_prcmu_system_reset(u16 reset_code) {}
+static inline int db5500_prcmu_get_ape_opp(void)
+{
+ return 0;
+}
-static inline void db5500_prcmu_enable_wakeups(u32 wakeups) {}
+static inline int db5500_prcmu_set_ddr_opp(u8 opp)
+{
+ return 0;
+}
-#ifdef CONFIG_MFD_DB5500_PRCMU
+static inline int db5500_prcmu_get_ddr_opp(void)
+{
+ return 0;
+}
-void db5500_prcmu_early_init(void);
-int db5500_prcmu_set_display_clocks(void);
-int db5500_prcmu_disable_dsipll(void);
-int db5500_prcmu_enable_dsipll(void);
-int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
-int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
+static inline u32 db5500_prcmu_read(unsigned int reg)
+{
+ return 0;
+}
-#else /* !CONFIG_UX500_SOC_DB5500 */
+static inline void db5500_prcmu_write(unsigned int reg, u32 value) {}
-static inline void db5500_prcmu_early_init(void) {}
+static inline void db5500_prcmu_write_masked(unsigned int reg, u32 mask,
+ u32 value) {}
-static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+static inline int db5500_prcmu_get_hotdog(void)
{
return -ENOSYS;
}
+static inline int db5500_prcmu_config_hotdog(u8 threshold)
+{
+ return 0;
+}
-static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+static inline int db5500_prcmu_config_hotmon(u8 low, u8 high)
{
- return -ENOSYS;
+ return 0;
}
-static inline int db5500_prcmu_set_display_clocks(void)
+static inline int db5500_prcmu_start_temp_sense(u16 cycles32k)
+{
+ return 0;
+}
+static inline int db5500_prcmu_stop_temp_sense(void)
{
return 0;
}
-static inline int db5500_prcmu_disable_dsipll(void)
+static inline int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
{
return 0;
}
-static inline int db5500_prcmu_enable_dsipll(void)
+static inline int db5500_prcmu_enable_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_disable_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_kick_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_load_a9wdog(u8 id, u32 timeout)
{
return 0;
}
diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h
index b3a43b1263f..705af54b89f 100644
--- a/include/linux/mfd/db8500-prcmu.h
+++ b/include/linux/mfd/db8500-prcmu.h
@@ -30,6 +30,11 @@
#define DB8500_PRCM_DSI_SW_RESET_DSI1_SW_RESETN BIT(1)
#define DB8500_PRCM_DSI_SW_RESET_DSI2_SW_RESETN BIT(2)
+#define DB8500_PRCM_DSI_GLITCHFREE_EN 0x534
+#define DB8500_PRCM_DSI_GLITCHFREE_EN_DSI0_BYTE_CLK BIT(0)
+#define DB8500_PRCM_DSI_GLITCHFREE_EN_DSI1_BYTE_CLK BIT(8)
+#define DB8500_PRCM_DSI_GLITCHFREE_EN_DSI2_BYTE_CLK BIT(16)
+
/* This portion previously known as <mach/prcmu-fw-defs_v1.h> */
/**
@@ -181,84 +186,6 @@ enum hw_acc_state {
};
/**
- * enum mbox_2_arm_stat - Status messages definition for mbox_arm
- * @BOOT_TO_EXECUTEOK: The apBoot to apExecute state transition has been
- * completed
- * @DEEPSLEEPOK: The apExecute to apDeepSleep state transition has been
- * completed
- * @SLEEPOK: The apExecute to apSleep state transition has been completed
- * @IDLEOK: The apExecute to apIdle state transition has been completed
- * @SOFTRESETOK: The A9 watchdog/ SoftReset state has been completed
- * @SOFTRESETGO : The A9 watchdog/SoftReset state is on going
- * @BOOT_TO_EXECUTE: The apBoot to apExecute state transition is on going
- * @EXECUTE_TO_DEEPSLEEP: The apExecute to apDeepSleep state transition is on
- * going
- * @DEEPSLEEP_TO_EXECUTE: The apDeepSleep to apExecute state transition is on
- * going
- * @DEEPSLEEP_TO_EXECUTEOK: The apDeepSleep to apExecute state transition has
- * been completed
- * @EXECUTE_TO_SLEEP: The apExecute to apSleep state transition is on going
- * @SLEEP_TO_EXECUTE: The apSleep to apExecute state transition is on going
- * @SLEEP_TO_EXECUTEOK: The apSleep to apExecute state transition has been
- * completed
- * @EXECUTE_TO_IDLE: The apExecute to apIdle state transition is on going
- * @IDLE_TO_EXECUTE: The apIdle to apExecute state transition is on going
- * @IDLE_TO_EXECUTEOK: The apIdle to apExecute state transition has been
- * completed
- * @INIT_STATUS: Status init
- */
-enum ap_pwrsttr_status {
- BOOT_TO_EXECUTEOK = 0xFF,
- DEEPSLEEPOK = 0xFE,
- SLEEPOK = 0xFD,
- IDLEOK = 0xFC,
- SOFTRESETOK = 0xFB,
- SOFTRESETGO = 0xFA,
- BOOT_TO_EXECUTE = 0xF9,
- EXECUTE_TO_DEEPSLEEP = 0xF8,
- DEEPSLEEP_TO_EXECUTE = 0xF7,
- DEEPSLEEP_TO_EXECUTEOK = 0xF6,
- EXECUTE_TO_SLEEP = 0xF5,
- SLEEP_TO_EXECUTE = 0xF4,
- SLEEP_TO_EXECUTEOK = 0xF3,
- EXECUTE_TO_IDLE = 0xF2,
- IDLE_TO_EXECUTE = 0xF1,
- IDLE_TO_EXECUTEOK = 0xF0,
- RDYTODS_RETURNTOEXE = 0xEF,
- NORDYTODS_RETURNTOEXE = 0xEE,
- EXETOSLEEP_RETURNTOEXE = 0xED,
- EXETOIDLE_RETURNTOEXE = 0xEC,
- INIT_STATUS = 0xEB,
-
- /*error messages */
- INITERROR = 0x00,
- PLLARMLOCKP_ER = 0x01,
- PLLDDRLOCKP_ER = 0x02,
- PLLSOCLOCKP_ER = 0x03,
- PLLSOCK1LOCKP_ER = 0x04,
- ARMWFI_ER = 0x05,
- SYSCLKOK_ER = 0x06,
- I2C_NACK_DATA_ER = 0x07,
- BOOT_ER = 0x08,
- I2C_STATUS_ALWAYS_1 = 0x0A,
- I2C_NACK_REG_ADDR_ER = 0x0B,
- I2C_NACK_DATA0123_ER = 0x1B,
- I2C_NACK_ADDR_ER = 0x1F,
- CURAPPWRSTISNOT_BOOT = 0x20,
- CURAPPWRSTISNOT_EXECUTE = 0x21,
- CURAPPWRSTISNOT_SLEEPMODE = 0x22,
- CURAPPWRSTISNOT_CORRECTFORIT10 = 0x23,
- FIFO4500WUISNOT_WUPEVENT = 0x24,
- PLL32KLOCKP_ER = 0x29,
- DDRDEEPSLEEPOK_ER = 0x2A,
- ROMCODEREADY_ER = 0x50,
- WUPBEFOREDS = 0x51,
- DDRCONFIG_ER = 0x52,
- WUPBEFORESLEEP = 0x53,
- WUPBEFOREIDLE = 0x54
-}; /* earlier called as mbox_2_arm_stat */
-
-/**
* enum dvfs_stat - DVFS status messages definition
* @DVFS_GO: A state transition DVFS is on going
* @DVFS_ARM100OPPOK: The state transition DVFS has been completed for 100OPP
@@ -559,6 +486,7 @@ void db8500_prcmu_config_abb_event_readout(u32 abb_events);
void db8500_prcmu_get_abb_event_buffer(void __iomem **buf);
int db8500_prcmu_config_esram0_deep_sleep(u8 state);
u16 db8500_prcmu_get_reset_code(void);
+u32 db8500_prcmu_get_reset_status(void);
bool db8500_prcmu_is_ac_wake_requested(void);
int db8500_prcmu_set_arm_opp(u8 opp);
int db8500_prcmu_get_arm_opp(void);
@@ -571,6 +499,8 @@ u32 db8500_prcmu_read(unsigned int reg);
void db8500_prcmu_write(unsigned int reg, u32 value);
void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value);
+void db8500_prcmu_vc(bool enable);
+
#else /* !CONFIG_MFD_DB8500_PRCMU */
static inline void db8500_prcmu_early_init(void) {}
@@ -740,6 +670,11 @@ static inline u16 db8500_prcmu_get_reset_code(void)
return 0;
}
+static inline u32 db8500_prcmu_get_reset_status(void)
+{
+ return 0;
+}
+
static inline int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
{
return 0;
@@ -790,6 +725,8 @@ static inline void db8500_prcmu_write(unsigned int reg, u32 value) {}
static inline void db8500_prcmu_write_masked(unsigned int reg, u32 mask,
u32 value) {}
+void db8500_prcmu_vc(bool enable) {}
+
#endif /* !CONFIG_MFD_DB8500_PRCMU */
#endif /* __MFD_DB8500_PRCMU_H */
diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h
index d7674eb7305..818209b84c0 100644
--- a/include/linux/mfd/dbx500-prcmu.h
+++ b/include/linux/mfd/dbx500-prcmu.h
@@ -276,7 +276,7 @@ static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk,
static inline u8 prcmu_get_power_state_result(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_get_power_state_result();
else
return db8500_prcmu_get_power_state_result();
}
@@ -332,7 +332,7 @@ static inline bool prcmu_pending_irq(void)
static inline int prcmu_set_epod(u16 epod_id, u8 epod_state)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_set_epod(epod_id, epod_state);
else
return db8500_prcmu_set_epod(epod_id, epod_state);
}
@@ -387,14 +387,14 @@ int prcmu_set_clock_rate(u8 clock, unsigned long rate);
static inline int prcmu_set_ddr_opp(u8 opp)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_set_ddr_opp(opp);
else
return db8500_prcmu_set_ddr_opp(opp);
}
static inline int prcmu_get_ddr_opp(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_get_ddr_opp();
else
return db8500_prcmu_get_ddr_opp();
}
@@ -402,7 +402,7 @@ static inline int prcmu_get_ddr_opp(void)
static inline int prcmu_set_arm_opp(u8 opp)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_set_arm_opp(opp);
else
return db8500_prcmu_set_arm_opp(opp);
}
@@ -410,7 +410,7 @@ static inline int prcmu_set_arm_opp(u8 opp)
static inline int prcmu_get_arm_opp(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_get_arm_opp();
else
return db8500_prcmu_get_arm_opp();
}
@@ -418,7 +418,7 @@ static inline int prcmu_get_arm_opp(void)
static inline int prcmu_set_ape_opp(u8 opp)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_set_ape_opp(opp);
else
return db8500_prcmu_set_ape_opp(opp);
}
@@ -426,7 +426,7 @@ static inline int prcmu_set_ape_opp(u8 opp)
static inline int prcmu_get_ape_opp(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_get_ape_opp();
else
return db8500_prcmu_get_ape_opp();
}
@@ -447,12 +447,20 @@ static inline u16 prcmu_get_reset_code(void)
return db8500_prcmu_get_reset_code();
}
+static inline u32 prcmu_get_reset_status(void)
+{
+ if (cpu_is_u8500())
+ return db8500_prcmu_get_reset_status();
+ else
+ return 0;
+}
+
void prcmu_ac_wake_req(void);
void prcmu_ac_sleep_req(void);
static inline void prcmu_modem_reset(void)
{
if (cpu_is_u5500())
- return;
+ return db5500_prcmu_modem_reset();
else
return db8500_prcmu_modem_reset();
}
@@ -460,7 +468,7 @@ static inline void prcmu_modem_reset(void)
static inline bool prcmu_is_ac_wake_requested(void)
{
if (cpu_is_u5500())
- return db5500_prcmu_is_ac_wake_requested();
+ return db5500_prcmu_is_modem_requested();
else
return db8500_prcmu_is_ac_wake_requested();
}
@@ -492,7 +500,7 @@ static inline int prcmu_enable_dsipll(void)
static inline int prcmu_config_esram0_deep_sleep(u8 state)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_config_esram0_deep_sleep(state);
else
return db8500_prcmu_config_esram0_deep_sleep(state);
}
@@ -500,7 +508,7 @@ static inline int prcmu_config_esram0_deep_sleep(u8 state)
static inline int prcmu_config_hotdog(u8 threshold)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_config_hotdog(threshold);
else
return db8500_prcmu_config_hotdog(threshold);
}
@@ -508,7 +516,7 @@ static inline int prcmu_config_hotdog(u8 threshold)
static inline int prcmu_config_hotmon(u8 low, u8 high)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_config_hotmon(low, high);
else
return db8500_prcmu_config_hotmon(low, high);
}
@@ -516,7 +524,7 @@ static inline int prcmu_config_hotmon(u8 low, u8 high)
static inline int prcmu_start_temp_sense(u16 cycles32k)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_start_temp_sense(cycles32k);
else
return db8500_prcmu_start_temp_sense(cycles32k);
}
@@ -524,7 +532,7 @@ static inline int prcmu_start_temp_sense(u16 cycles32k)
static inline int prcmu_stop_temp_sense(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_stop_temp_sense();
else
return db8500_prcmu_stop_temp_sense();
}
@@ -532,7 +540,7 @@ static inline int prcmu_stop_temp_sense(void)
static inline u32 prcmu_read(unsigned int reg)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_read(reg);
else
return db8500_prcmu_read(reg);
}
@@ -540,7 +548,7 @@ static inline u32 prcmu_read(unsigned int reg)
static inline void prcmu_write(unsigned int reg, u32 value)
{
if (cpu_is_u5500())
- return;
+ db5500_prcmu_write(reg, value);
else
db8500_prcmu_write(reg, value);
}
@@ -548,7 +556,7 @@ static inline void prcmu_write(unsigned int reg, u32 value)
static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
{
if (cpu_is_u5500())
- return;
+ db5500_prcmu_write_masked(reg, mask, value);
else
db8500_prcmu_write_masked(reg, mask, value);
}
@@ -556,7 +564,7 @@ static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
static inline int prcmu_enable_a9wdog(u8 id)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_enable_a9wdog(id);
else
return db8500_prcmu_enable_a9wdog(id);
}
@@ -564,7 +572,7 @@ static inline int prcmu_enable_a9wdog(u8 id)
static inline int prcmu_disable_a9wdog(u8 id)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_disable_a9wdog(id);
else
return db8500_prcmu_disable_a9wdog(id);
}
@@ -572,7 +580,7 @@ static inline int prcmu_disable_a9wdog(u8 id)
static inline int prcmu_kick_a9wdog(u8 id)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_kick_a9wdog(id);
else
return db8500_prcmu_kick_a9wdog(id);
}
@@ -580,7 +588,7 @@ static inline int prcmu_kick_a9wdog(u8 id)
static inline int prcmu_load_a9wdog(u8 id, u32 timeout)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_load_a9wdog(id, timeout);
else
return db8500_prcmu_load_a9wdog(id, timeout);
}
@@ -588,10 +596,17 @@ static inline int prcmu_load_a9wdog(u8 id, u32 timeout)
static inline int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_config_a9wdog(num, sleep_auto_off);
else
return db8500_prcmu_config_a9wdog(num, sleep_auto_off);
}
+
+static inline void prcmu_vc(bool enable)
+{
+ if (cpu_is_u8500())
+ db8500_prcmu_vc(enable);
+}
+
#else
static inline void __init prcmu_early_init(void) {}
@@ -689,6 +704,11 @@ static inline u16 prcmu_get_reset_code(void)
return 0;
}
+static inline u32 prcmu_get_reset_status(void)
+{
+ return 0;
+}
+
static inline void prcmu_ac_wake_req(void) {}
static inline void prcmu_ac_sleep_req(void) {}
@@ -756,6 +776,8 @@ static inline void prcmu_write(unsigned int reg, u32 value) {}
static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value) {}
+static inline void prcmu_vc(bool enable) {}
+
#endif
static inline void prcmu_set(unsigned int reg, u32 bits)
@@ -866,6 +888,7 @@ int prcmu_qos_add_notifier(int prcmu_qos_class,
struct notifier_block *notifier);
int prcmu_qos_remove_notifier(int prcmu_qos_class,
struct notifier_block *notifier);
+void prcmu_qos_voice_call_override(bool enable);
#else
@@ -909,7 +932,7 @@ static inline int prcmu_qos_remove_notifier(int prcmu_qos_class,
{
return 0;
}
-
+static inline void prcmu_qos_voice_call_override(bool enable) {}
#endif
#endif /* __MACH_PRCMU_H */
diff --git a/include/linux/regulator/ab5500.h b/include/linux/regulator/ab5500.h
new file mode 100644
index 00000000000..04673d3494f
--- /dev/null
+++ b/include/linux/regulator/ab5500.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __LINUX_REGULATOR_AB5500_H
+#define __LINUX_REGULATOR_AB5500_H
+
+enum ab5500_regulator_id {
+ AB5500_LDO_G,
+ AB5500_LDO_H,
+ AB5500_LDO_K,
+ AB5500_LDO_L,
+ AB5500_LDO_VDIGMIC,
+ AB5500_LDO_SIM,
+ AB5500_BIAS1,
+ AB5500_BIAS2,
+ AB5500_NUM_REGULATORS,
+};
+
+struct regulator_init_data;
+
+struct ab5500_regulator_data {
+ bool off_is_lowpower;
+};
+
+struct ab5500_regulator_platform_data {
+ struct regulator_init_data *regulator;
+ struct ab5500_regulator_data *data;
+ int num_regulator;
+};
+
+#endif
diff --git a/include/linux/regulator/ab8500-debug.h b/include/linux/regulator/ab8500-debug.h
new file mode 100644
index 00000000000..01655fc7fc1
--- /dev/null
+++ b/include/linux/regulator/ab8500-debug.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ */
+
+#ifndef __LINUX_MFD_AB8500_REGULATOR_DEBUG_H
+#define __LINUX_MFD_AB8500_REGULATOR_DEBUG_H
+
+#ifdef CONFIG_REGULATOR_AB8500_DEBUG
+/* AB8500 debug force/restore functions */
+void ab8500_regulator_debug_force(void);
+void ab8500_regulator_debug_restore(void);
+#else
+static inline void ab8500_regulator_debug_force(void) {}
+static inline void ab8500_regulator_debug_restore(void) {}
+#endif
+
+#endif
diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h
index 7bd73bbdfd1..9f50d640ead 100644
--- a/include/linux/regulator/ab8500.h
+++ b/include/linux/regulator/ab8500.h
@@ -10,6 +10,8 @@
#ifndef __LINUX_MFD_AB8500_REGULATOR_H
#define __LINUX_MFD_AB8500_REGULATOR_H
+#include <linux/platform_device.h>
+
/* AB8500 regulators */
enum ab8500_regulator_id {
AB8500_LDO_AUX1,
@@ -17,12 +19,13 @@ enum ab8500_regulator_id {
AB8500_LDO_AUX3,
AB8500_LDO_INTCORE,
AB8500_LDO_TVOUT,
- AB8500_LDO_USB,
AB8500_LDO_AUDIO,
AB8500_LDO_ANAMIC1,
AB8500_LDO_ANAMIC2,
AB8500_LDO_DMIC,
AB8500_LDO_ANA,
+ AB8500_SYSCLKREQ_2,
+ AB8500_SYSCLKREQ_4,
AB8500_NUM_REGULATORS,
};
@@ -48,13 +51,15 @@ enum ab9540_regulator_id {
/* AB8500 and AB9540 register initialization */
struct ab8500_regulator_reg_init {
int id;
+ u8 mask;
u8 value;
};
-#define INIT_REGULATOR_REGISTER(_id, _value) \
- { \
- .id = _id, \
- .value = _value, \
+#define INIT_REGULATOR_REGISTER(_id, _mask, _value) \
+ { \
+ .id = _id, \
+ .mask = _mask, \
+ .value = _value, \
}
/* AB8500 registers */
@@ -86,7 +91,6 @@ enum ab8500_regulator_reg {
AB8500_REGUCTRL2SPARE,
AB8500_REGUCTRLDISCH,
AB8500_REGUCTRLDISCH2,
- AB8500_VSMPS1SEL1,
AB8500_NUM_REGULATOR_REGISTERS,
};
@@ -139,4 +143,35 @@ enum ab9540_regulator_reg {
AB9540_NUM_REGULATOR_REGISTERS,
};
+/* AB8500 external regulators */
+struct ab8500_ext_regulator_cfg {
+ bool hwreq; /* requires hw mode or high power mode */
+};
+
+enum ab8500_ext_regulator_id {
+ AB8500_EXT_SUPPLY1,
+ AB8500_EXT_SUPPLY2,
+ AB8500_EXT_SUPPLY3,
+ AB8500_NUM_EXT_REGULATORS,
+};
+
+/* AB8500 regulator platform data */
+struct ab8500_regulator_platform_data {
+ int num_reg_init;
+ struct ab8500_regulator_reg_init *reg_init;
+ int num_regulator;
+ struct regulator_init_data *regulator;
+ int num_ext_regulator;
+ struct regulator_init_data *ext_regulator;
+};
+
+/* AB8500 external regulator functions (internal) */
+#ifdef CONFIG_REGULATOR_AB8500_EXT
+__devinit int ab8500_ext_regulator_init(struct platform_device *pdev);
+__devexit int ab8500_ext_regulator_exit(struct platform_device *pdev);
+#else
+inline __devinit int ab8500_ext_regulator_init(struct platform_device *pdev) {}
+inline __devexit int ab8500_ext_regulator_exit(struct platform_device *pdev) {}
+#endif
+
#endif
diff --git a/include/linux/regulator/db5500-prcmu.h b/include/linux/regulator/db5500-prcmu.h
new file mode 100644
index 00000000000..fee68795867
--- /dev/null
+++ b/include/linux/regulator/db5500-prcmu.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * Interface to power domain regulators on DB5500
+ */
+
+#ifndef __DB5500_REGULATOR_H__
+#define __DB5500_REGULATOR_H__
+
+#include <linux/regulator/dbx500-prcmu.h>
+
+/* Number of DB5500 regulators and regulator enumeration */
+enum db5500_regulator_id {
+ DB5500_REGULATOR_VAPE,
+ DB5500_REGULATOR_SWITCH_SGA,
+ DB5500_REGULATOR_SWITCH_HVA,
+ DB5500_REGULATOR_SWITCH_SIA,
+ DB5500_REGULATOR_SWITCH_DISP,
+ DB5500_REGULATOR_SWITCH_ESRAM12,
+ DB5500_NUM_REGULATORS
+};
+
+#endif
diff --git a/include/linux/regulator/dbx500-prcmu.h b/include/linux/regulator/dbx500-prcmu.h
new file mode 100644
index 00000000000..2ecb34c56aa
--- /dev/null
+++ b/include/linux/regulator/dbx500-prcmu.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) ST Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ */
+#ifndef __LINUX_REGULATOR_DBX500_H
+#define __LINUX_REGULATOR_DBX500_H
+
+struct ux500_regulator;
+
+#ifdef CONFIG_REGULATOR
+/*
+ * NOTE! The device will be connected to the correct regulator by this
+ * new framework. A list with connections will match up dev_name(dev)
+ * to the specific regulator. This follows the same principle as the
+ * normal regulator framework.
+ *
+ * This framework shall only be used in special cases when a regulator
+ * has to be enabled/disabled in atomic context.
+ */
+
+/**
+ * ux500_regulator_get()
+ *
+ * @dev: Drivers device struct
+ *
+ * Returns a ux500_regulator struct. Shall be used as argument for
+ * ux500_regulator_atomic_enable/disable calls.
+ * Return ERR_PTR(-EINVAL) upon no matching regulator found.
+ */
+struct ux500_regulator *__must_check ux500_regulator_get(struct device *dev);
+
+/**
+ * ux500_regulator_atomic_enable()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ *
+ * The enable/disable functions keep an internal counter, so every
+ * enable must be paired with an disable in order to turn off regulator.
+ */
+int ux500_regulator_atomic_enable(struct ux500_regulator *regulator);
+
+/**
+ * ux500_regulator_atomic_disable()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ *
+ */
+int ux500_regulator_atomic_disable(struct ux500_regulator *regulator);
+
+/**
+ * ux500_regulator_put()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ */
+void ux500_regulator_put(struct ux500_regulator *regulator);
+
+#else
+
+static inline struct ux500_regulator *__must_check
+ux500_regulator_get(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int
+ux500_regulator_atomic_enable(struct ux500_regulator *regulator)
+{
+ return -EINVAL;
+}
+
+static inline int
+ux500_regulator_atomic_disable(struct ux500_regulator *regulator)
+{
+ return -EINVAL;
+}
+
+static inline void ux500_regulator_put(struct ux500_regulator *regulator)
+{
+}
+#endif /* CONFIG_REGULATOR */
+
+#ifdef CONFIG_REGULATOR_DEBUG
+void ux500_regulator_suspend_debug(void);
+void ux500_regulator_resume_debug(void);
+#else
+static inline void ux500_regulator_suspend_debug(void) { }
+static inline void ux500_regulator_resume_debug(void) { }
+#endif
+
+#endif
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 396d262b8fd..e53c663a5e9 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/suspend.h>
+#include <linux/kthread.h>
#include <linux/syscore_ops.h>
#include <trace/events/power.h>
@@ -35,6 +36,10 @@ const char *const pm_states[PM_SUSPEND_MAX] = {
static const struct platform_suspend_ops *suspend_ops;
+static struct completion second_cpu_complete = {1,
+ __WAIT_QUEUE_HEAD_INITIALIZER((second_cpu_complete).wait)
+};
+
/**
* suspend_set_ops - Set the global suspend method table.
* @ops: Suspend operations to use.
@@ -157,9 +162,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
if (suspend_test(TEST_PLATFORM))
goto Platform_wake;
+
error = disable_nonboot_cpus();
if (error || suspend_test(TEST_CPUS))
- goto Enable_cpus;
+ goto Platform_wake;
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
@@ -177,9 +183,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
- Enable_cpus:
- enable_nonboot_cpus();
-
Platform_wake:
if (suspend_ops->wake)
suspend_ops->wake();
@@ -257,6 +260,18 @@ static void suspend_finish(void)
pm_restore_console();
}
+static int plug_secondary_cpus(void *data)
+{
+ if (!(suspend_test(TEST_FREEZER) ||
+ suspend_test(TEST_DEVICES) ||
+ suspend_test(TEST_PLATFORM)))
+ enable_nonboot_cpus();
+
+ complete(&second_cpu_complete);
+
+ return 0;
+}
+
/**
* enter_state - Do common work needed to enter system sleep state.
* @state: System sleep state to enter.
@@ -268,6 +283,7 @@ static void suspend_finish(void)
static int enter_state(suspend_state_t state)
{
int error;
+ struct task_struct *cpu_task;
if (!valid_state(state))
return -ENODEV;
@@ -275,6 +291,14 @@ static int enter_state(suspend_state_t state)
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
+ /*
+ * Assure that previous started thread is completed before
+ * attempting to suspend again.
+ */
+ error = wait_for_completion_timeout(&second_cpu_complete,
+ msecs_to_jiffies(500));
+ WARN_ON(error == 0);
+
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.\n");
@@ -296,6 +320,11 @@ static int enter_state(suspend_state_t state)
pr_debug("PM: Finishing wakeup.\n");
suspend_finish();
Unlock:
+
+ cpu_task = kthread_run(plug_secondary_cpus,
+ NULL, "cpu-plug");
+ BUG_ON(IS_ERR(cpu_task));
+
mutex_unlock(&pm_mutex);
return error;
}