* [PATCH 2/2] regulator: pwm: Add ramp delay for exponential voltage transition
From: Laxman Dewangan @ 2016-11-04 17:37 UTC (permalink / raw)
To: broonie, robh+dt, mark.rutland
Cc: linux-kernel, devicetree, Laxman Dewangan, Douglas Anderson,
Aleksandr Frid
In-Reply-To: <1478281075-3498-1-git-send-email-ldewangan@nvidia.com>
Some PWM regulator has the exponential transition in voltage change as
opposite to fixed slew-rate linear transition on other regulators.
For such PWM regulators, voltage transition is same for all voltage
change.
Add support for handling the voltage transition ramp time when voltage
transition is exponential.
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
CC: Douglas Anderson <dianders@chromium.org>
CC: Aleksandr Frid <afrid@nvidia.com>
---
This patch is continuation of discussion on patch
regulator: pwm: Fix regulator ramp delay for continuous mode
https://patchwork.kernel.org/patch/9216857/
where is it discussed to have separate property for PWM which has
exponential voltage transition.
---
drivers/regulator/pwm-regulator.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index 1b88e0e1..f990b86 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -47,6 +47,9 @@ struct pwm_regulator_data {
/* Enable GPIO */
struct gpio_desc *enb_gpio;
+
+ /* Voltage ramp time */
+ u32 voltage_ramp_time;
};
struct pwm_voltages {
@@ -233,6 +236,14 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
return 0;
}
+static int pwm_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ int old_uV, int new_uV)
+{
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
+
+ return drvdata->voltage_ramp_time;
+}
+
static struct regulator_ops pwm_regulator_voltage_table_ops = {
.set_voltage_sel = pwm_regulator_set_voltage_sel,
.get_voltage_sel = pwm_regulator_get_voltage_sel,
@@ -305,6 +316,13 @@ static int pwm_regulator_init_continuous(struct platform_device *pdev,
memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops,
sizeof(drvdata->ops));
+
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "pwm-regulator-voltage-ramp-time-us",
+ &drvdata->voltage_ramp_time))
+ drvdata->ops.set_voltage_time =
+ pwm_regulator_set_voltage_time_sel;
+
drvdata->desc.ops = &drvdata->ops;
drvdata->desc.continuous_voltage_range = true;
--
2.1.4
^ permalink raw reply related
* [PATCH 1/2] regulator: pwm: DT: Add ramp delay for exponential voltage transition
From: Laxman Dewangan @ 2016-11-04 17:37 UTC (permalink / raw)
To: broonie, robh+dt, mark.rutland
Cc: linux-kernel, devicetree, Laxman Dewangan, Douglas Anderson,
Aleksandr Frid
Some PWM regulator has the exponential transition in voltage change as
opposite to fixed slew-rate linear transition on other regulators.
For such PWM regulators, add the property for providing the delay
from DT node.
Add DT binding details of the new property
"pwm-regulator-voltage-ramp-time-us" added for providing voltage
transition delay.
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
CC: Douglas Anderson <dianders@chromium.org>
CC: Aleksandr Frid <afrid@nvidia.com>
---
This patch is continuation of discussion on patch
regulator: pwm: Fix regulator ramp delay for continuous mode
https://patchwork.kernel.org/patch/9216857/
where is it discussed to have separate property for PWM which has
exponential voltage transition.
---
Documentation/devicetree/bindings/regulator/pwm-regulator.txt | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
index 3aeba9f..a163f42 100644
--- a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
@@ -54,6 +54,16 @@ Optional properties:
--------------------
- enable-gpios: GPIO to use to enable/disable the regulator
+- pwm-regulator-voltage-ramp-time-us: Integer, voltage ramp time in
+ microseconds. Some PWM regulator has the exponential
+ transition in voltage change as opposite to fixed
+ slew-rate linear transition on other regulators.
+ For such regulator, this property is used to provide
+ the settling time for voltage change which is same
+ for any voltage change.
+ If PWM regulator supports the fixed slew rate then
+ property "regulator-ramp-delay" should be used.
+
Any property defined as part of the core regulator binding can also be used.
(See: ../regulator/regulator.txt)
--
2.1.4
^ permalink raw reply related
* Re: [PATCH v5 0/3] Broadcom STB AVS CPUfreq driver
From: Markus Mayer @ 2016-11-04 17:00 UTC (permalink / raw)
To: Markus Mayer
Cc: Rob Herring, Viresh Kumar, Rafael J . Wysocki,
Broadcom Kernel List, Device Tree List, Power Management List,
Linux Kernel Mailing List
In-Reply-To: <20161027210536.98988-1-code@mmayer.net>
On 27 October 2016 at 14:05, Markus Mayer <code@mmayer.net> wrote:
> From: Markus Mayer <mmayer@broadcom.com>
>
> This series contains the CPUfreq driver for Broadcom SoCs that use "AVS
> Firmware" for voltage and frequency scaling. All voltage and frequency
> transitions are performed by the firmware and are therefore hidden from
> Linux.
>
> The driver provides a standard CPUfreq interface to other kernel
> components and to userland on the one hand and communicates with the
> AVS co-processor on the other.
>
> Communication between the two processors is via shared mailbox
> registers and interrupts (ARM -> AVS to tell the firmware that there is
> a command to process and AVS -> ARM to tell the driver that a command
> finished executing).
Viresh, Rafael, are you ready to take this series? Rob gave his Ack
for the DT binding.
You can pull from [1] if you prefer. The branch already has all the Acks.
Thanks,
-Markus
[1] https://github.com/mmayer/linux/commits/upstream-avs-cpufreq-4.9-v5
> Changes from v4:
> - Addressed comments regarding the binding document (see also
> https://lkml.org/lkml/2016/10/19/642)
> - Added Virsh's Acks
> - Rebased on v4.9-rc1
> - Patches sent from non-Broadcom account on purpose to prevent series
> from being marked as spam (this is currently happening for some
> recipients)
> - No changes to any code
>
> Changes from v3:
> - moved code from brcm_avs_cpufreq_exit() into brcm_avs_cpufreq_remove()
> - removed brcm_avs_cpufreq_exit() altogether
>
> Changes from v2:
> - removed file Documentation/cpu-freq/brcmstb-avs-cpufreq.txt, instead
> adding the text as comment in the driver itself
> - wildcard match in MAINTAINERS file (drivers/cpufreq/brcmstb*)
> - drivers/cpufreq/Kconfig.arm: have ARM_BRCMSTB_AVS_CPUFREQ default y
> for ARCH_BRCMSTB
> - fixed naming and Kconfig entry for CONFIG_ARM_BRCMSTB_AVS_CPUFREQ_DEBUG
> - reordered and fixed include files
> - made sure driver name doesn't exceed size of cpufreq_driver.name
> - got rid of device_node pointers in struct private_data; there's no
> need for them, as we can call of_node_put() in __map_region()
> - don't set driver_data for CPUFREQ_TABLE_END
> - have brcm_avs_cpufreq_get() read from the firmware rather than return
> policy->cur
> - don't update policy->cur in brcm_avs_target_index()
> - created new function brcm_avs_prepare_init() to handle compatibility
> checks during initialization and moved corresponding code from
> brcm_avs_cpufreq_init() to the new function
> - brcm_avs_prepare_init() is called from brcm_avs_cpufreq_probe() and
> therefore will only be called once, even in the case of an error,
> whereas brcm_avs_cpufreq_init() is called for every core if there is
> an error
> - renamed brcm_avs_cpu_(init|exit) to brcm_avs_cpufreq_(init|exit)
> - added platform_set_drvdata(pdev, NULL) upon deregistration
> - fixed various style complaints by checkpatch.pl --strict
> - fixed other style issues
> - updated and improved some comments
> - added Viresh's Acks to patches 1/3 and 3/3
>
> Changes from v1:
> - renamed binding document
> - rewrote the introduction of the binding document
> - created a new driver documentation file that contains Linux specific
> information that was previously part of the binding document
> - renamed the driver (and related config options) to include a reference
> to "STB", since this implementation is primarily intended for use on
> set-top boxes
> - improved comments
> - updated function __map_region()
> - updated struct private_data
> - added code to unmap memory regions in the error and exit paths
> - added new sysfs property to report frequency directly from the
> co-processor register
>
> Markus Mayer (3):
> dt: cpufreq: brcm: New binding document for brcmstb-avs-cpufreq
> cpufreq: brcmstb-avs-cpufreq: AVS CPUfreq driver for Broadcom STB SoCs
> cpufreq: brcmstb-avs-cpufreq: add debugfs support
>
> .../bindings/cpufreq/brcm,stb-avs-cpu-freq.txt | 78 ++
> MAINTAINERS | 8 +
> drivers/cpufreq/Kconfig.arm | 21 +
> drivers/cpufreq/Makefile | 1 +
> drivers/cpufreq/brcmstb-avs-cpufreq.c | 1057 ++++++++++++++++++++
> 5 files changed, 1165 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/cpufreq/brcm,stb-avs-cpu-freq.txt
> create mode 100644 drivers/cpufreq/brcmstb-avs-cpufreq.c
>
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH v6 7/7] arm64: dts: NS2: add AMAC ethernet support
From: Jon Mason @ 2016-11-04 16:30 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: David Miller, Rob Herring, Mark Rutland, Florian Fainelli, rafal,
bcm-kernel-feedback-list, netdev, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <69ecd9b9-d495-9dfa-ad26-4fa622d951a0@cogentembedded.com>
On Fri, Nov 04, 2016 at 04:31:40PM +0300, Sergei Shtylyov wrote:
> Hello.
>
> On 11/4/2016 8:11 AM, Jon Mason wrote:
>
> >Add support for the AMAC ethernet to the Broadcom Northstar2 SoC device
> >tree
> >
> >Signed-off-by: Jon Mason <jon.mason@broadcom.com>
> >---
> > arch/arm64/boot/dts/broadcom/ns2-svk.dts | 5 +++++
> > arch/arm64/boot/dts/broadcom/ns2.dtsi | 12 ++++++++++++
> > 2 files changed, 17 insertions(+)
> >
> >diff --git a/arch/arm64/boot/dts/broadcom/ns2-svk.dts b/arch/arm64/boot/dts/broadcom/ns2-svk.dts
> >index b09f3bc..c4d5442 100644
> >--- a/arch/arm64/boot/dts/broadcom/ns2-svk.dts
> >+++ b/arch/arm64/boot/dts/broadcom/ns2-svk.dts
> >@@ -56,6 +56,10 @@
> > };
> > };
> >
> >+&enet {
> >+ status = "ok";
>
> The spec dictates it should be "okay" (although "ok" is also recognized).
The rest of the file uses "ok". So, the addition above is consistent
with the other entries.
Perhaps a patch outside this series to convert the entire file from
"ok" to "okay" would be acceptable to you.
Thanks,
Jon
>
> >+};
> >+
> > &pci_phy0 {
> > status = "ok";
> > };
> >@@ -174,6 +178,7 @@
> > &mdio_mux_iproc {
> > mdio@10 {
> > gphy0: eth-phy@10 {
> >+ enet-phy-lane-swap;
> > reg = <0x10>;
> > };
> > };
> [...]
>
> MBR, Sergei
>
^ permalink raw reply
* Re: [PATCH v7 3/3] fpga: Add support for Lattice iCE40 FPGAs
From: atull @ 2016-11-04 15:48 UTC (permalink / raw)
To: Joel Holdsworth
Cc: moritz.fischer, geert, robh, devicetree, linux-kernel, linux-spi,
marex
In-Reply-To: <1478236004-7852-3-git-send-email-joel@airwebreathe.org.uk>
On Fri, 4 Nov 2016, Joel Holdsworth wrote:
> The Lattice iCE40 is a family of FPGAs with a minimalistic architecture
> and very regular structure, designed for low-cost, high-volume consumer
> and system applications.
>
> This patch adds support to the FPGA manager for configuring the SRAM of
> iCE40LM, iCE40LP, iCE40HX, iCE40 Ultra, iCE40 UltraLite and iCE40
> UltraPlus devices, through slave SPI.
>
> The iCE40 family is notable because it is the first FPGA family to have
> complete reverse engineered bit-stream documentation for the iCE40LP and
> iCE40HX devices. Furthermore, there is now a Free Software Verilog
> synthesis tool-chain: the "IceStorm" tool-chain.
>
> This project is the work of Clifford Wolf, who is the maintainer of
> Yosys Verilog RTL synthesis framework, and Mathias Lasser, with notable
> contributions from "Cotton Seed", the main author of "arachne-pnr"; a
> place-and-route tool for iCE40 FPGAs.
>
> Having a Free Software synthesis tool-chain offers interesting
> opportunities for embedded devices that are able reconfigure themselves
> with open firmware that is generated on the device itself. For example
> a mobile device might have an application processor with an iCE40 FPGA
> attached, which implements slave devices, or through which the processor
> communicates with other devices through the FPGA fabric.
>
> A kernel driver for the iCE40 is useful, because in some cases, the FPGA
> may need to be configured before other devices can be accessed.
>
> An example of such a device is the icoBoard; a RaspberryPI HAT which
> features an iCE40HX8K with a 1 or 8 MBit SRAM and ports for
> Digilent-compatible PMOD modules. A PMOD module may contain a device
> with which the kernel communicates, via the FPGA.
> ---
> drivers/fpga/Kconfig | 6 ++
> drivers/fpga/Makefile | 1 +
> drivers/fpga/ice40-spi.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 205 insertions(+)
> create mode 100644 drivers/fpga/ice40-spi.c
>
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index d614102..85ff429 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -13,6 +13,12 @@ config FPGA
>
> if FPGA
>
> +config FPGA_MGR_ICE40_SPI
> + tristate "Lattice iCE40 SPI"
> + depends on SPI
> + help
> + FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
> +
> config FPGA_MGR_SOCFPGA
> tristate "Altera SOCFPGA FPGA Manager"
> depends on ARCH_SOCFPGA
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 8d83fc6..adb5811 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -6,5 +6,6 @@
> obj-$(CONFIG_FPGA) += fpga-mgr.o
>
> # FPGA Manager Drivers
> +obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
> obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
> obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> new file mode 100644
> index 0000000..a977f19
> --- /dev/null
> +++ b/drivers/fpga/ice40-spi.c
> @@ -0,0 +1,198 @@
> +/*
> + * FPGA Manager Driver for Lattice iCE40.
> + *
> + * Copyright (c) 2016 Joel Holdsworth
> + *
> + * 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 of the License.
> + *
> + * This driver adds support to the FPGA manager for configuring the SRAM of
> + * Lattice iCE40 FPGAs through slave SPI.
> + */
> +
> +#include <linux/fpga/fpga-mgr.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/of_gpio.h>
> +#include <linux/spi/spi.h>
Hi Joel,
It needs
#include <linux/module.h>
to be able to build.
Alan
> +
> +#define ICE40_SPI_FPGAMGR_RESET_DELAY 1 /* us (>200ns) */
> +#define ICE40_SPI_FPGAMGR_HOUSEKEEPING_DELAY 1200 /* us */
> +
> +#define ICE40_SPI_FPGAMGR_NUM_ACTIVATION_BITS 49 /* bits */
> +
> +struct ice40_fpga_priv {
> + struct spi_device *dev;
> + struct gpio_desc *reset;
> + struct gpio_desc *cdone;
> +};
> +
> +static enum fpga_mgr_states ice40_fpga_ops_state(struct fpga_manager *mgr)
> +{
> + struct ice40_fpga_priv *priv = mgr->priv;
> +
> + return gpiod_get_value(priv->cdone) ? FPGA_MGR_STATE_OPERATING :
> + FPGA_MGR_STATE_UNKNOWN;
> +}
> +
> +static int ice40_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
> + const char *buf, size_t count)
> +{
> + struct ice40_fpga_priv *priv = mgr->priv;
> + struct spi_device *dev = priv->dev;
> + struct spi_message message;
> + int ret;
> +
> + if ((flags & FPGA_MGR_PARTIAL_RECONFIG)) {
> + dev_err(&dev->dev,
> + "Partial reconfiguration is not supported\n");
> + return -ENOTSUPP;
> + }
> +
> + /* Lock the bus, assert CRESET_B and SS_B and delay >200ns */
> + spi_bus_lock(dev->master);
> +
> + gpiod_set_value(priv->reset, 1);
> +
> + spi_message_init(&message);
> + spi_message_add_tail(&(struct spi_transfer){.cs_change = 1,
> + .delay_usecs = ICE40_SPI_FPGAMGR_RESET_DELAY}, &message);
> + ret = spi_sync_locked(dev, &message);
> + if (ret) {
> + spi_bus_unlock(dev->master);
> + return ret;
> + }
> +
> + /* Come out of reset */
> + gpiod_set_value(priv->reset, 0);
> +
> + /* Check CDONE is de-asserted i.e. the FPGA is reset */
> + if (gpiod_get_value(priv->cdone)) {
> + dev_err(&dev->dev, "Device reset failed, CDONE is asserted\n");
> + spi_bus_unlock(dev->master);
> + return -EIO;
> + }
> +
> + /* Wait for the housekeeping to complete, and release SS_B */
> + spi_message_init(&message);
> + spi_message_add_tail(&(struct spi_transfer){
> + .delay_usecs = ICE40_SPI_FPGAMGR_HOUSEKEEPING_DELAY}, &message);
> + ret = spi_sync_locked(dev, &message);
> +
> + spi_bus_unlock(dev->master);
> +
> + return ret;
> +}
> +
> +static int ice40_fpga_ops_write(struct fpga_manager *mgr,
> + const char *buf, size_t count)
> +{
> + return spi_write(((struct ice40_fpga_priv *)mgr->priv)->dev,
> + buf, count);
> +}
> +
> +static int ice40_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags)
> +{
> + struct ice40_fpga_priv *priv = mgr->priv;
> + struct spi_device *dev = priv->dev;
> +
> + /* Check CDONE is asserted */
> + if (!gpiod_get_value(priv->cdone)) {
> + dev_err(&dev->dev,
> + "CDONE was not asserted after firmware transfer\n");
> + return -EIO;
> + }
> +
> + /* Send of zero-padding to activate the firmware */
> + return spi_write(dev, NULL, (ICE40_SPI_FPGAMGR_NUM_ACTIVATION_BITS +
> + dev->bits_per_word - 1) / dev->bits_per_word);
> +}
> +
> +static const struct fpga_manager_ops ice40_fpga_ops = {
> + .state = ice40_fpga_ops_state,
> + .write_init = ice40_fpga_ops_write_init,
> + .write = ice40_fpga_ops_write,
> + .write_complete = ice40_fpga_ops_write_complete,
> +};
> +
> +static int ice40_fpga_probe(struct spi_device *spi)
> +{
> + struct device *dev = &spi->dev;
> + struct device_node *np = spi->dev.of_node;
> + struct ice40_fpga_priv *priv;
> + int ret;
> +
> + if (!np) {
> + dev_err(dev, "No Device Tree entry\n");
> + return -EINVAL;
> + }
> +
> + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->dev = spi;
> +
> + /* Check board setup data. */
> + if (spi->max_speed_hz > 25000000) {
> + dev_err(dev, "Speed is too high\n");
> + return -EINVAL;
> + } else if (spi->mode & SPI_CPHA) {
> + dev_err(dev, "Bad mode\n");
> + return -EINVAL;
> + }
> +
> + /* Set up the GPIOs */
> + priv->cdone = devm_gpiod_get(dev, "cdone", GPIOD_IN);
> + if (IS_ERR(priv->cdone)) {
> + dev_err(dev, "Failed to get CDONE GPIO: %ld\n",
> + PTR_ERR(priv->cdone));
> + return ret;
> + }
> +
> + priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->reset)) {
> + dev_err(dev, "Failed to get CRESET_B GPIO: %ld\n",
> + PTR_ERR(priv->reset));
> + return ret;
> + }
> +
> + /* Register with the FPGA manager */
> + ret = fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> + &ice40_fpga_ops, priv);
> + if (ret) {
> + dev_err(dev, "unable to register FPGA manager");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ice40_fpga_remove(struct spi_device *spi)
> +{
> + fpga_mgr_unregister(&spi->dev);
> + return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id ice40_fpga_of_match[] = {
> + { .compatible = "lattice,ice40-fpga-mgr", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
> +#endif
> +
> +static struct spi_driver ice40_fpga_driver = {
> + .probe = ice40_fpga_probe,
> + .remove = ice40_fpga_remove,
> + .driver = {
> + .name = "ice40spi",
> + .of_match_table = of_match_ptr(ice40_fpga_of_match),
> + },
> +};
> +
> +module_spi_driver(ice40_fpga_driver);
> +
> +MODULE_AUTHOR("Joel Holdsworth <joel@airwebreathe.org.uk>");
> +MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
> +MODULE_LICENSE("GPL v2");
> --
> 2.7.4
>
>
^ permalink raw reply
* Re: [PATCH v7 3/3] fpga: Add support for Lattice iCE40 FPGAs
From: atull @ 2016-11-04 15:09 UTC (permalink / raw)
To: Joel Holdsworth
Cc: moritz.fischer-+aYTwkv1SeIAvxtiuMwx3w,
geert-Td1EMuHUCqxL1ZNQvxDV9g, robh-DgEjT+Ai2ygdnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-spi-u79uwXL29TY76Z2rM5mHXA, marex-mMYRjVVXLP4
In-Reply-To: <1478236004-7852-3-git-send-email-joel-IJEoVVyKhCJXvIrf17iDB/XRex20P6io@public.gmane.org>
On Fri, 4 Nov 2016, Joel Holdsworth wrote:
> The Lattice iCE40 is a family of FPGAs with a minimalistic architecture
> and very regular structure, designed for low-cost, high-volume consumer
> and system applications.
>
> This patch adds support to the FPGA manager for configuring the SRAM of
> iCE40LM, iCE40LP, iCE40HX, iCE40 Ultra, iCE40 UltraLite and iCE40
> UltraPlus devices, through slave SPI.
>
> The iCE40 family is notable because it is the first FPGA family to have
> complete reverse engineered bit-stream documentation for the iCE40LP and
> iCE40HX devices. Furthermore, there is now a Free Software Verilog
> synthesis tool-chain: the "IceStorm" tool-chain.
>
> This project is the work of Clifford Wolf, who is the maintainer of
> Yosys Verilog RTL synthesis framework, and Mathias Lasser, with notable
> contributions from "Cotton Seed", the main author of "arachne-pnr"; a
> place-and-route tool for iCE40 FPGAs.
>
> Having a Free Software synthesis tool-chain offers interesting
> opportunities for embedded devices that are able reconfigure themselves
> with open firmware that is generated on the device itself. For example
> a mobile device might have an application processor with an iCE40 FPGA
> attached, which implements slave devices, or through which the processor
> communicates with other devices through the FPGA fabric.
>
> A kernel driver for the iCE40 is useful, because in some cases, the FPGA
> may need to be configured before other devices can be accessed.
>
> An example of such a device is the icoBoard; a RaspberryPI HAT which
> features an iCE40HX8K with a 1 or 8 MBit SRAM and ports for
> Digilent-compatible PMOD modules. A PMOD module may contain a device
> with which the kernel communicates, via the FPGA.
> ---
> drivers/fpga/Kconfig | 6 ++
> drivers/fpga/Makefile | 1 +
> drivers/fpga/ice40-spi.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 205 insertions(+)
> create mode 100644 drivers/fpga/ice40-spi.c
>
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index d614102..85ff429 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -13,6 +13,12 @@ config FPGA
>
> if FPGA
>
> +config FPGA_MGR_ICE40_SPI
> + tristate "Lattice iCE40 SPI"
> + depends on SPI
> + help
> + FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
> +
> config FPGA_MGR_SOCFPGA
> tristate "Altera SOCFPGA FPGA Manager"
> depends on ARCH_SOCFPGA
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 8d83fc6..adb5811 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -6,5 +6,6 @@
> obj-$(CONFIG_FPGA) += fpga-mgr.o
>
> # FPGA Manager Drivers
> +obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
> obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
> obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> new file mode 100644
> index 0000000..a977f19
> --- /dev/null
> +++ b/drivers/fpga/ice40-spi.c
> @@ -0,0 +1,198 @@
> +/*
> + * FPGA Manager Driver for Lattice iCE40.
> + *
> + * Copyright (c) 2016 Joel Holdsworth
> + *
> + * 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 of the License.
> + *
> + * This driver adds support to the FPGA manager for configuring the SRAM of
> + * Lattice iCE40 FPGAs through slave SPI.
> + */
> +
> +#include <linux/fpga/fpga-mgr.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/of_gpio.h>
> +#include <linux/spi/spi.h>
Hi Joel,
The build breaks without this:
#include <linux/module.h>
Alan
> +
> +#define ICE40_SPI_FPGAMGR_RESET_DELAY 1 /* us (>200ns) */
> +#define ICE40_SPI_FPGAMGR_HOUSEKEEPING_DELAY 1200 /* us */
> +
> +#define ICE40_SPI_FPGAMGR_NUM_ACTIVATION_BITS 49 /* bits */
> +
> +struct ice40_fpga_priv {
> + struct spi_device *dev;
> + struct gpio_desc *reset;
> + struct gpio_desc *cdone;
> +};
> +
> +static enum fpga_mgr_states ice40_fpga_ops_state(struct fpga_manager *mgr)
> +{
> + struct ice40_fpga_priv *priv = mgr->priv;
> +
> + return gpiod_get_value(priv->cdone) ? FPGA_MGR_STATE_OPERATING :
> + FPGA_MGR_STATE_UNKNOWN;
> +}
> +
> +static int ice40_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
> + const char *buf, size_t count)
> +{
> + struct ice40_fpga_priv *priv = mgr->priv;
> + struct spi_device *dev = priv->dev;
> + struct spi_message message;
> + int ret;
> +
> + if ((flags & FPGA_MGR_PARTIAL_RECONFIG)) {
> + dev_err(&dev->dev,
> + "Partial reconfiguration is not supported\n");
> + return -ENOTSUPP;
> + }
> +
> + /* Lock the bus, assert CRESET_B and SS_B and delay >200ns */
> + spi_bus_lock(dev->master);
> +
> + gpiod_set_value(priv->reset, 1);
> +
> + spi_message_init(&message);
> + spi_message_add_tail(&(struct spi_transfer){.cs_change = 1,
> + .delay_usecs = ICE40_SPI_FPGAMGR_RESET_DELAY}, &message);
> + ret = spi_sync_locked(dev, &message);
> + if (ret) {
> + spi_bus_unlock(dev->master);
> + return ret;
> + }
> +
> + /* Come out of reset */
> + gpiod_set_value(priv->reset, 0);
> +
> + /* Check CDONE is de-asserted i.e. the FPGA is reset */
> + if (gpiod_get_value(priv->cdone)) {
> + dev_err(&dev->dev, "Device reset failed, CDONE is asserted\n");
> + spi_bus_unlock(dev->master);
> + return -EIO;
> + }
> +
> + /* Wait for the housekeeping to complete, and release SS_B */
> + spi_message_init(&message);
> + spi_message_add_tail(&(struct spi_transfer){
> + .delay_usecs = ICE40_SPI_FPGAMGR_HOUSEKEEPING_DELAY}, &message);
> + ret = spi_sync_locked(dev, &message);
> +
> + spi_bus_unlock(dev->master);
> +
> + return ret;
> +}
> +
> +static int ice40_fpga_ops_write(struct fpga_manager *mgr,
> + const char *buf, size_t count)
> +{
> + return spi_write(((struct ice40_fpga_priv *)mgr->priv)->dev,
> + buf, count);
> +}
> +
> +static int ice40_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags)
> +{
> + struct ice40_fpga_priv *priv = mgr->priv;
> + struct spi_device *dev = priv->dev;
> +
> + /* Check CDONE is asserted */
> + if (!gpiod_get_value(priv->cdone)) {
> + dev_err(&dev->dev,
> + "CDONE was not asserted after firmware transfer\n");
> + return -EIO;
> + }
> +
> + /* Send of zero-padding to activate the firmware */
> + return spi_write(dev, NULL, (ICE40_SPI_FPGAMGR_NUM_ACTIVATION_BITS +
> + dev->bits_per_word - 1) / dev->bits_per_word);
> +}
> +
> +static const struct fpga_manager_ops ice40_fpga_ops = {
> + .state = ice40_fpga_ops_state,
> + .write_init = ice40_fpga_ops_write_init,
> + .write = ice40_fpga_ops_write,
> + .write_complete = ice40_fpga_ops_write_complete,
> +};
> +
> +static int ice40_fpga_probe(struct spi_device *spi)
> +{
> + struct device *dev = &spi->dev;
> + struct device_node *np = spi->dev.of_node;
> + struct ice40_fpga_priv *priv;
> + int ret;
> +
> + if (!np) {
> + dev_err(dev, "No Device Tree entry\n");
> + return -EINVAL;
> + }
> +
> + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->dev = spi;
> +
> + /* Check board setup data. */
> + if (spi->max_speed_hz > 25000000) {
> + dev_err(dev, "Speed is too high\n");
> + return -EINVAL;
> + } else if (spi->mode & SPI_CPHA) {
> + dev_err(dev, "Bad mode\n");
> + return -EINVAL;
> + }
> +
> + /* Set up the GPIOs */
> + priv->cdone = devm_gpiod_get(dev, "cdone", GPIOD_IN);
> + if (IS_ERR(priv->cdone)) {
> + dev_err(dev, "Failed to get CDONE GPIO: %ld\n",
> + PTR_ERR(priv->cdone));
> + return ret;
> + }
> +
> + priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->reset)) {
> + dev_err(dev, "Failed to get CRESET_B GPIO: %ld\n",
> + PTR_ERR(priv->reset));
> + return ret;
> + }
> +
> + /* Register with the FPGA manager */
> + ret = fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> + &ice40_fpga_ops, priv);
> + if (ret) {
> + dev_err(dev, "unable to register FPGA manager");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ice40_fpga_remove(struct spi_device *spi)
> +{
> + fpga_mgr_unregister(&spi->dev);
> + return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id ice40_fpga_of_match[] = {
> + { .compatible = "lattice,ice40-fpga-mgr", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
> +#endif
> +
> +static struct spi_driver ice40_fpga_driver = {
> + .probe = ice40_fpga_probe,
> + .remove = ice40_fpga_remove,
> + .driver = {
> + .name = "ice40spi",
> + .of_match_table = of_match_ptr(ice40_fpga_of_match),
> + },
> +};
> +
> +module_spi_driver(ice40_fpga_driver);
> +
> +MODULE_AUTHOR("Joel Holdsworth <joel-IJEoVVyKhCJXvIrf17iDB/XRex20P6io@public.gmane.org>");
> +MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
> +MODULE_LICENSE("GPL v2");
> --
> 2.7.4
>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 10/13] ARM: dts: exynos: replace to "max-frequecy" instead of "clock-freq-min-max"
From: Krzysztof Kozlowski @ 2016-11-04 15:04 UTC (permalink / raw)
To: Heiko Stuebner
Cc: Jaehoon Chung, Krzysztof Kozlowski, linux-mmc, devicetree,
linux-kernel, linux-arm-kernel, linux-samsung-soc, linux-rockchip,
ulf.hansson, robh+dt, shawn.lin
In-Reply-To: <4066252.a1W9ejEKHX@phil>
On Fri, Nov 04, 2016 at 12:19:49PM +0100, Heiko Stuebner wrote:
> Hi Jaehoon,
>
> Am Freitag, 4. November 2016, 19:21:30 CET schrieb Jaehoon Chung:
> > On 11/04/2016 03:41 AM, Krzysztof Kozlowski wrote:
> > > On Thu, Nov 03, 2016 at 03:21:32PM +0900, Jaehoon Chung wrote:
> > >> In drivers/mmc/core/host.c, there is "max-frequency" property.
> > >> It should be same behavior. So Use the "max-frequency" instead of
> > >> "clock-freq-min-max".
> > >>
> > >> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> > >> ---
> > >>
> > >> arch/arm/boot/dts/exynos3250-artik5-eval.dts | 2 +-
> > >> arch/arm/boot/dts/exynos3250-artik5.dtsi | 2 +-
> > >> arch/arm/boot/dts/exynos3250-monk.dts | 2 +-
> > >> arch/arm/boot/dts/exynos3250-rinato.dts | 2 +-
> > >> 4 files changed, 4 insertions(+), 4 deletions(-)
> > >
> > > This looks totally independent to rest of patches so it can be applied
> > > separately without any functional impact (except lack of minimum
> > > frequency). Is that correct?
> >
> > You're right. I will split the patches. And will resend.
> > Thanks!
>
> I think what Krzysztof was asking was just if he can simply pick up this patch
> alone, as it does not require any of the previous changes.
>
> Same is true for the Rockchip patches I guess, so we could just take them
> individually into samsung/rockchip dts branches.
Yes, I wanted to get exactly this information. I couldn't find it in
cover letter.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH 4/9] pinctrl: meson: allow gpio to request irq
From: Linus Walleij @ 2016-11-04 15:03 UTC (permalink / raw)
To: Jerome Brunet
Cc: Marc Zyngier, Carlo Caione, Kevin Hilman,
open list:ARM/Amlogic Meson...,
linux-arm-kernel@lists.infradead.org, linux-gpio@vger.kernel.org,
linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
Thomas Gleixner, Jason Cooper, Rob Herring, Catalin Marinas,
Will Deacon, Russell King, Alexandre TORGUE, Maxime Coquelin
In-Reply-To: <1477564941.2482.240.camel@baylibre.com>
On Thu, Oct 27, 2016 at 12:42 PM, Jerome Brunet <jbrunet@baylibre.com> wrote:
> Ressource issue : When you create an irq mapping, in case of hierarchic
> domain, it calls the "alloc" function of the domain, which will
> eventually call the "alloc" function of the parent domain ... until you
> reach the "root" domain (here the gic).
(...)
> We are looking to create mapping "on-demand" to make the best use of
> the little number of interrupts we have. To catch request of drivers,
> like gpio-keys, which use gpio_to_irq, it looks the only viable place
> is the to_irq callback (at the moment).
>
> Drivers using gpio_to_irq in their probe function expect that this will
> give them the corresponding virq, so create the mapping if need be.
OK what I need to know is the following:
If this was not a gpio chip, just some random hierarchical irqchip
or mux from drivers/irqchip, where would you make the translation?
The answer to that question applies equally to any GPIO controller
that also provides an irqchip. .to_irq() is not the place to do the
translation.
I looked around and for example irq-s3c24xx.c seems to do this
in the irqdomain xlate() callback, which should only be called
when the interrupt is resolved for a consumer.
If that is the code that you partitioned over to drivers/irqchip,
then maybe this is a sign that this code should not be there
at all, but instead inside this gpiochip driver, or atleast accessible
by it so that your gpiochip driver has direct access to the
irqdomain it is using.
> However, I now get why you don't want that, it seems we have 2 types of
> platforms in the kernel right now:
>
> 1. The one creating the mapping in the to_irq callback. It might be
> because they just copy/paste from another driver doing it, or they may
> have a good reason for it (like I think we do)
>
> 2. the one which call gpio_to_irq in interrupt handlers. Honestly I did
> not know that one could that, but they are in the mainline too, and
> probably have a good reason for doing it.
They are probably all bad or legacy. None of this works with a
irqchip from DT like this:
foo: gpio@0 {
#gpio-cells = <2>;
gpio-controller;
interrupt-cells = <2>;
interrupt-controller;
}
bar: bar@1 {
interrupt-parent = <&foo>;
interrupt = <4>;
};
Here notice that bar is NOT doing gpios = <&foo 4>;
which is what you would do to get a GPIO and then call
.to_irq() on it. It just uses it as an interrupt controller.
This MUST work, or you cannot put interrupt-controller;
as a keyword on the gpiochip.
> irq_find_mapping looks safe in interrupt handler, I does not seem to
> sleep (please correct me if I'm wrong).
> irq_create_mapping definitely isn't, because of the irq_domain mutex.
Yep.
> We probably got into this situation because it wasn't clear enough
> whether to_irq was fast or slow (at least it took me a few mails to
> understand this ...)
I don't know either. It's just supposed to be a quick helper
to find the corresponding interrupt for a GPIO, it is not supposed
to have semantic side-effects.
> The two platform groups are most likely exclusive so nobody is sleeping
> in irq, everybody is happy. As a maintainer, I understand that you
> can't allow a dangerous situation like this to continue.
It's a mess allright. I need everyone's help to fix the mess.
> To fix the situation we could add a few things in the gpio subsys:
> - Make it clear that to_irq is fast (like you just did)
Sure patches accepted.
> - add a new callback (to_irq_slow ? provide_irq ? something else) which
> would be allowed to sleep and create mappings.
> - in gpio_to_irq function keeps calling to_irq like it does but also
> call to_irq_slow only if we are not in an interrupt context and a
> mapping could not be found. We could maybe use "in_interrupt()" for
> this ?
None of them should be allowed to create mappings because of the
explanation above: gpiochips and irqchips need to be
orthogonal.
> This way, we could keep the existing drivers working as they are (even
> if they are wrong) and slowly fix things up.
It doesn't seem to help with anything.
Instead go back to what I described above: if it was just
two irqchips: forget the fact that one of them is a GPIO chip
for a while, just two irqchips.
How does one irqchip map to another one?
That is what needs to happen, solely using the irqchip
infrastructure, NOT using .to_irq().
> Sorry, it was kind of long explanation but I hope things are more clear
> now.
I think I understand... famous last words.
>> I just misunderstand it as the global GPIO number, that is
>> not OK.
>
> Ok. Just to be clear, you are ok with the function
> "meson_gpio_to_hwirq" which just does this translation, right ?
That seems all right, the problem is relying on gpio_to_irq()
being called for the interrupts to work.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v2 2/2] of: changesets: Introduce changeset helper methods
From: Hans de Goede @ 2016-11-04 14:42 UTC (permalink / raw)
To: Rob Herring, Frank Rowand, Pantelis Antoniou
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Hans de Goede
In-Reply-To: <20161104144241.18002-1-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
From: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
Changesets are very powerful, but the lack of a helper API
makes using them cumbersome. Introduce a simple copy based
API that makes things considerably easier.
To wit, adding a property using the raw API.
struct property *prop;
prop = kzalloc(sizeof(*prop)), GFP_KERNEL);
prop->name = kstrdup("compatible");
prop->value = kstrdup("foo,bar");
prop->length = strlen(prop->value) + 1;
of_changeset_add_property(ocs, np, prop);
while using the helper API
of_changeset_add_property_string(ocs, np, "compatible",
"foo,bar");
Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
Changes in v2 (hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org):
-Address review comments from:
https://www.spinics.net/lists/kernel/msg2252845.html
-Simplify (and fix) __of_changeset_add_update_property_copy OOM handling
-Remove (by manual inlining) these 2 static helpers:
__of_changeset_add_update_property_u32
__of_changeset_add_update_property_bool
-Remove the following exported helper method:
of_changeset_node_move_to
---
drivers/of/dynamic.c | 428 +++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/of.h | 135 ++++++++++++++++
2 files changed, 563 insertions(+)
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index cc64675..d0b473b 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -830,3 +830,431 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action,
return 0;
}
EXPORT_SYMBOL_GPL(of_changeset_action);
+
+/* changeset helpers */
+
+/**
+ * of_changeset_create_device_node - Create an empty device node
+ *
+ * @ocs: changeset pointer
+ * @parent: parent device node
+ * @fmt: format string for the node's full_name
+ * @args: argument list for the format string
+ *
+ * Create an empty device node, marking it as detached and allocated.
+ *
+ * Returns a device node on success, an error encoded pointer otherwise
+ */
+struct device_node *of_changeset_create_device_nodev(
+ struct of_changeset *ocs, struct device_node *parent,
+ const char *fmt, va_list vargs)
+{
+ struct device_node *node;
+
+ node = __of_node_dupv(NULL, fmt, vargs);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+
+ node->parent = parent;
+ return node;
+}
+EXPORT_SYMBOL_GPL(of_changeset_create_device_nodev);
+
+/**
+ * of_changeset_create_device_node - Create an empty device node
+ *
+ * @ocs: changeset pointer
+ * @parent: parent device node
+ * @fmt: Format string for the node's full_name
+ * ... Arguments
+ *
+ * Create an empty device node, marking it as detached and allocated.
+ *
+ * Returns a device node on success, an error encoded pointer otherwise
+ */
+struct device_node *of_changeset_create_device_node(
+ struct of_changeset *ocs, struct device_node *parent,
+ const char *fmt, ...)
+{
+ va_list vargs;
+ struct device_node *node;
+
+ va_start(vargs, fmt);
+ node = of_changeset_create_device_nodev(ocs, parent, fmt, vargs);
+ va_end(vargs);
+ return node;
+}
+EXPORT_SYMBOL_GPL(of_changeset_create_device_node);
+
+static int __of_changeset_add_update_property_copy(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const void *value,
+ int length, bool update)
+{
+ struct property *prop;
+ int ret = -ENOMEM;
+
+ prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+ if (!prop)
+ return ret;
+
+ prop->name = kstrdup(name, GFP_KERNEL);
+ if (!prop->name)
+ goto out;
+
+ /*
+ * NOTE: There is no check for zero length value.
+ * In case of a boolean property, this will allocate a value
+ * of zero bytes. We do this to work around the use
+ * of of_get_property() calls on boolean values.
+ */
+ prop->value = kmemdup(value, length, GFP_KERNEL);
+ if (!prop->value)
+ goto out;
+
+ of_property_set_flag(prop, OF_DYNAMIC);
+ prop->length = length;
+
+ if (!update)
+ ret = of_changeset_add_property(ocs, np, prop);
+ else
+ ret = of_changeset_update_property(ocs, np, prop);
+
+ if (ret == 0)
+ return 0;
+
+out:
+ kfree(prop->value);
+ kfree(prop->name);
+ kfree(prop);
+ return ret;
+}
+
+static int __of_changeset_add_update_property_string(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *str,
+ bool update)
+{
+ return __of_changeset_add_update_property_copy(ocs, np, name, str,
+ strlen(str) + 1, update);
+}
+
+static int __of_changeset_add_update_property_stringv(struct of_changeset *ocs,
+ struct device_node *np, const char *name,
+ const char *fmt, va_list vargs, bool update)
+{
+ char *str;
+ int ret;
+
+ str = kvasprintf(GFP_KERNEL, fmt, vargs);
+ if (!str)
+ return -ENOMEM;
+
+ ret = __of_changeset_add_update_property_string(ocs, np, name, str, update);
+
+ kfree(str);
+
+ return ret;
+}
+
+static int __of_changeset_add_update_property_string_list(
+ struct of_changeset *ocs, struct device_node *np, const char *name,
+ const char **strs, int count, bool update)
+{
+ int total = 0, i, ret;
+ char *value, *s;
+
+ for (i = 0; i < count; i++) {
+ /* check if it's NULL */
+ if (!strs[i])
+ return -EINVAL;
+ total += strlen(strs[i]) + 1;
+ }
+
+ value = kmalloc(total, GFP_KERNEL);
+ if (!value)
+ return -ENOMEM;
+
+ for (i = 0, s = value; i < count; i++) {
+ /* no need to check for NULL, check above */
+ strcpy(s, strs[i]);
+ s += strlen(strs[i]) + 1;
+ }
+
+ ret = __of_changeset_add_update_property_copy(ocs, np, name, value,
+ total, update);
+
+ kfree(value);
+
+ return ret;
+}
+
+/**
+ * of_changeset_add_property_copy - Create a new property copying name & value
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @value: pointer to the value data
+ * @length: length of the value in bytes
+ *
+ * Adds a property to the changeset by making copies of the name & value
+ * entries.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_add_property_copy(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const void *value,
+ int length)
+{
+ return __of_changeset_add_update_property_copy(ocs, np, name, value,
+ length, false);
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_property_copy);
+
+/**
+ * of_changeset_add_property_string - Create a new string property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @str: string property
+ *
+ * Adds a string property to the changeset by making copies of the name
+ * and the string value.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_add_property_string(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *str)
+{
+ return __of_changeset_add_update_property_string(ocs, np, name, str,
+ false);
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_property_string);
+
+/**
+ * of_changeset_add_property_stringf - Create a new formatted string property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @fmt: format of string property
+ * ... arguments of the format string
+ *
+ * Adds a string property to the changeset by making copies of the name
+ * and the formatted value.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_add_property_stringf(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *fmt, ...)
+{
+ va_list vargs;
+ int ret;
+
+ va_start(vargs, fmt);
+ ret = __of_changeset_add_update_property_stringv(ocs, np, name, fmt,
+ vargs, false);
+ va_end(vargs);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_property_stringf);
+
+/**
+ * of_changeset_add_property_string_list - Create a new string list property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @strs: pointer to the string list
+ * @count: string count
+ *
+ * Adds a string list property to the changeset.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_add_property_string_list(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char **strs,
+ int count)
+{
+ return __of_changeset_add_update_property_string_list(ocs, np, name,
+ strs, count, false);
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_property_string_list);
+
+/**
+ * of_changeset_add_property_u32 - Create a new u32 property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @val: value in host endian format
+ *
+ * Adds a u32 property to the changeset.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_add_property_u32(struct of_changeset *ocs,
+ struct device_node *np, const char *name, u32 val)
+{
+ __be32 _val = cpu_to_be32(val);
+ return __of_changeset_add_update_property_copy(ocs, np, name, &_val,
+ sizeof(_val), false);
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_property_u32);
+
+/**
+ * of_changeset_add_property_bool - Create a new u32 property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ *
+ * Adds a bool property to the changeset. Note that there is
+ * no option to set the value to false, since the property
+ * existing sets it to true.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_add_property_bool(struct of_changeset *ocs,
+ struct device_node *np, const char *name)
+{
+ return __of_changeset_add_update_property_copy(ocs, np, name, "", 0,
+ false);
+}
+EXPORT_SYMBOL_GPL(of_changeset_add_property_bool);
+
+/**
+ * of_changeset_update_property_copy - Update a property copying name & value
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @value: pointer to the value data
+ * @length: length of the value in bytes
+ *
+ * Update a property to the changeset by making copies of the name & value
+ * entries.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_update_property_copy(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const void *value,
+ int length)
+{
+ return __of_changeset_add_update_property_copy(ocs, np, name, value,
+ length, true);
+}
+EXPORT_SYMBOL_GPL(of_changeset_update_property_copy);
+
+/**
+ * of_changeset_update_property_string - Create a new string property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @str: string property
+ *
+ * Updates a string property to the changeset by making copies of the name
+ * and the string value.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_update_property_string(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *str)
+{
+ return __of_changeset_add_update_property_string(ocs, np, name, str,
+ true);
+}
+EXPORT_SYMBOL_GPL(of_changeset_update_property_string);
+
+/**
+ * of_changeset_update_property_stringf - Update formatted string property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @fmt: format of string property
+ * ... arguments of the format string
+ *
+ * Updates a string property to the changeset by making copies of the name
+ * and the formatted value.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_update_property_stringf(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *fmt, ...)
+{
+ va_list vargs;
+ int ret;
+
+ va_start(vargs, fmt);
+ ret = __of_changeset_add_update_property_stringv(ocs, np, name, fmt,
+ vargs, true);
+ va_end(vargs);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_changeset_update_property_stringf);
+
+/**
+ * of_changeset_update_property_string_list - Update string list property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @strs: pointer to the string list
+ * @count: string count
+ *
+ * Updates a string list property to the changeset.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_update_property_string_list(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char **strs,
+ int count)
+{
+ return __of_changeset_add_update_property_string_list(ocs, np, name,
+ strs, count, true);
+}
+EXPORT_SYMBOL_GPL(of_changeset_update_property_string_list);
+
+/**
+ * of_changeset_update_property_u32 - Update u32 property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ * @val: value in host endian format
+ *
+ * Updates a u32 property to the changeset.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_update_property_u32(struct of_changeset *ocs,
+ struct device_node *np, const char *name, u32 val)
+{
+ __be32 _val = cpu_to_be32(val);
+ return __of_changeset_add_update_property_copy(ocs, np, name, &_val,
+ sizeof(_val), true);
+}
+EXPORT_SYMBOL_GPL(of_changeset_update_property_u32);
+
+/**
+ * of_changeset_update_property_bool - Update a bool property
+ *
+ * @ocs: changeset pointer
+ * @np: device node pointer
+ * @name: name of the property
+ *
+ * Updates a property to the changeset. Note that there is
+ * no option to set the value to false, since the property
+ * existing sets it to true.
+ *
+ * Returns zero on success, a negative error value otherwise.
+ */
+int of_changeset_update_property_bool(struct of_changeset *ocs,
+ struct device_node *np, const char *name)
+{
+ return __of_changeset_add_update_property_copy(ocs, np, name, "", 0,
+ true);
+}
+EXPORT_SYMBOL_GPL(of_changeset_update_property_bool);
diff --git a/include/linux/of.h b/include/linux/of.h
index 299aeb1..9c80457 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -1227,6 +1227,40 @@ static inline int of_changeset_update_property(struct of_changeset *ocs,
{
return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop);
}
+
+struct device_node *of_changeset_create_device_nodev(
+ struct of_changeset *ocs, struct device_node *parent,
+ const char *fmt, va_list vargs);
+__printf(3, 4) struct device_node *of_changeset_create_device_node(
+ struct of_changeset *ocs, struct device_node *parent,
+ const char *fmt, ...);
+int of_changeset_add_property_copy(struct of_changeset *ocs,
+ struct device_node *np, const char *name,
+ const void *value, int length);
+int of_changeset_add_property_string(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *str);
+__printf(4, 5) int of_changeset_add_property_stringf(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *fmt, ...);
+int of_changeset_add_property_string_list(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char **strs, int count);
+int of_changeset_add_property_u32(struct of_changeset *ocs,
+ struct device_node *np, const char *name, u32 val);
+int of_changeset_add_property_bool(struct of_changeset *ocs,
+ struct device_node *np, const char *name);
+int of_changeset_update_property_copy(struct of_changeset *ocs,
+ struct device_node *np, const char *name,
+ const void *value, int length);
+int of_changeset_update_property_string(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *str);
+__printf(4, 5) int of_changeset_update_property_stringf(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *fmt, ...);
+int of_changeset_update_property_string_list(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char **strs, int count);
+int of_changeset_update_property_u32(struct of_changeset *ocs,
+ struct device_node *np, const char *name, u32 val);
+int of_changeset_update_property_bool(struct of_changeset *ocs,
+ struct device_node *np, const char *name);
+
#else /* CONFIG_OF_DYNAMIC */
static inline int of_reconfig_notifier_register(struct notifier_block *nb)
{
@@ -1246,6 +1280,107 @@ static inline int of_reconfig_get_state_change(unsigned long action,
{
return -EINVAL;
}
+
+static inline int of_changeset_create_device_node(struct of_changeset *ocs,
+ struct device_node *parent, const char *fmt, ...)
+{
+ return -EINVAL;
+}
+
+int of_changeset_add_property_copy(struct of_changeset *ocs,
+ struct device_node *np, const char *name,
+ const void *value, int length)
+{
+ return -EINVAL;
+}
+
+int of_changeset_add_property_string(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *str)
+{
+ return -EINVAL;
+}
+
+static inline struct device_node *of_changeset_create_device_nodev(
+ struct of_changeset *ocs, struct device_node *parent,
+ const char *fmt, va_list vargs)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline __printf(4, 5) struct device_node *
+ of_changeset_add_property_stringf(
+ struct of_changeset *ocs, struct device_node *np,
+ const char *name, const char *fmt, ...)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int of_changeset_add_property_string_list(
+ struct of_changeset *ocs, struct device_node *np, const char *name,
+ const char **strs, int count)
+{
+ return -EINVAL;
+}
+
+static inline int of_changeset_add_property_u32(struct of_changeset *ocs,
+ struct device_node *np, const char *name, u32 val)
+{
+ return -EINVAL;
+}
+
+static inline int of_changeset_add_property_bool(struct of_changeset *ocs,
+ struct device_node *np, const char *name)
+{
+ return -EINVAL;
+}
+
+int of_changeset_update_property_copy(struct of_changeset *ocs,
+ struct device_node *np, const char *name,
+ const void *value, int length)
+{
+ return -EINVAL;
+}
+
+int of_changeset_update_property_string(struct of_changeset *ocs,
+ struct device_node *np, const char *name, const char *str)
+{
+ return -EINVAL;
+}
+
+static inline struct device_node *of_changeset_create_device_nodev(
+ struct of_changeset *ocs, struct device_node *parent,
+ const char *fmt, va_list vargs)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline __printf(4, 5) struct device_node *
+ of_changeset_update_property_stringf(
+ struct of_changeset *ocs, struct device_node *np,
+ const char *name, const char *fmt, ...)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int of_changeset_update_property_string_list(
+ struct of_changeset *ocs, struct device_node *np, const char *name,
+ const char **strs, int count)
+{
+ return -EINVAL;
+}
+
+static inline int of_changeset_update_property_u32(struct of_changeset *ocs,
+ struct device_node *np, const char *name, u32 val)
+{
+ return -EINVAL;
+}
+
+static inline int of_changeset_update_property_bool(struct of_changeset *ocs,
+ struct device_node *np, const char *name)
+{
+ return -EINVAL;
+}
+
#endif /* CONFIG_OF_DYNAMIC */
/* CONFIG_OF_RESOLVE api */
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 1/2] of: dynamic: Add __of_node_dupv()
From: Hans de Goede @ 2016-11-04 14:42 UTC (permalink / raw)
To: Rob Herring, Frank Rowand, Pantelis Antoniou
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Hans de Goede
From: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
Add an __of_node_dupv() private method and make __of_node_dup() use it.
This is required for the subsequent changeset accessors which will
make use of it.
Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
Changes in v2 (hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org):
-No changes (unmodified preparation patch)
---
drivers/of/dynamic.c | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 888fdbc..cc64675 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -397,8 +397,9 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
}
/**
- * __of_node_dup() - Duplicate or create an empty device node dynamically.
- * @fmt: Format string (plus vargs) for new full name of the device node
+ * __of_node_dupv() - Duplicate or create an empty device node dynamically.
+ * @fmt: Format string for new full name of the device node
+ * @vargs: va_list containing the arugments for the node full name
*
* Create an device tree node, either by duplicating an empty node or by allocating
* an empty one suitable for further modification. The node data are
@@ -406,17 +407,15 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
* OF_DETACHED bits set. Returns the newly allocated node or NULL on out of
* memory error.
*/
-struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...)
+struct device_node *__of_node_dupv(const struct device_node *np,
+ const char *fmt, va_list vargs)
{
- va_list vargs;
struct device_node *node;
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return NULL;
- va_start(vargs, fmt);
node->full_name = kvasprintf(GFP_KERNEL, fmt, vargs);
- va_end(vargs);
if (!node->full_name) {
kfree(node);
return NULL;
@@ -448,6 +447,24 @@ struct device_node *__of_node_dup(const struct device_node *np, const char *fmt,
return NULL;
}
+/**
+ * __of_node_dup() - Duplicate or create an empty device node dynamically.
+ * @fmt: Format string (plus vargs) for new full name of the device node
+ *
+ * See: __of_node_dupv()
+ */
+struct device_node *__of_node_dup(const struct device_node *np,
+ const char *fmt, ...)
+{
+ va_list vargs;
+ struct device_node *node;
+
+ va_start(vargs, fmt);
+ node = __of_node_dupv(np, fmt, vargs);
+ va_end(vargs);
+ return node;
+}
+
static void __of_changeset_entry_destroy(struct of_changeset_entry *ce)
{
of_node_put(ce->np);
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH 4/9] pinctrl: meson: allow gpio to request irq
From: Linus Walleij @ 2016-11-04 14:40 UTC (permalink / raw)
To: Kevin Hilman, Alexandre TORGUE, Maxime Coquelin
Cc: Jerome Brunet, Marc Zyngier, Carlo Caione,
open list:ARM/Amlogic Meson...,
linux-arm-kernel@lists.infradead.org, linux-gpio@vger.kernel.org,
linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
Thomas Gleixner, Jason Cooper, Rob Herring, Catalin Marinas,
Will Deacon, Russell King
In-Reply-To: <7hk2cvrtxt.fsf@baylibre.com>
On Wed, Oct 26, 2016 at 5:50 PM, Kevin Hilman <khilman@baylibre.com> wrote:
>> Yes they are all wrong. They should all be using irq_find_mapping().
>
> So, dumb question from someone trying (but having a hard time) to follow
> and understand the rationale...
>
> If it's wrong enough to completely reject, why are changes still being
> merged that are doing it so wrong? (e.g. like this one[1], just merged
> for v4.9)
It's a bug.
It's that problem that Wolfram brought up in a recent lecture
about maintainer scaling: if noone but the subsystem maintainer
reviews the code, things like this will happen.
I need more review...
> [1] 0eb9f683336d pinctrl: Add IRQ support to STM32 gpios
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/pinctrl/stm32/pinctrl-stm32.c?id=0eb9f683336d7eb99a3b75987620417c574ffb57
Alexandre, Maxime: can you please make a patch for the STM32
driver that remove the semantic dependence for .to_irq() to be called
before an interrupt can be used? It should be possible to use
the irqs directly from the irqchip.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH 2/5] drivers: gpio: Add support for multiple IPs
From: Linus Walleij @ 2016-11-04 14:28 UTC (permalink / raw)
To: Keerthy
Cc: Roger Quadros, Grygorii Strashko, Alexandre Courbot, Lokesh Vutla,
Rob Herring, linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux-OMAP
In-Reply-To: <c047fb27-a8bb-c10d-2b16-a3e6bf45d88f-l0cyMroinI0@public.gmane.org>
On Thu, Oct 27, 2016 at 10:07 AM, Keerthy <j-keerthy-l0cyMroinI0@public.gmane.org> wrote:
> On Thursday 27 October 2016 01:23 PM, Roger Quadros wrote:
>> On 27/10/16 06:42, Keerthy wrote:
>>> On Sunday 23 October 2016 04:02 PM, Linus Walleij wrote:
>>>> On Wed, Oct 19, 2016 at 7:33 AM, Keerthy <j-keerthy-l0cyMroinI0@public.gmane.org> wrote:
>>>>> From: Lokesh Vutla <lokeshvutla-l0cyMroinI0@public.gmane.org>
>>> In case of k2g. There are 2 big GPIO modules GPIO0 and GPIO1.
>>> GPIO0 comprises of 144 GPIOs
>>> and GPIO1 has about 68 GPIOs. Wanted feedback from you on how this is
>>> being modeled.
>>>
>>> I am creating a controller for every 32 GPIOs under the big module each
>>> containing a gpio_chip. Each 32 GPIOs chip has 2 banks of 16 GPIOs each.
>>> Each 16 GPIO bank has an interrupt.
>>>
>>> Is this modeling fine or do you think creating one chip with 144 pins and
>>> another with 68 pins is a better way?
>>
>>
>> If GPIO0 has 144 GPIOs, why don't we model it as a gpiochip with 144
>> GPIOs?
>> What is the benefit of partitioning it into gpiochips of 32 GPIOs each?
>
> 144 GPIOs where in 16 GPIOs form a bank. So about 9 banks with one interrupt
> each. So split it into gpiochips with 32 GPIOs each handling 2 Interrupts.
I'm a bit confused.
This sounds like you should either have one gpio_chip per interrupt
(if that fits with how the device tree looks) or one big gpio_chip for
all the lines.
The DT model sort of mandates how the interrupts should be mapped
at this point, and as far as I can tell from the binding the example looks
like so:
gpio: gpio@1e26000 {
compatible = "ti,dm6441-gpio";
gpio-controller;
#gpio-cells = <2>;
reg = <0x226000 0x1000>;
interrupt-parent = <&intc>;
interrupts = <42 IRQ_TYPE_EDGE_BOTH 43 IRQ_TYPE_EDGE_BOTH
44 IRQ_TYPE_EDGE_BOTH 45 IRQ_TYPE_EDGE_BOTH
46 IRQ_TYPE_EDGE_BOTH 47 IRQ_TYPE_EDGE_BOTH
48 IRQ_TYPE_EDGE_BOTH 49 IRQ_TYPE_EDGE_BOTH
50 IRQ_TYPE_EDGE_BOTH>;
ti,ngpio = <144>;
ti,davinci-gpio-unbanked = <0>;
interrupt-controller;
#interrupt-cells = <2>;
};
So I don't see any reason to split this up in subchips internally in
Linux?
It looks like the irqdomain will be a bit tricksy though.
Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2 1/3] remoteproc: qcom: Encapsulate pvt data structure for q6v56 hexagon.
From: Avaneesh Kumar Dwivedi @ 2016-11-04 14:00 UTC (permalink / raw)
To: bjorn.andersson
Cc: Avaneesh Kumar Dwivedi, Ohad Ben-Cohen, Rob Herring, Mark Rutland,
open list:REMOTE PROCESSOR REMOTEPROC SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <1478268057-11847-1-git-send-email-akdwived@codeaurora.org>
Encapsulate resources specific to each version of hexagon chip to
device node to avoid conditional check for manipulation of those
resources in driver code.
Signed-off-by: Avaneesh Kumar Dwivedi <akdwived@codeaurora.org>
---
.../devicetree/bindings/remoteproc/qcom,q6v5.txt | 1 +
drivers/remoteproc/qcom_q6v5_pil.c | 137 ++++++++++++++++++---
2 files changed, 120 insertions(+), 18 deletions(-)
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt
index 57cb49e..cbc165c 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt
@@ -8,6 +8,7 @@ on the Qualcomm Hexagon core.
Value type: <string>
Definition: must be one of:
"qcom,q6v5-pil"
+ "qcom,q6v56-pil"
- reg:
Usage: required
diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
index 2e0caaa..3d26199 100644
--- a/drivers/remoteproc/qcom_q6v5_pil.c
+++ b/drivers/remoteproc/qcom_q6v5_pil.c
@@ -30,13 +30,13 @@
#include <linux/reset.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
+#include <linux/of_device.h>
#include "remoteproc_internal.h"
#include "qcom_mdt_loader.h"
#include <linux/qcom_scm.h>
-#define MBA_FIRMWARE_NAME "mba.b00"
#define MPSS_FIRMWARE_NAME "modem.mdt"
#define MPSS_CRASH_REASON_SMEM 421
@@ -93,13 +93,32 @@
#define QDSS_BHS_ON BIT(21)
#define QDSS_LDO_BYP BIT(22)
+struct q6_rproc_res {
+ char **proxy_clks;
+ int proxy_clk_cnt;
+ char **active_clks;
+ int active_clk_cnt;
+ char **proxy_regs;
+ int proxy_reg_cnt;
+ char **active_regs;
+ int active_reg_cnt;
+ int **proxy_reg_action;
+ int **active_reg_action;
+ int *proxy_reg_load;
+ int *active_reg_load;
+ int *proxy_reg_voltage;
+ int *active_reg_voltage;
+ char *q6_version;
+ char *q6_mba_image;
+ int (*q6_reset_init)(void *q, void *p);
+};
struct q6v5 {
struct device *dev;
struct rproc *rproc;
void __iomem *reg_base;
void __iomem *rmb_base;
-
+ void __iomem *restart_reg;
struct regmap *halt_map;
u32 halt_q6;
u32 halt_modem;
@@ -111,7 +130,7 @@ struct q6v5 {
unsigned stop_bit;
struct regulator_bulk_data supply[4];
-
+ struct q6_rproc_res *q6_rproc_res;
struct clk *ahb_clk;
struct clk *axi_clk;
struct clk *rom_clk;
@@ -198,7 +217,7 @@ static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
return 0;
}
-static const struct rproc_fw_ops q6v5_fw_ops = {
+static const struct rproc_fw_ops q6_fw_ops = {
.find_rsc_table = qcom_mdt_find_rsc_table,
.load = q6v5_load,
};
@@ -599,7 +618,7 @@ static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len)
return qproc->mpss_region + offset;
}
-static const struct rproc_ops q6v5_ops = {
+static const struct rproc_ops q6_ops = {
.start = q6v5_start,
.stop = q6v5_stop,
.da_to_va = q6v5_da_to_va,
@@ -736,6 +755,22 @@ static int q6v5_init_reset(struct q6v5 *qproc)
return 0;
}
+static int q6v56_init_reset(void *q, void *p)
+{
+ struct resource *res;
+ struct q6v5 *qproc = q;
+ struct platform_device *pdev = p;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg");
+ qproc->restart_reg = devm_ioremap(qproc->dev, res->start,
+ resource_size(res));
+ if (IS_ERR(qproc->restart_reg)) {
+ dev_err(qproc->dev, "failed to get restart_reg\n");
+ return PTR_ERR(qproc->restart_reg);
+ }
+
+ return 0;
+}
static int q6v5_request_irq(struct q6v5 *qproc,
struct platform_device *pdev,
const char *name,
@@ -803,20 +838,25 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
return 0;
}
-static int q6v5_probe(struct platform_device *pdev)
+static int q6_probe(struct platform_device *pdev)
{
struct q6v5 *qproc;
struct rproc *rproc;
+ struct q6_rproc_res *desc;
int ret;
- rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops,
- MBA_FIRMWARE_NAME, sizeof(*qproc));
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ rproc = rproc_alloc(&pdev->dev, pdev->name, &q6_ops,
+ desc->q6_mba_image, sizeof(*qproc));
if (!rproc) {
dev_err(&pdev->dev, "failed to allocate rproc\n");
return -ENOMEM;
}
- rproc->fw_ops = &q6v5_fw_ops;
+ rproc->fw_ops = &q6_fw_ops;
qproc = (struct q6v5 *)rproc->priv;
qproc->dev = &pdev->dev;
@@ -826,6 +866,7 @@ static int q6v5_probe(struct platform_device *pdev)
init_completion(&qproc->start_done);
init_completion(&qproc->stop_done);
+ qproc->q6_rproc_res = desc;
ret = q6v5_init_mem(qproc, pdev);
if (ret)
goto free_rproc;
@@ -842,7 +883,7 @@ static int q6v5_probe(struct platform_device *pdev)
if (ret)
goto free_rproc;
- ret = q6v5_init_reset(qproc);
+ ret = qproc->q6_rproc_res->q6_reset_init(qproc, pdev);
if (ret)
goto free_rproc;
@@ -880,7 +921,7 @@ static int q6v5_probe(struct platform_device *pdev)
return ret;
}
-static int q6v5_remove(struct platform_device *pdev)
+static int q6_remove(struct platform_device *pdev)
{
struct q6v5 *qproc = platform_get_drvdata(pdev);
@@ -890,20 +931,80 @@ static int q6v5_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id q6v5_of_match[] = {
- { .compatible = "qcom,q6v5-pil", },
+char *proxy_8x96_reg_str[] = {"mx", "cx", "vdd_pll"};
+int proxy_8x96_reg_action[3][2] = { {0, 1}, {1, 1}, {1, 0} };
+int proxy_8x96_reg_load[] = {0, 100000, 100000};
+int proxy_8x96_reg_min_voltage[] = {1050000, 1250000, 0};
+char *proxy_8x96_clk_str[] = {"xo", "pnoc", "qdss"};
+char *active_8x96_clk_str[] = {"iface", "bus", "mem", "gpll0_mss_clk",
+ "snoc_axi_clk", "mnoc_axi_clk"};
+
+static struct q6_rproc_res msm_8996_res = {
+ .proxy_clks = proxy_8x96_clk_str,
+ .proxy_clk_cnt = 3,
+ .active_clks = active_8x96_clk_str,
+ .active_clk_cnt = 6,
+ .proxy_regs = proxy_8x96_reg_str,
+ .active_regs = NULL,
+ .proxy_reg_action = (int **)proxy_8x96_reg_action,
+ .proxy_reg_load = (int *)proxy_8x96_reg_load,
+ .active_reg_action = NULL,
+ .active_reg_load = NULL,
+ .proxy_reg_voltage = (int *)proxy_8x96_reg_min_voltage,
+ .active_reg_voltage = NULL,
+ .proxy_reg_cnt = 3,
+ .active_reg_cnt = 0,
+ .q6_reset_init = q6v56_init_reset,
+ .q6_version = "v56",
+ .q6_mba_image = "mba.mbn",
+};
+
+char *proxy_8x16_reg_str[] = {"mx", "cx", "pll"};
+char *active_8x16_reg_str[] = {"mss"};
+int proxy_8x16_reg_action[4][2] = { {0, 1}, {1, 0}, {1, 0} };
+int active_8x16_reg_action[1][2] = { {1, 1} };
+int proxy_8x16_reg_load[] = {100000, 0, 100000, 100000};
+int active_8x16_reg_load[] = {100000};
+int proxy_8x16_reg_min_voltage[] = {1050000, 0, 0};
+int active_8x16_reg_min_voltage[] = {1000000};
+char *proxy_8x16_clk_str[] = {"xo"};
+char *active_8x16_clk_str[] = {"iface", "bus", "mem"};
+
+static struct q6_rproc_res msm_8916_res = {
+ .proxy_clks = proxy_8x16_clk_str,
+ .proxy_clk_cnt = 1,
+ .active_clks = active_8x16_clk_str,
+ .active_clk_cnt = 3,
+ .proxy_regs = proxy_8x16_reg_str,
+ .active_regs = active_8x16_reg_str,
+ .proxy_reg_action = (int **)proxy_8x16_reg_action,
+ .proxy_reg_load = (int *)proxy_8x16_reg_load,
+ .active_reg_action = (int **)active_8x16_reg_action,
+ .active_reg_load = (int *)active_8x16_reg_load,
+ .proxy_reg_voltage = (int *)proxy_8x16_reg_min_voltage,
+ .active_reg_voltage = active_8x16_reg_min_voltage,
+ .proxy_reg_cnt = 3,
+ .active_reg_cnt = 1,
+ .q6_reset_init = q6v5_init_reset,
+ .q6_version = "v5",
+ .q6_mba_image = "mba.b00",
+};
+
+static const struct of_device_id q6_of_match[] = {
+ { .compatible = "qcom,q6v5-pil", .data = &msm_8916_res},
+ { .compatible = "qcom,q6v56-pil", .data = &msm_8996_res},
{ },
};
-static struct platform_driver q6v5_driver = {
- .probe = q6v5_probe,
- .remove = q6v5_remove,
+static struct platform_driver q6_driver = {
+ .probe = q6_probe,
+ .remove = q6_remove,
.driver = {
.name = "qcom-q6v5-pil",
- .of_match_table = q6v5_of_match,
+ .of_match_table = q6_of_match,
},
};
-module_platform_driver(q6v5_driver);
+module_platform_driver(q6_driver);
MODULE_DESCRIPTION("Peripheral Image Loader for Hexagon");
MODULE_LICENSE("GPL v2");
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* Re: [PATCH v6 7/7] arm64: dts: NS2: add AMAC ethernet support
From: Sergei Shtylyov @ 2016-11-04 13:31 UTC (permalink / raw)
To: Jon Mason, David Miller, Rob Herring, Mark Rutland,
Florian Fainelli
Cc: rafal-g1n6cQUeyibVItvQsEIGlw,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1478236262-3351-8-git-send-email-jon.mason-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
Hello.
On 11/4/2016 8:11 AM, Jon Mason wrote:
> Add support for the AMAC ethernet to the Broadcom Northstar2 SoC device
> tree
>
> Signed-off-by: Jon Mason <jon.mason-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
> ---
> arch/arm64/boot/dts/broadcom/ns2-svk.dts | 5 +++++
> arch/arm64/boot/dts/broadcom/ns2.dtsi | 12 ++++++++++++
> 2 files changed, 17 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/broadcom/ns2-svk.dts b/arch/arm64/boot/dts/broadcom/ns2-svk.dts
> index b09f3bc..c4d5442 100644
> --- a/arch/arm64/boot/dts/broadcom/ns2-svk.dts
> +++ b/arch/arm64/boot/dts/broadcom/ns2-svk.dts
> @@ -56,6 +56,10 @@
> };
> };
>
> +&enet {
> + status = "ok";
The spec dictates it should be "okay" (although "ok" is also recognized).
> +};
> +
> &pci_phy0 {
> status = "ok";
> };
> @@ -174,6 +178,7 @@
> &mdio_mux_iproc {
> mdio@10 {
> gphy0: eth-phy@10 {
> + enet-phy-lane-swap;
> reg = <0x10>;
> };
> };
[...]
MBR, Sergei
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [REGRESSION] "console: don't prefer first registered if DT specifies stdout-path" breaks console on video outputs of various ARM boards
From: Hans de Goede @ 2016-11-04 13:22 UTC (permalink / raw)
To: Paul Burton
Cc: Linus Torvalds, Andrew Morton, Rob Herring, Frank Rowand,
Thorsten Leemhuis, Greg Kroah-Hartman, Tejun Heo,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <17833033.PtOSTzKXGD@np-p-burton>
Hi,
On 04-11-16 13:30, Paul Burton wrote:
> Hi Hans,
>
> On Friday, 4 November 2016 13:11:34 GMT Hans de Goede wrote:
>> Hi All,
>>
>> While booting 4.9-rc# for the first time on an Allwinner A33 tablet,
>> I noticed that after u-boot the LCD display stayed black. It turns out
>> that there was an issue which caused X to never get up, and all kernel
>> (and other startup) messages prior to that only went to ttyS0 which
>> consists of 2 tiny testpads on the PCB with this tablet.
>>
>> The same issue will also happen on any ARM boards which have a HDMI or
>> composite video output and which use a stdout-path pointing to their
>> serial console. I think this will e.g. also impact the Raspberry Pi,
>> I know for certain that this will impact the 99 different Allwinnner
>> boards currently supported by mainline u-boot + the mainline kernel.
>>
>> This is a behavior changes from previous kernels and I consider this
>> a regression. Thus I propose to revert the commit in question, for more
>> info here is a partial copy of the commit message of the proposed revert:
>>
>> The reverted commit changes existing behavior on which many ARM boards
>> rely. Many ARM small-board-computers, like e.g. the Raspberry Pi have
>> both a video output and a serial console. Depending on whether the user
>> is using the device as a more regular computer; or as a headless device
>> we need to have the console on either one or the other.
>>
>> Many users rely on the kernel behavior of the console being present on
>> both outputs, before the reverted commit the console setup with no
>> console= kernel arguments on an ARM board which sets stdout-path in dt
>> would look like this:
>>
>> [root@localhost ~]# cat /proc/consoles
>> ttyS0 -W- (EC p a) 4:64
>> tty0 -WU (E p ) 4:1
>>
>> Where as after the reverted commit, it looks like this:
>>
>> [root@localhost ~]# cat /proc/consoles
>> ttyS0 -W- (EC p a) 4:64
>>
>> This commit reverts commit 05fd007e4629 ("console: don't prefer first
>> registered if DT specifies stdout-path") restoring the original behavior.
>>
>> Regards,
>>
>> Hans
>
> Ugh... so the devices you're talking about rely upon set stdout-path in their
> device tree but effectively rely upon us ignoring it?
No they rely on the kernel using stdout-path as an extra console while keeping
tty0 as console, not ignoring it. This how stdout-path has always worked
(at least as long as the Allwinner boards have used it, which has been
2 - 3 years now).
If you want only the console specified by stdout-path you can get this by
specifying it with console=... on the kernel cmdline.
> If that's the case then I guess reverting is probably the best option, but it
> does restore us to a position where we honor stdout-path for earlycon & then
> essentially ignore it for the proper kernel console. That seems pretty bust to
> me...
We do not ignore it, we use both the tty pointed to by stdout-path and tty0.
Regards,
Hans
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v3 6/6] arm64: arch_timer: acpi: add hisi timer errata data
From: Ding Tianhong @ 2016-11-04 13:06 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1478264794-14652-1-git-send-email-dingtianhong@huawei.com>
From: Hanjun Guo <hanjun.guo@linaro.org>
Add hisi timer specific erratum fixes.
v3: add hisilicon erratum 161601 for ACPI mode.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
drivers/clocksource/arm_arch_timer.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 9bc93e5..270d179 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1078,10 +1078,26 @@ struct gtdt_arch_timer_fixup {
u32 erratum;
};
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+static void __init erratum_workaround_enable(u32 erratum)
+{
+ if (erratum & HISILICON_161601) {
+ timer_unstable_counter_workaround = &arch_timer_hisi_161601;
+ static_branch_enable(&arch_timer_read_ool_enabled);
+ pr_info("Enabling workaround for HISILICON ERRATUM 161601\n");
+ }
+}
+#endif
+
/* note: this needs to be updated according to the doc of OEM ID
* and TABLE ID for different board.
*/
struct gtdt_arch_timer_fixup arch_timer_quirks[] __initdata = {
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+ {"HISI", "hip05", 0, &erratum_workaround_enable, HISILICON_161601},
+ {"HISI", "hip06", 0, &erratum_workaround_enable, HISILICON_161601},
+ {"HISI", "hip07", 0, &erratum_workaround_enable, HISILICON_161601},
+#endif
};
void __init arch_timer_acpi_quirks_handler(char *oem_id,
--
1.9.0
^ permalink raw reply related
* [PATCH v3 5/6] arm64: arch_timer: apci: Introduce a generic aquirk framework for erratum
From: Ding Tianhong @ 2016-11-04 13:06 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1478264794-14652-1-git-send-email-dingtianhong@huawei.com>
From: Hanjun Guo <hanjun.guo@linaro.org>
Introduce a general quirk framework for each timer erratum in ACPI,
which use the oem information in GTDT table for platform specific erratums.
The struct gtdt_arch_timer_fixup is introduced to record the oem
information to match the quirk and handle the erratum.
v3: Introduce a generic aquick framework for erratum in ACPI mode.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
drivers/clocksource/arm_arch_timer.c | 37 ++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 3d59af1..9bc93e5 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1068,6 +1068,40 @@ CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
arch_timer_mem_init);
#ifdef CONFIG_ACPI
+struct gtdt_arch_timer_fixup {
+ char oem_id[ACPI_OEM_ID_SIZE];
+ char oem_table_id[ACPI_OEM_TABLE_ID_SIZE];
+ u32 oem_revision;
+
+ /* quirk handler for arch timer erratum */
+ void (*handler)(u32 erratum);
+ u32 erratum;
+};
+
+/* note: this needs to be updated according to the doc of OEM ID
+ * and TABLE ID for different board.
+ */
+struct gtdt_arch_timer_fixup arch_timer_quirks[] __initdata = {
+};
+
+void __init arch_timer_acpi_quirks_handler(char *oem_id,
+ char *oem_table_id,
+ u32 oem_revision)
+{
+ struct gtdt_arch_timer_fixup *quirks = arch_timer_quirks;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arch_timer_quirks); i++, quirks++) {
+ if (!memcmp(quirks->oem_id, oem_id, ACPI_OEM_ID_SIZE) &&
+ !memcmp(quirks->oem_table_id, oem_table_id, ACPI_OEM_TABLE_ID_SIZE) &&
+ quirks->oem_revision == oem_revision) {
+ if (quirks->handler && quirks->erratum)
+ quirks->handler(quirks->erratum);
+ break;
+ }
+ }
+}
+
static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
{
int trigger, polarity;
@@ -1094,6 +1128,9 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
return -EINVAL;
}
+ arch_timer_acpi_quirks_handler(table->oem_id, table->oem_table_id,
+ table->oem_revision);
+
gtdt = container_of(table, struct acpi_table_gtdt, header);
arch_timers_present |= ARCH_CP15_TIMER;
--
1.9.0
^ permalink raw reply related
* [PATCH v3 4/6] arm64: arch timer: Add timer erratum property for Hip05-d02 and Hip06-d03
From: Ding Tianhong @ 2016-11-04 13:06 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1478264794-14652-1-git-send-email-dingtianhong@huawei.com>
Enable workaround for hisilicon erratum 161601 on Hip05-d02 and Hip06-d03 board.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
arch/arm64/boot/dts/hisilicon/hip05.dtsi | 1 +
arch/arm64/boot/dts/hisilicon/hip06.dtsi | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hip05.dtsi b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
index bf322ed..f815d94 100644
--- a/arch/arm64/boot/dts/hisilicon/hip05.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
@@ -281,6 +281,7 @@
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ hisilicon,erratum-161601;
};
pmu {
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index 5927bc4..d63990b 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -260,6 +260,7 @@
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ hisilicon,erratum-161601;
};
pmu {
--
1.9.0
^ permalink raw reply related
* [PATCH v3 3/6] arm64: arch_timer: Work around Erratum Hisilicon-161601
From: Ding Tianhong @ 2016-11-04 13:06 UTC (permalink / raw)
To: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
marc.zyngier-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
oss-fOR+EgIDQEHk1uMJSBkQmQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
shawnguo-DgEjT+Ai2ygdnm+yROfE0A, stuart.yoder-3arQi8VN3Tc,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linuxarm-hv44wF8Li93QT0dZR+AlfA,
hanjun.guo-QSEj5FYQhm4dnm+yROfE0A
Cc: Ding Tianhong
In-Reply-To: <1478264794-14652-1-git-send-email-dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
Erratum Hisilicon-161601 says that the ARM generic timer counter "has the
potential to contain an erroneous value when the timer value changes".
Accesses to TVAL (both read and write) are also affected due to the implicit counter
read. Accesses to CVAL are not affected.
The workaround is to reread the system count registers until the value of the second
read is larger than the first one by less than 32, the system counter can be guaranteed
not to return wrong value twice by back-to-back read and the error value is always larger
than the correct one by 32. Writes to TVAL are replaced with an equivalent write to CVAL.
The workaround is enabled if the hisilicon,erratum-161601 property is found in
the timer node in the device tree. This can be overridden with the
clocksource.arm_arch_timer.hisilicon-161601 boot parameter, which allows KVM
users to enable the workaround until a mechanism is implemented to
automatically communicate this information.
Fix some description for fsl erratum a008585.
v2: Significant rework based on feedback, including seperate the fsl erratum a008585
to another patch, update the erratum name and remove unwanted code.
v3: Significant rework based on feedback, including fix some alignment problem, make the
#define __hisi_161601_read_reg to be private to the .c file instead of being globally
visible, add more accurate annotation and modify a bit of logical format to enable
arch_timer_read_ool_enabled, remove the kernel commandline parameter
clocksource.arm_arch_timer.hisilicon-161601.
Signed-off-by: Ding Tianhong <dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
---
Documentation/arm64/silicon-errata.txt | 1 +
arch/arm64/include/asm/arch_timer.h | 2 +-
drivers/clocksource/Kconfig | 9 +++++
drivers/clocksource/arm_arch_timer.c | 67 +++++++++++++++++++++++++++++++---
4 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index 405da11..1c1a95f 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -63,3 +63,4 @@ stable kernels.
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
| | | | |
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
+| Hisilicon | Hip0{5,6,7} | #161601 | HISILICON_ERRATUM_161601|
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index f882c7c..ebf4cde 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -29,7 +29,7 @@
#include <clocksource/arm_arch_timer.h>
-#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
extern struct static_key_false arch_timer_read_ool_enabled;
#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 8a753fd..6c03ed0 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -315,6 +315,15 @@ config FSL_ERRATUM_A008585
value"). The workaround will only be active if the
fsl,erratum-a008585 property is found in the timer node.
+config HISILICON_ERRATUM_161601
+ bool "Workaround for Hisilicon Erratum 161601"
+ default y
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a workaround for Hisilicon Erratum
+ 161601. The workaround will be active if the hisilicon,erratum-161601
+ property is found in the timer node.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select CLKSRC_OF if OF
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 696386f..3d59af1 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -94,15 +94,18 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
* Architected system timer support.
*/
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if CONFIG_FSL_ERRATUM_A008585 || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
#define FSL_A008585 0x0001
+#define HISILICON_161601 0x0002
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+#endif
+#ifdef CONFIG_FSL_ERRATUM_A008585
/*
* The number of retries is an arbitrary value well beyond the highest number
* of iterations the loop has been observed to take.
@@ -144,6 +147,51 @@ static struct arch_timer_erratum_workaround arch_timer_fsl_a008585 = {
};
#endif /* CONFIG_FSL_ERRATUM_A008585 */
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+/*
+ * Theoretically the erratum should not occur more than twice in succession,
+ * so set the retry count to 2 is sufficient here.
+ * Verify whether the value of the second read is larger than the first by
+ * less than 32 is the only way to confirm the value is correct, so clear the
+ * lower 5 bits to check whether the difference is greater than 32 or not.
+ */
+#define __hisi_161601_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 2; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely((_new - _old) >> 5) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 hisi_161601_read_cntp_tval_el0(void)
+{
+ return __hisi_161601_read_reg(cntp_tval_el0);
+}
+
+static u32 hisi_161601_read_cntv_tval_el0(void)
+{
+ return __hisi_161601_read_reg(cntv_tval_el0);
+}
+
+static u64 hisi_161601_read_cntvct_el0(void)
+{
+ return __hisi_161601_read_reg(cntvct_el0);
+}
+
+static struct arch_timer_erratum_workaround arch_timer_hisi_161601 = {
+ .erratum = HISILICON_161601,
+ .read_cntp_tval_el0 = hisi_161601_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = hisi_161601_read_cntv_tval_el0,
+ .read_cntvct_el0 = hisi_161601_read_cntvct_el0,
+};
+#endif /* CONFIG_HISILICON_ERRATUM_161601 */
+
static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
struct clock_event_device *clk)
@@ -293,7 +341,7 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
@@ -357,7 +405,7 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
@@ -617,7 +665,7 @@ static void __init arch_counter_register(unsigned type)
clocksource_counter.archdata.vdso_direct = true;
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
/*
* Don't use the vdso fastpath if errata require using
* the out-of-line counter accessor.
@@ -906,10 +954,19 @@ static int __init arch_timer_of_init(struct device_node *np)
#ifdef CONFIG_FSL_ERRATUM_A008585
if (!timer_unstable_counter_workaround && of_property_read_bool(np, "fsl,erratum-a008585"))
timer_unstable_counter_workaround = &arch_timer_fsl_a008585;
+#endif
+
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+ if (!timer_unstable_counter_workaround && of_property_read_bool(np, "hisilicon,erratum-161601"))
+ timer_unstable_counter_workaround = &arch_timer_hisi_161601;
+#endif
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
if (timer_unstable_counter_workaround) {
static_branch_enable(&arch_timer_read_ool_enabled);
- pr_info("Enabling workaround for FSL erratum A-008585\n");
+ pr_info("Enabling workaround for %s\n",
+ timer_unstable_counter_workaround->erratum == FSL_A008585 ?
+ "FSL ERRATUM A-008585" : "HISILICON ERRATUM 161601");
}
#endif
--
1.9.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 2/6] arm64: arch_timer: Introduce a generic erratum handing mechanism for fsl-a008585
From: Ding Tianhong @ 2016-11-04 13:06 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1478264794-14652-1-git-send-email-dingtianhong@huawei.com>
The workaround for hisilicon,161601 will check the return value of the system counter
by different way, in order to distinguish with the fsl-a008585 workaround, introduce
a new generic erratum handing mechanism for fsl-a008585 and rename some functions.
v2: Introducing a new generic erratum handling mechanism for fsl erratum a008585.
v3: Introducing the erratum_workaround_set_sne generic function for fsl erratum a008585
and make the #define __fsl_a008585_read_reg to be private to the .c file instead of
being globally visible. After discussion with Marc and Will, a consensus decision was
made to remove the commandline parameter for enabling fsl,erratum-a008585 erratum,
and make some generic name more specific, export timer_unstable_counter_workaround
for module access.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
Documentation/kernel-parameters.txt | 9 -----
arch/arm64/include/asm/arch_timer.h | 36 ++++++-----------
drivers/clocksource/arm_arch_timer.c | 78 +++++++++++++++++++++---------------
3 files changed, 58 insertions(+), 65 deletions(-)
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 6fa1d8a..738de4e 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -698,15 +698,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
loops can be debugged more effectively on production
systems.
- clocksource.arm_arch_timer.fsl-a008585=
- [ARM64]
- Format: <bool>
- Enable/disable the workaround of Freescale/NXP
- erratum A-008585. This can be useful for KVM
- guests, if the guest device tree doesn't show the
- erratum. If unspecified, the workaround is
- enabled based on the device tree.
-
clearcpuid=BITNUM [X86]
Disable CPUID feature X for the kernel. See
arch/x86/include/asm/cpufeatures.h for the valid bit
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index eaa5bbe..f882c7c 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -31,39 +31,27 @@
#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
extern struct static_key_false arch_timer_read_ool_enabled;
-#define needs_fsl_a008585_workaround() \
+#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
#else
-#define needs_fsl_a008585_workaround() false
+#define needs_unstable_timer_counter_workaround() false
#endif
-u32 __fsl_a008585_read_cntp_tval_el0(void);
-u32 __fsl_a008585_read_cntv_tval_el0(void);
-u64 __fsl_a008585_read_cntvct_el0(void);
-/*
- * The number of retries is an arbitrary value well beyond the highest number
- * of iterations the loop has been observed to take.
- */
-#define __fsl_a008585_read_reg(reg) ({ \
- u64 _old, _new; \
- int _retries = 200; \
- \
- do { \
- _old = read_sysreg(reg); \
- _new = read_sysreg(reg); \
- _retries--; \
- } while (unlikely(_old != _new) && _retries); \
- \
- WARN_ON_ONCE(!_retries); \
- _new; \
-})
+struct arch_timer_erratum_workaround {
+ int erratum; /* Indicate the Erratum ID */
+ u32 (*read_cntp_tval_el0)(void);
+ u32 (*read_cntv_tval_el0)(void);
+ u64 (*read_cntvct_el0)(void);
+};
+
+extern struct arch_timer_erratum_workaround *timer_unstable_counter_workaround;
#define arch_timer_reg_read_stable(reg) \
({ \
u64 _val; \
- if (needs_fsl_a008585_workaround()) \
- _val = __fsl_a008585_read_##reg(); \
+ if (needs_unstable_timer_counter_workaround()) \
+ _val = timer_unstable_counter_workaround->read_##reg();\
else \
_val = read_sysreg(reg); \
_val; \
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 73c487d..696386f 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -95,40 +95,53 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
*/
#ifdef CONFIG_FSL_ERRATUM_A008585
-DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
-EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
-
-static int fsl_a008585_enable = -1;
+struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
+EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
-static int __init early_fsl_a008585_cfg(char *buf)
-{
- int ret;
- bool val;
+#define FSL_A008585 0x0001
- ret = strtobool(buf, &val);
- if (ret)
- return ret;
-
- fsl_a008585_enable = val;
- return 0;
-}
-early_param("clocksource.arm_arch_timer.fsl-a008585", early_fsl_a008585_cfg);
+DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
+EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
-u32 __fsl_a008585_read_cntp_tval_el0(void)
+/*
+ * The number of retries is an arbitrary value well beyond the highest number
+ * of iterations the loop has been observed to take.
+ */
+#define __fsl_a008585_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 200; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely(_old != _new) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 fsl_a008585_read_cntp_tval_el0(void)
{
return __fsl_a008585_read_reg(cntp_tval_el0);
}
-u32 __fsl_a008585_read_cntv_tval_el0(void)
+static u32 fsl_a008585_read_cntv_tval_el0(void)
{
return __fsl_a008585_read_reg(cntv_tval_el0);
}
-u64 __fsl_a008585_read_cntvct_el0(void)
+static u64 fsl_a008585_read_cntvct_el0(void)
{
return __fsl_a008585_read_reg(cntvct_el0);
}
-EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
+
+static struct arch_timer_erratum_workaround arch_timer_fsl_a008585 = {
+ .erratum = FSL_A008585,
+ .read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0,
+ .read_cntvct_el0 = fsl_a008585_read_cntvct_el0,
+};
#endif /* CONFIG_FSL_ERRATUM_A008585 */
static __always_inline
@@ -281,7 +294,7 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
}
#ifdef CONFIG_FSL_ERRATUM_A008585
-static __always_inline void fsl_a008585_set_next_event(const int access,
+static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
unsigned long ctrl;
@@ -299,17 +312,17 @@ static __always_inline void fsl_a008585_set_next_event(const int access,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-static int fsl_a008585_set_next_event_virt(unsigned long evt,
+static int erratum_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_VIRT_ACCESS, evt, clk);
return 0;
}
-static int fsl_a008585_set_next_event_phys(unsigned long evt,
+static int erratum_set_next_event_phys(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_PHYS_ACCESS, evt, clk);
return 0;
}
#endif /* CONFIG_FSL_ERRATUM_A008585 */
@@ -342,16 +355,16 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
return 0;
}
-static void fsl_a008585_set_sne(struct clock_event_device *clk)
+static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
#ifdef CONFIG_FSL_ERRATUM_A008585
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
if (arch_timer_uses_ppi == VIRT_PPI)
- clk->set_next_event = fsl_a008585_set_next_event_virt;
+ clk->set_next_event = erratum_set_next_event_virt;
else
- clk->set_next_event = fsl_a008585_set_next_event_phys;
+ clk->set_next_event = erratum_set_next_event_phys;
#endif
}
@@ -384,7 +397,7 @@ static void __arch_timer_setup(unsigned type,
BUG();
}
- fsl_a008585_set_sne(clk);
+ erratum_workaround_set_sne(clk);
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
@@ -891,9 +904,10 @@ static int __init arch_timer_of_init(struct device_node *np)
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
#ifdef CONFIG_FSL_ERRATUM_A008585
- if (fsl_a008585_enable < 0)
- fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
- if (fsl_a008585_enable) {
+ if (!timer_unstable_counter_workaround && of_property_read_bool(np, "fsl,erratum-a008585"))
+ timer_unstable_counter_workaround = &arch_timer_fsl_a008585;
+
+ if (timer_unstable_counter_workaround) {
static_branch_enable(&arch_timer_read_ool_enabled);
pr_info("Enabling workaround for FSL erratum A-008585\n");
}
--
1.9.0
^ permalink raw reply related
* [PATCH v3 1/6] arm64: arch_timer: Add device tree binding for hisilicon-161601 erratum
From: Ding Tianhong @ 2016-11-04 13:06 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
This erratum describes a bug in logic outside the core, so MIDR can't be
used to identify its presence, and reading an SoC-specific revision
register from common arch timer code would be awkward. So, describe it
in the device tree.
v2: Use the new erratum name and update the description.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/arm/arch_timer.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index ef5fbe9..c27b2c4 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -31,6 +31,14 @@ to deliver its interrupts via SPIs.
This also affects writes to the tval register, due to the implicit
counter read.
+- hisilicon,erratum-161601 : A boolean property. Indicates the presence of
+ erratum 161601, which says that reading the counter is unreliable unless
+ reading twice on the register and the value of the second read is larger
+ than the first by less than 32. If the verification is unsuccessful, then
+ discard the value of this read and repeat this procedure until the verification
+ is successful. This also affects writes to the tval register, due to the
+ implicit counter read.
+
** Optional properties:
- arm,cpu-registers-not-fw-configured : Firmware does not initialize
--
1.9.0
^ permalink raw reply related
* [PATCH v3 3/3] PCI: qcom: add runtime pm support to pcie_port
From: Srinivas Kandagatla @ 2016-11-04 12:59 UTC (permalink / raw)
To: svarbanov, Bjorn Helgaas, linux-pci
Cc: Rob Herring, Mark Rutland, devicetree, linux-kernel,
linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1478264387-17914-1-git-send-email-srinivas.kandagatla@linaro.org>
This patch is required when the pcie controller sits on a bus with
its own power domain and clocks which are controlled via a bus driver
like simple pm bus. As these bus driver have runtime pm enabled, it makes
sense to update the usage counter so that the runtime pm does not suspend
the clks or power domain associated with the bus driver.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
drivers/pci/host/pcie-qcom.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/host/pcie-qcom.c
index f37c690..3751635 100644
--- a/drivers/pci/host/pcie-qcom.c
+++ b/drivers/pci/host/pcie-qcom.c
@@ -587,6 +587,9 @@ static void qcom_pcie_host_init(struct pcie_port *pp)
struct qcom_pcie *pcie = to_qcom_pcie(pp);
int ret;
+
+ pm_runtime_get_sync(pp->dev);
+
qcom_ep_reset_assert(pcie);
ret = pcie->ops->init(pcie);
@@ -617,6 +620,7 @@ static void qcom_pcie_host_init(struct pcie_port *pp)
phy_power_off(pcie->phy);
err_deinit:
pcie->ops->deinit(pcie);
+ pm_runtime_put_sync(pp->dev);
}
static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
@@ -669,10 +673,12 @@ static int qcom_pcie_probe(struct platform_device *pdev)
struct pcie_port *pp;
int ret;
+
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
+ pm_runtime_enable(dev);
pp = &pcie->pp;
pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev);
--
2.10.1
^ permalink raw reply related
* [PATCH v3 2/3] PCI: qcom: add support to msm8996 PCIE controller
From: Srinivas Kandagatla @ 2016-11-04 12:59 UTC (permalink / raw)
To: svarbanov, Bjorn Helgaas, linux-pci
Cc: Rob Herring, Mark Rutland, devicetree, linux-kernel,
linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1478264387-17914-1-git-send-email-srinivas.kandagatla@linaro.org>
This patch adds support to msm8996/apq8096 pcie, MSM8996 supports
Gen 1/2, One lane, 3 pcie root-complex with support to MSI and
legacy interrupts and it conforms to PCI Express Base 2.1 specification.
This patch adds post_init callback to qcom_pcie_ops, as this is pcie
pipe clocks are only setup after the phy is powered on.
It also adds ltssm_enable callback as it is very much different to other
supported SOCs in the driver.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
.../devicetree/bindings/pci/qcom,pcie.txt | 68 +++++++-
drivers/pci/host/pcie-qcom.c | 177 ++++++++++++++++++++-
2 files changed, 239 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
index 4059a6f..4a0538d 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
@@ -7,6 +7,7 @@
- "qcom,pcie-ipq8064" for ipq8064
- "qcom,pcie-apq8064" for apq8064
- "qcom,pcie-apq8084" for apq8084
+ - "qcom,pcie-msm8996" for msm8996 or apq8096
- reg:
Usage: required
@@ -92,6 +93,16 @@
- "aux" Auxiliary (AUX) clock
- "bus_master" Master AXI clock
- "bus_slave" Slave AXI clock
+
+- clock-names:
+ Usage: required for msm8996/apq8096
+ Value type: <stringlist>
+ Definition: Should contain the following entries
+ - "aux" Auxiliary (AUX) clock.
+ - "bus_master" Master AXI clock.
+ - "bus_slave" Slave AXI clock.
+ - "pipe" Pipe Clock driving internal logic.
+ - "cfg" Configuration clk.
- resets:
Usage: required
Value type: <prop-encoded-array>
@@ -115,7 +126,7 @@
- "core" Core reset
- power-domains:
- Usage: required for apq8084
+ Usage: required for apq8084 and msm8996/apq8096
Value type: <prop-encoded-array>
Definition: A phandle and power domain specifier pair to the
power domain which is responsible for collapsing
@@ -231,3 +242,58 @@
pinctrl-0 = <&pcie0_pins_default>;
pinctrl-names = "default";
};
+
+* Example for apq8096:
+
+ pcie@00608000{
+ compatible = "qcom,pcie-msm8996", "snps,dw-pcie";
+ power-domains = <&gcc PCIE1_GDSC>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+
+ status = "disabled";
+
+ reg = <0x00608000 0x2000>,
+ <0x0d000000 0xf1d>,
+ <0x0d000f20 0xa8>,
+ <0x0d100000 0x100000>;
+
+ reg-names = "parf", "dbi", "elbi", "config";
+
+ phys = <&pcie_phy 1>;
+ phy-names = "pciephy";
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x01000000 0x0 0x0d200000 0x0d200000 0x0 0x100000>,
+ <0x02000000 0x0 0x0d300000 0x0d300000 0x0 0xd00000>;
+
+ interrupts = <GIC_SPI 413 IRQ_TYPE_NONE>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 272 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc 0 273 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc 0 274 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc 0 275 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&pcie1_clkreq_default &pcie1_perst_default &pcie1_wake_default>;
+ pinctrl-1 = <&pcie1_clkreq_sleep &pcie1_perst_default &pcie1_wake_sleep>;
+
+ vdda-1p8-supply = <&pm8994_l12>;
+ vdda-supply = <&pm8994_l28>;
+ linux,pci-domain = <1>;
+
+ clocks = <&gcc GCC_PCIE_1_PIPE_CLK>,
+ <&gcc GCC_PCIE_1_AUX_CLK>,
+ <&gcc GCC_PCIE_1_CFG_AHB_CLK>,
+ <&gcc GCC_PCIE_1_MSTR_AXI_CLK>,
+ <&gcc GCC_PCIE_1_SLV_AXI_CLK>;
+
+ clock-names = "pipe",
+ "aux",
+ "cfg",
+ "bus_master",
+ "bus_slave";
+ };
diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/host/pcie-qcom.c
index 3593640..f37c690 100644
--- a/drivers/pci/host/pcie-qcom.c
+++ b/drivers/pci/host/pcie-qcom.c
@@ -36,11 +36,19 @@
#include "pcie-designware.h"
+#define PCIE20_PARF_DBI_BASE_ADDR 0x168
+
+#define PCIE20_PARF_SYS_CTRL 0x00
#define PCIE20_PARF_PHY_CTRL 0x40
#define PCIE20_PARF_PHY_REFCLK 0x4C
#define PCIE20_PARF_DBI_BASE_ADDR 0x168
#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
+#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174
#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
+#define MSM8996_PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x1A8
+#define PCIE20_PARF_LTSSM 0x1B0
+#define PCIE20_PARF_SID_OFFSET 0x234
+#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C
#define PCIE20_ELBI_SYS_CTRL 0x04
#define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0)
@@ -72,9 +80,18 @@ struct qcom_pcie_resources_v1 {
struct regulator *vdda;
};
+struct qcom_pcie_resources_v2 {
+ struct clk *aux_clk;
+ struct clk *master_clk;
+ struct clk *slave_clk;
+ struct clk *cfg_clk;
+ struct clk *pipe_clk;
+};
+
union qcom_pcie_resources {
struct qcom_pcie_resources_v0 v0;
struct qcom_pcie_resources_v1 v1;
+ struct qcom_pcie_resources_v2 v2;
};
struct qcom_pcie;
@@ -82,7 +99,9 @@ struct qcom_pcie;
struct qcom_pcie_ops {
int (*get_resources)(struct qcom_pcie *pcie);
int (*init)(struct qcom_pcie *pcie);
+ int (*post_init)(struct qcom_pcie *pcie);
void (*deinit)(struct qcom_pcie *pcie);
+ void (*ltssm_enable)(struct qcom_pcie *pcie);
};
struct qcom_pcie {
@@ -116,17 +135,33 @@ static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
return dw_handle_msi_irq(pp);
}
-static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
+static void qcom_pcie_v0_v1_ltssm_enable(struct qcom_pcie *pcie)
{
u32 val;
-
- if (dw_pcie_link_up(&pcie->pp))
- return 0;
-
/* enable link training */
val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL);
val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE;
writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL);
+}
+
+static void qcom_pcie_v2_ltssm_enable(struct qcom_pcie *pcie)
+{
+ u32 val;
+ /* enable link training */
+ val = readl(pcie->parf + PCIE20_PARF_LTSSM);
+ val |= BIT(8);
+ writel(val, pcie->parf + PCIE20_PARF_LTSSM);
+}
+
+static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
+{
+
+ if (dw_pcie_link_up(&pcie->pp))
+ return 0;
+
+ /* Enable Link Training state machine */
+ if (pcie->ops->ltssm_enable)
+ pcie->ops->ltssm_enable(pcie);
return dw_pcie_wait_for_link(&pcie->pp);
}
@@ -421,6 +456,113 @@ static int qcom_pcie_init_v1(struct qcom_pcie *pcie)
return ret;
}
+static int qcom_pcie_get_resources_v2(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+ struct device *dev = pcie->pp.dev;
+
+ res->aux_clk = devm_clk_get(dev, "aux");
+ if (IS_ERR(res->aux_clk))
+ return PTR_ERR(res->aux_clk);
+
+ res->cfg_clk = devm_clk_get(dev, "cfg");
+ if (IS_ERR(res->cfg_clk))
+ return PTR_ERR(res->cfg_clk);
+
+ res->master_clk = devm_clk_get(dev, "bus_master");
+ if (IS_ERR(res->master_clk))
+ return PTR_ERR(res->master_clk);
+
+ res->slave_clk = devm_clk_get(dev, "bus_slave");
+ if (IS_ERR(res->slave_clk))
+ return PTR_ERR(res->slave_clk);
+
+ res->pipe_clk = devm_clk_get(dev, "pipe");
+ if (IS_ERR(res->pipe_clk))
+ return PTR_ERR(res->pipe_clk);
+
+ return 0;
+}
+
+static int qcom_pcie_init_v2(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+ struct device *dev = pcie->pp.dev;
+ u32 val;
+ int ret = 0;
+
+ ret = clk_prepare_enable(res->aux_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable aux clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(res->cfg_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable cfg clock\n");
+ goto err_cfg_clk;
+ }
+
+ ret = clk_prepare_enable(res->master_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable master clock\n");
+ goto err_master_clk;
+ }
+
+ ret = clk_prepare_enable(res->slave_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable slave clock\n");
+ goto err_slave_clk;
+ }
+
+ /* enable PCIe clocks and resets */
+ val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
+ val &= ~BIT(0);
+ writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
+
+ /* change DBI base address */
+ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
+
+ /* MAC PHY_POWERDOWN MUX DISABLE */
+ val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL);
+ val &= ~BIT(29);
+ writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL);
+
+ val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
+ val |= BIT(4);
+ writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
+
+ val = readl(pcie->parf + MSM8996_PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
+ val |= BIT(31);
+ writel(val, pcie->parf + MSM8996_PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
+
+ return 0;
+
+err_slave_clk:
+ clk_disable_unprepare(res->master_clk);
+err_master_clk:
+ clk_disable_unprepare(res->cfg_clk);
+err_cfg_clk:
+ clk_disable_unprepare(res->aux_clk);
+
+ return ret;
+}
+
+static int qcom_pcie_post_init_v2(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+ struct device *dev = pcie->pp.dev;
+ int ret;
+
+ ret = clk_prepare_enable(res->pipe_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable pipe clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int qcom_pcie_link_up(struct pcie_port *pp)
{
struct qcom_pcie *pcie = to_qcom_pcie(pp);
@@ -429,6 +571,17 @@ static int qcom_pcie_link_up(struct pcie_port *pp)
return !!(val & PCI_EXP_LNKSTA_DLLLA);
}
+static void qcom_pcie_deinit_v2(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+
+ clk_disable_unprepare(res->slave_clk);
+ clk_disable_unprepare(res->master_clk);
+ clk_disable_unprepare(res->cfg_clk);
+ clk_disable_unprepare(res->aux_clk);
+ clk_disable_unprepare(res->pipe_clk);
+}
+
static void qcom_pcie_host_init(struct pcie_port *pp)
{
struct qcom_pcie *pcie = to_qcom_pcie(pp);
@@ -444,6 +597,9 @@ static void qcom_pcie_host_init(struct pcie_port *pp)
if (ret)
goto err_deinit;
+ if (pcie->ops->post_init)
+ pcie->ops->post_init(pcie);
+
dw_pcie_setup_rc(pp);
if (IS_ENABLED(CONFIG_PCI_MSI))
@@ -487,12 +643,22 @@ static const struct qcom_pcie_ops ops_v0 = {
.get_resources = qcom_pcie_get_resources_v0,
.init = qcom_pcie_init_v0,
.deinit = qcom_pcie_deinit_v0,
+ .ltssm_enable = qcom_pcie_v0_v1_ltssm_enable,
};
static const struct qcom_pcie_ops ops_v1 = {
.get_resources = qcom_pcie_get_resources_v1,
.init = qcom_pcie_init_v1,
.deinit = qcom_pcie_deinit_v1,
+ .ltssm_enable = qcom_pcie_v0_v1_ltssm_enable,
+};
+
+static const struct qcom_pcie_ops ops_v2 = {
+ .get_resources = qcom_pcie_get_resources_v2,
+ .init = qcom_pcie_init_v2,
+ .post_init = qcom_pcie_post_init_v2,
+ .deinit = qcom_pcie_deinit_v2,
+ .ltssm_enable = qcom_pcie_v2_ltssm_enable,
};
static int qcom_pcie_probe(struct platform_device *pdev)
@@ -572,6 +738,7 @@ static const struct of_device_id qcom_pcie_match[] = {
{ .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 },
{ .compatible = "qcom,pcie-apq8064", .data = &ops_v0 },
{ .compatible = "qcom,pcie-apq8084", .data = &ops_v1 },
+ { .compatible = "qcom,pcie-msm8996", .data = &ops_v2 },
{ }
};
--
2.10.1
^ permalink raw reply related
* [PATCH v3 1/3] bus: simple-pm: add support to pm clocks
From: Srinivas Kandagatla @ 2016-11-04 12:59 UTC (permalink / raw)
To: svarbanov, Bjorn Helgaas, linux-pci
Cc: Rob Herring, Mark Rutland, devicetree, linux-kernel,
linux-arm-msm, Srinivas Kandagatla
In-Reply-To: <1478264387-17914-1-git-send-email-srinivas.kandagatla@linaro.org>
This patch adds support to pm clocks via device tree, so that the clocks
can be turned on and off during runtime pm. This patch is required for
Qualcomm msm8996 pcie controller which sits on a bus with its own
power-domain and clocks.
Without this patch the clock associated with the bus are never turned on.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
drivers/bus/simple-pm-bus.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c
index c5eb46c..63b7e8c 100644
--- a/drivers/bus/simple-pm-bus.c
+++ b/drivers/bus/simple-pm-bus.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
#include <linux/pm_runtime.h>
@@ -22,17 +23,26 @@ static int simple_pm_bus_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
- if (np)
+ if (np) {
+ of_pm_clk_add_clks(&pdev->dev);
of_platform_populate(np, NULL, NULL, &pdev->dev);
+ }
return 0;
}
+static const struct dev_pm_ops simple_pm_bus_pm_ops = {
+ SET_RUNTIME_PM_OPS(pm_clk_suspend,
+ pm_clk_resume, NULL)
+};
+
static int simple_pm_bus_remove(struct platform_device *pdev)
{
dev_dbg(&pdev->dev, "%s\n", __func__);
pm_runtime_disable(&pdev->dev);
+ pm_clk_destroy(&pdev->dev);
+
return 0;
}
@@ -48,6 +58,7 @@ static struct platform_driver simple_pm_bus_driver = {
.driver = {
.name = "simple-pm-bus",
.of_match_table = simple_pm_bus_of_match,
+ .pm = &simple_pm_bus_pm_ops,
},
};
--
2.10.1
^ permalink raw reply related
* [PATCH v3 0/3] PCI: qcom: Add support to msm8996 pcie controller.
From: Srinivas Kandagatla @ 2016-11-04 12:59 UTC (permalink / raw)
To: svarbanov, Bjorn Helgaas, linux-pci
Cc: Rob Herring, Mark Rutland, devicetree, linux-kernel,
linux-arm-msm, Srinivas Kandagatla
This patchset adds support to msm8996 pcie controller. I tested this patch on
v4.9-rc2 along with phy driver patch [1] and
"PCI: designware: check for iATU unroll support after initializing host"
fix [2] on DB820c APQ8096 board on port B and port C using sata and
ethernet controller.
Changes since v2:
- Removed regulators that belong to phy, spotted by Stephen
- Removed clocks in to simple pm bus driver, spotted by Stephen
- renamed msm8996 ops to v2 ops as suggested by Stephen.
- cleanups as suggested by Stephen.
- Add runtime pm support to driver.
- Added pm clk support to simple pm bus driver.
Changes since v1:
- Fixed dt example as suggested by Rob
- added smmu bus clk dependency as smmu sits in between
system NOC and PCIe.
- Removed smmu configuration from bindings and driver as
the smmu Level2 translation on this SOC is controlled by
the secure world, and level 1 translation is disabled,
so there is one-to-one mapping of the address space.
Thanks,
srini
[1] https://patchwork.kernel.org/patch/9384711/
[2] https://patchwork.kernel.org/patch/9377557/
Srinivas Kandagatla (3):
bus: simple-pm: add support to pm clocks
PCI: qcom: add support to msm8996 PCIE controller
PCI: qcom: add runtime pm support to pcie_port
.../devicetree/bindings/pci/qcom,pcie.txt | 68 +++++++-
drivers/bus/simple-pm-bus.c | 13 +-
drivers/pci/host/pcie-qcom.c | 183 ++++++++++++++++++++-
3 files changed, 257 insertions(+), 7 deletions(-)
--
2.10.1
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox