Commit b44a3d2a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC driver updates from Olof Johansson:
 "As we've enabled multiplatform kernels on ARM, and greatly done away
  with the contents under arch/arm/mach-*, there's still need for
  SoC-related drivers to go somewhere.

  Many of them go in through other driver trees, but we still have
  drivers/soc to hold some of the "doesn't fit anywhere" lowlevel code
  that might be shared between ARM and ARM64 (or just in general makes
  sense to not have under the architecture directory).

  This branch contains mostly such code:

   - Drivers for qualcomm SoCs for SMEM, SMD and SMD-RPM, used to
     communicate with power management blocks on these SoCs for use by
     clock, regulator and bus frequency drivers.

   - Allwinner Reduced Serial Bus driver, again used to communicate with
     PMICs.

   - Drivers for ARM's SCPI (System Control Processor).  Not to be
     confused with PSCI (Power State Coordination Interface).  SCPI is
     used to communicate with the assistant embedded cores doing power
     management, and we have yet to see how many of them will implement
     this for their hardware vs abstracting in other ways (or not at all
     like in the past).

   - To make confusion between SCPI and PSCI more likely, this release
     also includes an update of PSCI to interface version 1.0.

   - Rockchip support for power domains.

   - A driver to talk to the firmware on Raspberry Pi"

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (57 commits)
  soc: qcom: smd-rpm: Correct size of outgoing message
  bus: sunxi-rsb: Add driver for Allwinner Reduced Serial Bus
  bus: sunxi-rsb: Add Allwinner Reduced Serial Bus (RSB) controller bindings
  ARM: bcm2835: add mutual inclusion protection
  drivers: psci: make PSCI 1.0 functions initialization version dependent
  dt-bindings: Correct paths in Rockchip power domains binding document
  soc: rockchip: power-domain: don't try to print the clock name in error case
  soc: qcom/smem: add HWSPINLOCK dependency
  clk: berlin: add cpuclk
  ARM: berlin: dts: add CLKID_CPU for BG2Q
  ARM: bcm2835: Add the Raspberry Pi firmware driver
  soc: qcom: smem: Move RPM message ram out of smem DT node
  soc: qcom: smd-rpm: Correct the active vs sleep state flagging
  soc: qcom: smd: delete unneeded of_node_put
  firmware: qcom-scm: build for correct architecture level
  soc: qcom: smd: Correct SMEM items for upper channels
  qcom-scm: add missing prototype for qcom_scm_is_available()
  qcom-scm: fix endianess issue in __qcom_scm_is_call_available
  soc: qcom: smd: Reject send of too big packets
  soc: qcom: smd: Handle big endian CPUs
  ...
parents 56e04649 d13a5c8c
System Control and Power Interface (SCPI) Message Protocol
----------------------------------------------------------
Firmware implementing the SCPI described in ARM document number ARM DUI 0922B
("ARM Compute Subsystem SCP: Message Interface Protocols")[0] can be used
by Linux to initiate various system control and power operations.
Required properties:
- compatible : should be "arm,scpi"
- mboxes: List of phandle and mailbox channel specifiers
All the channels reserved by remote SCP firmware for use by
SCPI message protocol should be specified in any order
- shmem : List of phandle pointing to the shared memory(SHM) area between the
processors using these mailboxes for IPC, one for each mailbox
SHM can be any memory reserved for the purpose of this communication
between the processors.
See Documentation/devicetree/bindings/mailbox/mailbox.txt
for more details about the generic mailbox controller and
client driver bindings.
Clock bindings for the clocks based on SCPI Message Protocol
------------------------------------------------------------
This binding uses the common clock binding[1].
Container Node
==============
Required properties:
- compatible : should be "arm,scpi-clocks"
All the clocks provided by SCP firmware via SCPI message
protocol much be listed as sub-nodes under this node.
Sub-nodes
=========
Required properties:
- compatible : shall include one of the following
"arm,scpi-dvfs-clocks" - all the clocks that are variable and index based.
These clocks don't provide an entire range of values between the
limits but only discrete points within the range. The firmware
provides the mapping for each such operating frequency and the
index associated with it. The firmware also manages the
voltage scaling appropriately with the clock scaling.
"arm,scpi-variable-clocks" - all the clocks that are variable and provide full
range within the specified range. The firmware provides the
range of values within a specified range.
Other required properties for all clocks(all from common clock binding):
- #clock-cells : Should be 1. Contains the Clock ID value used by SCPI commands.
- clock-output-names : shall be the corresponding names of the outputs.
- clock-indices: The identifying number for the clocks(i.e.clock_id) in the
node. It can be non linear and hence provide the mapping of identifiers
into the clock-output-names array.
SRAM and Shared Memory for SCPI
-------------------------------
A small area of SRAM is reserved for SCPI communication between application
processors and SCP.
Required properties:
- compatible : should be "arm,juno-sram-ns" for Non-secure SRAM on Juno
The rest of the properties should follow the generic mmio-sram description
found in ../../misc/sysram.txt
Each sub-node represents the reserved area for SCPI.
Required sub-node properties:
- reg : The base offset and size of the reserved area with the SRAM
- compatible : should be "arm,juno-scp-shmem" for Non-secure SRAM based
shared memory on Juno platforms
Sensor bindings for the sensors based on SCPI Message Protocol
--------------------------------------------------------------
SCPI provides an API to access the various sensors on the SoC.
Required properties:
- compatible : should be "arm,scpi-sensors".
- #thermal-sensor-cells: should be set to 1. This property follows the
thermal device tree bindings[2].
Valid cell values are raw identifiers (Sensor
ID) as used by the firmware. Refer to
platform documentation for your
implementation for the IDs to use. For Juno
R0 and Juno R1 refer to [3].
[0] http://infocenter.arm.com/help/topic/com.arm.doc.dui0922b/index.html
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Documentation/devicetree/bindings/thermal/thermal.txt
[3] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/apas03s22.html
Example:
sram: sram@50000000 {
compatible = "arm,juno-sram-ns", "mmio-sram";
reg = <0x0 0x50000000 0x0 0x10000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x0 0x50000000 0x10000>;
cpu_scp_lpri: scp-shmem@0 {
compatible = "arm,juno-scp-shmem";
reg = <0x0 0x200>;
};
cpu_scp_hpri: scp-shmem@200 {
compatible = "arm,juno-scp-shmem";
reg = <0x200 0x200>;
};
};
mailbox: mailbox0@40000000 {
....
#mbox-cells = <1>;
};
scpi_protocol: scpi@2e000000 {
compatible = "arm,scpi";
mboxes = <&mailbox 0 &mailbox 1>;
shmem = <&cpu_scp_lpri &cpu_scp_hpri>;
clocks {
compatible = "arm,scpi-clocks";
scpi_dvfs: scpi_clocks@0 {
compatible = "arm,scpi-dvfs-clocks";
#clock-cells = <1>;
clock-indices = <0>, <1>, <2>;
clock-output-names = "atlclk", "aplclk","gpuclk";
};
scpi_clk: scpi_clocks@3 {
compatible = "arm,scpi-variable-clocks";
#clock-cells = <1>;
clock-indices = <3>, <4>;
clock-output-names = "pxlclk0", "pxlclk1";
};
};
scpi_sensors0: sensors {
compatible = "arm,scpi-sensors";
#thermal-sensor-cells = <1>;
};
};
cpu@0 {
...
reg = <0 0>;
clocks = <&scpi_dvfs 0>;
};
hdlcd@7ff60000 {
...
reg = <0 0x7ff60000 0 0x1000>;
clocks = <&scpi_clk 4>;
};
thermal-zones {
soc_thermal {
polling-delay-passive = <100>;
polling-delay = <1000>;
/* sensor ID */
thermal-sensors = <&scpi_sensors0 3>;
...
};
};
In the above example, the #clock-cells is set to 1 as required.
scpi_dvfs has 3 output clocks namely: atlclk, aplclk, and gpuclk with 0,
1 and 2 as clock-indices. scpi_clk has 2 output clocks namely: pxlclk0
and pxlclk1 with 3 and 4 as clock-indices.
The first consumer in the example is cpu@0 and it has '0' as the clock
specifier which points to the first entry in the output clocks of
scpi_dvfs i.e. "atlclk".
Similarly the second example is hdlcd@7ff60000 and it has pxlclk1 as input
clock. '4' in the clock specifier here points to the second entry
in the output clocks of scpi_clocks i.e. "pxlclk1"
The thermal-sensors property in the soc_thermal node uses the
temperature sensor provided by SCP firmware to setup a thermal
zone. The ID "3" is the sensor identifier for the temperature sensor
as used by the firmware.
......@@ -31,6 +31,10 @@ Main node required properties:
support, but are permitted to be present for compatibility with
existing software when "arm,psci" is later in the compatible list.
* "arm,psci-1.0" : for implementations complying to PSCI 1.0. PSCI 1.0 is
backward compatible with PSCI 0.2 with minor specification updates,
as defined in the PSCI specification[2].
- method : The method of calling the PSCI firmware. Permitted
values are:
......@@ -100,3 +104,5 @@ Case 3: PSCI v0.2 and PSCI v0.1.
[1] Kernel documentation - ARM idle states bindings
Documentation/devicetree/bindings/arm/idle-states.txt
[2] Power State Coordination Interface (PSCI) specification
http://infocenter.arm.com/help/topic/com.arm.doc.den0022c/DEN0022C_Power_State_Coordination_Interface.pdf
Allwinner Reduced Serial Bus (RSB) controller
The RSB controller found on later Allwinner SoCs is an SMBus like 2 wire
serial bus with 1 master and up to 15 slaves. It is represented by a node
for the controller itself, and child nodes representing the slave devices.
Required properties :
- reg : Offset and length of the register set for the controller.
- compatible : Shall be "allwinner,sun8i-a23-rsb".
- interrupts : The interrupt line associated to the RSB controller.
- clocks : The gate clk associated to the RSB controller.
- resets : The reset line associated to the RSB controller.
- #address-cells : shall be 1
- #size-cells : shall be 0
Optional properties :
- clock-frequency : Desired RSB bus clock frequency in Hz. Maximum is 20MHz.
If not set this defaults to 3MHz.
Child nodes:
An RSB controller node can contain zero or more child nodes representing
slave devices on the bus. Child 'reg' properties should contain the slave
device's hardware address. The hardware address is hardwired in the device,
which can normally be found in the datasheet.
Example:
rsb@01f03400 {
compatible = "allwinner,sun8i-a23-rsb";
reg = <0x01f03400 0x400>;
interrupts = <0 39 4>;
clocks = <&apb0_gates 3>;
clock-frequency = <3000000>;
resets = <&apb0_rst 3>;
#address-cells = <1>;
#size-cells = <0>;
pmic@3e3 {
compatible = "...";
reg = <0x3e3>;
/* ... */
};
};
* Device tree bindings for ARM PL172 MultiPort Memory Controller
* Device tree bindings for ARM PL172/PL175/PL176 MultiPort Memory Controller
Required properties:
- compatible: "arm,pl172", "arm,primecell"
- compatible: Must be "arm,primecell" and exactly one from
"arm,pl172", "arm,pl175" or "arm,pl176".
- reg: Must contains offset/length value for controller.
......@@ -56,7 +57,8 @@ Optional child cs node config properties:
- mpmc,extended-wait: Enable extended wait.
- mpmc,buffer-enable: Enable write buffer.
- mpmc,buffer-enable: Enable write buffer, option is not supported by
PL175 and PL176 controllers.
- mpmc,write-protect: Enable write protect.
......
* Rockchip Power Domains
Rockchip processors include support for multiple power domains which can be
powered up/down by software based on different application scenes to save power.
Required properties for power domain controller:
- compatible: Should be one of the following.
"rockchip,rk3288-power-controller" - for RK3288 SoCs.
- #power-domain-cells: Number of cells in a power-domain specifier.
Should be 1 for multiple PM domains.
- #address-cells: Should be 1.
- #size-cells: Should be 0.
Required properties for power domain sub nodes:
- reg: index of the power domain, should use macros in:
"include/dt-bindings/power/rk3288-power.h" - for RK3288 type power domain.
- clocks (optional): phandles to clocks which need to be enabled while power domain
switches state.
Example:
power: power-controller {
compatible = "rockchip,rk3288-power-controller";
#power-domain-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
pd_gpu {
reg = <RK3288_PD_GPU>;
clocks = <&cru ACLK_GPU>;
};
};
Node of a device using power domains must have a power-domains property,
containing a phandle to the power device node and an index specifying which
power domain to use.
The index should use macros in:
"include/dt-bindings/power/rk3288-power.h" - for rk3288 type power domain.
Example of the node using power domain:
node {
/* ... */
power-domains = <&power RK3288_PD_GPU>;
/* ... */
};
Kernel driver scpi-hwmon
========================
Supported chips:
* Chips based on ARM System Control Processor Interface
Addresses scanned: -
Datasheet: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/index.html
Author: Punit Agrawal <punit.agrawal@arm.com>
Description
-----------
This driver supports hardware monitoring for SoC's based on the ARM
System Control Processor (SCP) implementing the System Control
Processor Interface (SCPI). The following sensor types are supported
by the SCP -
* temperature
* voltage
* current
* power
The SCP interface provides an API to query the available sensors and
their values which are then exported to userspace by this driver.
Usage Notes
-----------
The driver relies on device tree node to indicate the presence of SCPI
support in the kernel. See
Documentation/devicetree/bindings/arm/arm,scpi.txt for details of the
devicetree node.
\ No newline at end of file
......@@ -9362,6 +9362,16 @@ W: http://www.sunplus.com
S: Supported
F: arch/score/
SYSTEM CONTROL & POWER INTERFACE (SCPI) Message Protocol drivers
M: Sudeep Holla <sudeep.holla@arm.com>
L: linux-arm-kernel@lists.infradead.org
S: Maintained
F: Documentation/devicetree/bindings/arm/arm,scpi.txt
F: drivers/clk/clk-scpi.c
F: drivers/cpufreq/scpi-cpufreq.c
F: drivers/firmware/arm_scpi.c
F: include/linux/scpi_protocol.h
SCSI CDROM DRIVER
M: Jens Axboe <axboe@kernel.dk>
L: linux-scsi@vger.kernel.org
......
......@@ -100,6 +100,15 @@ timer {
clock-frequency = <19200000>;
};
smem {
compatible = "qcom,smem";
memory-region = <&smem_region>;
qcom,rpm-msg-ram = <&rpm_msg_ram>;
hwlocks = <&tcsr_mutex 3>;
};
soc: soc {
#address-cells = <1>;
#size-cells = <1>;
......@@ -250,13 +259,9 @@ tcsr_mutex: tcsr-mutex {
#hwlock-cells = <1>;
};
smem@fa00000 {
compatible = "qcom,smem";
memory-region = <&smem_region>;
rpm_msg_ram: memory@fc428000 {
compatible = "qcom,rpm-msg-ram";
reg = <0xfc428000 0x4000>;
hwlocks = <&tcsr_mutex 3>;
};
blsp1_uart2: serial@f991e000 {
......
......@@ -30,20 +30,6 @@
#include <asm/smp_plat.h>
#include <asm/suspend.h>
static bool psci_power_state_loses_context(u32 state)
{
return state & PSCI_0_2_POWER_STATE_TYPE_MASK;
}
static bool psci_power_state_is_valid(u32 state)
{
const u32 valid_mask = PSCI_0_2_POWER_STATE_ID_MASK |
PSCI_0_2_POWER_STATE_TYPE_MASK |
PSCI_0_2_POWER_STATE_AFFL_MASK;
return !(state & ~valid_mask);
}
static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
......
......@@ -93,7 +93,7 @@ static int __pm_clk_add(struct device *dev, const char *con_id,
return -ENOMEM;
}
} else {
if (IS_ERR(clk) || !__clk_get(clk)) {
if (IS_ERR(clk)) {
kfree(ce);
return -ENOENT;
}
......@@ -127,7 +127,9 @@ int pm_clk_add(struct device *dev, const char *con_id)
* @clk: Clock pointer
*
* Add the clock to the list of clocks used for the power management of @dev.
* It will increment refcount on clock pointer, use clk_put() on it when done.
* The power-management code will take control of the clock reference, so
* callers should not call clk_put() on @clk after this function sucessfully
* returned.
*/
int pm_clk_add_clk(struct device *dev, struct clk *clk)
{
......
......@@ -120,6 +120,17 @@ config SIMPLE_PM_BUS
Controller (BSC, sometimes called "LBSC within Bus Bridge", or
"External Bus Interface") as found on several Renesas ARM SoCs.
config SUNXI_RSB
tristate "Allwinner sunXi Reduced Serial Bus Driver"
default MACH_SUN8I || MACH_SUN9I
depends on ARCH_SUNXI
select REGMAP
help
Say y here to enable support for Allwinner's Reduced Serial Bus
(RSB) support. This controller is responsible for communicating
with various RSB based devices, such as AXP223, AXP8XX PMICs,
and AC100/AC200 ICs.
config VEXPRESS_CONFIG
bool "Versatile Express configuration bus"
default y if ARCH_VEXPRESS
......
......@@ -15,5 +15,6 @@ obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
This diff is collapsed.
......@@ -60,6 +60,16 @@ config COMMON_CLK_RK808
clocked at 32KHz each. Clkout1 is always on, Clkout2 can off
by control register.
config COMMON_CLK_SCPI
tristate "Clock driver controlled via SCPI interface"
depends on ARM_SCPI_PROTOCOL || COMPILE_TEST
---help---
This driver provides support for clocks that are controlled
by firmware that implements the SCPI interface.
This driver uses SCPI Message Protocol to interact with the
firmware providing all the clock controls.
config COMMON_CLK_SI5351
tristate "Clock driver for SiLabs 5351A/B/C"
depends on I2C
......
......@@ -36,6 +36,7 @@ obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o
obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
......
......@@ -45,7 +45,7 @@
#define REG_SDIO0XIN_CLKCTL 0x0158
#define REG_SDIO1XIN_CLKCTL 0x015c
#define MAX_CLKS 27
#define MAX_CLKS 28
static struct clk *clks[MAX_CLKS];
static struct clk_onecell_data clk_data;
static DEFINE_SPINLOCK(lock);
......@@ -356,13 +356,13 @@ static void __init berlin2q_clock_setup(struct device_node *np)
gd->bit_idx, 0, &lock);
}
/*
* twdclk is derived from cpu/3
* TODO: use cpupll until cpuclk is not available
*/
/* cpuclk divider is fixed to 1 */
clks[CLKID_CPU] =
clk_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL],
0, 1, 1);
/* twdclk is derived from cpu/3 */
clks[CLKID_TWD] =
clk_register_fixed_factor(NULL, "twd", clk_names[CPUPLL],
0, 1, 3);
clk_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
......
/*
* System Control and Power Interface (SCPI) Protocol based clock driver
*
* Copyright (C) 2015 ARM Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/scpi_protocol.h>
struct scpi_clk {
u32 id;
struct clk_hw hw;
struct scpi_dvfs_info *info;
struct scpi_ops *scpi_ops;
};
#define to_scpi_clk(clk) container_of(clk, struct scpi_clk, hw)
static struct platform_device *cpufreq_dev;
static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct scpi_clk *clk = to_scpi_clk(hw);
return clk->scpi_ops->clk_get_val(clk->id);
}
static long scpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
/*
* We can't figure out what rate it will be, so just return the
* rate back to the caller. scpi_clk_recalc_rate() will be called
* after the rate is set and we'll know what rate the clock is
* running at then.
*/
return rate;
}
static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct scpi_clk *clk = to_scpi_clk(hw);
return clk->scpi_ops->clk_set_val(clk->id, rate);
}
static const struct clk_ops scpi_clk_ops = {
.recalc_rate = scpi_clk_recalc_rate,
.round_rate = scpi_clk_round_rate,
.set_rate = scpi_clk_set_rate,
};
/* find closest match to given frequency in OPP table */
static int __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate)
{
int idx;
u32 fmin = 0, fmax = ~0, ftmp;
const struct scpi_opp *opp = clk->info->opps;
for (idx = 0; idx < clk->info->count; idx++, opp++) {
ftmp = opp->freq;
if (ftmp >= (u32)rate) {
if (ftmp <= fmax)
fmax = ftmp;
break;
} else if (ftmp >= fmin) {
fmin = ftmp;
}
}
return fmax != ~0 ? fmax : fmin;
}
static unsigned long scpi_dvfs_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct scpi_clk *clk = to_scpi_clk(hw);
int idx = clk->scpi_ops->dvfs_get_idx(clk->id);
const struct scpi_opp *opp;
if (idx < 0)
return 0;
opp = clk->info->opps + idx;
return opp->freq;
}
static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct scpi_clk *clk = to_scpi_clk(hw);
return __scpi_dvfs_round_rate(clk, rate);
}
static int __scpi_find_dvfs_index(struct scpi_clk *clk, unsigned long rate)
{
int idx, max_opp = clk->info->count;
const struct scpi_opp *opp = clk->info->opps;
for (idx = 0; idx < max_opp; idx++, opp++)
if (opp->freq == rate)
return idx;
return -EINVAL;
}
static int scpi_dvfs_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct scpi_clk *clk = to_scpi_clk(hw);
int ret = __scpi_find_dvfs_index(clk, rate);
if (ret < 0)
return ret;
return clk->scpi_ops->dvfs_set_idx(clk->id, (u8)ret);
}
static const struct clk_ops scpi_dvfs_ops = {
.recalc_rate = scpi_dvfs_recalc_rate,
.round_rate = scpi_dvfs_round_rate,
.set_rate = scpi_dvfs_set_rate,
};
static const struct of_device_id scpi_clk_match[] = {
{ .compatible = "arm,scpi-dvfs-clocks", .data = &scpi_dvfs_ops, },
{ .compatible = "arm,scpi-variable-clocks", .data = &scpi_clk_ops, },
{}
};
static struct clk *
scpi_clk_ops_init(struct device *dev, const struct of_device_id *match,
struct scpi_clk *sclk, const char *name)
{
struct clk_init_data init;
struct clk *clk;
unsigned long min = 0, max = 0;
init.name = name;
init.flags = CLK_IS_ROOT;
init.num_parents = 0;
init.ops = match->data;
sclk->hw.init = &init;
sclk->scpi_ops = get_scpi_ops();
if (init.ops == &scpi_dvfs_ops) {
sclk->info = sclk->scpi_ops->dvfs_get_info(sclk->id);
if (IS_ERR(sclk->info))
return NULL;
} else if (init.ops == &scpi_clk_ops) {
if (sclk->scpi_ops->clk_get_range(sclk->id, &min, &max) || !max)
return NULL;
} else {
return NULL;
}
clk = devm_clk_register(dev, &sclk->hw);
if (!IS_ERR(clk) && max)
clk_hw_set_rate_range(&sclk->hw, min, max);
return clk;
}
struct scpi_clk_data {
struct scpi_clk **clk;
unsigned int clk_num;
};
static struct clk *
scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data)
{
struct scpi_clk *sclk;
struct scpi_clk_data *clk_data = data;
unsigned int idx = clkspec->args[0], count;
for (count = 0; count < clk_data->clk_num; count++) {
sclk = clk_data->clk[count];
if (idx == sclk->id)
return sclk->hw.clk;
}
return ERR_PTR(-EINVAL);
}
static int scpi_clk_add(struct device *dev, struct device_node *np,
const struct of_device_id *match)
{
struct clk **clks;
int idx, count;
struct scpi_clk_data *clk_data;
count = of_property_count_strings(np, "clock-output-names");
if (count < 0) {
dev_err(dev, "%s: invalid clock output count\n", np->name);
return -EINVAL;
}
clk_data = devm_kmalloc(dev, sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->clk_num = count;
clk_data->clk = devm_kcalloc(dev, count, sizeof(*clk_data->clk),
GFP_KERNEL);
if (!clk_data->clk)
return -ENOMEM;
clks = devm_kcalloc(dev, count, sizeof(*clks), GFP_KERNEL);
if (!clks)
return -ENOMEM;
for (idx = 0; idx < count; idx++) {
struct scpi_clk *sclk;
const char *name;
u32 val;
sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
if (!sclk)
return -ENOMEM;
if (of_property_read_string_index(np, "clock-output-names",
idx, &name)) {
dev_err(dev, "invalid clock name @ %s\n", np->name);
return -EINVAL;
}
if (of_property_read_u32_index(np, "clock-indices",
idx, &val)) {
dev_err(dev, "invalid clock index @ %s\n", np->name);
return -EINVAL;
}
sclk->id = val;
clks[idx] = scpi_clk_ops_init(dev, match, sclk, name);
if (IS_ERR_OR_NULL(clks[idx]))
dev_err(dev, "failed to register clock '%s'\n", name);
else
dev_dbg(dev, "Registered clock '%s'\n", name);
clk_data->clk[idx] = sclk;
}
return of_clk_add_provider(np, scpi_of_clk_src_get, clk_data);
}
static int scpi_clocks_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *child, *np = dev->of_node;
if (cpufreq_dev) {
platform_device_unregister(cpufreq_dev);
cpufreq_dev = NULL;
}
for_each_available_child_of_node(np, child)
of_clk_del_provider(np);
return 0;
}
static int scpi_clocks_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct device_node *child, *np = dev->of_node;
const struct of_device_id *match;
if (!get_scpi_ops())
return -ENXIO;
for_each_available_child_of_node(np, child) {
match = of_match_node(scpi_clk_match, child);
if (!match)
continue;
ret = scpi_clk_add(dev, child, match);
if (ret) {
scpi_clocks_remove(pdev);
return ret;
}
}
/* Add the virtual cpufreq device */
cpufreq_dev = platform_device_register_simple("scpi-cpufreq",
-1, NULL, 0);
if (!cpufreq_dev)
pr_warn("unable to register cpufreq device");
return 0;
}
static const struct of_device_id scpi_clocks_ids[] = {
{ .compatible = "arm,scpi-clocks", },
{}
};
MODULE_DEVICE_TABLE(of, scpi_clocks_ids);
static struct platform_driver scpi_clocks_driver = {
.driver = {
.name = "scpi_clocks",
.of_match_table = scpi_clocks_ids,
},
.probe = scpi_clocks_probe,
.remove = scpi_clocks_remove,
};
module_platform_driver(scpi_clocks_driver);
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCPI clock driver");
MODULE_LICENSE("GPL v2");
......@@ -193,10 +193,17 @@ static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
struct clk *t2_clk = tc->clk[2];
int irq = tc->irq[2];
ret = clk_prepare_enable(tc->slow_clk);
if (ret)
return ret;
/* try to enable t2 clk to avoid future errors in mode change */
ret = clk_prepare_enable(t2_clk);
if (ret)
if (ret) {
clk_disable_unprepare(tc->slow_clk);
return ret;
}
clk_disable(t2_clk);
clkevt.regs = tc->regs;
......@@ -208,7 +215,8 @@ static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt);
if (ret) {
clk_disable_unprepare(t2_clk);
clk_unprepare(t2_clk);
clk_disable_unprepare(tc->slow_clk);
return ret;
}
......
......@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/export.h>
#include <linux/mfd/syscon.h>
......@@ -33,9 +34,7 @@ static unsigned long last_crtr;
static u32 irqmask;
static struct clock_event_device clkevt;
static struct regmap *regmap_st;
#define AT91_SLOW_CLOCK 32768
#define RM9200_TIMER_LATCH ((AT91_SLOW_CLOCK + HZ/2) / HZ)
static int timer_latch;
/*
* The ST_CRTR is updated asynchronously to the master clock ... but
......@@ -82,8 +81,8 @@ static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id)
if (sr & AT91_ST_PITS) {
u32 crtr = read_CRTR();
while (((crtr - last_crtr) & AT91_ST_CRTV) >= RM9200_TIMER_LATCH) {
last_crtr += RM9200_TIMER_LATCH;
while (((crtr - last_crtr) & AT91_ST_CRTV) >= timer_latch) {
last_crtr += timer_latch;
clkevt.event_handler(&clkevt);
}
return IRQ_HANDLED;
......@@ -144,7 +143,7 @@ static int clkevt32k_set_periodic(struct clock_event_device *dev)
/* PIT for periodic irqs; fixed rate of 1/HZ */
irqmask = AT91_ST_PITS;
regmap_write(regmap_st, AT91_ST_PIMR, RM9200_TIMER_LATCH);
regmap_write(regmap_st, AT91_ST_PIMR, timer_latch);
regmap_write(regmap_st, AT91_ST_IER, irqmask);
return 0;
}
......@@ -197,7 +196,8 @@ static struct clock_event_device clkevt = {
*/
static void __init atmel_st_timer_init(struct device_node *node)
{
unsigned int val;
struct clk *sclk;
unsigned int sclk_rate, val;
int irq, ret;
regmap_st = syscon_node_to_regmap(node);
......@@ -221,6 +221,19 @@ static void __init atmel_st_timer_init(struct device_node *node)
if (ret)
panic(pr_fmt("Unable to setup IRQ\n"));
sclk = of_clk_get(node, 0);
if (IS_ERR(sclk))
panic(pr_fmt("Unable to get slow clock\n"));
clk_prepare_enable(sclk);
if (ret)
panic(pr_fmt("Could not enable slow clock\n"));
sclk_rate = clk_get_rate(sclk);
if (!sclk_rate)
panic(pr_fmt("Invalid slow clock rate\n"));
timer_latch = (sclk_rate + HZ / 2) / HZ;
/* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used
* directly for the clocksource and all clockevents, after adjusting
* its prescaler from the 1 Hz default.
......@@ -229,11 +242,11 @@ static void __init atmel_st_timer_init(struct device_node *node)
/* Setup timer clockevent, with minimum of two ticks (important!!) */
clkevt.cpumask = cpumask_of(0);
clockevents_config_and_register(&clkevt, AT91_SLOW_CLOCK,
clockevents_config_and_register(&clkevt, sclk_rate,
2, AT91_ST_ALMV);
/* register clocksource */
clocksource_register_hz(&clk32k, AT91_SLOW_CLOCK);
clocksource_register_hz(&clk32k, sclk_rate);
}
CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st",
atmel_st_timer_init);
......@@ -199,6 +199,16 @@ config ARM_SA1100_CPUFREQ
config ARM_SA1110_CPUFREQ
bool
config ARM_SCPI_CPUFREQ
tristate "SCPI based CPUfreq driver"
depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL
help
This adds the CPUfreq driver support for ARM big.LITTLE platforms
using SCPI protocol for CPU power management.
This driver uses SCPI Message Protocol driver to interact with the
firmware providing the CPU DVFS functionality.
config ARM_SPEAR_CPUFREQ
bool "SPEAr CPUFreq support"
depends on PLAT_SPEAR
......
......@@ -71,6 +71,7 @@ obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
......
/*
* System Control and Power Interface (SCPI) based CPUFreq Interface driver
*
* It provides necessary ops to arm_big_little cpufreq driver.
*
* Copyright (C) 2015 ARM Ltd.
* Sudeep Holla <sudeep.holla@arm.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.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/cpufreq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/scpi_protocol.h>
#include <linux/types.h>
#include "arm_big_little.h"
static struct scpi_ops *scpi_ops;
static struct scpi_dvfs_info *scpi_get_dvfs_info(struct device *cpu_dev)
{
u8 domain = topology_physical_package_id(cpu_dev->id);
if (domain < 0)
return ERR_PTR(-EINVAL);
return scpi_ops->dvfs_get_info(domain);
}
static int scpi_opp_table_ops(struct device *cpu_dev, bool remove)
{
int idx, ret = 0;
struct scpi_opp *opp;
struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
if (IS_ERR(info))
return PTR_ERR(info);
if (!info->opps)
return -EIO;
for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
if (remove)
dev_pm_opp_remove(cpu_dev, opp->freq);
else
ret = dev_pm_opp_add(cpu_dev, opp->freq,
opp->m_volt * 1000);
if (ret) {
dev_warn(cpu_dev, "failed to add opp %uHz %umV\n",
opp->freq, opp->m_volt);
while (idx-- > 0)
dev_pm_opp_remove(cpu_dev, (--opp)->freq);
return ret;
}
}
return ret;
}
static int scpi_get_transition_latency(struct device *cpu_dev)
{
struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
if (IS_ERR(info))
return PTR_ERR(info);
return info->latency;
}
static int scpi_init_opp_table(struct device *cpu_dev)
{
return scpi_opp_table_ops(cpu_dev, false);
}
static void scpi_free_opp_table(struct device *cpu_dev)
{
scpi_opp_table_ops(cpu_dev, true);
}
static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
.name = "scpi",
.get_transition_latency = scpi_get_transition_latency,
.init_opp_table = scpi_init_opp_table,
.free_opp_table = scpi_free_opp_table,
};
static int scpi_cpufreq_probe(struct platform_device *pdev)
{
scpi_ops = get_scpi_ops();
if (!scpi_ops)
return -EIO;
return bL_cpufreq_register(&scpi_cpufreq_ops);
}
static int scpi_cpufreq_remove(struct platform_device *pdev)
{
bL_cpufreq_unregister(&scpi_cpufreq_ops);
scpi_ops = NULL;
return 0;
}
static struct platform_driver scpi_cpufreq_platdrv = {
.driver = {
.name = "scpi-cpufreq",
.owner = THIS_MODULE,
},
.probe = scpi_cpufreq_probe,
.remove = scpi_cpufreq_remove,
};
module_platform_driver(scpi_cpufreq_platdrv);
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
MODULE_LICENSE("GPL v2");
......@@ -8,6 +8,25 @@ menu "Firmware Drivers"
config ARM_PSCI_FW
bool
config ARM_SCPI_PROTOCOL
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
depends on ARM_MHU
help
System Control and Power Interface (SCPI) Message Protocol is
defined for the purpose of communication between the Application
Cores(AP) and the System Control Processor(SCP). The MHU peripheral
provides a mechanism for inter-processor communication between SCP
and AP.
SCP controls most of the power managament on the Application
Processors. It offers control and management of: the core/cluster
power states, various power domain DVFS including the core/cluster,
certain system clocks configuration, thermal sensors and many
others.
This protocol library provides interface for all the client drivers
making use of the features offered by the SCP.
config EDD
tristate "BIOS Enhanced Disk Drive calls determine boot disk"
depends on X86
......@@ -135,6 +154,13 @@ config ISCSI_IBFT
detect iSCSI boot parameters dynamically during system boot, say Y.
Otherwise, say N.
config RASPBERRYPI_FIRMWARE
tristate "Raspberry Pi Firmware Driver"
depends on BCM2835_MBOX
help
This option enables support for communicating with the firmware on the
Raspberry Pi.
config QCOM_SCM
bool
depends on ARM || ARM64
......
......@@ -2,6 +2,7 @@
# Makefile for the linux kernel.
#
obj-$(CONFIG_ARM_PSCI_FW) += psci.o
obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o
obj-$(CONFIG_DMI) += dmi_scan.o
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
obj-$(CONFIG_EDD) += edd.o
......@@ -12,10 +13,11 @@ obj-$(CONFIG_DMIID) += dmi-id.o
obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o
obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o
obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
obj-y += broadcom/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
......
This diff is collapsed.
......@@ -20,23 +20,25 @@
#include <linux/printk.h>
#include <linux/psci.h>
#include <linux/reboot.h>
#include <linux/suspend.h>
#include <uapi/linux/psci.h>
#include <asm/cputype.h>
#include <asm/system_misc.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>
/*
* While a 64-bit OS can make calls with SMC32 calling conventions, for some
* calls it is necessary to use SMC64 to pass or return 64-bit values. For such
* calls PSCI_0_2_FN_NATIVE(x) will choose the appropriate (native-width)
* function ID.
* calls it is necessary to use SMC64 to pass or return 64-bit values.
* For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate
* (native-width) function ID.
*/
#ifdef CONFIG_64BIT
#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN64_##name
#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name
#else
#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN_##name
#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name
#endif
/*
......@@ -70,6 +72,41 @@ enum psci_function {
static u32 psci_function_id[PSCI_FN_MAX];
#define PSCI_0_2_POWER_STATE_MASK \
(PSCI_0_2_POWER_STATE_ID_MASK | \
PSCI_0_2_POWER_STATE_TYPE_MASK | \
PSCI_0_2_POWER_STATE_AFFL_MASK)
#define PSCI_1_0_EXT_POWER_STATE_MASK \
(PSCI_1_0_EXT_POWER_STATE_ID_MASK | \
PSCI_1_0_EXT_POWER_STATE_TYPE_MASK)
static u32 psci_cpu_suspend_feature;
static inline bool psci_has_ext_power_state(void)
{
return psci_cpu_suspend_feature &
PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK;
}
bool psci_power_state_loses_context(u32 state)
{
const u32 mask = psci_has_ext_power_state() ?
PSCI_1_0_EXT_POWER_STATE_TYPE_MASK :
PSCI_0_2_POWER_STATE_TYPE_MASK;
return state & mask;
}
bool psci_power_state_is_valid(u32 state)
{
const u32 valid_mask = psci_has_ext_power_state() ?
PSCI_1_0_EXT_POWER_STATE_MASK :
PSCI_0_2_POWER_STATE_MASK;
return !(state & ~valid_mask);
}
static int psci_to_linux_errno(int errno)
{
switch (errno) {
......@@ -78,6 +115,7 @@ static int psci_to_linux_errno(int errno)
case PSCI_RET_NOT_SUPPORTED:
return -EOPNOTSUPP;
case PSCI_RET_INVALID_PARAMS:
case PSCI_RET_INVALID_ADDRESS:
return -EINVAL;
case PSCI_RET_DENIED:
return -EPERM;
......@@ -134,7 +172,7 @@ static int psci_migrate(unsigned long cpuid)
static int psci_affinity_info(unsigned long target_affinity,
unsigned long lowest_affinity_level)
{
return invoke_psci_fn(PSCI_0_2_FN_NATIVE(AFFINITY_INFO),
return invoke_psci_fn(PSCI_FN_NATIVE(0_2, AFFINITY_INFO),
target_affinity, lowest_affinity_level, 0);
}
......@@ -145,7 +183,7 @@ static int psci_migrate_info_type(void)
static unsigned long psci_migrate_info_up_cpu(void)
{
return invoke_psci_fn(PSCI_0_2_FN_NATIVE(MIGRATE_INFO_UP_CPU),
return invoke_psci_fn(PSCI_FN_NATIVE(0_2, MIGRATE_INFO_UP_CPU),
0, 0, 0);
}
......@@ -181,6 +219,49 @@ static void psci_sys_poweroff(void)
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
}
static int __init psci_features(u32 psci_func_id)
{
return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES,
psci_func_id, 0, 0);
}
static int psci_system_suspend(unsigned long unused)
{
return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
virt_to_phys(cpu_resume), 0, 0);
}
static int psci_system_suspend_enter(suspend_state_t state)
{
return cpu_suspend(0, psci_system_suspend);
}
static const struct platform_suspend_ops psci_suspend_ops = {
.valid = suspend_valid_only_mem,
.enter = psci_system_suspend_enter,
};
static void __init psci_init_system_suspend(void)
{
int ret;
if (!IS_ENABLED(CONFIG_SUSPEND))
return;
ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND));
if (ret != PSCI_RET_NOT_SUPPORTED)
suspend_set_ops(&psci_suspend_ops);
}
static void __init psci_init_cpu_suspend(void)
{
int feature = psci_features(psci_function_id[PSCI_FN_CPU_SUSPEND]);
if (feature != PSCI_RET_NOT_SUPPORTED)
psci_cpu_suspend_feature = feature;
}
/*
* Detect the presence of a resident Trusted OS which may cause CPU_OFF to
* return DENIED (which would be fatal).
......@@ -224,16 +305,17 @@ static void __init psci_init_migrate(void)
static void __init psci_0_2_set_functions(void)
{
pr_info("Using standard PSCI v0.2 function IDs\n");
psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_NATIVE(CPU_SUSPEND);
psci_function_id[PSCI_FN_CPU_SUSPEND] =
PSCI_FN_NATIVE(0_2, CPU_SUSPEND);
psci_ops.cpu_suspend = psci_cpu_suspend;
psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
psci_ops.cpu_off = psci_cpu_off;
psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_NATIVE(CPU_ON);
psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_NATIVE(0_2, CPU_ON);
psci_ops.cpu_on = psci_cpu_on;
psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_NATIVE(MIGRATE);
psci_function_id[PSCI_FN_MIGRATE] = PSCI_FN_NATIVE(0_2, MIGRATE);
psci_ops.migrate = psci_migrate;
psci_ops.affinity_info = psci_affinity_info;
......@@ -265,6 +347,11 @@ static int __init psci_probe(void)
psci_init_migrate();
if (PSCI_VERSION_MAJOR(ver) >= 1) {
psci_init_cpu_suspend();
psci_init_system_suspend();
}
return 0;
}
......@@ -340,6 +427,7 @@ static int __init psci_0_1_init(struct device_node *np)
static const struct of_device_id const psci_of_match[] __initconst = {
{ .compatible = "arm,psci", .data = psci_0_1_init},
{ .compatible = "arm,psci-0.2", .data = psci_0_2_init},
{ .compatible = "arm,psci-1.0", .data = psci_0_2_init},
{},
};
......
......@@ -480,15 +480,15 @@ void __qcom_scm_cpu_power_down(u32 flags)
int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id)
{
int ret;
u32 svc_cmd = (svc_id << 10) | cmd_id;
u32 ret_val = 0;
__le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id);
__le32 ret_val = 0;
ret = qcom_scm_call(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, &svc_cmd,
sizeof(svc_cmd), &ret_val, sizeof(ret_val));
if (ret)
return ret;
return ret_val;
return le32_to_cpu(ret_val);
}
int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
......
/*
* Defines interfaces for interacting wtih the Raspberry Pi firmware's
* property channel.
*
* Copyright © 2015 Broadcom
*
* 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/dma-mapping.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf))
#define MBOX_CHAN(msg) ((msg) & 0xf)
#define MBOX_DATA28(msg) ((msg) & ~0xf)
#define MBOX_CHAN_PROPERTY 8
struct rpi_firmware {
struct mbox_client cl;
struct mbox_chan *chan; /* The property channel. */
struct completion c;
u32 enabled;
};
static DEFINE_MUTEX(transaction_lock);
static void response_callback(struct mbox_client *cl, void *msg)
{
struct rpi_firmware *fw = container_of(cl, struct rpi_firmware, cl);
complete(&fw->c);
}
/*
* Sends a request to the firmware through the BCM2835 mailbox driver,
* and synchronously waits for the reply.
*/
static int
rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data)
{
u32 message = MBOX_MSG(chan, data);
int ret;
WARN_ON(data & 0xf);
mutex_lock(&transaction_lock);
reinit_completion(&fw->c);
ret = mbox_send_message(fw->chan, &message);
if (ret >= 0) {
wait_for_completion(&fw->c);
ret = 0;
} else {
dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret);
}
mutex_unlock(&transaction_lock);
return ret;
}
/**
* rpi_firmware_property_list - Submit firmware property list
* @fw: Pointer to firmware structure from rpi_firmware_get().
* @data: Buffer holding tags.
* @tag_size: Size of tags buffer.
*
* Submits a set of concatenated tags to the VPU firmware through the
* mailbox property interface.
*
* The buffer header and the ending tag are added by this function and
* don't need to be supplied, just the actual tags for your operation.
* See struct rpi_firmware_property_tag_header for the per-tag
* structure.
*/
int rpi_firmware_property_list(struct rpi_firmware *fw,
void *data, size_t tag_size)
{
size_t size = tag_size + 12;
u32 *buf;
dma_addr_t bus_addr;
int ret;
/* Packets are processed a dword at a time. */
if (size & 3)
return -EINVAL;
buf = dma_alloc_coherent(fw->cl.dev, PAGE_ALIGN(size), &bus_addr,
GFP_ATOMIC);
if (!buf)
return -ENOMEM;
/* The firmware will error out without parsing in this case. */
WARN_ON(size >= 1024 * 1024);
buf[0] = size;
buf[1] = RPI_FIRMWARE_STATUS_REQUEST;
memcpy(&buf[2], data, tag_size);
buf[size / 4 - 1] = RPI_FIRMWARE_PROPERTY_END;
wmb();
ret = rpi_firmware_transaction(fw, MBOX_CHAN_PROPERTY, bus_addr);
rmb();
memcpy(data, &buf[2], tag_size);
if (ret == 0 && buf[1] != RPI_FIRMWARE_STATUS_SUCCESS) {
/*
* The tag name here might not be the one causing the
* error, if there were multiple tags in the request.
* But single-tag is the most common, so go with it.
*/
dev_err(fw->cl.dev, "Request 0x%08x returned status 0x%08x\n",
buf[2], buf[1]);
ret = -EINVAL;
}
dma_free_coherent(fw->cl.dev, PAGE_ALIGN(size), buf, bus_addr);
return ret;
}
EXPORT_SYMBOL_GPL(rpi_firmware_property_list);
/**
* rpi_firmware_property - Submit single firmware property
* @fw: Pointer to firmware structure from rpi_firmware_get().
* @tag: One of enum_mbox_property_tag.
* @tag_data: Tag data buffer.
* @buf_size: Buffer size.
*
* Submits a single tag to the VPU firmware through the mailbox
* property interface.
*
* This is a convenience wrapper around
* rpi_firmware_property_list() to avoid some of the
* boilerplate in property calls.
*/
int rpi_firmware_property(struct rpi_firmware *fw,
u32 tag, void *tag_data, size_t buf_size)
{
/* Single tags are very small (generally 8 bytes), so the
* stack should be safe.
*/
u8 data[buf_size + sizeof(struct rpi_firmware_property_tag_header)];
struct rpi_firmware_property_tag_header *header =
(struct rpi_firmware_property_tag_header *)data;
int ret;
header->tag = tag;
header->buf_size = buf_size;
header->req_resp_size = 0;
memcpy(data + sizeof(struct rpi_firmware_property_tag_header),
tag_data, buf_size);
ret = rpi_firmware_property_list(fw, &data, sizeof(data));
memcpy(tag_data,
data + sizeof(struct rpi_firmware_property_tag_header),
buf_size);
return ret;
}
EXPORT_SYMBOL_GPL(rpi_firmware_property);
static void
rpi_firmware_print_firmware_revision(struct rpi_firmware *fw)
{
u32 packet;
int ret = rpi_firmware_property(fw,
RPI_FIRMWARE_GET_FIRMWARE_REVISION,
&packet, sizeof(packet));
if (ret == 0) {
struct tm tm;
time_to_tm(packet, 0, &tm);
dev_info(fw->cl.dev,
"Attached to firmware from %04ld-%02d-%02d %02d:%02d\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min);
}
}
static int rpi_firmware_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rpi_firmware *fw;
fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL);
if (!fw)
return -ENOMEM;
fw->cl.dev = dev;
fw->cl.rx_callback = response_callback;
fw->cl.tx_block = true;
fw->chan = mbox_request_channel(&fw->cl, 0);
if (IS_ERR(fw->chan)) {
int ret = PTR_ERR(fw->chan);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get mbox channel: %d\n", ret);
return ret;
}
init_completion(&fw->c);
platform_set_drvdata(pdev, fw);
rpi_firmware_print_firmware_revision(fw);
return 0;
}
static int rpi_firmware_remove(struct platform_device *pdev)
{
struct rpi_firmware *fw = platform_get_drvdata(pdev);
mbox_free_channel(fw->chan);
return 0;
}
/**
* rpi_firmware_get - Get pointer to rpi_firmware structure.
* @firmware_node: Pointer to the firmware Device Tree node.
*
* Returns NULL is the firmware device is not ready.
*/
struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node)
{
struct platform_device *pdev = of_find_device_by_node(firmware_node);
if (!pdev)
return NULL;
return platform_get_drvdata(pdev);
}
EXPORT_SYMBOL_GPL(rpi_firmware_get);
static const struct of_device_id rpi_firmware_of_match[] = {
{ .compatible = "raspberrypi,bcm2835-firmware", },
{},
};
MODULE_DEVICE_TABLE(of, rpi_firmware_of_match);
static struct platform_driver rpi_firmware_driver = {
.driver = {
.name = "raspberrypi-firmware",
.of_match_table = rpi_firmware_of_match,
},
.probe = rpi_firmware_probe,
.remove = rpi_firmware_remove,
};
module_platform_driver(rpi_firmware_driver);
MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
MODULE_DESCRIPTION("Raspberry Pi firmware driver");
MODULE_LICENSE("GPL v2");
......@@ -321,6 +321,14 @@ config SENSORS_APPLESMC
Say Y here if you have an applicable laptop and want to experience
the awesome power of applesmc.
config SENSORS_ARM_SCPI
tristate "ARM SCPI Sensors"
depends on ARM_SCPI_PROTOCOL
help
This driver provides support for temperature, voltage, current
and power sensors available on ARM Ltd's SCP based platforms. The
actual number and type of sensors exported depend on the platform.
config SENSORS_ASB100
tristate "Asus ASB100 Bach"
depends on X86 && I2C
......
......@@ -44,6 +44,7 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
......
/*
* System Control and Power Interface(SCPI) based hwmon sensor driver
*
* Copyright (C) 2015 ARM Ltd.
* Punit Agrawal <punit.agrawal@arm.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.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/scpi_protocol.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/thermal.h>
struct sensor_data {
struct scpi_sensor_info info;
struct device_attribute dev_attr_input;
struct device_attribute dev_attr_label;
char input[20];
char label[20];
};
struct scpi_thermal_zone {
struct list_head list;
int sensor_id;
struct scpi_sensors *scpi_sensors;
struct thermal_zone_device *tzd;
};
struct scpi_sensors {
struct scpi_ops *scpi_ops;
struct sensor_data *data;
struct list_head thermal_zones;
struct attribute **attrs;
struct attribute_group group;
const struct attribute_group *groups[2];
};
static int scpi_read_temp(void *dev, int *temp)
{
struct scpi_thermal_zone *zone = dev;
struct scpi_sensors *scpi_sensors = zone->scpi_sensors;
struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
struct sensor_data *sensor = &scpi_sensors->data[zone->sensor_id];
u32 value;
int ret;
ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
if (ret)
return ret;
*temp = value;
return 0;
}
/* hwmon callback functions */
static ssize_t
scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf)
{
struct scpi_sensors *scpi_sensors = dev_get_drvdata(dev);
struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
struct sensor_data *sensor;
u32 value;
int ret;
sensor = container_of(attr, struct sensor_data, dev_attr_input);
ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
if (ret)
return ret;
return sprintf(buf, "%u\n", value);
}
static ssize_t
scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sensor_data *sensor;
sensor = container_of(attr, struct sensor_data, dev_attr_label);
return sprintf(buf, "%s\n", sensor->info.name);
}
static void
unregister_thermal_zones(struct platform_device *pdev,
struct scpi_sensors *scpi_sensors)
{
struct list_head *pos;
list_for_each(pos, &scpi_sensors->thermal_zones) {
struct scpi_thermal_zone *zone;
zone = list_entry(pos, struct scpi_thermal_zone, list);
thermal_zone_of_sensor_unregister(&pdev->dev, zone->tzd);
}
}
static struct thermal_zone_of_device_ops scpi_sensor_ops = {
.get_temp = scpi_read_temp,
};
static int scpi_hwmon_probe(struct platform_device *pdev)
{
u16 nr_sensors, i;
int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0;
struct scpi_ops *scpi_ops;
struct device *hwdev, *dev = &pdev->dev;
struct scpi_sensors *scpi_sensors;
int ret;
scpi_ops = get_scpi_ops();
if (!scpi_ops)
return -EPROBE_DEFER;
ret = scpi_ops->sensor_get_capability(&nr_sensors);
if (ret)
return ret;
if (!nr_sensors)
return -ENODEV;
scpi_sensors = devm_kzalloc(dev, sizeof(*scpi_sensors), GFP_KERNEL);
if (!scpi_sensors)
return -ENOMEM;
scpi_sensors->data = devm_kcalloc(dev, nr_sensors,
sizeof(*scpi_sensors->data), GFP_KERNEL);
if (!scpi_sensors->data)
return -ENOMEM;
scpi_sensors->attrs = devm_kcalloc(dev, (nr_sensors * 2) + 1,
sizeof(*scpi_sensors->attrs), GFP_KERNEL);
if (!scpi_sensors->attrs)
return -ENOMEM;
scpi_sensors->scpi_ops = scpi_ops;
for (i = 0; i < nr_sensors; i++) {
struct sensor_data *sensor = &scpi_sensors->data[i];
ret = scpi_ops->sensor_get_info(i, &sensor->info);
if (ret)
return ret;
switch (sensor->info.class) {
case TEMPERATURE:
snprintf(sensor->input, sizeof(sensor->input),
"temp%d_input", num_temp + 1);
snprintf(sensor->label, sizeof(sensor->input),
"temp%d_label", num_temp + 1);
num_temp++;
break;
case VOLTAGE:
snprintf(sensor->input, sizeof(sensor->input),
"in%d_input", num_volt);
snprintf(sensor->label, sizeof(sensor->input),
"in%d_label", num_volt);
num_volt++;
break;
case CURRENT:
snprintf(sensor->input, sizeof(sensor->input),
"curr%d_input", num_current + 1);
snprintf(sensor->label, sizeof(sensor->input),
"curr%d_label", num_current + 1);
num_current++;
break;
case POWER:
snprintf(sensor->input, sizeof(sensor->input),
"power%d_input", num_power + 1);
snprintf(sensor->label, sizeof(sensor->input),
"power%d_label", num_power + 1);
num_power++;
break;
default:
break;
}
sensor->dev_attr_input.attr.mode = S_IRUGO;
sensor->dev_attr_input.show = scpi_show_sensor;
sensor->dev_attr_input.attr.name = sensor->input;
sensor->dev_attr_label.attr.mode = S_IRUGO;
sensor->dev_attr_label.show = scpi_show_label;
sensor->dev_attr_label.attr.name = sensor->label;
scpi_sensors->attrs[i << 1] = &sensor->dev_attr_input.attr;
scpi_sensors->attrs[(i << 1) + 1] = &sensor->dev_attr_label.attr;
sysfs_attr_init(scpi_sensors->attrs[i << 1]);
sysfs_attr_init(scpi_sensors->attrs[(i << 1) + 1]);
}
scpi_sensors->group.attrs = scpi_sensors->attrs;
scpi_sensors->groups[0] = &scpi_sensors->group;
platform_set_drvdata(pdev, scpi_sensors);
hwdev = devm_hwmon_device_register_with_groups(dev,
"scpi_sensors", scpi_sensors, scpi_sensors->groups);
if (IS_ERR(hwdev))
return PTR_ERR(hwdev);
/*
* Register the temperature sensors with the thermal framework
* to allow their usage in setting up the thermal zones from
* device tree.
*
* NOTE: Not all temperature sensors maybe used for thermal
* control
*/
INIT_LIST_HEAD(&scpi_sensors->thermal_zones);
for (i = 0; i < nr_sensors; i++) {
struct sensor_data *sensor = &scpi_sensors->data[i];
struct scpi_thermal_zone *zone;
if (sensor->info.class != TEMPERATURE)
continue;
zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
if (!zone) {
ret = -ENOMEM;
goto unregister_tzd;
}
zone->sensor_id = i;
zone->scpi_sensors = scpi_sensors;
zone->tzd = thermal_zone_of_sensor_register(dev, i, zone,
&scpi_sensor_ops);
/*
* The call to thermal_zone_of_sensor_register returns
* an error for sensors that are not associated with
* any thermal zones or if the thermal subsystem is
* not configured.
*/
if (IS_ERR(zone->tzd)) {
devm_kfree(dev, zone);
continue;
}
list_add(&zone->list, &scpi_sensors->thermal_zones);
}
return 0;
unregister_tzd:
unregister_thermal_zones(pdev, scpi_sensors);
return ret;
}
static int scpi_hwmon_remove(struct platform_device *pdev)
{
struct scpi_sensors *scpi_sensors = platform_get_drvdata(pdev);
unregister_thermal_zones(pdev, scpi_sensors);
return 0;
}
static const struct of_device_id scpi_of_match[] = {
{.compatible = "arm,scpi-sensors"},
{},
};
static struct platform_driver scpi_hwmon_platdrv = {
.driver = {
.name = "scpi-hwmon",
.owner = THIS_MODULE,
.of_match_table = scpi_of_match,
},
.probe = scpi_hwmon_probe,
.remove = scpi_hwmon_remove,
};
module_platform_driver(scpi_hwmon_platdrv);
MODULE_AUTHOR("Punit Agrawal <punit.agrawal@arm.com>");
MODULE_DESCRIPTION("ARM SCPI HWMON interface driver");
MODULE_LICENSE("GPL v2");
......@@ -118,7 +118,8 @@ static int pl172_setup_static(struct amba_device *adev,
if (of_property_read_bool(np, "mpmc,extended-wait"))
cfg |= MPMC_STATIC_CFG_EW;
if (of_property_read_bool(np, "mpmc,buffer-enable"))
if (amba_part(adev) == 0x172 &&
of_property_read_bool(np, "mpmc,buffer-enable"))
cfg |= MPMC_STATIC_CFG_B;
if (of_property_read_bool(np, "mpmc,write-protect"))
......@@ -190,6 +191,8 @@ static int pl172_parse_cs_config(struct amba_device *adev,
}
static const char * const pl172_revisions[] = {"r1", "r2", "r2p3", "r2p4"};
static const char * const pl175_revisions[] = {"r1"};
static const char * const pl176_revisions[] = {"r0"};
static int pl172_probe(struct amba_device *adev, const struct amba_id *id)
{
......@@ -202,6 +205,12 @@ static int pl172_probe(struct amba_device *adev, const struct amba_id *id)
if (amba_part(adev) == 0x172) {
if (amba_rev(adev) < ARRAY_SIZE(pl172_revisions))
rev = pl172_revisions[amba_rev(adev)];
} else if (amba_part(adev) == 0x175) {
if (amba_rev(adev) < ARRAY_SIZE(pl175_revisions))
rev = pl175_revisions[amba_rev(adev)];
} else if (amba_part(adev) == 0x176) {
if (amba_rev(adev) < ARRAY_SIZE(pl176_revisions))
rev = pl176_revisions[amba_rev(adev)];
}
dev_info(dev, "ARM PL%x revision %s\n", amba_part(adev), rev);
......@@ -278,9 +287,20 @@ static int pl172_remove(struct amba_device *adev)
}
static const struct amba_id pl172_ids[] = {
/* PrimeCell MPMC PL172, EMC found on NXP LPC18xx and LPC43xx */
{
.id = 0x07341172,
.mask = 0xffffffff,
.id = 0x07041172,
.mask = 0x3f0fffff,
},
/* PrimeCell MPMC PL175, EMC found on NXP LPC32xx */
{
.id = 0x07041175,
.mask = 0x3f0fffff,
},
/* PrimeCell MPMC PL176 */
{
.id = 0x89041176,
.mask = 0xff0fffff,
},
{ 0, 0 },
};
......
......@@ -125,6 +125,10 @@ static int __init tc_probe(struct platform_device *pdev)
if (IS_ERR(clk))
return PTR_ERR(clk);
tc->slow_clk = devm_clk_get(&pdev->dev, "slow_clk");
if (IS_ERR(tc->slow_clk))
return PTR_ERR(tc->slow_clk);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tc->regs = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(tc->regs))
......
......@@ -305,7 +305,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
*/
if (i == 5) {
i = slowclk;
rate = 32768;
rate = clk_get_rate(tc->slow_clk);
min = div_u64(NSEC_PER_SEC, rate);
max = min << tc->tcb_config->counter_width;
......@@ -387,9 +387,9 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL);
if (tcbpwm == NULL) {
atmel_tc_free(tc);
err = -ENOMEM;
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
goto err_free_tc;
}
tcbpwm->chip.dev = &pdev->dev;
......@@ -400,17 +400,27 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
tcbpwm->chip.npwm = NPWM;
tcbpwm->tc = tc;
err = clk_prepare_enable(tc->slow_clk);
if (err)
goto err_free_tc;
spin_lock_init(&tcbpwm->lock);
err = pwmchip_add(&tcbpwm->chip);
if (err < 0) {
atmel_tc_free(tc);
return err;
}
if (err < 0)
goto err_disable_clk;
platform_set_drvdata(pdev, tcbpwm);
return 0;
err_disable_clk:
clk_disable_unprepare(tcbpwm->tc->slow_clk);
err_free_tc:
atmel_tc_free(tc);
return err;
}
static int atmel_tcb_pwm_remove(struct platform_device *pdev)
......@@ -418,6 +428,8 @@ static int atmel_tcb_pwm_remove(struct platform_device *pdev)
struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
int err;
clk_disable_unprepare(tcbpwm->tc->slow_clk);
err = pwmchip_remove(&tcbpwm->chip);
if (err < 0)
return err;
......
......@@ -3,6 +3,7 @@ menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/brcmstb/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/rockchip/Kconfig"
source "drivers/soc/sunxi/Kconfig"
source "drivers/soc/ti/Kconfig"
source "drivers/soc/versatile/Kconfig"
......
......@@ -6,6 +6,7 @@ obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/
obj-$(CONFIG_MACH_DOVE) += dove/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_SOC_TI) += ti/
......
......@@ -19,6 +19,15 @@ config QCOM_PM
modes. It interface with various system drivers to put the cores in
low power modes.
config QCOM_SMEM
tristate "Qualcomm Shared Memory Manager (SMEM)"
depends on ARCH_QCOM
depends on HWSPINLOCK
help
Say y here to enable support for the Qualcomm Shared Memory Manager.
The driver provides an interface to items in a heap shared among all
processors in a Qualcomm platform.
config QCOM_SMD
tristate "Qualcomm Shared Memory Driver (SMD)"
depends on QCOM_SMEM
......@@ -40,11 +49,3 @@ config QCOM_SMD_RPM
Say M here if you want to include support for the Qualcomm RPM as a
module. This will build a module called "qcom-smd-rpm".
config QCOM_SMEM
tristate "Qualcomm Shared Memory Manager (SMEM)"
depends on ARCH_QCOM
help
Say y here to enable support for the Qualcomm Shared Memory Manager.
The driver provides an interface to items in a heap shared among all
processors in a Qualcomm platform.
......@@ -17,6 +17,7 @@
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
#include <linux/soc/qcom/smd-rpm.h>
......@@ -44,8 +45,8 @@ struct qcom_smd_rpm {
* @length: length of the payload
*/
struct qcom_rpm_header {
u32 service_type;
u32 length;
__le32 service_type;
__le32 length;
};
/**
......@@ -57,11 +58,11 @@ struct qcom_rpm_header {
* @data_len: length of the payload following this header
*/
struct qcom_rpm_request {
u32 msg_id;
u32 flags;
u32 type;
u32 id;
u32 data_len;
__le32 msg_id;
__le32 flags;
__le32 type;
__le32 id;
__le32 data_len;
};
/**
......@@ -74,10 +75,10 @@ struct qcom_rpm_request {
* Multiple of these messages can be stacked in an rpm message.
*/
struct qcom_rpm_message {
u32 msg_type;
u32 length;
__le32 msg_type;
__le32 length;
union {
u32 msg_id;
__le32 msg_id;
u8 message[0];
};
};
......@@ -104,30 +105,34 @@ int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm,
static unsigned msg_id = 1;
int left;
int ret;
struct {
struct qcom_rpm_header hdr;
struct qcom_rpm_request req;
u8 payload[count];
} pkt;
u8 payload[];
} *pkt;
size_t size = sizeof(*pkt) + count;
/* SMD packets to the RPM may not exceed 256 bytes */
if (WARN_ON(sizeof(pkt) >= 256))
if (WARN_ON(size >= 256))
return -EINVAL;
pkt = kmalloc(size, GFP_KERNEL);
if (!pkt)
return -ENOMEM;
mutex_lock(&rpm->lock);
pkt.hdr.service_type = RPM_SERVICE_TYPE_REQUEST;
pkt.hdr.length = sizeof(struct qcom_rpm_request) + count;
pkt->hdr.service_type = cpu_to_le32(RPM_SERVICE_TYPE_REQUEST);
pkt->hdr.length = cpu_to_le32(sizeof(struct qcom_rpm_request) + count);
pkt.req.msg_id = msg_id++;
pkt.req.flags = BIT(state);
pkt.req.type = type;
pkt.req.id = id;
pkt.req.data_len = count;
memcpy(pkt.payload, buf, count);
pkt->req.msg_id = cpu_to_le32(msg_id++);
pkt->req.flags = cpu_to_le32(state);
pkt->req.type = cpu_to_le32(type);
pkt->req.id = cpu_to_le32(id);
pkt->req.data_len = cpu_to_le32(count);
memcpy(pkt->payload, buf, count);
ret = qcom_smd_send(rpm->rpm_channel, &pkt, sizeof(pkt));
ret = qcom_smd_send(rpm->rpm_channel, pkt, size);
if (ret)
goto out;
......@@ -138,6 +143,7 @@ int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm,
ret = rpm->ack_status;
out:
kfree(pkt);
mutex_unlock(&rpm->lock);
return ret;
}
......@@ -148,27 +154,29 @@ static int qcom_smd_rpm_callback(struct qcom_smd_device *qsdev,
size_t count)
{
const struct qcom_rpm_header *hdr = data;
size_t hdr_length = le32_to_cpu(hdr->length);
const struct qcom_rpm_message *msg;
struct qcom_smd_rpm *rpm = dev_get_drvdata(&qsdev->dev);
const u8 *buf = data + sizeof(struct qcom_rpm_header);
const u8 *end = buf + hdr->length;
const u8 *end = buf + hdr_length;
char msgbuf[32];
int status = 0;
u32 len;
u32 len, msg_length;
if (hdr->service_type != RPM_SERVICE_TYPE_REQUEST ||
hdr->length < sizeof(struct qcom_rpm_message)) {
if (le32_to_cpu(hdr->service_type) != RPM_SERVICE_TYPE_REQUEST ||
hdr_length < sizeof(struct qcom_rpm_message)) {
dev_err(&qsdev->dev, "invalid request\n");
return 0;
}
while (buf < end) {
msg = (struct qcom_rpm_message *)buf;
switch (msg->msg_type) {
msg_length = le32_to_cpu(msg->length);
switch (le32_to_cpu(msg->msg_type)) {
case RPM_MSG_TYPE_MSG_ID:
break;
case RPM_MSG_TYPE_ERR:
len = min_t(u32, ALIGN(msg->length, 4), sizeof(msgbuf));
len = min_t(u32, ALIGN(msg_length, 4), sizeof(msgbuf));
memcpy_fromio(msgbuf, msg->message, len);
msgbuf[len - 1] = 0;
......@@ -179,7 +187,7 @@ static int qcom_smd_rpm_callback(struct qcom_smd_device *qsdev,
break;
}
buf = PTR_ALIGN(buf + 2 * sizeof(u32) + msg->length, 4);
buf = PTR_ALIGN(buf + 2 * sizeof(u32) + msg_length, 4);
}
rpm->ack_status = status;
......
This diff is collapsed.
This diff is collapsed.
if ARCH_ROCKCHIP || COMPILE_TEST
#
# Rockchip Soc drivers
#
config ROCKCHIP_PM_DOMAINS
bool "Rockchip generic power domain"
depends on PM
select PM_GENERIC_DOMAINS
help
Say y here to enable power domain support.
In order to meet high performance and low power requirements, a power
management unit is designed or saving power when RK3288 in low power
mode. The RK3288 PMU is dedicated for managing the power of the whole chip.
If unsure, say N.
endif
#
# Rockchip Soc drivers
#
obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o
This diff is collapsed.
......@@ -29,3 +29,4 @@
#define CLKID_SMEMC 24
#define CLKID_PCIE 25
#define CLKID_TWD 26
#define CLKID_CPU 27
#ifndef __DT_BINDINGS_POWER_RK3288_POWER_H__
#define __DT_BINDINGS_POWER_RK3288_POWER_H__
/**
* RK3288 Power Domain and Voltage Domain Summary.
*/
/* VD_CORE */
#define RK3288_PD_A17_0 0
#define RK3288_PD_A17_1 1
#define RK3288_PD_A17_2 2
#define RK3288_PD_A17_3 3
#define RK3288_PD_SCU 4
#define RK3288_PD_DEBUG 5
#define RK3288_PD_MEM 6
/* VD_LOGIC */
#define RK3288_PD_BUS 7
#define RK3288_PD_PERI 8
#define RK3288_PD_VIO 9
#define RK3288_PD_ALIVE 10
#define RK3288_PD_HEVC 11
#define RK3288_PD_VIDEO 12
/* VD_GPU */
#define RK3288_PD_GPU 13
/* VD_PMU */
#define RK3288_PD_PMU 14
#endif
......@@ -67,6 +67,7 @@ struct atmel_tc {
const struct atmel_tcb_config *tcb_config;
int irq[3];
struct clk *clk[3];
struct clk *slow_clk;
struct list_head node;
bool allocated;
};
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment