Commit 9638685e 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:
 "Driver updates for ARM SoCs.  Some for SoC-family code under
  drivers/soc, but also some other driver updates that don't belong
  anywhere else.  We also bring in the drivers/reset code through
  arm-soc.

  Some of the larger updates:

   - Qualcomm support for SMEM, SMSM, SMP2P.  All used to communicate
     with other parts of the chip/board on these platforms, all
     proprietary protocols that don't fit into other subsystems and live
     in drivers/soc for now.

   - System bus driver for UniPhier

   - Driver for the TI Wakeup M3 IPC device

   - Power management for Raspberry PI

  + Again a bunch of other smaller updates and patches"

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (38 commits)
  bus: uniphier: allow only built-in driver
  ARM: bcm2835: clarify RASPBERRYPI_FIRMWARE dependency
  MAINTAINERS: Drop Kumar Gala from QCOM
  bus: uniphier-system-bus: add UniPhier System Bus driver
  ARM: bcm2835: add rpi power domain driver
  dt-bindings: add rpi power domain driver bindings
  ARM: bcm2835: Define two new packets from the latest firmware.
  drivers/soc: make mediatek/mtk-scpsys.c explicitly non-modular
  soc: mediatek: SCPSYS: Add regulator support
  MAINTAINERS: Change QCOM entries
  soc: qcom: smd-rpm: Add existing platform support
  memory/tegra: Add number of TLB lines for Tegra124
  reset: hi6220: fix modular build
  soc: qcom: Introduce WCNSS_CTRL SMD client
  ARM: qcom: select ARM_CPU_SUSPEND for power management
  MAINTAINERS: Add rules for Qualcomm dts files
  soc: qcom: enable smsm/smp2p modular build
  serial: msm_serial: Make config tristate
  soc: qcom: smp2p: Qualcomm Shared Memory Point to Point
  soc: qcom: smsm: Add driver for Qualcomm SMSM
  ...
parents 03d7d124 ce96cb73
UniPhier System Bus
The UniPhier System Bus is an external bus that connects on-board devices to
the UniPhier SoC. It is a simple (semi-)parallel bus with address, data, and
some control signals. It supports up to 8 banks (chip selects).
Before any access to the bus, the bus controller must be configured; the bus
controller registers provide the control for the translation from the offset
within each bank to the CPU-viewed address. The needed setup includes the base
address, the size of each bank. Optionally, some timing parameters can be
optimized for faster bus access.
Required properties:
- compatible: should be "socionext,uniphier-system-bus".
- reg: offset and length of the register set for the bus controller device.
- #address-cells: should be 2. The first cell is the bank number (chip select).
The second cell is the address offset within the bank.
- #size-cells: should be 1.
- ranges: should provide a proper address translation from the System Bus to
the parent bus.
Note:
The address region(s) that can be assigned for the System Bus is implementation
defined. Some SoCs can use 0x00000000-0x0fffffff and 0x40000000-0x4fffffff,
while other SoCs can only use 0x40000000-0x4fffffff. There might be additional
limitations depending on SoCs and the boot mode. The address translation is
arbitrary as long as the banks are assigned in the supported address space with
the required alignment and they do not overlap one another.
For example, it is possible to map:
bank 0 to 0x42000000-0x43ffffff, bank 5 to 0x46000000-0x46ffffff
It is also possible to map:
bank 0 to 0x48000000-0x49ffffff, bank 5 to 0x44000000-0x44ffffff
There is no reason to stick to a particular translation mapping, but the
"ranges" property should provide a "reasonable" default that is known to work.
The software should initialize the bus controller according to it.
Example:
system-bus {
compatible = "socionext,uniphier-system-bus";
reg = <0x58c00000 0x400>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <1 0x00000000 0x42000000 0x02000000
5 0x00000000 0x46000000 0x01000000>;
ethernet@1,01f00000 {
compatible = "smsc,lan9115";
reg = <1 0x01f00000 0x1000>;
interrupts = <0 48 4>
phy-mode = "mii";
};
uart@5,00200000 {
compatible = "ns16550a";
reg = <5 0x00200000 0x20>;
interrupts = <0 49 4>
clock-frequency = <12288000>;
};
};
In this example,
- the Ethernet device is connected at the offset 0x01f00000 of CS1 and
mapped to 0x43f00000 of the parent bus.
- the UART device is connected at the offset 0x00200000 of CS5 and
mapped to 0x46200000 of the parent bus.
Hisilicon System Reset Controller
======================================
Please also refer to reset.txt in this directory for common reset
controller binding usage.
The reset controller registers are part of the system-ctl block on
hi6220 SoC.
Required properties:
- compatible: may be "hisilicon,hi6220-sysctrl"
- reg: should be register base and length as documented in the
datasheet
- #reset-cells: 1, see below
Example:
sys_ctrl: sys_ctrl@f7030000 {
compatible = "hisilicon,hi6220-sysctrl", "syscon";
reg = <0x0 0xf7030000 0x0 0x2000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
Specifying reset lines connected to IP modules
==============================================
example:
uart1: serial@..... {
...
resets = <&sys_ctrl PERIPH_RSTEN3_UART1>;
...
};
The index could be found in <dt-bindings/reset/hisi,hi6220-resets.h>.
Raspberry Pi power domain driver
Required properties:
- compatible: Should be "raspberrypi,bcm2835-power".
- firmware: Reference to the RPi firmware device node.
- #power-domain-cells: Should be <1>, we providing multiple power domains.
The valid defines for power domain are:
RPI_POWER_DOMAIN_I2C0
RPI_POWER_DOMAIN_I2C1
RPI_POWER_DOMAIN_I2C2
RPI_POWER_DOMAIN_VIDEO_SCALER
RPI_POWER_DOMAIN_VPU1
RPI_POWER_DOMAIN_HDMI
RPI_POWER_DOMAIN_USB
RPI_POWER_DOMAIN_VEC
RPI_POWER_DOMAIN_JPEG
RPI_POWER_DOMAIN_H264
RPI_POWER_DOMAIN_V3D
RPI_POWER_DOMAIN_ISP
RPI_POWER_DOMAIN_UNICAM0
RPI_POWER_DOMAIN_UNICAM1
RPI_POWER_DOMAIN_CCP2RX
RPI_POWER_DOMAIN_CSI2
RPI_POWER_DOMAIN_CPI
RPI_POWER_DOMAIN_DSI0
RPI_POWER_DOMAIN_DSI1
RPI_POWER_DOMAIN_TRANSPOSER
RPI_POWER_DOMAIN_CCP2TX
RPI_POWER_DOMAIN_CDP
RPI_POWER_DOMAIN_ARM
Example:
power: power {
compatible = "raspberrypi,bcm2835-power";
firmware = <&firmware>;
#power-domain-cells = <1>;
};
Example for using power domain:
&usb {
power-domains = <&power RPI_POWER_DOMAIN_USB>;
};
Wakeup M3 IPC Driver
=====================
The TI AM33xx and AM43xx family of devices use a small Cortex M3 co-processor
(commonly referred to as Wakeup M3 or CM3) to help with various low power tasks
that cannot be controlled from the MPU, like suspend/resume and certain deep
C-states for CPU Idle. Once the wkup_m3_ipc driver uses the wkup_m3_rproc driver
to boot the wkup_m3, it handles communication with the CM3 using IPC registers
present in the SoC's control module and a mailbox. The wkup_m3_ipc exposes an
API to allow the SoC PM code to execute specific PM tasks.
Wkup M3 Device Node:
====================
A wkup_m3_ipc device node is used to represent the IPC registers within an
SoC.
Required properties:
--------------------
- compatible: Should be,
"ti,am3352-wkup-m3-ipc" for AM33xx SoCs
"ti,am4372-wkup-m3-ipc" for AM43xx SoCs
- reg: Contains the IPC register address space to communicate
with the Wakeup M3 processor
- interrupts: Contains the interrupt information for the wkup_m3
interrupt that signals the MPU.
- ti,rproc: phandle to the wkup_m3 rproc node so the IPC driver
can boot it.
- mboxes: phandles used by IPC framework to get correct mbox
channel for communication. Must point to appropriate
mbox_wkupm3 child node.
Example:
--------
/* AM33xx */
l4_wkup: l4_wkup@44c00000 {
...
scm: scm@210000 {
compatible = "ti,am3-scm", "simple-bus";
reg = <0x210000 0x2000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x210000 0x2000>;
...
wkup_m3_ipc: wkup_m3_ipc@1324 {
compatible = "ti,am3352-wkup-m3-ipc";
reg = <0x1324 0x24>;
interrupts = <78>;
ti,rproc = <&wkup_m3>;
mboxes = <&mailbox &mbox_wkupm3>;
};
...
};
};
...@@ -1415,12 +1415,13 @@ W: http://www.arm.linux.org.uk/ ...@@ -1415,12 +1415,13 @@ W: http://www.arm.linux.org.uk/
S: Maintained S: Maintained
ARM/QUALCOMM SUPPORT ARM/QUALCOMM SUPPORT
M: Kumar Gala <galak@codeaurora.org> M: Andy Gross <andy.gross@linaro.org>
M: Andy Gross <agross@codeaurora.org> M: David Brown <david.brown@linaro.org>
M: David Brown <davidb@codeaurora.org>
L: linux-arm-msm@vger.kernel.org L: linux-arm-msm@vger.kernel.org
L: linux-soc@vger.kernel.org L: linux-soc@vger.kernel.org
S: Maintained S: Maintained
F: arch/arm/boot/dts/qcom-*.dts
F: arch/arm/boot/dts/qcom-*.dtsi
F: arch/arm/mach-qcom/ F: arch/arm/mach-qcom/
F: drivers/soc/qcom/ F: drivers/soc/qcom/
F: drivers/tty/serial/msm_serial.h F: drivers/tty/serial/msm_serial.h
...@@ -1428,7 +1429,7 @@ F: drivers/tty/serial/msm_serial.c ...@@ -1428,7 +1429,7 @@ F: drivers/tty/serial/msm_serial.c
F: drivers/*/pm8???-* F: drivers/*/pm8???-*
F: drivers/mfd/ssbi.c F: drivers/mfd/ssbi.c
F: drivers/firmware/qcom_scm.c F: drivers/firmware/qcom_scm.c
T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux.git
ARM/RADISYS ENP2611 MACHINE SUPPORT ARM/RADISYS ENP2611 MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org> M: Lennert Buytenhek <kernel@wantstofly.org>
...@@ -1673,6 +1674,7 @@ F: arch/arm/include/asm/hardware/cache-uniphier.h ...@@ -1673,6 +1674,7 @@ F: arch/arm/include/asm/hardware/cache-uniphier.h
F: arch/arm/mach-uniphier/ F: arch/arm/mach-uniphier/
F: arch/arm/mm/cache-uniphier.c F: arch/arm/mm/cache-uniphier.c
F: arch/arm64/boot/dts/socionext/ F: arch/arm64/boot/dts/socionext/
F: drivers/bus/uniphier-system-bus.c
F: drivers/i2c/busses/i2c-uniphier* F: drivers/i2c/busses/i2c-uniphier*
F: drivers/pinctrl/uniphier/ F: drivers/pinctrl/uniphier/
F: drivers/tty/serial/8250/8250_uniphier.c F: drivers/tty/serial/8250/8250_uniphier.c
......
...@@ -147,6 +147,7 @@ sys_ctrl: sys_ctrl@f7030000 { ...@@ -147,6 +147,7 @@ sys_ctrl: sys_ctrl@f7030000 {
compatible = "hisilicon,hi6220-sysctrl", "syscon"; compatible = "hisilicon,hi6220-sysctrl", "syscon";
reg = <0x0 0xf7030000 0x0 0x2000>; reg = <0x0 0xf7030000 0x0 0x2000>;
#clock-cells = <1>; #clock-cells = <1>;
#reset-cells = <1>;
}; };
media_ctrl: media_ctrl@f4410000 { media_ctrl: media_ctrl@f4410000 {
......
...@@ -131,6 +131,14 @@ config SUNXI_RSB ...@@ -131,6 +131,14 @@ config SUNXI_RSB
with various RSB based devices, such as AXP223, AXP8XX PMICs, with various RSB based devices, such as AXP223, AXP8XX PMICs,
and AC100/AC200 ICs. and AC100/AC200 ICs.
config UNIPHIER_SYSTEM_BUS
bool "UniPhier System Bus driver"
depends on ARCH_UNIPHIER && OF
default y
help
Support for UniPhier System Bus, a simple external bus. This is
needed to use on-board devices connected to UniPhier SoCs.
config VEXPRESS_CONFIG config VEXPRESS_CONFIG
bool "Versatile Express configuration bus" bool "Versatile Express configuration bus"
default y if ARCH_VEXPRESS default y if ARCH_VEXPRESS
......
...@@ -17,4 +17,5 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o ...@@ -17,4 +17,5 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
/*
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/io.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
/* System Bus Controller registers */
#define UNIPHIER_SBC_BASE 0x100 /* base address of bank0 space */
#define UNIPHIER_SBC_BASE_BE BIT(0) /* bank_enable */
#define UNIPHIER_SBC_CTRL0 0x200 /* timing parameter 0 of bank0 */
#define UNIPHIER_SBC_CTRL1 0x204 /* timing parameter 1 of bank0 */
#define UNIPHIER_SBC_CTRL2 0x208 /* timing parameter 2 of bank0 */
#define UNIPHIER_SBC_CTRL3 0x20c /* timing parameter 3 of bank0 */
#define UNIPHIER_SBC_CTRL4 0x300 /* timing parameter 4 of bank0 */
#define UNIPHIER_SBC_STRIDE 0x10 /* register stride to next bank */
#define UNIPHIER_SBC_NR_BANKS 8 /* number of banks (chip select) */
#define UNIPHIER_SBC_BASE_DUMMY 0xffffffff /* data to squash bank 0, 1 */
struct uniphier_system_bus_bank {
u32 base;
u32 end;
};
struct uniphier_system_bus_priv {
struct device *dev;
void __iomem *membase;
struct uniphier_system_bus_bank bank[UNIPHIER_SBC_NR_BANKS];
};
static int uniphier_system_bus_add_bank(struct uniphier_system_bus_priv *priv,
int bank, u32 addr, u64 paddr, u32 size)
{
u64 end, mask;
dev_dbg(priv->dev,
"range found: bank = %d, addr = %08x, paddr = %08llx, size = %08x\n",
bank, addr, paddr, size);
if (bank >= ARRAY_SIZE(priv->bank)) {
dev_err(priv->dev, "unsupported bank number %d\n", bank);
return -EINVAL;
}
if (priv->bank[bank].base || priv->bank[bank].end) {
dev_err(priv->dev,
"range for bank %d has already been specified\n", bank);
return -EINVAL;
}
if (paddr > U32_MAX) {
dev_err(priv->dev, "base address %llx is too high\n", paddr);
return -EINVAL;
}
end = paddr + size;
if (addr > paddr) {
dev_err(priv->dev,
"base %08x cannot be mapped to %08llx of parent\n",
addr, paddr);
return -EINVAL;
}
paddr -= addr;
paddr = round_down(paddr, 0x00020000);
end = round_up(end, 0x00020000);
if (end > U32_MAX) {
dev_err(priv->dev, "end address %08llx is too high\n", end);
return -EINVAL;
}
mask = paddr ^ (end - 1);
mask = roundup_pow_of_two(mask);
paddr = round_down(paddr, mask);
end = round_up(end, mask);
priv->bank[bank].base = paddr;
priv->bank[bank].end = end;
dev_dbg(priv->dev, "range added: bank = %d, addr = %08x, end = %08x\n",
bank, priv->bank[bank].base, priv->bank[bank].end);
return 0;
}
static int uniphier_system_bus_check_overlap(
const struct uniphier_system_bus_priv *priv)
{
int i, j;
for (i = 0; i < ARRAY_SIZE(priv->bank); i++) {
for (j = i + 1; j < ARRAY_SIZE(priv->bank); j++) {
if (priv->bank[i].end > priv->bank[j].base ||
priv->bank[i].base < priv->bank[j].end) {
dev_err(priv->dev,
"region overlap between bank%d and bank%d\n",
i, j);
return -EINVAL;
}
}
}
return 0;
}
static void uniphier_system_bus_check_boot_swap(
struct uniphier_system_bus_priv *priv)
{
void __iomem *base_reg = priv->membase + UNIPHIER_SBC_BASE;
int is_swapped;
is_swapped = !(readl(base_reg) & UNIPHIER_SBC_BASE_BE);
dev_dbg(priv->dev, "Boot Swap: %s\n", is_swapped ? "on" : "off");
/*
* If BOOT_SWAP was asserted on power-on-reset, the CS0 and CS1 are
* swapped. In this case, bank0 and bank1 should be swapped as well.
*/
if (is_swapped)
swap(priv->bank[0], priv->bank[1]);
}
static void uniphier_system_bus_set_reg(
const struct uniphier_system_bus_priv *priv)
{
void __iomem *base_reg = priv->membase + UNIPHIER_SBC_BASE;
u32 base, end, mask, val;
int i;
for (i = 0; i < ARRAY_SIZE(priv->bank); i++) {
base = priv->bank[i].base;
end = priv->bank[i].end;
if (base == end) {
/*
* If SBC_BASE0 or SBC_BASE1 is set to zero, the access
* to anywhere in the system bus space is routed to
* bank 0 (if boot swap if off) or bank 1 (if boot swap
* if on). It means that CPUs cannot get access to
* bank 2 or later. In other words, bank 0/1 cannot
* be disabled even if its bank_enable bits is cleared.
* This seems odd, but it is how this hardware goes.
* As a workaround, dummy data (0xffffffff) should be
* set when the bank 0/1 is unused. As for bank 2 and
* later, they can be simply disable by clearing the
* bank_enable bit.
*/
if (i < 2)
val = UNIPHIER_SBC_BASE_DUMMY;
else
val = 0;
} else {
mask = base ^ (end - 1);
val = base & 0xfffe0000;
val |= (~mask >> 16) & 0xfffe;
val |= UNIPHIER_SBC_BASE_BE;
}
dev_dbg(priv->dev, "SBC_BASE[%d] = 0x%08x\n", i, val);
writel(val, base_reg + UNIPHIER_SBC_STRIDE * i);
}
}
static int uniphier_system_bus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_system_bus_priv *priv;
struct resource *regs;
const __be32 *ranges;
u32 cells, addr, size;
u64 paddr;
int pna, bank, rlen, rone, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->membase = devm_ioremap_resource(dev, regs);
if (IS_ERR(priv->membase))
return PTR_ERR(priv->membase);
priv->dev = dev;
pna = of_n_addr_cells(dev->of_node);
ret = of_property_read_u32(dev->of_node, "#address-cells", &cells);
if (ret) {
dev_err(dev, "failed to get #address-cells\n");
return ret;
}
if (cells != 2) {
dev_err(dev, "#address-cells must be 2\n");
return -EINVAL;
}
ret = of_property_read_u32(dev->of_node, "#size-cells", &cells);
if (ret) {
dev_err(dev, "failed to get #size-cells\n");
return ret;
}
if (cells != 1) {
dev_err(dev, "#size-cells must be 1\n");
return -EINVAL;
}
ranges = of_get_property(dev->of_node, "ranges", &rlen);
if (!ranges) {
dev_err(dev, "failed to get ranges property\n");
return -ENOENT;
}
rlen /= sizeof(*ranges);
rone = pna + 2;
for (; rlen >= rone; rlen -= rone) {
bank = be32_to_cpup(ranges++);
addr = be32_to_cpup(ranges++);
paddr = of_translate_address(dev->of_node, ranges);
if (paddr == OF_BAD_ADDR)
return -EINVAL;
ranges += pna;
size = be32_to_cpup(ranges++);
ret = uniphier_system_bus_add_bank(priv, bank, addr,
paddr, size);
if (ret)
return ret;
}
ret = uniphier_system_bus_check_overlap(priv);
if (ret)
return ret;
uniphier_system_bus_check_boot_swap(priv);
uniphier_system_bus_set_reg(priv);
/* Now, the bus is configured. Populate platform_devices below it */
return of_platform_populate(dev->of_node, of_default_bus_match_table,
NULL, dev);
}
static const struct of_device_id uniphier_system_bus_match[] = {
{ .compatible = "socionext,uniphier-system-bus" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_system_bus_match);
static struct platform_driver uniphier_system_bus_driver = {
.probe = uniphier_system_bus_probe,
.driver = {
.name = "uniphier-system-bus",
.of_match_table = uniphier_system_bus_match,
},
};
module_platform_driver(uniphier_system_bus_driver);
MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier System Bus driver");
MODULE_LICENSE("GPL");
...@@ -1007,6 +1007,7 @@ static const struct tegra_smmu_soc tegra124_smmu_soc = { ...@@ -1007,6 +1007,7 @@ static const struct tegra_smmu_soc tegra124_smmu_soc = {
.num_swgroups = ARRAY_SIZE(tegra124_swgroups), .num_swgroups = ARRAY_SIZE(tegra124_swgroups),
.supports_round_robin_arbitration = true, .supports_round_robin_arbitration = true,
.supports_request_limit = true, .supports_request_limit = true,
.num_tlb_lines = 32,
.num_asids = 128, .num_asids = 128,
}; };
......
...@@ -13,3 +13,4 @@ menuconfig RESET_CONTROLLER ...@@ -13,3 +13,4 @@ menuconfig RESET_CONTROLLER
If unsure, say no. If unsure, say no.
source "drivers/reset/sti/Kconfig" source "drivers/reset/sti/Kconfig"
source "drivers/reset/hisilicon/Kconfig"
obj-$(CONFIG_RESET_CONTROLLER) += core.o obj-y += core.o
obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
obj-$(CONFIG_ARCH_STI) += sti/ obj-$(CONFIG_ARCH_STI) += sti/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o
obj-$(CONFIG_ATH79) += reset-ath79.o obj-$(CONFIG_ATH79) += reset-ath79.o
...@@ -30,7 +30,6 @@ static LIST_HEAD(reset_controller_list); ...@@ -30,7 +30,6 @@ static LIST_HEAD(reset_controller_list);
*/ */
struct reset_control { struct reset_control {
struct reset_controller_dev *rcdev; struct reset_controller_dev *rcdev;
struct device *dev;
unsigned int id; unsigned int id;
}; };
...@@ -95,7 +94,7 @@ int reset_control_reset(struct reset_control *rstc) ...@@ -95,7 +94,7 @@ int reset_control_reset(struct reset_control *rstc)
if (rstc->rcdev->ops->reset) if (rstc->rcdev->ops->reset)
return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
return -ENOSYS; return -ENOTSUPP;
} }
EXPORT_SYMBOL_GPL(reset_control_reset); EXPORT_SYMBOL_GPL(reset_control_reset);
...@@ -108,7 +107,7 @@ int reset_control_assert(struct reset_control *rstc) ...@@ -108,7 +107,7 @@ int reset_control_assert(struct reset_control *rstc)
if (rstc->rcdev->ops->assert) if (rstc->rcdev->ops->assert)
return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
return -ENOSYS; return -ENOTSUPP;
} }
EXPORT_SYMBOL_GPL(reset_control_assert); EXPORT_SYMBOL_GPL(reset_control_assert);
...@@ -121,7 +120,7 @@ int reset_control_deassert(struct reset_control *rstc) ...@@ -121,7 +120,7 @@ int reset_control_deassert(struct reset_control *rstc)
if (rstc->rcdev->ops->deassert) if (rstc->rcdev->ops->deassert)
return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id);
return -ENOSYS; return -ENOTSUPP;
} }
EXPORT_SYMBOL_GPL(reset_control_deassert); EXPORT_SYMBOL_GPL(reset_control_deassert);
...@@ -136,32 +135,29 @@ int reset_control_status(struct reset_control *rstc) ...@@ -136,32 +135,29 @@ int reset_control_status(struct reset_control *rstc)
if (rstc->rcdev->ops->status) if (rstc->rcdev->ops->status)
return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
return -ENOSYS; return -ENOTSUPP;
} }
EXPORT_SYMBOL_GPL(reset_control_status); EXPORT_SYMBOL_GPL(reset_control_status);
/** /**
* of_reset_control_get - Lookup and obtain a reference to a reset controller. * of_reset_control_get_by_index - Lookup and obtain a reference to a reset
* controller by index.
* @node: device to be reset by the controller * @node: device to be reset by the controller
* @id: reset line name * @index: index of the reset controller
* *
* Returns a struct reset_control or IS_ERR() condition containing errno. * This is to be used to perform a list of resets for a device or power domain
* * in whatever order. Returns a struct reset_control or IS_ERR() condition
* Use of id names is optional. * containing errno.
*/ */
struct reset_control *of_reset_control_get(struct device_node *node, struct reset_control *of_reset_control_get_by_index(struct device_node *node,
const char *id) int index)
{ {
struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER);
struct reset_controller_dev *r, *rcdev; struct reset_controller_dev *r, *rcdev;
struct of_phandle_args args; struct of_phandle_args args;
int index = 0;
int rstc_id; int rstc_id;
int ret; int ret;
if (id)
index = of_property_match_string(node,
"reset-names", id);
ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", ret = of_parse_phandle_with_args(node, "resets", "#reset-cells",
index, &args); index, &args);
if (ret) if (ret)
...@@ -202,6 +198,30 @@ struct reset_control *of_reset_control_get(struct device_node *node, ...@@ -202,6 +198,30 @@ struct reset_control *of_reset_control_get(struct device_node *node,
return rstc; return rstc;
} }
EXPORT_SYMBOL_GPL(of_reset_control_get_by_index);
/**
* of_reset_control_get - Lookup and obtain a reference to a reset controller.
* @node: device to be reset by the controller
* @id: reset line name
*
* Returns a struct reset_control or IS_ERR() condition containing errno.
*
* Use of id names is optional.
*/
struct reset_control *of_reset_control_get(struct device_node *node,
const char *id)
{
int index = 0;
if (id) {
index = of_property_match_string(node,
"reset-names", id);
if (index < 0)
return ERR_PTR(-ENOENT);
}
return of_reset_control_get_by_index(node, index);
}
EXPORT_SYMBOL_GPL(of_reset_control_get); EXPORT_SYMBOL_GPL(of_reset_control_get);
/** /**
...@@ -215,16 +235,10 @@ EXPORT_SYMBOL_GPL(of_reset_control_get); ...@@ -215,16 +235,10 @@ EXPORT_SYMBOL_GPL(of_reset_control_get);
*/ */
struct reset_control *reset_control_get(struct device *dev, const char *id) struct reset_control *reset_control_get(struct device *dev, const char *id)
{ {
struct reset_control *rstc;
if (!dev) if (!dev)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
rstc = of_reset_control_get(dev->of_node, id); return of_reset_control_get(dev->of_node, id);
if (!IS_ERR(rstc))
rstc->dev = dev;
return rstc;
} }
EXPORT_SYMBOL_GPL(reset_control_get); EXPORT_SYMBOL_GPL(reset_control_get);
......
config COMMON_RESET_HI6220
tristate "Hi6220 Reset Driver"
depends on (ARCH_HISI && RESET_CONTROLLER)
help
Build the Hisilicon Hi6220 reset driver.
obj-$(CONFIG_COMMON_RESET_HI6220) += hi6220_reset.o
/*
* Hisilicon Hi6220 reset controller driver
*
* Copyright (c) 2015 Hisilicon Limited.
*
* Author: Feng Chen <puck.chen@hisilicon.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/io.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/of.h>
#include <linux/reset-controller.h>
#include <linux/reset.h>
#include <linux/platform_device.h>
#define ASSERT_OFFSET 0x300
#define DEASSERT_OFFSET 0x304
#define MAX_INDEX 0x509
#define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev)
struct hi6220_reset_data {
void __iomem *assert_base;
void __iomem *deassert_base;
struct reset_controller_dev rc_dev;
};
static int hi6220_reset_assert(struct reset_controller_dev *rc_dev,
unsigned long idx)
{
struct hi6220_reset_data *data = to_reset_data(rc_dev);
int bank = idx >> 8;
int offset = idx & 0xff;
writel(BIT(offset), data->assert_base + (bank * 0x10));
return 0;
}
static int hi6220_reset_deassert(struct reset_controller_dev *rc_dev,
unsigned long idx)
{
struct hi6220_reset_data *data = to_reset_data(rc_dev);
int bank = idx >> 8;
int offset = idx & 0xff;
writel(BIT(offset), data->deassert_base + (bank * 0x10));
return 0;
}
static struct reset_control_ops hi6220_reset_ops = {
.assert = hi6220_reset_assert,
.deassert = hi6220_reset_deassert,
};
static int hi6220_reset_probe(struct platform_device *pdev)
{
struct hi6220_reset_data *data;
struct resource *res;
void __iomem *src_base;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
src_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(src_base))
return PTR_ERR(src_base);
data->assert_base = src_base + ASSERT_OFFSET;
data->deassert_base = src_base + DEASSERT_OFFSET;
data->rc_dev.nr_resets = MAX_INDEX;
data->rc_dev.ops = &hi6220_reset_ops;
data->rc_dev.of_node = pdev->dev.of_node;
reset_controller_register(&data->rc_dev);
return 0;
}
static const struct of_device_id hi6220_reset_match[] = {
{ .compatible = "hisilicon,hi6220-sysctrl" },
{ },
};
static struct platform_driver hi6220_reset_driver = {
.probe = hi6220_reset_probe,
.driver = {
.name = "reset-hi6220",
.of_match_table = hi6220_reset_match,
},
};
static int __init hi6220_reset_init(void)
{
return platform_driver_register(&hi6220_reset_driver);
}
postcore_initcall(hi6220_reset_init);
...@@ -15,13 +15,17 @@ ...@@ -15,13 +15,17 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include <linux/reboot.h>
struct ath79_reset { struct ath79_reset {
struct reset_controller_dev rcdev; struct reset_controller_dev rcdev;
struct notifier_block restart_nb;
void __iomem *base; void __iomem *base;
spinlock_t lock; spinlock_t lock;
}; };
#define FULL_CHIP_RESET 24
static int ath79_reset_update(struct reset_controller_dev *rcdev, static int ath79_reset_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert) unsigned long id, bool assert)
{ {
...@@ -72,10 +76,22 @@ static struct reset_control_ops ath79_reset_ops = { ...@@ -72,10 +76,22 @@ static struct reset_control_ops ath79_reset_ops = {
.status = ath79_reset_status, .status = ath79_reset_status,
}; };
static int ath79_reset_restart_handler(struct notifier_block *nb,
unsigned long action, void *data)
{
struct ath79_reset *ath79_reset =
container_of(nb, struct ath79_reset, restart_nb);
ath79_reset_assert(&ath79_reset->rcdev, FULL_CHIP_RESET);
return NOTIFY_DONE;
}
static int ath79_reset_probe(struct platform_device *pdev) static int ath79_reset_probe(struct platform_device *pdev)
{ {
struct ath79_reset *ath79_reset; struct ath79_reset *ath79_reset;
struct resource *res; struct resource *res;
int err;
ath79_reset = devm_kzalloc(&pdev->dev, ath79_reset = devm_kzalloc(&pdev->dev,
sizeof(*ath79_reset), GFP_KERNEL); sizeof(*ath79_reset), GFP_KERNEL);
...@@ -96,13 +112,25 @@ static int ath79_reset_probe(struct platform_device *pdev) ...@@ -96,13 +112,25 @@ static int ath79_reset_probe(struct platform_device *pdev)
ath79_reset->rcdev.of_reset_n_cells = 1; ath79_reset->rcdev.of_reset_n_cells = 1;
ath79_reset->rcdev.nr_resets = 32; ath79_reset->rcdev.nr_resets = 32;
return reset_controller_register(&ath79_reset->rcdev); err = reset_controller_register(&ath79_reset->rcdev);
if (err)
return err;
ath79_reset->restart_nb.notifier_call = ath79_reset_restart_handler;
ath79_reset->restart_nb.priority = 128;
err = register_restart_handler(&ath79_reset->restart_nb);
if (err)
dev_warn(&pdev->dev, "Failed to register restart handler\n");
return 0;
} }
static int ath79_reset_remove(struct platform_device *pdev) static int ath79_reset_remove(struct platform_device *pdev)
{ {
struct ath79_reset *ath79_reset = platform_get_drvdata(pdev); struct ath79_reset *ath79_reset = platform_get_drvdata(pdev);
unregister_restart_handler(&ath79_reset->restart_nb);
reset_controller_unregister(&ath79_reset->rcdev); reset_controller_unregister(&ath79_reset->rcdev);
return 0; return 0;
......
...@@ -87,9 +87,7 @@ static int berlin2_reset_probe(struct platform_device *pdev) ...@@ -87,9 +87,7 @@ static int berlin2_reset_probe(struct platform_device *pdev)
priv->rcdev.of_reset_n_cells = 2; priv->rcdev.of_reset_n_cells = 2;
priv->rcdev.of_xlate = berlin_reset_xlate; priv->rcdev.of_xlate = berlin_reset_xlate;
reset_controller_register(&priv->rcdev); return reset_controller_register(&priv->rcdev);
return 0;
} }
static const struct of_device_id berlin_reset_dt_match[] = { static const struct of_device_id berlin_reset_dt_match[] = {
......
...@@ -133,9 +133,8 @@ static int socfpga_reset_probe(struct platform_device *pdev) ...@@ -133,9 +133,8 @@ static int socfpga_reset_probe(struct platform_device *pdev)
data->rcdev.nr_resets = NR_BANKS * BITS_PER_LONG; data->rcdev.nr_resets = NR_BANKS * BITS_PER_LONG;
data->rcdev.ops = &socfpga_reset_ops; data->rcdev.ops = &socfpga_reset_ops;
data->rcdev.of_node = pdev->dev.of_node; data->rcdev.of_node = pdev->dev.of_node;
reset_controller_register(&data->rcdev);
return 0; return reset_controller_register(&data->rcdev);
} }
static int socfpga_reset_remove(struct platform_device *pdev) static int socfpga_reset_remove(struct platform_device *pdev)
......
...@@ -108,9 +108,8 @@ static int sunxi_reset_init(struct device_node *np) ...@@ -108,9 +108,8 @@ static int sunxi_reset_init(struct device_node *np)
data->rcdev.nr_resets = size * 32; data->rcdev.nr_resets = size * 32;
data->rcdev.ops = &sunxi_reset_ops; data->rcdev.ops = &sunxi_reset_ops;
data->rcdev.of_node = np; data->rcdev.of_node = np;
reset_controller_register(&data->rcdev);
return 0; return reset_controller_register(&data->rcdev);
err_alloc: err_alloc:
kfree(data); kfree(data);
...@@ -122,7 +121,7 @@ static int sunxi_reset_init(struct device_node *np) ...@@ -122,7 +121,7 @@ static int sunxi_reset_init(struct device_node *np)
* our system, before we can even think of using a regular device * our system, before we can even think of using a regular device
* driver for it. * driver for it.
*/ */
static const struct of_device_id sunxi_early_reset_dt_ids[] __initdata = { static const struct of_device_id sunxi_early_reset_dt_ids[] __initconst = {
{ .compatible = "allwinner,sun6i-a31-ahb1-reset", }, { .compatible = "allwinner,sun6i-a31-ahb1-reset", },
{ /* sentinel */ }, { /* sentinel */ },
}; };
......
...@@ -121,9 +121,8 @@ static int zynq_reset_probe(struct platform_device *pdev) ...@@ -121,9 +121,8 @@ static int zynq_reset_probe(struct platform_device *pdev)
priv->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_LONG; priv->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_LONG;
priv->rcdev.ops = &zynq_reset_ops; priv->rcdev.ops = &zynq_reset_ops;
priv->rcdev.of_node = pdev->dev.of_node; priv->rcdev.of_node = pdev->dev.of_node;
reset_controller_register(&priv->rcdev);
return 0; return reset_controller_register(&priv->rcdev);
} }
static int zynq_reset_remove(struct platform_device *pdev) static int zynq_reset_remove(struct platform_device *pdev)
......
...@@ -52,6 +52,7 @@ static const struct syscfg_reset_channel_data stih407_powerdowns[] = { ...@@ -52,6 +52,7 @@ static const struct syscfg_reset_channel_data stih407_powerdowns[] = {
}; };
/* Reset Generator control 0/1 */ /* Reset Generator control 0/1 */
#define SYSCFG_5128 0x200
#define SYSCFG_5131 0x20c #define SYSCFG_5131 0x20c
#define SYSCFG_5132 0x210 #define SYSCFG_5132 0x210
...@@ -96,6 +97,10 @@ static const struct syscfg_reset_channel_data stih407_softresets[] = { ...@@ -96,6 +97,10 @@ static const struct syscfg_reset_channel_data stih407_softresets[] = {
[STIH407_ERAM_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 1), [STIH407_ERAM_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 1),
[STIH407_LPM_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 2), [STIH407_LPM_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 2),
[STIH407_KEYSCAN_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 8), [STIH407_KEYSCAN_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 8),
[STIH407_ST231_AUD_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 26),
[STIH407_ST231_DMU_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 27),
[STIH407_ST231_GP0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 28),
[STIH407_ST231_GP1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5128, 2),
}; };
/* PicoPHY reset/control */ /* PicoPHY reset/control */
......
...@@ -103,17 +103,42 @@ static int syscfg_reset_deassert(struct reset_controller_dev *rcdev, ...@@ -103,17 +103,42 @@ static int syscfg_reset_deassert(struct reset_controller_dev *rcdev,
static int syscfg_reset_dev(struct reset_controller_dev *rcdev, static int syscfg_reset_dev(struct reset_controller_dev *rcdev,
unsigned long idx) unsigned long idx)
{ {
int err = syscfg_reset_assert(rcdev, idx); int err;
err = syscfg_reset_assert(rcdev, idx);
if (err) if (err)
return err; return err;
return syscfg_reset_deassert(rcdev, idx); return syscfg_reset_deassert(rcdev, idx);
} }
static int syscfg_reset_status(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev);
const struct syscfg_reset_channel *ch;
u32 ret_val = 0;
int err;
if (idx >= rcdev->nr_resets)
return -EINVAL;
ch = &rst->channels[idx];
if (ch->ack)
err = regmap_field_read(ch->ack, &ret_val);
else
err = regmap_field_read(ch->reset, &ret_val);
if (err)
return err;
return rst->active_low ? !ret_val : !!ret_val;
}
static struct reset_control_ops syscfg_reset_ops = { static struct reset_control_ops syscfg_reset_ops = {
.reset = syscfg_reset_dev, .reset = syscfg_reset_dev,
.assert = syscfg_reset_assert, .assert = syscfg_reset_assert,
.deassert = syscfg_reset_deassert, .deassert = syscfg_reset_deassert,
.status = syscfg_reset_status,
}; };
static int syscfg_reset_controller_register(struct device *dev, static int syscfg_reset_controller_register(struct device *dev,
......
menu "SOC (System On Chip) specific Drivers" menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/bcm/Kconfig"
source "drivers/soc/brcmstb/Kconfig" source "drivers/soc/brcmstb/Kconfig"
source "drivers/soc/fsl/qe/Kconfig" source "drivers/soc/fsl/qe/Kconfig"
source "drivers/soc/mediatek/Kconfig" source "drivers/soc/mediatek/Kconfig"
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# Makefile for the Linux Kernel SOC specific device drivers. # Makefile for the Linux Kernel SOC specific device drivers.
# #
obj-y += bcm/
obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/
obj-$(CONFIG_ARCH_DOVE) += dove/ obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/ obj-$(CONFIG_MACH_DOVE) += dove/
......
config RASPBERRYPI_POWER
bool "Raspberry Pi power domain driver"
depends on ARCH_BCM2835 || COMPILE_TEST
depends on RASPBERRYPI_FIRMWARE=y
select PM_GENERIC_DOMAINS if PM
select PM_GENERIC_DOMAINS_OF if PM
help
This enables support for the RPi power domains which can be enabled
or disabled via the RPi firmware.
obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o
/* (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
*
* 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.
*
* Authors:
* Alexander Aring <aar@pengutronix.de>
* Eric Anholt <eric@anholt.net>
*/
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <dt-bindings/power/raspberrypi-power.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
/*
* Firmware indices for the old power domains interface. Only a few
* of them were actually implemented.
*/
#define RPI_OLD_POWER_DOMAIN_USB 3
#define RPI_OLD_POWER_DOMAIN_V3D 10
struct rpi_power_domain {
u32 domain;
bool enabled;
bool old_interface;
struct generic_pm_domain base;
struct rpi_firmware *fw;
};
struct rpi_power_domains {
bool has_new_interface;
struct genpd_onecell_data xlate;
struct rpi_firmware *fw;
struct rpi_power_domain domains[RPI_POWER_DOMAIN_COUNT];
};
/*
* Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and
* RPI_FIRMWARE_SET_DOMAIN_STATE
*/
struct rpi_power_domain_packet {
u32 domain;
u32 on;
} __packet;
/*
* Asks the firmware to enable or disable power on a specific power
* domain.
*/
static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on)
{
struct rpi_power_domain_packet packet;
packet.domain = rpi_domain->domain;
packet.on = on;
return rpi_firmware_property(rpi_domain->fw,
rpi_domain->old_interface ?
RPI_FIRMWARE_SET_POWER_STATE :
RPI_FIRMWARE_SET_DOMAIN_STATE,
&packet, sizeof(packet));
}
static int rpi_domain_off(struct generic_pm_domain *domain)
{
struct rpi_power_domain *rpi_domain =
container_of(domain, struct rpi_power_domain, base);
return rpi_firmware_set_power(rpi_domain, false);
}
static int rpi_domain_on(struct generic_pm_domain *domain)
{
struct rpi_power_domain *rpi_domain =
container_of(domain, struct rpi_power_domain, base);
return rpi_firmware_set_power(rpi_domain, true);
}
static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains,
int xlate_index, const char *name)
{
struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
dom->fw = rpi_domains->fw;
dom->base.name = name;
dom->base.power_on = rpi_domain_on;
dom->base.power_off = rpi_domain_off;
/*
* Treat all power domains as off at boot.
*
* The firmware itself may be keeping some domains on, but
* from Linux's perspective all we control is the refcounts
* that we give to the firmware, and we can't ask the firmware
* to turn off something that we haven't ourselves turned on.
*/
pm_genpd_init(&dom->base, NULL, true);
rpi_domains->xlate.domains[xlate_index] = &dom->base;
}
static void rpi_init_power_domain(struct rpi_power_domains *rpi_domains,
int xlate_index, const char *name)
{
struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
if (!rpi_domains->has_new_interface)
return;
/* The DT binding index is the firmware's domain index minus one. */
dom->domain = xlate_index + 1;
rpi_common_init_power_domain(rpi_domains, xlate_index, name);
}
static void rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains,
int xlate_index, int domain,
const char *name)
{
struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index];
dom->old_interface = true;
dom->domain = domain;
rpi_common_init_power_domain(rpi_domains, xlate_index, name);
}
/*
* Detects whether the firmware supports the new power domains interface.
*
* The firmware doesn't actually return an error on an unknown tag,
* and just skips over it, so we do the detection by putting an
* unexpected value in the return field and checking if it was
* unchanged.
*/
static bool
rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains)
{
struct rpi_power_domain_packet packet;
int ret;
packet.domain = RPI_POWER_DOMAIN_ARM;
packet.on = ~0;
ret = rpi_firmware_property(rpi_domains->fw,
RPI_FIRMWARE_GET_DOMAIN_STATE,
&packet, sizeof(packet));
return ret == 0 && packet.on != ~0;
}
static int rpi_power_probe(struct platform_device *pdev)
{
struct device_node *fw_np;
struct device *dev = &pdev->dev;
struct rpi_power_domains *rpi_domains;
rpi_domains = devm_kzalloc(dev, sizeof(*rpi_domains), GFP_KERNEL);
if (!rpi_domains)
return -ENOMEM;
rpi_domains->xlate.domains =
devm_kzalloc(dev, sizeof(*rpi_domains->xlate.domains) *
RPI_POWER_DOMAIN_COUNT, GFP_KERNEL);
if (!rpi_domains->xlate.domains)
return -ENOMEM;
rpi_domains->xlate.num_domains = RPI_POWER_DOMAIN_COUNT;
fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
if (!fw_np) {
dev_err(&pdev->dev, "no firmware node\n");
return -ENODEV;
}
rpi_domains->fw = rpi_firmware_get(fw_np);
of_node_put(fw_np);
if (!rpi_domains->fw)
return -EPROBE_DEFER;
rpi_domains->has_new_interface =
rpi_has_new_domain_support(rpi_domains);
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C0, "I2C0");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C1, "I2C1");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C2, "I2C2");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VIDEO_SCALER,
"VIDEO_SCALER");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VPU1, "VPU1");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_HDMI, "HDMI");
/*
* Use the old firmware interface for USB power, so that we
* can turn it on even if the firmware hasn't been updated.
*/
rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB,
RPI_OLD_POWER_DOMAIN_USB, "USB");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VEC, "VEC");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_JPEG, "JPEG");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_H264, "H264");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_V3D, "V3D");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ISP, "ISP");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM0, "UNICAM0");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM1, "UNICAM1");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2RX, "CCP2RX");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CSI2, "CSI2");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CPI, "CPI");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI0, "DSI0");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI1, "DSI1");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_TRANSPOSER,
"TRANSPOSER");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2TX, "CCP2TX");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CDP, "CDP");
rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ARM, "ARM");
of_genpd_add_provider_onecell(dev->of_node, &rpi_domains->xlate);
platform_set_drvdata(pdev, rpi_domains);
return 0;
}
static const struct of_device_id rpi_power_of_match[] = {
{ .compatible = "raspberrypi,bcm2835-power", },
{},
};
MODULE_DEVICE_TABLE(of, rpi_power_of_match);
static struct platform_driver rpi_power_driver = {
.driver = {
.name = "raspberrypi-power",
.of_match_table = rpi_power_of_match,
},
.probe = rpi_power_probe,
};
builtin_platform_driver(rpi_power_driver);
MODULE_AUTHOR("Alexander Aring <aar@pengutronix.de>");
MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
MODULE_DESCRIPTION("Raspberry Pi power domain driver");
MODULE_LICENSE("GPL v2");
...@@ -15,12 +15,13 @@ ...@@ -15,12 +15,13 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/init.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/soc/mediatek/infracfg.h> #include <linux/soc/mediatek/infracfg.h>
#include <linux/regulator/consumer.h>
#include <dt-bindings/power/mt8173-power.h> #include <dt-bindings/power/mt8173-power.h>
#define SPM_VDE_PWR_CON 0x0210 #define SPM_VDE_PWR_CON 0x0210
...@@ -179,6 +180,7 @@ struct scp_domain { ...@@ -179,6 +180,7 @@ struct scp_domain {
u32 sram_pdn_ack_bits; u32 sram_pdn_ack_bits;
u32 bus_prot_mask; u32 bus_prot_mask;
bool active_wakeup; bool active_wakeup;
struct regulator *supply;
}; };
struct scp { struct scp {
...@@ -221,6 +223,12 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) ...@@ -221,6 +223,12 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
int ret; int ret;
int i; int i;
if (scpd->supply) {
ret = regulator_enable(scpd->supply);
if (ret)
return ret;
}
for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) { for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) {
ret = clk_prepare_enable(scpd->clk[i]); ret = clk_prepare_enable(scpd->clk[i]);
if (ret) { if (ret) {
...@@ -299,6 +307,9 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) ...@@ -299,6 +307,9 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
clk_disable_unprepare(scpd->clk[i]); clk_disable_unprepare(scpd->clk[i]);
} }
err_clk: err_clk:
if (scpd->supply)
regulator_disable(scpd->supply);
dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name);
return ret; return ret;
...@@ -379,6 +390,9 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) ...@@ -379,6 +390,9 @@ static int scpsys_power_off(struct generic_pm_domain *genpd)
for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++)
clk_disable_unprepare(scpd->clk[i]); clk_disable_unprepare(scpd->clk[i]);
if (scpd->supply)
regulator_disable(scpd->supply);
return 0; return 0;
out: out:
...@@ -448,6 +462,19 @@ static int __init scpsys_probe(struct platform_device *pdev) ...@@ -448,6 +462,19 @@ static int __init scpsys_probe(struct platform_device *pdev)
return PTR_ERR(scp->infracfg); return PTR_ERR(scp->infracfg);
} }
for (i = 0; i < NUM_DOMAINS; i++) {
struct scp_domain *scpd = &scp->domains[i];
const struct scp_domain_data *data = &scp_domain_data[i];
scpd->supply = devm_regulator_get_optional(&pdev->dev, data->name);
if (IS_ERR(scpd->supply)) {
if (PTR_ERR(scpd->supply) == -ENODEV)
scpd->supply = NULL;
else
return PTR_ERR(scpd->supply);
}
}
pd_data->num_domains = NUM_DOMAINS; pd_data->num_domains = NUM_DOMAINS;
for (i = 0; i < NUM_DOMAINS; i++) { for (i = 0; i < NUM_DOMAINS; i++) {
...@@ -521,5 +548,4 @@ static struct platform_driver scpsys_drv = { ...@@ -521,5 +548,4 @@ static struct platform_driver scpsys_drv = {
.of_match_table = of_match_ptr(of_scpsys_match_tbl), .of_match_table = of_match_ptr(of_scpsys_match_tbl),
}, },
}; };
builtin_platform_driver_probe(scpsys_drv, scpsys_probe);
module_platform_driver_probe(scpsys_drv, scpsys_probe);
...@@ -13,6 +13,7 @@ config QCOM_GSBI ...@@ -13,6 +13,7 @@ config QCOM_GSBI
config QCOM_PM config QCOM_PM
bool "Qualcomm Power Management" bool "Qualcomm Power Management"
depends on ARCH_QCOM && !ARM64 depends on ARCH_QCOM && !ARM64
select ARM_CPU_SUSPEND
select QCOM_SCM select QCOM_SCM
help help
QCOM Platform specific power driver to manage cores and L2 low power QCOM Platform specific power driver to manage cores and L2 low power
...@@ -49,3 +50,29 @@ config QCOM_SMD_RPM ...@@ -49,3 +50,29 @@ config QCOM_SMD_RPM
Say M here if you want to include support for the Qualcomm RPM as a 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". module. This will build a module called "qcom-smd-rpm".
config QCOM_SMEM_STATE
bool
config QCOM_SMP2P
tristate "Qualcomm Shared Memory Point to Point support"
depends on QCOM_SMEM
select QCOM_SMEM_STATE
help
Say yes here to support the Qualcomm Shared Memory Point to Point
protocol.
config QCOM_SMSM
tristate "Qualcomm Shared Memory State Machine"
depends on QCOM_SMEM
select QCOM_SMEM_STATE
help
Say yes here to support the Qualcomm Shared Memory State Machine.
The state machine is represented by bits in shared memory.
config QCOM_WCNSS_CTRL
tristate "Qualcomm WCNSS control driver"
depends on QCOM_SMD
help
Client driver for the WCNSS_CTRL SMD channel, used to download nv
firmware to a newly booted WCNSS chip.
...@@ -3,3 +3,7 @@ obj-$(CONFIG_QCOM_PM) += spm.o ...@@ -3,3 +3,7 @@ obj-$(CONFIG_QCOM_PM) += spm.o
obj-$(CONFIG_QCOM_SMD) += smd.o obj-$(CONFIG_QCOM_SMD) += smd.o
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM) += smem.o
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
...@@ -219,6 +219,8 @@ static void qcom_smd_rpm_remove(struct qcom_smd_device *sdev) ...@@ -219,6 +219,8 @@ static void qcom_smd_rpm_remove(struct qcom_smd_device *sdev)
} }
static const struct of_device_id qcom_smd_rpm_of_match[] = { static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-apq8084" },
{ .compatible = "qcom,rpm-msm8916" },
{ .compatible = "qcom,rpm-msm8974" }, { .compatible = "qcom,rpm-msm8974" },
{} {}
}; };
......
/*
* Copyright (c) 2015, Sony Mobile Communications Inc.
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#include <linux/device.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smem_state.h>
static LIST_HEAD(smem_states);
static DEFINE_MUTEX(list_lock);
/**
* struct qcom_smem_state - state context
* @refcount: refcount for the state
* @orphan: boolean indicator that this state has been unregistered
* @list: entry in smem_states list
* @of_node: of_node to use for matching the state in DT
* @priv: implementation private data
* @ops: ops for the state
*/
struct qcom_smem_state {
struct kref refcount;
bool orphan;
struct list_head list;
struct device_node *of_node;
void *priv;
struct qcom_smem_state_ops ops;
};
/**
* qcom_smem_state_update_bits() - update the masked bits in state with value
* @state: state handle acquired by calling qcom_smem_state_get()
* @mask: bit mask for the change
* @value: new value for the masked bits
*
* Returns 0 on success, otherwise negative errno.
*/
int qcom_smem_state_update_bits(struct qcom_smem_state *state,
u32 mask,
u32 value)
{
if (state->orphan)
return -ENXIO;
if (!state->ops.update_bits)
return -ENOTSUPP;
return state->ops.update_bits(state->priv, mask, value);
}
EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits);
static struct qcom_smem_state *of_node_to_state(struct device_node *np)
{
struct qcom_smem_state *state;
mutex_lock(&list_lock);
list_for_each_entry(state, &smem_states, list) {
if (state->of_node == np) {
kref_get(&state->refcount);
goto unlock;
}
}
state = ERR_PTR(-EPROBE_DEFER);
unlock:
mutex_unlock(&list_lock);
return state;
}
/**
* qcom_smem_state_get() - acquire handle to a state
* @dev: client device pointer
* @con_id: name of the state to lookup
* @bit: flags from the state reference, indicating which bit's affected
*
* Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be
* called to release the returned state handle.
*/
struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
const char *con_id,
unsigned *bit)
{
struct qcom_smem_state *state;
struct of_phandle_args args;
int index = 0;
int ret;
if (con_id) {
index = of_property_match_string(dev->of_node,
"qcom,state-names",
con_id);
if (index < 0) {
dev_err(dev, "missing qcom,state-names\n");
return ERR_PTR(index);
}
}
ret = of_parse_phandle_with_args(dev->of_node,
"qcom,state",
"#qcom,state-cells",
index,
&args);
if (ret) {
dev_err(dev, "failed to parse qcom,state property\n");
return ERR_PTR(ret);
}
if (args.args_count != 1) {
dev_err(dev, "invalid #qcom,state-cells\n");
return ERR_PTR(-EINVAL);
}
state = of_node_to_state(args.np);
if (IS_ERR(state))
goto put;
*bit = args.args[0];
put:
of_node_put(args.np);
return state;
}
EXPORT_SYMBOL_GPL(qcom_smem_state_get);
static void qcom_smem_state_release(struct kref *ref)
{
struct qcom_smem_state *state = container_of(ref, struct qcom_smem_state, refcount);
list_del(&state->list);
kfree(state);
}
/**
* qcom_smem_state_put() - release state handle
* @state: state handle to be released
*/
void qcom_smem_state_put(struct qcom_smem_state *state)
{
mutex_lock(&list_lock);
kref_put(&state->refcount, qcom_smem_state_release);
mutex_unlock(&list_lock);
}
EXPORT_SYMBOL_GPL(qcom_smem_state_put);
/**
* qcom_smem_state_register() - register a new state
* @of_node: of_node used for matching client lookups
* @ops: implementation ops
* @priv: implementation specific private data
*/
struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,
const struct qcom_smem_state_ops *ops,
void *priv)
{
struct qcom_smem_state *state;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return ERR_PTR(-ENOMEM);
kref_init(&state->refcount);
state->of_node = of_node;
state->ops = *ops;
state->priv = priv;
mutex_lock(&list_lock);
list_add(&state->list, &smem_states);
mutex_unlock(&list_lock);
return state;
}
EXPORT_SYMBOL_GPL(qcom_smem_state_register);
/**
* qcom_smem_state_unregister() - unregister a registered state
* @state: state handle to be unregistered
*/
void qcom_smem_state_unregister(struct qcom_smem_state *state)
{
state->orphan = true;
qcom_smem_state_put(state);
}
EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
#define WCNSS_REQUEST_TIMEOUT (5 * HZ)
#define NV_FRAGMENT_SIZE 3072
#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
/**
* struct wcnss_ctrl - driver context
* @dev: device handle
* @channel: SMD channel handle
* @ack: completion for outstanding requests
* @ack_status: status of the outstanding request
* @download_nv_work: worker for uploading nv binary
*/
struct wcnss_ctrl {
struct device *dev;
struct qcom_smd_channel *channel;
struct completion ack;
int ack_status;
struct work_struct download_nv_work;
};
/* message types */
enum {
WCNSS_VERSION_REQ = 0x01000000,
WCNSS_VERSION_RESP,
WCNSS_DOWNLOAD_NV_REQ,
WCNSS_DOWNLOAD_NV_RESP,
WCNSS_UPLOAD_CAL_REQ,
WCNSS_UPLOAD_CAL_RESP,
WCNSS_DOWNLOAD_CAL_REQ,
WCNSS_DOWNLOAD_CAL_RESP,
};
/**
* struct wcnss_msg_hdr - common packet header for requests and responses
* @type: packet message type
* @len: total length of the packet, including this header
*/
struct wcnss_msg_hdr {
u32 type;
u32 len;
} __packed;
/**
* struct wcnss_version_resp - version request response
* @hdr: common packet wcnss_msg_hdr header
*/
struct wcnss_version_resp {
struct wcnss_msg_hdr hdr;
u8 major;
u8 minor;
u8 version;
u8 revision;
} __packed;
/**
* struct wcnss_download_nv_req - firmware fragment request
* @hdr: common packet wcnss_msg_hdr header
* @seq: sequence number of this fragment
* @last: boolean indicator of this being the last fragment of the binary
* @frag_size: length of this fragment
* @fragment: fragment data
*/
struct wcnss_download_nv_req {
struct wcnss_msg_hdr hdr;
u16 seq;
u16 last;
u32 frag_size;
u8 fragment[];
} __packed;
/**
* struct wcnss_download_nv_resp - firmware download response
* @hdr: common packet wcnss_msg_hdr header
* @status: boolean to indicate success of the download
*/
struct wcnss_download_nv_resp {
struct wcnss_msg_hdr hdr;
u8 status;
} __packed;
/**
* wcnss_ctrl_smd_callback() - handler from SMD responses
* @qsdev: smd device handle
* @data: pointer to the incoming data packet
* @count: size of the incoming data packet
*
* Handles any incoming packets from the remote WCNSS_CTRL service.
*/
static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev,
const void *data,
size_t count)
{
struct wcnss_ctrl *wcnss = dev_get_drvdata(&qsdev->dev);
const struct wcnss_download_nv_resp *nvresp;
const struct wcnss_version_resp *version;
const struct wcnss_msg_hdr *hdr = data;
switch (hdr->type) {
case WCNSS_VERSION_RESP:
if (count != sizeof(*version)) {
dev_err(wcnss->dev,
"invalid size of version response\n");
break;
}
version = data;
dev_info(wcnss->dev, "WCNSS Version %d.%d %d.%d\n",
version->major, version->minor,
version->version, version->revision);
schedule_work(&wcnss->download_nv_work);
break;
case WCNSS_DOWNLOAD_NV_RESP:
if (count != sizeof(*nvresp)) {
dev_err(wcnss->dev,
"invalid size of download response\n");
break;
}
nvresp = data;
wcnss->ack_status = nvresp->status;
complete(&wcnss->ack);
break;
default:
dev_info(wcnss->dev, "unknown message type %d\n", hdr->type);
break;
}
return 0;
}
/**
* wcnss_request_version() - send a version request to WCNSS
* @wcnss: wcnss ctrl driver context
*/
static int wcnss_request_version(struct wcnss_ctrl *wcnss)
{
struct wcnss_msg_hdr msg;
msg.type = WCNSS_VERSION_REQ;
msg.len = sizeof(msg);
return qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
}
/**
* wcnss_download_nv() - send nv binary to WCNSS
* @work: work struct to acquire wcnss context
*/
static void wcnss_download_nv(struct work_struct *work)
{
struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work);
struct wcnss_download_nv_req *req;
const struct firmware *fw;
const void *data;
ssize_t left;
int ret;
req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
if (!req)
return;
ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
if (ret) {
dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
NVBIN_FILE, ret);
goto free_req;
}
data = fw->data;
left = fw->size;
req->hdr.type = WCNSS_DOWNLOAD_NV_REQ;
req->hdr.len = sizeof(*req) + NV_FRAGMENT_SIZE;
req->last = 0;
req->frag_size = NV_FRAGMENT_SIZE;
req->seq = 0;
do {
if (left <= NV_FRAGMENT_SIZE) {
req->last = 1;
req->frag_size = left;
req->hdr.len = sizeof(*req) + left;
}
memcpy(req->fragment, data, req->frag_size);
ret = qcom_smd_send(wcnss->channel, req, req->hdr.len);
if (ret) {
dev_err(wcnss->dev, "failed to send smd packet\n");
goto release_fw;
}
/* Increment for next fragment */
req->seq++;
data += req->hdr.len;
left -= NV_FRAGMENT_SIZE;
} while (left > 0);
ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
if (!ret)
dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
else if (wcnss->ack_status != 1)
dev_err(wcnss->dev, "nv upload response failed err: %d\n",
wcnss->ack_status);
release_fw:
release_firmware(fw);
free_req:
kfree(req);
}
static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
{
struct wcnss_ctrl *wcnss;
wcnss = devm_kzalloc(&sdev->dev, sizeof(*wcnss), GFP_KERNEL);
if (!wcnss)
return -ENOMEM;
wcnss->dev = &sdev->dev;
wcnss->channel = sdev->channel;
init_completion(&wcnss->ack);
INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv);
dev_set_drvdata(&sdev->dev, wcnss);
return wcnss_request_version(wcnss);
}
static const struct qcom_smd_id wcnss_ctrl_smd_match[] = {
{ .name = "WCNSS_CTRL" },
{}
};
static struct qcom_smd_driver wcnss_ctrl_driver = {
.probe = wcnss_ctrl_probe,
.callback = wcnss_ctrl_smd_callback,
.smd_match_table = wcnss_ctrl_smd_match,
.driver = {
.name = "qcom_wcnss_ctrl",
.owner = THIS_MODULE,
},
};
module_qcom_smd_driver(wcnss_ctrl_driver);
MODULE_DESCRIPTION("Qualcomm WCNSS control driver");
MODULE_LICENSE("GPL v2");
...@@ -28,4 +28,14 @@ config KEYSTONE_NAVIGATOR_DMA ...@@ -28,4 +28,14 @@ config KEYSTONE_NAVIGATOR_DMA
If unsure, say N. If unsure, say N.
config WKUP_M3_IPC
tristate "TI AMx3 Wkup-M3 IPC Driver"
depends on WKUP_M3_RPROC
depends on OMAP2PLUS_MBOX
help
TI AM33XX and AM43XX have a Cortex M3, the Wakeup M3, to handle
low power transitions. This IPC driver provides the necessary API
to communicate and use the Wakeup M3 for PM features like suspend
resume and boots it using wkup_m3_rproc driver.
endif # SOC_TI endif # SOC_TI
...@@ -4,3 +4,4 @@ ...@@ -4,3 +4,4 @@
obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o
knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o
obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o
obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o
This diff is collapsed.
...@@ -1047,7 +1047,7 @@ config SERIAL_SGI_IOC3 ...@@ -1047,7 +1047,7 @@ config SERIAL_SGI_IOC3
say Y or M. Otherwise, say N. say Y or M. Otherwise, say N.
config SERIAL_MSM config SERIAL_MSM
bool "MSM on-chip serial port support" tristate "MSM on-chip serial port support"
depends on ARCH_QCOM depends on ARCH_QCOM
select SERIAL_CORE select SERIAL_CORE
......
/*
* 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.
*/
#ifndef _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H
#define _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H
/* These power domain indices are the firmware interface's indices
* minus one.
*/
#define RPI_POWER_DOMAIN_I2C0 0
#define RPI_POWER_DOMAIN_I2C1 1
#define RPI_POWER_DOMAIN_I2C2 2
#define RPI_POWER_DOMAIN_VIDEO_SCALER 3
#define RPI_POWER_DOMAIN_VPU1 4
#define RPI_POWER_DOMAIN_HDMI 5
#define RPI_POWER_DOMAIN_USB 6
#define RPI_POWER_DOMAIN_VEC 7
#define RPI_POWER_DOMAIN_JPEG 8
#define RPI_POWER_DOMAIN_H264 9
#define RPI_POWER_DOMAIN_V3D 10
#define RPI_POWER_DOMAIN_ISP 11
#define RPI_POWER_DOMAIN_UNICAM0 12
#define RPI_POWER_DOMAIN_UNICAM1 13
#define RPI_POWER_DOMAIN_CCP2RX 14
#define RPI_POWER_DOMAIN_CSI2 15
#define RPI_POWER_DOMAIN_CPI 16
#define RPI_POWER_DOMAIN_DSI0 17
#define RPI_POWER_DOMAIN_DSI1 18
#define RPI_POWER_DOMAIN_TRANSPOSER 19
#define RPI_POWER_DOMAIN_CCP2TX 20
#define RPI_POWER_DOMAIN_CDP 21
#define RPI_POWER_DOMAIN_ARM 22
#define RPI_POWER_DOMAIN_COUNT 23
#endif /* _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H */
/**
* This header provides index for the reset controller
* based on hi6220 SoC.
*/
#ifndef _DT_BINDINGS_RESET_CONTROLLER_HI6220
#define _DT_BINDINGS_RESET_CONTROLLER_HI6220
#define PERIPH_RSTDIS0_MMC0 0x000
#define PERIPH_RSTDIS0_MMC1 0x001
#define PERIPH_RSTDIS0_MMC2 0x002
#define PERIPH_RSTDIS0_NANDC 0x003
#define PERIPH_RSTDIS0_USBOTG_BUS 0x004
#define PERIPH_RSTDIS0_POR_PICOPHY 0x005
#define PERIPH_RSTDIS0_USBOTG 0x006
#define PERIPH_RSTDIS0_USBOTG_32K 0x007
#define PERIPH_RSTDIS1_HIFI 0x100
#define PERIPH_RSTDIS1_DIGACODEC 0x105
#define PERIPH_RSTEN2_IPF 0x200
#define PERIPH_RSTEN2_SOCP 0x201
#define PERIPH_RSTEN2_DMAC 0x202
#define PERIPH_RSTEN2_SECENG 0x203
#define PERIPH_RSTEN2_ABB 0x204
#define PERIPH_RSTEN2_HPM0 0x205
#define PERIPH_RSTEN2_HPM1 0x206
#define PERIPH_RSTEN2_HPM2 0x207
#define PERIPH_RSTEN2_HPM3 0x208
#define PERIPH_RSTEN3_CSSYS 0x300
#define PERIPH_RSTEN3_I2C0 0x301
#define PERIPH_RSTEN3_I2C1 0x302
#define PERIPH_RSTEN3_I2C2 0x303
#define PERIPH_RSTEN3_I2C3 0x304
#define PERIPH_RSTEN3_UART1 0x305
#define PERIPH_RSTEN3_UART2 0x306
#define PERIPH_RSTEN3_UART3 0x307
#define PERIPH_RSTEN3_UART4 0x308
#define PERIPH_RSTEN3_SSP 0x309
#define PERIPH_RSTEN3_PWM 0x30a
#define PERIPH_RSTEN3_BLPWM 0x30b
#define PERIPH_RSTEN3_TSENSOR 0x30c
#define PERIPH_RSTEN3_DAPB 0x312
#define PERIPH_RSTEN3_HKADC 0x313
#define PERIPH_RSTEN3_CODEC_SSI 0x314
#define PERIPH_RSTEN3_PMUSSI1 0x316
#define PERIPH_RSTEN8_RS0 0x400
#define PERIPH_RSTEN8_RS2 0x401
#define PERIPH_RSTEN8_RS3 0x402
#define PERIPH_RSTEN8_MS0 0x403
#define PERIPH_RSTEN8_MS2 0x405
#define PERIPH_RSTEN8_XG2RAM0 0x406
#define PERIPH_RSTEN8_X2SRAM_TZMA 0x407
#define PERIPH_RSTEN8_SRAM 0x408
#define PERIPH_RSTEN8_HARQ 0x40a
#define PERIPH_RSTEN8_DDRC 0x40c
#define PERIPH_RSTEN8_DDRC_APB 0x40d
#define PERIPH_RSTEN8_DDRPACK_APB 0x40e
#define PERIPH_RSTEN8_DDRT 0x411
#define PERIPH_RSDIST9_CARM_DAP 0x500
#define PERIPH_RSDIST9_CARM_ATB 0x501
#define PERIPH_RSDIST9_CARM_LBUS 0x502
#define PERIPH_RSDIST9_CARM_POR 0x503
#define PERIPH_RSDIST9_CARM_CORE 0x504
#define PERIPH_RSDIST9_CARM_DBG 0x505
#define PERIPH_RSDIST9_CARM_L2 0x506
#define PERIPH_RSDIST9_CARM_SOCDBG 0x507
#define PERIPH_RSDIST9_CARM_ETM 0x508
#endif /*_DT_BINDINGS_RESET_CONTROLLER_HI6220*/
...@@ -52,6 +52,10 @@ ...@@ -52,6 +52,10 @@
#define STIH407_KEYSCAN_SOFTRESET 26 #define STIH407_KEYSCAN_SOFTRESET 26
#define STIH407_USB2_PORT0_SOFTRESET 27 #define STIH407_USB2_PORT0_SOFTRESET 27
#define STIH407_USB2_PORT1_SOFTRESET 28 #define STIH407_USB2_PORT1_SOFTRESET 28
#define STIH407_ST231_AUD_SOFTRESET 29
#define STIH407_ST231_DMU_SOFTRESET 30
#define STIH407_ST231_GP0_SOFTRESET 31
#define STIH407_ST231_GP1_SOFTRESET 32
/* Picophy reset defines */ /* Picophy reset defines */
#define STIH407_PICOPHY0_RESET 0 #define STIH407_PICOPHY0_RESET 0
......
...@@ -38,6 +38,9 @@ static inline struct reset_control *devm_reset_control_get_optional( ...@@ -38,6 +38,9 @@ static inline struct reset_control *devm_reset_control_get_optional(
struct reset_control *of_reset_control_get(struct device_node *node, struct reset_control *of_reset_control_get(struct device_node *node,
const char *id); const char *id);
struct reset_control *of_reset_control_get_by_index(
struct device_node *node, int index);
#else #else
static inline int reset_control_reset(struct reset_control *rstc) static inline int reset_control_reset(struct reset_control *rstc)
...@@ -71,7 +74,7 @@ static inline void reset_control_put(struct reset_control *rstc) ...@@ -71,7 +74,7 @@ static inline void reset_control_put(struct reset_control *rstc)
static inline int device_reset_optional(struct device *dev) static inline int device_reset_optional(struct device *dev)
{ {
return -ENOSYS; return -ENOTSUPP;
} }
static inline struct reset_control *__must_check reset_control_get( static inline struct reset_control *__must_check reset_control_get(
...@@ -91,19 +94,25 @@ static inline struct reset_control *__must_check devm_reset_control_get( ...@@ -91,19 +94,25 @@ static inline struct reset_control *__must_check devm_reset_control_get(
static inline struct reset_control *reset_control_get_optional( static inline struct reset_control *reset_control_get_optional(
struct device *dev, const char *id) struct device *dev, const char *id)
{ {
return ERR_PTR(-ENOSYS); return ERR_PTR(-ENOTSUPP);
} }
static inline struct reset_control *devm_reset_control_get_optional( static inline struct reset_control *devm_reset_control_get_optional(
struct device *dev, const char *id) struct device *dev, const char *id)
{ {
return ERR_PTR(-ENOSYS); return ERR_PTR(-ENOTSUPP);
} }
static inline struct reset_control *of_reset_control_get( static inline struct reset_control *of_reset_control_get(
struct device_node *node, const char *id) struct device_node *node, const char *id)
{ {
return ERR_PTR(-ENOSYS); return ERR_PTR(-ENOTSUPP);
}
static inline struct reset_control *of_reset_control_get_by_index(
struct device_node *node, int index)
{
return ERR_PTR(-ENOTSUPP);
} }
#endif /* CONFIG_RESET_CONTROLLER */ #endif /* CONFIG_RESET_CONTROLLER */
......
#ifndef __QCOM_SMEM_STATE__
#define __QCOM_SMEM_STATE__
struct qcom_smem_state;
struct qcom_smem_state_ops {
int (*update_bits)(void *, u32, u32);
};
struct qcom_smem_state *qcom_smem_state_get(struct device *dev, const char *con_id, unsigned *bit);
void qcom_smem_state_put(struct qcom_smem_state *);
int qcom_smem_state_update_bits(struct qcom_smem_state *state, u32 mask, u32 value);
struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node, const struct qcom_smem_state_ops *ops, void *data);
void qcom_smem_state_unregister(struct qcom_smem_state *state);
#endif
/*
* TI Wakeup M3 for AMx3 SoCs Power Management Routines
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Dave Gerlach <d-gerlach@ti.com>
*
* 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 version 2.
*
* 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.
*/
#ifndef _LINUX_WKUP_M3_IPC_H
#define _LINUX_WKUP_M3_IPC_H
#define WKUP_M3_DEEPSLEEP 1
#define WKUP_M3_STANDBY 2
#define WKUP_M3_IDLE 3
#include <linux/mailbox_client.h>
struct wkup_m3_ipc_ops;
struct wkup_m3_ipc {
struct rproc *rproc;
void __iomem *ipc_mem_base;
struct device *dev;
int mem_type;
unsigned long resume_addr;
int state;
struct completion sync_complete;
struct mbox_client mbox_client;
struct mbox_chan *mbox;
struct wkup_m3_ipc_ops *ops;
};
struct wkup_m3_ipc_ops {
void (*set_mem_type)(struct wkup_m3_ipc *m3_ipc, int mem_type);
void (*set_resume_address)(struct wkup_m3_ipc *m3_ipc, void *addr);
int (*prepare_low_power)(struct wkup_m3_ipc *m3_ipc, int state);
int (*finish_low_power)(struct wkup_m3_ipc *m3_ipc);
int (*request_pm_status)(struct wkup_m3_ipc *m3_ipc);
};
struct wkup_m3_ipc *wkup_m3_ipc_get(void);
void wkup_m3_ipc_put(struct wkup_m3_ipc *m3_ipc);
#endif /* _LINUX_WKUP_M3_IPC_H */
...@@ -72,10 +72,12 @@ enum rpi_firmware_property_tag { ...@@ -72,10 +72,12 @@ enum rpi_firmware_property_tag {
RPI_FIRMWARE_SET_ENABLE_QPU = 0x00030012, RPI_FIRMWARE_SET_ENABLE_QPU = 0x00030012,
RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014, RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014,
RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020, RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020,
RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030,
RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001, RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001,
RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002, RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002,
RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, RPI_FIRMWARE_SET_VOLTAGE = 0x00038003,
RPI_FIRMWARE_SET_TURBO = 0x00038009, RPI_FIRMWARE_SET_TURBO = 0x00038009,
RPI_FIRMWARE_SET_DOMAIN_STATE = 0x00038030,
/* Dispmanx TAGS */ /* Dispmanx TAGS */
RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001,
......
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