* [PATCH v7 00/16] ACPI IORT ARM SMMU support
From: Rafael J. Wysocki @ 2016-11-15 13:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161115101257.GB16261@red-moon>
On Tue, Nov 15, 2016 at 11:12 AM, Lorenzo Pieralisi
<lorenzo.pieralisi@arm.com> wrote:
> Hi Rafael,
>
> On Thu, Nov 10, 2016 at 12:36:12AM +0100, Rafael J. Wysocki wrote:
>> Hi Lorenzo,
>>
>> On Wed, Nov 9, 2016 at 3:19 PM, Lorenzo Pieralisi
>> <lorenzo.pieralisi@arm.com> wrote:
>> > This patch series is v7 of a previous posting:
>> >
>> > https://lkml.org/lkml/2016/10/18/506
>>
>> I don't see anything objectionable in this series.
>>
>> Please let me know which patches in particular to look at in detail.
>
> Are patches 7 and consequently 16 (that builds on top of 7) ok with
> you ? They should be equivalent to nop on anything other than ARM64
> but they touch ACPI core so they require an ACK if they are ok please.
Yes, they are. Please feel free to add my ACKs to those or I will
send them separately later today.
Thanks,
Rafael
^ permalink raw reply
* [PATCH 02/19] phy: stih41x-usb: Remove usb phy driver and dt binding documentation.
From: Kishon Vijay Abraham I @ 2016-11-15 13:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473859677-9231-3-git-send-email-peter.griffin@linaro.org>
Hi,
On Wednesday 14 September 2016 06:57 PM, Peter Griffin wrote:
> This phy is only used on STiH415/6 based silicon, and support for
> these SoC's is being removed from the kernel.
>
> Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
> Cc: <kishon@ti.com>
I've merged the 1st 2 patches of this series. Since Patrice has already
mentioned that he's merged the MAINTAINERS patch, I'm not merging it.
Please let me know if you know of any problems.
Thanks
Kishon
> ---
> .../devicetree/bindings/phy/phy-stih41x-usb.txt | 24 ---
> drivers/phy/Kconfig | 8 -
> drivers/phy/Makefile | 1 -
> drivers/phy/phy-stih41x-usb.c | 188 ---------------------
> 4 files changed, 221 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/phy/phy-stih41x-usb.txt
> delete mode 100644 drivers/phy/phy-stih41x-usb.c
>
> diff --git a/Documentation/devicetree/bindings/phy/phy-stih41x-usb.txt b/Documentation/devicetree/bindings/phy/phy-stih41x-usb.txt
> deleted file mode 100644
> index 744b480..0000000
> --- a/Documentation/devicetree/bindings/phy/phy-stih41x-usb.txt
> +++ /dev/null
> @@ -1,24 +0,0 @@
> -STMicroelectronics STiH41x USB PHY binding
> -------------------------------------------
> -
> -This file contains documentation for the usb phy found in STiH415/6 SoCs from
> -STMicroelectronics.
> -
> -Required properties:
> -- compatible : should be "st,stih416-usb-phy" or "st,stih415-usb-phy"
> -- st,syscfg : should be a phandle of the syscfg node
> -- clock-names : must contain "osc_phy"
> -- clocks : must contain an entry for each name in clock-names.
> -See: Documentation/devicetree/bindings/clock/clock-bindings.txt
> -- #phy-cells : must be 0 for this phy
> -See: Documentation/devicetree/bindings/phy/phy-bindings.txt
> -
> -Example:
> -
> -usb2_phy: usb2phy at 0 {
> - compatible = "st,stih416-usb-phy";
> - #phy-cells = <0>;
> - st,syscfg = <&syscfg_rear>;
> - clocks = <&clk_sysin>;
> - clock-names = "osc_phy";
> -};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index d1f22ac..b4aa039 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -392,14 +392,6 @@ config PHY_STIH407_USB
> Enable this support to enable the picoPHY device used by USB2
> and USB3 controllers on STMicroelectronics STiH407 SoC families.
>
> -config PHY_STIH41X_USB
> - tristate "STMicroelectronics USB2 PHY driver for STiH41x series"
> - depends on ARCH_STI
> - select GENERIC_PHY
> - help
> - Enable this to support the USB transceiver that is part of
> - STMicroelectronics STiH41x SoC series.
> -
> config PHY_QCOM_UFS
> tristate "Qualcomm UFS PHY driver"
> depends on OF && ARCH_QCOM
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index d169d80..5e48741 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -45,7 +45,6 @@ obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
> obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
> obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
> obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
> -obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o
> obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
> obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
> obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
> diff --git a/drivers/phy/phy-stih41x-usb.c b/drivers/phy/phy-stih41x-usb.c
> deleted file mode 100644
> index 0ac7463..0000000
> --- a/drivers/phy/phy-stih41x-usb.c
> +++ /dev/null
> @@ -1,188 +0,0 @@
> -/*
> - * Copyright (C) 2014 STMicroelectronics
> - *
> - * STMicroelectronics PHY driver for STiH41x USB.
> - *
> - * Author: Maxime Coquelin <maxime.coquelin@st.com>
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License version 2, as
> - * published by the Free Software Foundation.
> - *
> - */
> -
> -#include <linux/platform_device.h>
> -#include <linux/io.h>
> -#include <linux/kernel.h>
> -#include <linux/module.h>
> -#include <linux/of.h>
> -#include <linux/of_platform.h>
> -#include <linux/clk.h>
> -#include <linux/phy/phy.h>
> -#include <linux/regmap.h>
> -#include <linux/mfd/syscon.h>
> -
> -#define SYSCFG332 0x80
> -#define SYSCFG2520 0x820
> -
> -/**
> - * struct stih41x_usb_cfg - SoC specific PHY register mapping
> - * @syscfg: Offset in syscfg registers bank
> - * @cfg_mask: Bits mask for PHY configuration
> - * @cfg: Static configuration value for PHY
> - * @oscok: Notify the PHY oscillator clock is ready
> - * Setting this bit enable the PHY
> - */
> -struct stih41x_usb_cfg {
> - u32 syscfg;
> - u32 cfg_mask;
> - u32 cfg;
> - u32 oscok;
> -};
> -
> -/**
> - * struct stih41x_usb_phy - Private data for the PHY
> - * @dev: device for this controller
> - * @regmap: Syscfg registers bank in which PHY is configured
> - * @cfg: SoC specific PHY register mapping
> - * @clk: Oscillator used by the PHY
> - */
> -struct stih41x_usb_phy {
> - struct device *dev;
> - struct regmap *regmap;
> - const struct stih41x_usb_cfg *cfg;
> - struct clk *clk;
> -};
> -
> -static struct stih41x_usb_cfg stih415_usb_phy_cfg = {
> - .syscfg = SYSCFG332,
> - .cfg_mask = 0x3f,
> - .cfg = 0x38,
> - .oscok = BIT(6),
> -};
> -
> -static struct stih41x_usb_cfg stih416_usb_phy_cfg = {
> - .syscfg = SYSCFG2520,
> - .cfg_mask = 0x33f,
> - .cfg = 0x238,
> - .oscok = BIT(6),
> -};
> -
> -static int stih41x_usb_phy_init(struct phy *phy)
> -{
> - struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy);
> -
> - return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
> - phy_dev->cfg->cfg_mask, phy_dev->cfg->cfg);
> -}
> -
> -static int stih41x_usb_phy_power_on(struct phy *phy)
> -{
> - struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy);
> - int ret;
> -
> - ret = clk_prepare_enable(phy_dev->clk);
> - if (ret) {
> - dev_err(phy_dev->dev, "Failed to enable osc_phy clock\n");
> - return ret;
> - }
> -
> - ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
> - phy_dev->cfg->oscok, phy_dev->cfg->oscok);
> - if (ret)
> - clk_disable_unprepare(phy_dev->clk);
> -
> - return ret;
> -}
> -
> -static int stih41x_usb_phy_power_off(struct phy *phy)
> -{
> - struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy);
> - int ret;
> -
> - ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
> - phy_dev->cfg->oscok, 0);
> - if (ret) {
> - dev_err(phy_dev->dev, "Failed to clear oscok bit\n");
> - return ret;
> - }
> -
> - clk_disable_unprepare(phy_dev->clk);
> -
> - return 0;
> -}
> -
> -static const struct phy_ops stih41x_usb_phy_ops = {
> - .init = stih41x_usb_phy_init,
> - .power_on = stih41x_usb_phy_power_on,
> - .power_off = stih41x_usb_phy_power_off,
> - .owner = THIS_MODULE,
> -};
> -
> -static const struct of_device_id stih41x_usb_phy_of_match[];
> -
> -static int stih41x_usb_phy_probe(struct platform_device *pdev)
> -{
> - struct device_node *np = pdev->dev.of_node;
> - const struct of_device_id *match;
> - struct stih41x_usb_phy *phy_dev;
> - struct device *dev = &pdev->dev;
> - struct phy_provider *phy_provider;
> - struct phy *phy;
> -
> - phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
> - if (!phy_dev)
> - return -ENOMEM;
> -
> - match = of_match_device(stih41x_usb_phy_of_match, &pdev->dev);
> - if (!match)
> - return -ENODEV;
> -
> - phy_dev->cfg = match->data;
> -
> - phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> - if (IS_ERR(phy_dev->regmap)) {
> - dev_err(dev, "No syscfg phandle specified\n");
> - return PTR_ERR(phy_dev->regmap);
> - }
> -
> - phy_dev->clk = devm_clk_get(dev, "osc_phy");
> - if (IS_ERR(phy_dev->clk)) {
> - dev_err(dev, "osc_phy clk not found\n");
> - return PTR_ERR(phy_dev->clk);
> - }
> -
> - phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops);
> -
> - if (IS_ERR(phy)) {
> - dev_err(dev, "failed to create phy\n");
> - return PTR_ERR(phy);
> - }
> -
> - phy_dev->dev = dev;
> -
> - phy_set_drvdata(phy, phy_dev);
> -
> - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> - return PTR_ERR_OR_ZERO(phy_provider);
> -}
> -
> -static const struct of_device_id stih41x_usb_phy_of_match[] = {
> - { .compatible = "st,stih415-usb-phy", .data = &stih415_usb_phy_cfg },
> - { .compatible = "st,stih416-usb-phy", .data = &stih416_usb_phy_cfg },
> - { /* sentinel */ },
> -};
> -MODULE_DEVICE_TABLE(of, stih41x_usb_phy_of_match);
> -
> -static struct platform_driver stih41x_usb_phy_driver = {
> - .probe = stih41x_usb_phy_probe,
> - .driver = {
> - .name = "stih41x-usb-phy",
> - .of_match_table = stih41x_usb_phy_of_match,
> - }
> -};
> -module_platform_driver(stih41x_usb_phy_driver);
> -
> -MODULE_AUTHOR("Maxime Coquelin <maxime.coquelin@st.com>");
> -MODULE_DESCRIPTION("STMicroelectronics USB PHY driver for STiH41x series");
> -MODULE_LICENSE("GPL v2");
>
^ permalink raw reply
* [RFC v3 00/10] KVM PCIe/MSI passthrough on ARM/ARM64 and IOVA reserved regions
From: Eric Auger @ 2016-11-15 13:09 UTC (permalink / raw)
To: linux-arm-kernel
Following LPC discussions, we now report reserved regions through
iommu-group sysfs reserved_regions attribute file.
Reserved regions are populated through the IOMMU get_resv_region callback
(former get_dm_regions), now implemented by amd-iommu, intel-iommu and
arm-smmu.
The intel-iommu reports the [FEE0_0000h - FEF0_000h] MSI window as an
IOMMU_RESV_NOMAP reserved region.
arm-smmu reports the MSI window (arbitrarily located at 0x8000000 and
1MB large) and the PCI host bridge windows.
The series integrates a not officially posted patch from Robin:
"iommu/dma: Allow MSI-only cookies".
This series currently does not address IRQ safety assessment.
Best Regards
Eric
Git: complete series available at
https://github.com/eauger/linux/tree/v4.9-rc5-reserved-rfc-v3
History:
RFC v2 -> v3:
- switch to an iommu-group sysfs API
- use new dummy allocator provided by Robin
- dummy allocator initialized by vfio-iommu-type1 after enumerating
the reserved regions
- at the moment ARM MSI base address/size is left unchanged compared
to v2
- we currently report reserved regions and not usable IOVA regions as
requested by Alex
RFC v1 -> v2:
- fix intel_add_reserved_regions
- add mutex lock/unlock in vfio_iommu_type1
Eric Auger (10):
iommu/dma: Allow MSI-only cookies
iommu: Rename iommu_dm_regions into iommu_resv_regions
iommu: Add new reserved IOMMU attributes
iommu: iommu_alloc_resv_region
iommu: Do not map reserved regions
iommu: iommu_get_group_resv_regions
iommu: Implement reserved_regions iommu-group sysfs file
iommu/vt-d: Implement reserved region get/put callbacks
iommu/arm-smmu: Implement reserved region get/put callbacks
vfio/type1: Get MSI cookie
drivers/iommu/amd_iommu.c | 20 +++---
drivers/iommu/arm-smmu.c | 52 +++++++++++++++
drivers/iommu/dma-iommu.c | 116 ++++++++++++++++++++++++++-------
drivers/iommu/intel-iommu.c | 50 ++++++++++----
drivers/iommu/iommu.c | 141 ++++++++++++++++++++++++++++++++++++----
drivers/vfio/vfio_iommu_type1.c | 26 ++++++++
include/linux/dma-iommu.h | 7 ++
include/linux/iommu.h | 49 ++++++++++----
8 files changed, 391 insertions(+), 70 deletions(-)
--
1.9.1
^ permalink raw reply
* [PATCH v15 00/13] acpi, clocksource: add GTDT driver and GTDT support in arm_arch_timer
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
From: Fu Wei <fu.wei@linaro.org>
This patchset:
(1)Preparation for adding GTDT support in arm_arch_timer:
1. Move some enums and marcos to header file;
2. Add a new enum for spi type;
3. Improve printk relevant code.
4. rename some enums and defines, and some cleanups.
5. separate out arch_timer_uses_ppi init code and fix a potential bug
6. Introduce some new structs and refactor the timer init code
(2)Introduce ACPI GTDT parser: drivers/acpi/arm64/acpi_gtdt.c
Parse all kinds of timer in GTDT table of ACPI:arch timer,
memory-mapped timer and SBSA Generic Watchdog timer.
This driver can help to simplify all the relevant timer drivers,
and separate all the ACPI GTDT knowledge from them.
(3)Simplify ACPI code for arm_arch_timer
(4)Add GTDT support for ARM memory-mapped timer, also refactor
original memory-mapped timer dt support for reusing some common
code.
This patchset has been tested on the following platforms with ACPI enabled:
(1)ARM Foundation v8 model
Changelog:
v15: https://lkml.org/lkml/2016/11/15/
Re-order patches
Add arm_arch_timer refactoring patches to prepare for GTDT:
1. rename some enums and defines, and some cleanups
2. separate out arch_timer_uses_ppi init code and fix a potential bug
3. Improve some new structs, refactor the timer init code.
Since the some structs have been changed, GTDT parser for memory-mapped
timer and SBSA Generic Watchdog timer have been update.
v14: https://lkml.org/lkml/2016/9/28/573
Separate memory-mapped timer GTDT support into two patches
1. Refactor the timer init code to prepare for GTDT
2. Add GTDT support for memory-mapped timer
v13: http://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1231717.html
Improve arm_arch_timer code for memory-mapped
timer GTDT support, refactor original memory-mapped timer
dt support for reusing some common code.
v12: https://lkml.org/lkml/2016/9/13/250
Rebase to latest Linux 4.8-rc6
Delete the confusing "skipping" in the error message.
V11: https://lkml.org/lkml/2016/9/6/354
Rebase to latest Linux 4.8-rc5
Delete typedef (suggested by checkpatch.pl)
V10: https://lkml.org/lkml/2016/7/26/215
Drop the "readq" patch.
Rebase to latest Linux 4.7.
V9: https://lkml.org/lkml/2016/7/25/345
Improve pr_err message in acpi gtdt driver.
Update Commit message for 7/9
shorten the irq mapping function name
Improve GTDT driver for memory-mapped timer
v8: https://lkml.org/lkml/2016/7/19/660
Improve "pr_fmt(fmt)" definition: add "ACPI" in front of "GTDT",
and also improve printk message.
Simplify is_timer_block and is_watchdog.
Merge acpi_gtdt_desc_init and gtdt_arch_timer_init into acpi_gtdt_init();
Delete __init in include/linux/acpi.h for GTDT API
Make ARM64 select GTDT.
Delete "#include <linux/module.h>" from acpi_gtdt.c
Simplify GT block parse code.
v7: https://lkml.org/lkml/2016/7/13/769
Move the GTDT driver to drivers/acpi/arm64
Add add the ARM64-specific ACPI Support maintainers in MAINTAINERS
Merge 3 patches of GTDT parser driver.
Fix the for_each_platform_timer bug.
v6: https://lkml.org/lkml/2016/6/29/580
split the GTDT driver to 4 parts: basic, arch_timer, memory-mapped timer,
and SBSA Generic Watchdog timer
Improve driver by suggestions and example code from Daniel Lezcano
v5: https://lkml.org/lkml/2016/5/24/356
Sorting out all patches, simplify the API of GTDT driver:
GTDT driver just fills the data struct for arm_arch_timer driver.
v4: https://lists.linaro.org/pipermail/linaro-acpi/2016-March/006667.html
Delete the kvm relevant patches
Separate two patches for sorting out the code for arm_arch_timer.
Improve irq info export code to allow missing irq info in GTDT table.
v3: https://lkml.org/lkml/2016/2/1/658
Improve GTDT driver code:
(1)improve pr_* by defining pr_fmt(fmt)
(2)simplify gtdt_sbsa_gwdt_init
(3)improve gtdt_arch_timer_data_init, if table is NULL, it will try
to get GTDT table.
Move enum ppi_nr to arm_arch_timer.h, and add enum spi_nr.
Add arm_arch_timer get ppi from DT and GTDT support for kvm.
v2: https://lkml.org/lkml/2015/12/2/10
Rebase to latest kernel version(4.4-rc3).
Fix the bug about the config problem,
use CONFIG_ACPI_GTDT instead of CONFIG_ACPI in arm_arch_timer.c
v1: The first upstreaming version: https://lkml.org/lkml/2015/10/28/553
Fu Wei (13):
clocksource/drivers/arm_arch_timer: Move enums and defines to header
file
clocksource/drivers/arm_arch_timer: Add a new enum for spi type
clocksource/drivers/arm_arch_timer: Improve printk relevant code
clocksource/drivers/arm_arch_timer: rename some enums and defines, and
some cleanups.
clocksource/drivers/arm_arch_timer: fix a bug in arch_timer_register
about arch_timer_uses_ppi
clocksource/drivers/arm_arch_timer: separate out arch_timer_uses_ppi
init code to prepare for GTDT.
clocksource/drivers/arm_arch_timer: Introduce some new structs to
prepare for GTDT
clocksource/drivers/arm_arch_timer: Refactor the timer init code to
prepare for GTDT
acpi/arm64: Add GTDT table parse driver
clocksource/drivers/arm_arch_timer: Simplify ACPI support code.
acpi/arm64: Add memory-mapped timer support in GTDT driver
clocksource/drivers/arm_arch_timer: Add GTDT support for memory-mapped
timer
acpi/arm64: Add SBSA Generic Watchdog support in GTDT driver
arch/arm64/Kconfig | 1 +
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/arm64/Makefile | 1 +
drivers/acpi/arm64/gtdt.c | 411 ++++++++++++++++++++++++++++++++++
drivers/clocksource/arm_arch_timer.c | 420 ++++++++++++++++++++---------------
drivers/watchdog/Kconfig | 1 +
include/clocksource/arm_arch_timer.h | 61 +++--
include/linux/acpi.h | 8 +
virt/kvm/arm/hyp/timer-sr.c | 6 +-
9 files changed, 720 insertions(+), 192 deletions(-)
create mode 100644 drivers/acpi/arm64/gtdt.c
--
2.7.4
^ permalink raw reply
* [PATCH v15 01/13] clocksource/drivers/arm_arch_timer: Move enums and defines to header file
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
To support the arm_arch_timer via ACPI we need to share defines and enums
between the driver and the ACPI parser code.
Split out the relevant defines and enums into arm_arch_timer.h, and
change "enum ppi_nr" to "enum arch_timer_ppi_nr" to avoid the potential
name clashes.
No functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
drivers/clocksource/arm_arch_timer.c | 11 -----------
include/clocksource/arm_arch_timer.h | 12 ++++++++++++
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 73c487d..9826f06 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -51,8 +51,6 @@
#define CNTV_TVAL 0x38
#define CNTV_CTL 0x3c
-#define ARCH_CP15_TIMER BIT(0)
-#define ARCH_MEM_TIMER BIT(1)
static unsigned arch_timers_present __initdata;
static void __iomem *arch_counter_base;
@@ -65,15 +63,6 @@ struct arch_timer {
#define to_arch_timer(e) container_of(e, struct arch_timer, evt)
static u32 arch_timer_rate;
-
-enum ppi_nr {
- PHYS_SECURE_PPI,
- PHYS_NONSECURE_PPI,
- VIRT_PPI,
- HYP_PPI,
- MAX_TIMER_PPI
-};
-
static int arch_timer_ppi[MAX_TIMER_PPI];
static struct clock_event_device __percpu *arch_timer_evt;
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index caedb74..557f869 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -16,9 +16,13 @@
#ifndef __CLKSOURCE_ARM_ARCH_TIMER_H
#define __CLKSOURCE_ARM_ARCH_TIMER_H
+#include <linux/bitops.h>
#include <linux/timecounter.h>
#include <linux/types.h>
+#define ARCH_CP15_TIMER BIT(0)
+#define ARCH_MEM_TIMER BIT(1)
+
#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
@@ -34,6 +38,14 @@ enum arch_timer_reg {
ARCH_TIMER_REG_TVAL,
};
+enum arch_timer_ppi_nr {
+ PHYS_SECURE_PPI,
+ PHYS_NONSECURE_PPI,
+ VIRT_PPI,
+ HYP_PPI,
+ MAX_TIMER_PPI
+};
+
#define ARCH_TIMER_PHYS_ACCESS 0
#define ARCH_TIMER_VIRT_ACCESS 1
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
--
2.7.4
^ permalink raw reply related
* [PATCH v15 02/13] clocksource/drivers/arm_arch_timer: Add a new enum for spi type
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
This patch add a new enum "arch_timer_spi_nr" and use it in the driver.
Just for code's readability, no functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
drivers/clocksource/arm_arch_timer.c | 4 ++--
include/clocksource/arm_arch_timer.h | 6 ++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 9826f06..96c88bb 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -960,9 +960,9 @@ static int __init arch_timer_mem_init(struct device_node *np)
}
if (arch_timer_mem_use_virtual)
- irq = irq_of_parse_and_map(best_frame, 1);
+ irq = irq_of_parse_and_map(best_frame, ARCH_TIMER_VIRT_SPI);
else
- irq = irq_of_parse_and_map(best_frame, 0);
+ irq = irq_of_parse_and_map(best_frame, ARCH_TIMER_PHYS_SPI);
ret = -EINVAL;
if (!irq) {
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index 557f869..d23c381 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -46,6 +46,12 @@ enum arch_timer_ppi_nr {
MAX_TIMER_PPI
};
+enum arch_timer_spi_nr {
+ ARCH_TIMER_PHYS_SPI,
+ ARCH_TIMER_VIRT_SPI,
+ ARCH_TIMER_MAX_TIMER_SPI
+};
+
#define ARCH_TIMER_PHYS_ACCESS 0
#define ARCH_TIMER_VIRT_ACCESS 1
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
--
2.7.4
^ permalink raw reply related
* [PATCH v15 03/13] clocksource/drivers/arm_arch_timer: Improve printk relevant code
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
This patch defines pr_fmt(fmt) for all pr_* functions,
then the pr_* doesn't need to add "arch_timer:" everytime.
According to the suggestion from checkpatch.pl:
(1) delete some Blank Spaces in arch_timer_banner;
(2) delete a redundant Tab in a bland line of arch_timer_init(void)
No functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
---
drivers/clocksource/arm_arch_timer.c | 49 ++++++++++++++++++------------------
1 file changed, 25 insertions(+), 24 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 96c88bb..b0365043 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -32,6 +32,9 @@
#include <clocksource/arm_arch_timer.h>
+#undef pr_fmt
+#define pr_fmt(fmt) "arch_timer: " fmt
+
#define CNTTIDR 0x08
#define CNTTIDR_VIRT(n) (BIT(1) << ((n) * 4))
@@ -504,22 +507,22 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
/* Check the timer frequency. */
if (arch_timer_rate == 0)
- pr_warn("Architected timer frequency not available\n");
+ pr_warn("frequency not available\n");
}
static void arch_timer_banner(unsigned type)
{
- pr_info("Architected %s%s%s timer(s) running at %lu.%02luMHz (%s%s%s).\n",
- type & ARCH_CP15_TIMER ? "cp15" : "",
- type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? " and " : "",
- type & ARCH_MEM_TIMER ? "mmio" : "",
- (unsigned long)arch_timer_rate / 1000000,
- (unsigned long)(arch_timer_rate / 10000) % 100,
- type & ARCH_CP15_TIMER ?
- (arch_timer_uses_ppi == VIRT_PPI) ? "virt" : "phys" :
+ pr_info("%s%s%s timer(s) running@%lu.%02luMHz (%s%s%s).\n",
+ type & ARCH_CP15_TIMER ? "cp15" : "",
+ type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? " and " : "",
+ type & ARCH_MEM_TIMER ? "mmio" : "",
+ (unsigned long)arch_timer_rate / 1000000,
+ (unsigned long)(arch_timer_rate / 10000) % 100,
+ type & ARCH_CP15_TIMER ?
+ (arch_timer_uses_ppi == VIRT_PPI) ? "virt" : "phys" :
"",
- type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? "/" : "",
- type & ARCH_MEM_TIMER ?
+ type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? "/" : "",
+ type & ARCH_MEM_TIMER ?
arch_timer_mem_use_virtual ? "virt" : "phys" :
"");
}
@@ -618,8 +621,7 @@ static void __init arch_counter_register(unsigned type)
static void arch_timer_stop(struct clock_event_device *clk)
{
- pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
- clk->irq, smp_processor_id());
+ pr_debug("disable IRQ%d cpu #%d\n", clk->irq, smp_processor_id());
disable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi]);
if (arch_timer_has_nonsecure_ppi())
@@ -712,8 +714,7 @@ static int __init arch_timer_register(void)
}
if (err) {
- pr_err("arch_timer: can't register interrupt %d (%d)\n",
- ppi, err);
+ pr_err("can't register interrupt %d (%d)\n", ppi, err);
goto out_free;
}
@@ -766,7 +767,7 @@ static int __init arch_timer_mem_register(void __iomem *base, unsigned int irq)
ret = request_irq(irq, func, IRQF_TIMER, "arch_mem_timer", &t->evt);
if (ret) {
- pr_err("arch_timer: Failed to request mem timer irq\n");
+ pr_err("Failed to request mem timer irq\n");
kfree(t);
}
@@ -844,7 +845,7 @@ static int __init arch_timer_init(void)
}
if (!has_ppi) {
- pr_warn("arch_timer: No interrupt available, giving up\n");
+ pr_warn("No interrupt available, giving up\n");
return -EINVAL;
}
}
@@ -858,7 +859,7 @@ static int __init arch_timer_init(void)
return ret;
arch_timer_kvm_info.virtual_irq = arch_timer_ppi[VIRT_PPI];
-
+
return 0;
}
@@ -867,7 +868,7 @@ static int __init arch_timer_of_init(struct device_node *np)
int i;
if (arch_timers_present & ARCH_CP15_TIMER) {
- pr_warn("arch_timer: multiple nodes in dt, skipping\n");
+ pr_warn("multiple nodes in dt, skipping\n");
return 0;
}
@@ -911,7 +912,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
arch_timers_present |= ARCH_MEM_TIMER;
cntctlbase = of_iomap(np, 0);
if (!cntctlbase) {
- pr_err("arch_timer: Can't find CNTCTLBase\n");
+ pr_err("Can't find CNTCTLBase\n");
return -ENXIO;
}
@@ -926,7 +927,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
u32 cntacr;
if (of_property_read_u32(frame, "frame-number", &n)) {
- pr_err("arch_timer: Missing frame-number\n");
+ pr_err("Missing frame-number\n");
of_node_put(frame);
goto out;
}
@@ -955,7 +956,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
ret= -ENXIO;
base = arch_counter_base = of_iomap(best_frame, 0);
if (!base) {
- pr_err("arch_timer: Can't map frame's registers\n");
+ pr_err("Can't map frame's registers\n");
goto out;
}
@@ -966,7 +967,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
ret = -EINVAL;
if (!irq) {
- pr_err("arch_timer: Frame missing %s irq",
+ pr_err("Frame missing %s irq",
arch_timer_mem_use_virtual ? "virt" : "phys");
goto out;
}
@@ -1008,7 +1009,7 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
struct acpi_table_gtdt *gtdt;
if (arch_timers_present & ARCH_CP15_TIMER) {
- pr_warn("arch_timer: already initialized, skipping\n");
+ pr_warn("already initialized, skipping\n");
return -EINVAL;
}
--
2.7.4
^ permalink raw reply related
* [PATCH v15 04/13] clocksource/drivers/arm_arch_timer: rename some enums and defines, and some cleanups.
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
Rename some enums and defines, to unify the format of enums and defines
in arm_arch_timer.h, also update all the users of these enums and defines:
drivers/clocksource/arm_arch_timer.c
virt/kvm/arm/hyp/timer-sr.c
And do some cleanups, according to the suggestion from checkpatch.pl:
(1) using BIT(nr) instead of (1 << nr)
(2) using 'unsigned int' instead of 'unsigned'
No functional change.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
---
drivers/clocksource/arm_arch_timer.c | 111 ++++++++++++++++++-----------------
include/clocksource/arm_arch_timer.h | 40 ++++++-------
virt/kvm/arm/hyp/timer-sr.c | 6 +-
3 files changed, 81 insertions(+), 76 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index b0365043..dd1040d 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -66,11 +66,11 @@ struct arch_timer {
#define to_arch_timer(e) container_of(e, struct arch_timer, evt)
static u32 arch_timer_rate;
-static int arch_timer_ppi[MAX_TIMER_PPI];
+static int arch_timer_ppi[ARCH_TIMER_MAX_TIMER_PPI];
static struct clock_event_device __percpu *arch_timer_evt;
-static enum ppi_nr arch_timer_uses_ppi = VIRT_PPI;
+static enum arch_timer_ppi_nr arch_timer_uses_ppi = ARCH_TIMER_VIRT_PPI;
static bool arch_timer_c3stop;
static bool arch_timer_mem_use_virtual;
@@ -340,7 +340,7 @@ static void fsl_a008585_set_sne(struct clock_event_device *clk)
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
- if (arch_timer_uses_ppi == VIRT_PPI)
+ if (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
clk->set_next_event = fsl_a008585_set_next_event_virt;
else
clk->set_next_event = fsl_a008585_set_next_event_phys;
@@ -352,7 +352,7 @@ static void __arch_timer_setup(unsigned type,
{
clk->features = CLOCK_EVT_FEAT_ONESHOT;
- if (type == ARCH_CP15_TIMER) {
+ if (type == ARCH_TIMER_TYPE_CP15) {
if (arch_timer_c3stop)
clk->features |= CLOCK_EVT_FEAT_C3STOP;
clk->name = "arch_sys_timer";
@@ -360,14 +360,14 @@ static void __arch_timer_setup(unsigned type,
clk->cpumask = cpumask_of(smp_processor_id());
clk->irq = arch_timer_ppi[arch_timer_uses_ppi];
switch (arch_timer_uses_ppi) {
- case VIRT_PPI:
+ case ARCH_TIMER_VIRT_PPI:
clk->set_state_shutdown = arch_timer_shutdown_virt;
clk->set_state_oneshot_stopped = arch_timer_shutdown_virt;
clk->set_next_event = arch_timer_set_next_event_virt;
break;
- case PHYS_SECURE_PPI:
- case PHYS_NONSECURE_PPI:
- case HYP_PPI:
+ case ARCH_TIMER_PHYS_SECURE_PPI:
+ case ARCH_TIMER_PHYS_NONSECURE_PPI:
+ case ARCH_TIMER_HYP_PPI:
clk->set_state_shutdown = arch_timer_shutdown_phys;
clk->set_state_oneshot_stopped = arch_timer_shutdown_phys;
clk->set_next_event = arch_timer_set_next_event_phys;
@@ -447,8 +447,8 @@ static void arch_counter_set_user_access(void)
static bool arch_timer_has_nonsecure_ppi(void)
{
- return (arch_timer_uses_ppi == PHYS_SECURE_PPI &&
- arch_timer_ppi[PHYS_NONSECURE_PPI]);
+ return (arch_timer_uses_ppi == ARCH_TIMER_PHYS_SECURE_PPI &&
+ arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
}
static u32 check_ppi_trigger(int irq)
@@ -469,14 +469,15 @@ static int arch_timer_starting_cpu(unsigned int cpu)
struct clock_event_device *clk = this_cpu_ptr(arch_timer_evt);
u32 flags;
- __arch_timer_setup(ARCH_CP15_TIMER, clk);
+ __arch_timer_setup(ARCH_TIMER_TYPE_CP15, clk);
flags = check_ppi_trigger(arch_timer_ppi[arch_timer_uses_ppi]);
enable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi], flags);
if (arch_timer_has_nonsecure_ppi()) {
- flags = check_ppi_trigger(arch_timer_ppi[PHYS_NONSECURE_PPI]);
- enable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI], flags);
+ flags = check_ppi_trigger(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
+ enable_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI],
+ flags);
}
arch_counter_set_user_access();
@@ -513,16 +514,17 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
static void arch_timer_banner(unsigned type)
{
pr_info("%s%s%s timer(s) running at %lu.%02luMHz (%s%s%s).\n",
- type & ARCH_CP15_TIMER ? "cp15" : "",
- type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? " and " : "",
- type & ARCH_MEM_TIMER ? "mmio" : "",
+ type & ARCH_TIMER_TYPE_CP15 ? "cp15" : "",
+ type == (ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM) ?
+ " and " : "",
+ type & ARCH_TIMER_TYPE_MEM ? "mmio" : "",
(unsigned long)arch_timer_rate / 1000000,
(unsigned long)(arch_timer_rate / 10000) % 100,
- type & ARCH_CP15_TIMER ?
- (arch_timer_uses_ppi == VIRT_PPI) ? "virt" : "phys" :
+ type & ARCH_TIMER_TYPE_CP15 ?
+ (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) ? "virt" : "phys" :
"",
- type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? "/" : "",
- type & ARCH_MEM_TIMER ?
+ type == (ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM) ? "/" : "",
+ type & ARCH_TIMER_TYPE_MEM ?
arch_timer_mem_use_virtual ? "virt" : "phys" :
"");
}
@@ -588,8 +590,9 @@ static void __init arch_counter_register(unsigned type)
u64 start_count;
/* Register the CP15 based counter if we have one */
- if (type & ARCH_CP15_TIMER) {
- if (IS_ENABLED(CONFIG_ARM64) || arch_timer_uses_ppi == VIRT_PPI)
+ if (type & ARCH_TIMER_TYPE_CP15) {
+ if (IS_ENABLED(CONFIG_ARM64) ||
+ arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
arch_timer_read_counter = arch_counter_get_cntvct;
else
arch_timer_read_counter = arch_counter_get_cntpct;
@@ -625,7 +628,7 @@ static void arch_timer_stop(struct clock_event_device *clk)
disable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi]);
if (arch_timer_has_nonsecure_ppi())
- disable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI]);
+ disable_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
clk->set_state_shutdown(clk);
}
@@ -688,24 +691,24 @@ static int __init arch_timer_register(void)
ppi = arch_timer_ppi[arch_timer_uses_ppi];
switch (arch_timer_uses_ppi) {
- case VIRT_PPI:
+ case ARCH_TIMER_VIRT_PPI:
err = request_percpu_irq(ppi, arch_timer_handler_virt,
"arch_timer", arch_timer_evt);
break;
- case PHYS_SECURE_PPI:
- case PHYS_NONSECURE_PPI:
+ case ARCH_TIMER_PHYS_SECURE_PPI:
+ case ARCH_TIMER_PHYS_NONSECURE_PPI:
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
- if (!err && arch_timer_ppi[PHYS_NONSECURE_PPI]) {
- ppi = arch_timer_ppi[PHYS_NONSECURE_PPI];
+ if (!err && arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]) {
+ ppi = arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI];
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
if (err)
- free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI],
+ free_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI],
arch_timer_evt);
}
break;
- case HYP_PPI:
+ case ARCH_TIMER_HYP_PPI:
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
break;
@@ -737,7 +740,7 @@ static int __init arch_timer_register(void)
out_unreg_notify:
free_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi], arch_timer_evt);
if (arch_timer_has_nonsecure_ppi())
- free_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI],
+ free_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI],
arch_timer_evt);
out_free:
@@ -758,7 +761,7 @@ static int __init arch_timer_mem_register(void __iomem *base, unsigned int irq)
t->base = base;
t->evt.irq = irq;
- __arch_timer_setup(ARCH_MEM_TIMER, &t->evt);
+ __arch_timer_setup(ARCH_TIMER_TYPE_MEM, &t->evt);
if (arch_timer_mem_use_virtual)
func = arch_timer_handler_virt_mem;
@@ -801,13 +804,15 @@ arch_timer_needs_probing(int type, const struct of_device_id *matches)
static int __init arch_timer_common_init(void)
{
- unsigned mask = ARCH_CP15_TIMER | ARCH_MEM_TIMER;
+ unsigned int mask = ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM;
/* Wait until both nodes are probed if we have two timers */
if ((arch_timers_present & mask) != mask) {
- if (arch_timer_needs_probing(ARCH_MEM_TIMER, arch_timer_mem_of_match))
+ if (arch_timer_needs_probing(ARCH_TIMER_TYPE_MEM,
+ arch_timer_mem_of_match))
return 0;
- if (arch_timer_needs_probing(ARCH_CP15_TIMER, arch_timer_of_match))
+ if (arch_timer_needs_probing(ARCH_TIMER_TYPE_CP15,
+ arch_timer_of_match))
return 0;
}
@@ -832,16 +837,16 @@ static int __init arch_timer_init(void)
* their CNTHP_*_EL2 counterparts, and use a different PPI
* number.
*/
- if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) {
+ if (is_hyp_mode_available() || !arch_timer_ppi[ARCH_TIMER_VIRT_PPI]) {
bool has_ppi;
if (is_kernel_in_hyp_mode()) {
- arch_timer_uses_ppi = HYP_PPI;
- has_ppi = !!arch_timer_ppi[HYP_PPI];
+ arch_timer_uses_ppi = ARCH_TIMER_HYP_PPI;
+ has_ppi = !!arch_timer_ppi[ARCH_TIMER_HYP_PPI];
} else {
- arch_timer_uses_ppi = PHYS_SECURE_PPI;
- has_ppi = (!!arch_timer_ppi[PHYS_SECURE_PPI] ||
- !!arch_timer_ppi[PHYS_NONSECURE_PPI]);
+ arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
+ has_ppi = (!!arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI] ||
+ !!arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
}
if (!has_ppi) {
@@ -858,7 +863,7 @@ static int __init arch_timer_init(void)
if (ret)
return ret;
- arch_timer_kvm_info.virtual_irq = arch_timer_ppi[VIRT_PPI];
+ arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI];
return 0;
}
@@ -867,13 +872,13 @@ static int __init arch_timer_of_init(struct device_node *np)
{
int i;
- if (arch_timers_present & ARCH_CP15_TIMER) {
+ if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
pr_warn("multiple nodes in dt, skipping\n");
return 0;
}
- arch_timers_present |= ARCH_CP15_TIMER;
- for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
+ arch_timers_present |= ARCH_TIMER_TYPE_CP15;
+ for (i = ARCH_TIMER_PHYS_SECURE_PPI; i < ARCH_TIMER_MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
arch_timer_detect_rate(NULL, np);
@@ -895,7 +900,7 @@ static int __init arch_timer_of_init(struct device_node *np)
*/
if (IS_ENABLED(CONFIG_ARM) &&
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
- arch_timer_uses_ppi = PHYS_SECURE_PPI;
+ arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
return arch_timer_init();
}
@@ -909,7 +914,7 @@ static int __init arch_timer_mem_init(struct device_node *np)
unsigned int irq, ret = -EINVAL;
u32 cnttidr;
- arch_timers_present |= ARCH_MEM_TIMER;
+ arch_timers_present |= ARCH_TIMER_TYPE_MEM;
cntctlbase = of_iomap(np, 0);
if (!cntctlbase) {
pr_err("Can't find CNTCTLBase\n");
@@ -1008,28 +1013,28 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
{
struct acpi_table_gtdt *gtdt;
- if (arch_timers_present & ARCH_CP15_TIMER) {
+ if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
pr_warn("already initialized, skipping\n");
return -EINVAL;
}
gtdt = container_of(table, struct acpi_table_gtdt, header);
- arch_timers_present |= ARCH_CP15_TIMER;
+ arch_timers_present |= ARCH_TIMER_TYPE_CP15;
- arch_timer_ppi[PHYS_SECURE_PPI] =
+ arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI] =
map_generic_timer_interrupt(gtdt->secure_el1_interrupt,
gtdt->secure_el1_flags);
- arch_timer_ppi[PHYS_NONSECURE_PPI] =
+ arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI] =
map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt,
gtdt->non_secure_el1_flags);
- arch_timer_ppi[VIRT_PPI] =
+ arch_timer_ppi[ARCH_TIMER_VIRT_PPI] =
map_generic_timer_interrupt(gtdt->virtual_timer_interrupt,
gtdt->virtual_timer_flags);
- arch_timer_ppi[HYP_PPI] =
+ arch_timer_ppi[ARCH_TIMER_HYP_PPI] =
map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt,
gtdt->non_secure_el2_flags);
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index d23c381..2625ff1 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -20,18 +20,18 @@
#include <linux/timecounter.h>
#include <linux/types.h>
-#define ARCH_CP15_TIMER BIT(0)
-#define ARCH_MEM_TIMER BIT(1)
+#define ARCH_TIMER_TYPE_CP15 BIT(0)
+#define ARCH_TIMER_TYPE_MEM BIT(1)
-#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
-#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
-#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
+#define ARCH_TIMER_CTRL_ENABLE BIT(0)
+#define ARCH_TIMER_CTRL_IT_MASK BIT(1)
+#define ARCH_TIMER_CTRL_IT_STAT BIT(2)
-#define CNTHCTL_EL1PCTEN (1 << 0)
-#define CNTHCTL_EL1PCEN (1 << 1)
-#define CNTHCTL_EVNTEN (1 << 2)
-#define CNTHCTL_EVNTDIR (1 << 3)
-#define CNTHCTL_EVNTI (0xF << 4)
+#define ARCH_TIMER_CNTHCTL_EL1PCTEN BIT(0)
+#define ARCH_TIMER_CNTHCTL_EL1PCEN BIT(1)
+#define ARCH_TIMER_CNTHCTL_EVNTEN BIT(2)
+#define ARCH_TIMER_CNTHCTL_EVNTDIR BIT(3)
+#define ARCH_TIMER_CNTHCTL_EVNTI (0xF << 4)
enum arch_timer_reg {
ARCH_TIMER_REG_CTRL,
@@ -39,11 +39,11 @@ enum arch_timer_reg {
};
enum arch_timer_ppi_nr {
- PHYS_SECURE_PPI,
- PHYS_NONSECURE_PPI,
- VIRT_PPI,
- HYP_PPI,
- MAX_TIMER_PPI
+ ARCH_TIMER_PHYS_SECURE_PPI,
+ ARCH_TIMER_PHYS_NONSECURE_PPI,
+ ARCH_TIMER_VIRT_PPI,
+ ARCH_TIMER_HYP_PPI,
+ ARCH_TIMER_MAX_TIMER_PPI
};
enum arch_timer_spi_nr {
@@ -57,13 +57,13 @@ enum arch_timer_spi_nr {
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
#define ARCH_TIMER_MEM_VIRT_ACCESS 3
-#define ARCH_TIMER_USR_PCT_ACCESS_EN (1 << 0) /* physical counter */
-#define ARCH_TIMER_USR_VCT_ACCESS_EN (1 << 1) /* virtual counter */
-#define ARCH_TIMER_VIRT_EVT_EN (1 << 2)
+#define ARCH_TIMER_USR_PCT_ACCESS_EN BIT(0) /* physical counter */
+#define ARCH_TIMER_USR_VCT_ACCESS_EN BIT(1) /* virtual counter */
+#define ARCH_TIMER_VIRT_EVT_EN BIT(2)
#define ARCH_TIMER_EVT_TRIGGER_SHIFT (4)
#define ARCH_TIMER_EVT_TRIGGER_MASK (0xF << ARCH_TIMER_EVT_TRIGGER_SHIFT)
-#define ARCH_TIMER_USR_VT_ACCESS_EN (1 << 8) /* virtual timer registers */
-#define ARCH_TIMER_USR_PT_ACCESS_EN (1 << 9) /* physical timer registers */
+#define ARCH_TIMER_USR_VT_ACCESS_EN BIT(8) /* virtual timer registers */
+#define ARCH_TIMER_USR_PT_ACCESS_EN BIT(9) /* physical timer registers */
#define ARCH_TIMER_EVT_STREAM_FREQ 10000 /* 100us */
diff --git a/virt/kvm/arm/hyp/timer-sr.c b/virt/kvm/arm/hyp/timer-sr.c
index 798866a..695b9d9 100644
--- a/virt/kvm/arm/hyp/timer-sr.c
+++ b/virt/kvm/arm/hyp/timer-sr.c
@@ -37,7 +37,7 @@ void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
/* Allow physical timer/counter access for the host */
val = read_sysreg(cnthctl_el2);
- val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
+ val |= ARCH_TIMER_CNTHCTL_EL1PCTEN | ARCH_TIMER_CNTHCTL_EL1PCEN;
write_sysreg(val, cnthctl_el2);
/* Clear cntvoff for the host */
@@ -55,8 +55,8 @@ void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
* Physical counter access is allowed
*/
val = read_sysreg(cnthctl_el2);
- val &= ~CNTHCTL_EL1PCEN;
- val |= CNTHCTL_EL1PCTEN;
+ val &= ~ARCH_TIMER_CNTHCTL_EL1PCEN;
+ val |= ARCH_TIMER_CNTHCTL_EL1PCTEN;
write_sysreg(val, cnthctl_el2);
if (timer->enabled) {
--
2.7.4
^ permalink raw reply related
* [PATCH v15 05/13] clocksource/drivers/arm_arch_timer: fix a bug in arch_timer_register about arch_timer_uses_ppi
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
The patch fix a potential bug about arch_timer_uses_ppi in
arch_timer_register.
On ARM64, we don't use ARCH_TIMER_PHYS_SECURE_PPI in Linux, so we will
just igorne it in init code. If arch_timer_uses_ppi is
ARCH_TIMER_PHYS_NONSECURE_PPI, the orignal code of
arch_timer_uses_ppi may go wrong.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
---
drivers/clocksource/arm_arch_timer.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index dd1040d..6de164f 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -699,7 +699,7 @@ static int __init arch_timer_register(void)
case ARCH_TIMER_PHYS_NONSECURE_PPI:
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
- if (!err && arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]) {
+ if (!err && arch_timer_has_nonsecure_ppi()) {
ppi = arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI];
err = request_percpu_irq(ppi, arch_timer_handler_phys,
"arch_timer", arch_timer_evt);
--
2.7.4
^ permalink raw reply related
* [PATCH v15 06/13] clocksource/drivers/arm_arch_timer: separate out arch_timer_uses_ppi init code to prepare for GTDT.
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
The patch refactor original arch_timer_uses_ppi init code:
(1) Extract a subfunction: arch_timer_uses_ppi_init
(2) Use the new subfunction in arch_timer_of_init and
arch_timer_acpi_init
Signed-off-by: Fu Wei <fu.wei@linaro.org>
---
drivers/clocksource/arm_arch_timer.c | 73 +++++++++++++++++++++---------------
1 file changed, 42 insertions(+), 31 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 6de164f..af22953 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -821,40 +821,42 @@ static int __init arch_timer_common_init(void)
return arch_timer_arch_init();
}
-static int __init arch_timer_init(void)
+/*
+ * If HYP mode is available, we know that the physical timer
+ * has been configured to be accessible from PL1. Use it, so
+ * that a guest can use the virtual timer instead.
+ *
+ * If no interrupt provided for virtual timer, we'll have to
+ * stick to the physical timer. It'd better be accessible...
+ * On ARM64, we we only use ARCH_TIMER_PHYS_NONSECURE_PPI in Linux.
+ *
+ * On ARMv8.1 with VH extensions, the kernel runs in HYP. VHE
+ * accesses to CNTP_*_EL1 registers are silently redirected to
+ * their CNTHP_*_EL2 counterparts, and use a different PPI
+ * number.
+ */
+static int __init arch_timer_uses_ppi_init(void)
{
- int ret;
- /*
- * If HYP mode is available, we know that the physical timer
- * has been configured to be accessible from PL1. Use it, so
- * that a guest can use the virtual timer instead.
- *
- * If no interrupt provided for virtual timer, we'll have to
- * stick to the physical timer. It'd better be accessible...
- *
- * On ARMv8.1 with VH extensions, the kernel runs in HYP. VHE
- * accesses to CNTP_*_EL1 registers are silently redirected to
- * their CNTHP_*_EL2 counterparts, and use a different PPI
- * number.
- */
- if (is_hyp_mode_available() || !arch_timer_ppi[ARCH_TIMER_VIRT_PPI]) {
- bool has_ppi;
-
- if (is_kernel_in_hyp_mode()) {
- arch_timer_uses_ppi = ARCH_TIMER_HYP_PPI;
- has_ppi = !!arch_timer_ppi[ARCH_TIMER_HYP_PPI];
- } else {
+ if (is_hyp_mode_available() && is_kernel_in_hyp_mode()) {
+ arch_timer_uses_ppi = ARCH_TIMER_HYP_PPI;
+ } else if (!arch_timer_ppi[ARCH_TIMER_VIRT_PPI]) {
+ if (IS_ENABLED(CONFIG_ARM64))
+ arch_timer_uses_ppi = ARCH_TIMER_PHYS_NONSECURE_PPI;
+ else
arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
- has_ppi = (!!arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI] ||
- !!arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
- }
-
- if (!has_ppi) {
- pr_warn("No interrupt available, giving up\n");
- return -EINVAL;
- }
}
+ if (arch_timer_ppi[arch_timer_uses_ppi])
+ return 0;
+
+ pr_warn("No interrupt available, giving up\n");
+ return -EINVAL;
+}
+
+static int __init arch_timer_init(void)
+{
+ int ret;
+
ret = arch_timer_register();
if (ret)
return ret;
@@ -870,7 +872,7 @@ static int __init arch_timer_init(void)
static int __init arch_timer_of_init(struct device_node *np)
{
- int i;
+ int i, ret;
if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
pr_warn("multiple nodes in dt, skipping\n");
@@ -902,6 +904,10 @@ static int __init arch_timer_of_init(struct device_node *np)
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
+ ret = arch_timer_uses_ppi_init();
+ if (ret)
+ return ret;
+
return arch_timer_init();
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
@@ -1011,6 +1017,7 @@ static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
/* Initialize per-processor generic timer */
static int __init arch_timer_acpi_init(struct acpi_table_header *table)
{
+ int ret;
struct acpi_table_gtdt *gtdt;
if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
@@ -1041,6 +1048,10 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
/* Get the frequency from CNTFRQ */
arch_timer_detect_rate(NULL, NULL);
+ ret = arch_timer_uses_ppi_init();
+ if (ret)
+ return ret;
+
/* Always-on capability */
arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
--
2.7.4
^ permalink raw reply related
* [PATCH v15 07/13] clocksource/drivers/arm_arch_timer: Introduce some new structs to prepare for GTDT
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
The patch introduce two new structs:
arch_timer_mem,
arch_timer_mem_frame.
And also introduce a new define: ARCH_TIMER_MEM_MAX_FRAMES
These will be used for refactoring the memory-mapped timer init code to
prepare for GTDT
Signed-off-by: Fu Wei <fu.wei@linaro.org>
---
include/clocksource/arm_arch_timer.h | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index 2625ff1..cc986a3 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -57,6 +57,8 @@ enum arch_timer_spi_nr {
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
#define ARCH_TIMER_MEM_VIRT_ACCESS 3
+#define ARCH_TIMER_MEM_MAX_FRAMES 8
+
#define ARCH_TIMER_USR_PCT_ACCESS_EN BIT(0) /* physical counter */
#define ARCH_TIMER_USR_VCT_ACCESS_EN BIT(1) /* virtual counter */
#define ARCH_TIMER_VIRT_EVT_EN BIT(2)
@@ -72,6 +74,21 @@ struct arch_timer_kvm_info {
int virtual_irq;
};
+struct arch_timer_mem_frame {
+ int frame_nr;
+ phys_addr_t cntbase;
+ size_t size;
+ int phys_irq;
+ int virt_irq;
+};
+
+struct arch_timer_mem {
+ phys_addr_t cntctlbase;
+ size_t size;
+ int num_frames;
+ struct arch_timer_mem_frame frame[ARCH_TIMER_MEM_MAX_FRAMES];
+};
+
#ifdef CONFIG_ARM_ARCH_TIMER
extern u32 arch_timer_get_rate(void);
--
2.7.4
^ permalink raw reply related
* [PATCH v15 08/13] clocksource/drivers/arm_arch_timer: Refactor the timer init code to prepare for GTDT
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
The patch refactor original memory-mapped timer init code:
(1) Extract a subfunction for detecting a bast time frame:
is_best_frame.
(2) Refactor "arch_timer_mem_init", make it become a common code for
memory-mapped timer init.
(3) Add a new function "arch_timer_mem_of_init" for DT init.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
---
drivers/clocksource/arm_arch_timer.c | 153 ++++++++++++++++++++++++-----------
1 file changed, 104 insertions(+), 49 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index af22953..a44e44d 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -913,17 +913,35 @@ static int __init arch_timer_of_init(struct device_node *np)
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
-static int __init arch_timer_mem_init(struct device_node *np)
+static bool __init is_best_frame(void __iomem *cntctlbase, u32 cnttidr, int n)
+{
+ u32 cntacr = CNTACR_RFRQ | CNTACR_RWPT | CNTACR_RPCT | CNTACR_RWVT |
+ CNTACR_RVOFF | CNTACR_RVCT;
+
+ /* Try enabling everything, and see what sticks */
+ writel_relaxed(cntacr, cntctlbase + CNTACR(n));
+ cntacr = readl_relaxed(cntctlbase + CNTACR(n));
+
+ if ((cnttidr & CNTTIDR_VIRT(n)) &&
+ !(~cntacr & (CNTACR_RWVT | CNTACR_RVCT)))
+ arch_timer_mem_use_virtual = true;
+ else if (~cntacr & (CNTACR_RWPT | CNTACR_RPCT))
+ return false;
+
+ return true;
+}
+
+static int __init arch_timer_mem_init(struct arch_timer_mem *timer_mem)
{
- struct device_node *frame, *best_frame = NULL;
void __iomem *cntctlbase, *base;
- unsigned int irq, ret = -EINVAL;
+ struct arch_timer_mem_frame *best_frame = NULL;
+ unsigned int irq;
u32 cnttidr;
+ int i, ret;
- arch_timers_present |= ARCH_TIMER_TYPE_MEM;
- cntctlbase = of_iomap(np, 0);
+ cntctlbase = ioremap(timer_mem->cntctlbase, timer_mem->size);
if (!cntctlbase) {
- pr_err("Can't find CNTCTLBase\n");
+ pr_err("Can't map CNTCTLBase.\n");
return -ENXIO;
}
@@ -933,69 +951,106 @@ static int __init arch_timer_mem_init(struct device_node *np)
* Try to find a virtual capable frame. Otherwise fall back to a
* physical capable frame.
*/
- for_each_available_child_of_node(np, frame) {
- int n;
- u32 cntacr;
-
- if (of_property_read_u32(frame, "frame-number", &n)) {
- pr_err("Missing frame-number\n");
- of_node_put(frame);
- goto out;
- }
-
- /* Try enabling everything, and see what sticks */
- cntacr = CNTACR_RFRQ | CNTACR_RWPT | CNTACR_RPCT |
- CNTACR_RWVT | CNTACR_RVOFF | CNTACR_RVCT;
- writel_relaxed(cntacr, cntctlbase + CNTACR(n));
- cntacr = readl_relaxed(cntctlbase + CNTACR(n));
-
- if ((cnttidr & CNTTIDR_VIRT(n)) &&
- !(~cntacr & (CNTACR_RWVT | CNTACR_RVCT))) {
- of_node_put(best_frame);
- best_frame = frame;
- arch_timer_mem_use_virtual = true;
- break;
+ for (i = 0; i < timer_mem->num_frames; i++) {
+ if (is_best_frame(cntctlbase, cnttidr,
+ timer_mem->frame[i].frame_nr)) {
+ best_frame = &timer_mem->frame[i];
+ if (arch_timer_mem_use_virtual)
+ break;
}
-
- if (~cntacr & (CNTACR_RWPT | CNTACR_RPCT))
- continue;
-
- of_node_put(best_frame);
- best_frame = of_node_get(frame);
}
+ iounmap(cntctlbase);
- ret= -ENXIO;
- base = arch_counter_base = of_iomap(best_frame, 0);
- if (!base) {
- pr_err("Can't map frame's registers\n");
- goto out;
+ if (!best_frame) {
+ pr_err("Can't find frame for register\n");
+ return -EINVAL;
}
if (arch_timer_mem_use_virtual)
- irq = irq_of_parse_and_map(best_frame, ARCH_TIMER_VIRT_SPI);
+ irq = best_frame->virt_irq;
else
- irq = irq_of_parse_and_map(best_frame, ARCH_TIMER_PHYS_SPI);
+ irq = best_frame->phys_irq;
- ret = -EINVAL;
if (!irq) {
pr_err("Frame missing %s irq",
arch_timer_mem_use_virtual ? "virt" : "phys");
- goto out;
+ return -EINVAL;
+ }
+
+ base = ioremap(best_frame->cntbase, best_frame->size);
+ if (!base) {
+ pr_err("Can't map frame's registers\n");
+ return -ENXIO;
}
- arch_timer_detect_rate(base, np);
+ arch_timer_detect_rate(base, NULL);
ret = arch_timer_mem_register(base, irq);
- if (ret)
+ if (ret) {
+ iounmap(base);
+ return ret;
+ }
+
+ arch_counter_base = base;
+ arch_timers_present |= ARCH_TIMER_TYPE_MEM;
+
+ return 0;
+}
+
+static int __init arch_timer_mem_of_init(struct device_node *np)
+{
+ struct arch_timer_mem *timer_mem;
+ struct device_node *frame_node;
+ struct resource res;
+ int i, ret = -EINVAL;
+
+ timer_mem = kzalloc(sizeof(*timer_mem), GFP_KERNEL);
+ if (!timer_mem)
+ return -ENOMEM;
+
+ if (of_address_to_resource(np, 0, &res))
goto out;
+ timer_mem->cntctlbase = res.start;
+ timer_mem->size = resource_size(&res);
- return arch_timer_common_init();
+ i = 0;
+ for_each_available_child_of_node(np, frame_node) {
+ int n;
+ struct arch_timer_mem_frame *frame = &timer_mem->frame[i];
+
+ if (of_property_read_u32(frame_node, "frame-number", &n)) {
+ pr_err("Missing frame-number\n");
+ of_node_put(frame_node);
+ goto out;
+ }
+ frame->frame_nr = n;
+
+ if (of_address_to_resource(frame_node, 0, &res)) {
+ of_node_put(frame_node);
+ goto out;
+ }
+ frame->cntbase = res.start;
+ frame->size = resource_size(&res);
+
+ frame->virt_irq = irq_of_parse_and_map(frame_node,
+ ARCH_TIMER_VIRT_SPI);
+ frame->phys_irq = irq_of_parse_and_map(frame_node,
+ ARCH_TIMER_PHYS_SPI);
+
+ if (++i >= ARCH_TIMER_MEM_MAX_FRAMES)
+ break;
+ }
+ timer_mem->num_frames = i;
+
+ arch_timer_detect_rate(NULL, np);
+ ret = arch_timer_mem_init(timer_mem);
+ if (!ret)
+ ret = arch_timer_common_init();
out:
- iounmap(cntctlbase);
- of_node_put(best_frame);
+ kfree(timer_mem);
return ret;
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
- arch_timer_mem_init);
+ arch_timer_mem_of_init);
#ifdef CONFIG_ACPI
static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
--
2.7.4
^ permalink raw reply related
* [PATCH v15 09/13] acpi/arm64: Add GTDT table parse driver
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
This patch adds support for parsing arch timer in GTDT,
provides some kernel APIs to parse all the PPIs and
always-on info in GTDT and export them.
By this driver, we can simplify arm_arch_timer drivers, and
separate the ACPI GTDT knowledge from it.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
arch/arm64/Kconfig | 1 +
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/arm64/Makefile | 1 +
drivers/acpi/arm64/gtdt.c | 216 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/acpi.h | 7 ++
5 files changed, 228 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 969ef88..4277a21 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -2,6 +2,7 @@ config ARM64
def_bool y
select ACPI_CCA_REQUIRED if ACPI
select ACPI_GENERIC_GSI if ACPI
+ select ACPI_GTDT if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
select ACPI_MCFG if ACPI
select ACPI_SPCR_TABLE if ACPI
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
index 4616da4..5a6f80f 100644
--- a/drivers/acpi/arm64/Kconfig
+++ b/drivers/acpi/arm64/Kconfig
@@ -4,3 +4,6 @@
config ACPI_IORT
bool
+
+config ACPI_GTDT
+ bool
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
index 72331f2..1017def 100644
--- a/drivers/acpi/arm64/Makefile
+++ b/drivers/acpi/arm64/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_ACPI_IORT) += iort.o
+obj-$(CONFIG_ACPI_GTDT) += gtdt.o
diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c
new file mode 100644
index 0000000..2de79aa
--- /dev/null
+++ b/drivers/acpi/arm64/gtdt.c
@@ -0,0 +1,216 @@
+/*
+ * ARM Specific GTDT table Support
+ *
+ * Copyright (C) 2016, Linaro Ltd.
+ * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
+ * Fu Wei <fu.wei@linaro.org>
+ * Hanjun Guo <hanjun.guo@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <clocksource/arm_arch_timer.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "ACPI GTDT: " fmt
+
+struct acpi_gtdt_descriptor {
+ struct acpi_table_gtdt *gtdt;
+ void *gtdt_end;
+ unsigned int timer_block_count;
+ unsigned int watchdog_count;
+};
+
+static struct acpi_gtdt_descriptor acpi_gtdt_desc __initdata;
+static struct acpi_gtdt_timer_block **timer_block __initdata;
+static struct acpi_gtdt_watchdog **watchdog __initdata;
+
+static inline void *next_platform_timer(void *platform_timer)
+{
+ struct acpi_gtdt_header *gh = platform_timer;
+
+ platform_timer += gh->length;
+ if (platform_timer < acpi_gtdt_desc.gtdt_end)
+ return platform_timer;
+
+ return NULL;
+}
+
+#define for_each_platform_timer(_g) for (; _g; _g = next_platform_timer(_g))
+
+static inline bool is_timer_block(void *platform_timer)
+{
+ struct acpi_gtdt_header *gh = platform_timer;
+
+ return gh->type == ACPI_GTDT_TYPE_TIMER_BLOCK;
+}
+
+static inline bool is_watchdog(void *platform_timer)
+{
+ struct acpi_gtdt_header *gh = platform_timer;
+
+ return gh->type == ACPI_GTDT_TYPE_WATCHDOG;
+}
+
+static int __init map_gt_gsi(u32 interrupt, u32 flags)
+{
+ int trigger, polarity;
+
+ trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
+ : ACPI_LEVEL_SENSITIVE;
+
+ polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
+ : ACPI_ACTIVE_HIGH;
+
+ return acpi_register_gsi(NULL, interrupt, trigger, polarity);
+}
+
+/*
+ * Map the PPIs of per-cpu arch_timer.
+ * @type: the type of PPI
+ * Returns 0 if error.
+ * Note: Linux on arm64 isn't supported on the secure side.
+ * So we only handle the non-secure timer PPIs,
+ * ARCH_TIMER_PHYS_SECURE_PPI is treated as invalid type.
+ */
+int __init acpi_gtdt_map_ppi(int type)
+{
+ struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt;
+
+ switch (type) {
+ case ARCH_TIMER_PHYS_NONSECURE_PPI:
+ return map_gt_gsi(gtdt->non_secure_el1_interrupt,
+ gtdt->non_secure_el1_flags);
+ case ARCH_TIMER_VIRT_PPI:
+ return map_gt_gsi(gtdt->virtual_timer_interrupt,
+ gtdt->virtual_timer_flags);
+
+ case ARCH_TIMER_HYP_PPI:
+ return map_gt_gsi(gtdt->non_secure_el2_interrupt,
+ gtdt->non_secure_el2_flags);
+ default:
+ pr_err("Failed to map timer interrupt: invalid type.\n");
+ }
+
+ return 0;
+}
+
+/*
+ * acpi_gtdt_c3stop - got c3stop info from GTDT
+ * @type: the type of PPI
+ * Returns 1 if the timer is powered in deep idle state, 0 otherwise.
+ */
+bool __init acpi_gtdt_c3stop(int type)
+{
+ struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt;
+
+ switch (type) {
+ case ARCH_TIMER_PHYS_NONSECURE_PPI:
+ return !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
+
+ case ARCH_TIMER_VIRT_PPI:
+ return !(gtdt->virtual_timer_flags & ACPI_GTDT_ALWAYS_ON);
+
+ case ARCH_TIMER_HYP_PPI:
+ return !(gtdt->non_secure_el2_flags & ACPI_GTDT_ALWAYS_ON);
+
+ default:
+ pr_err("Failed to get c3stop info: invalid type.\n");
+ }
+
+ return 0;
+}
+
+/*
+ * Release the memory we have allocated in acpi_gtdt_init.
+ * This should be called, when the driver who called "acpi_gtdt_init" previously
+ * doesn't need the GTDT info anymore.
+ */
+void __init acpi_gtdt_release(void)
+{
+ kfree(timer_block);
+ kfree(watchdog);
+ timer_block = NULL;
+ watchdog = NULL;
+}
+
+/*
+ * Get some basic info from GTDT table, and init the global variables above
+ * for all timers initialization of Generic Timer.
+ * This function does some validation on GTDT table.
+ */
+int __init acpi_gtdt_init(struct acpi_table_header *table)
+{
+ int timer_count;
+ void *platform_timer;
+ struct acpi_table_gtdt *gtdt;
+
+ gtdt = container_of(table, struct acpi_table_gtdt, header);
+
+ if (table->revision < 2) {
+ pr_debug("Revision:%d doesn't support Platform Timers.\n",
+ table->revision);
+ timer_count = 0;
+ } else if (!gtdt->platform_timer_count) {
+ pr_debug("No Platform Timer.\n");
+ timer_count = 0;
+ } else {
+ timer_count = gtdt->platform_timer_count;
+ }
+
+ acpi_gtdt_desc.gtdt = gtdt;
+ acpi_gtdt_desc.gtdt_end = (void *)table + table->length;
+
+ if (!timer_count)
+ return 0;
+
+ platform_timer = (void *)gtdt + gtdt->platform_timer_offset;
+ if (platform_timer < (void *)table + sizeof(struct acpi_table_gtdt)) {
+ pr_err(FW_BUG "Failed to retrieve timer info from firmware: invalid data.\n");
+ return -EINVAL;
+ }
+
+ timer_block = kcalloc(timer_count,
+ sizeof(struct acpi_gtdt_timer_block *),
+ GFP_KERNEL);
+ if (!timer_block)
+ return -ENOMEM;
+
+ watchdog = kcalloc(timer_count, sizeof(struct acpi_gtdt_watchdog *),
+ GFP_KERNEL);
+ if (!watchdog) {
+ kfree(timer_block);
+ timer_block = NULL;
+ return -ENOMEM;
+ }
+
+ acpi_gtdt_desc.timer_block_count = 0;
+ acpi_gtdt_desc.watchdog_count = 0;
+ for_each_platform_timer(platform_timer) {
+ if (is_timer_block(platform_timer)) {
+ timer_block[acpi_gtdt_desc.timer_block_count++] =
+ platform_timer;
+ } else if (is_watchdog(platform_timer)) {
+ watchdog[acpi_gtdt_desc.watchdog_count++] =
+ platform_timer;
+ } else {
+ pr_err(FW_BUG "Invalid platform timer type.\n");
+ goto error;
+ }
+ }
+
+ if (timer_count == acpi_gtdt_desc.watchdog_count +
+ acpi_gtdt_desc.timer_block_count)
+ return 0;
+
+ pr_err(FW_BUG "Invalid platform timer number.\n");
+error:
+ acpi_gtdt_release();
+ return -EINVAL;
+}
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 61a3d90..a1611d1 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -577,6 +577,13 @@ enum acpi_reconfig_event {
int acpi_reconfig_notifier_register(struct notifier_block *nb);
int acpi_reconfig_notifier_unregister(struct notifier_block *nb);
+#ifdef CONFIG_ACPI_GTDT
+int acpi_gtdt_init(struct acpi_table_header *table);
+int acpi_gtdt_map_ppi(int type);
+bool acpi_gtdt_c3stop(int type);
+void acpi_gtdt_release(void);
+#endif
+
#else /* !CONFIG_ACPI */
#define acpi_disabled 1
--
2.7.4
^ permalink raw reply related
* [PATCH v15 10/13] clocksource/drivers/arm_arch_timer: Simplify ACPI support code.
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
The patch update arm_arch_timer driver to use the function
provided by the new GTDT driver of ACPI.
By this way, arm_arch_timer.c can be simplified, and separate
all the ACPI GTDT knowledge from this timer driver.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
---
drivers/clocksource/arm_arch_timer.c | 55 +++++++++++++-----------------------
1 file changed, 19 insertions(+), 36 deletions(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index a44e44d..653374c 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1052,66 +1052,49 @@ static int __init arch_timer_mem_of_init(struct device_node *np)
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
arch_timer_mem_of_init);
-#ifdef CONFIG_ACPI
-static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
-{
- int trigger, polarity;
-
- if (!interrupt)
- return 0;
-
- trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
- : ACPI_LEVEL_SENSITIVE;
-
- polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
- : ACPI_ACTIVE_HIGH;
-
- return acpi_register_gsi(NULL, interrupt, trigger, polarity);
-}
-
+#ifdef CONFIG_ACPI_GTDT
/* Initialize per-processor generic timer */
static int __init arch_timer_acpi_init(struct acpi_table_header *table)
{
int ret;
- struct acpi_table_gtdt *gtdt;
if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
pr_warn("already initialized, skipping\n");
return -EINVAL;
}
- gtdt = container_of(table, struct acpi_table_gtdt, header);
-
arch_timers_present |= ARCH_TIMER_TYPE_CP15;
- arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI] =
- map_generic_timer_interrupt(gtdt->secure_el1_interrupt,
- gtdt->secure_el1_flags);
+ ret = acpi_gtdt_init(table);
+ if (ret) {
+ pr_err("Failed to init GTDT table.\n");
+ return ret;
+ }
arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI] =
- map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt,
- gtdt->non_secure_el1_flags);
+ acpi_gtdt_map_ppi(ARCH_TIMER_PHYS_NONSECURE_PPI);
arch_timer_ppi[ARCH_TIMER_VIRT_PPI] =
- map_generic_timer_interrupt(gtdt->virtual_timer_interrupt,
- gtdt->virtual_timer_flags);
+ acpi_gtdt_map_ppi(ARCH_TIMER_VIRT_PPI);
arch_timer_ppi[ARCH_TIMER_HYP_PPI] =
- map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt,
- gtdt->non_secure_el2_flags);
-
- /* Get the frequency from CNTFRQ */
- arch_timer_detect_rate(NULL, NULL);
+ acpi_gtdt_map_ppi(ARCH_TIMER_HYP_PPI);
ret = arch_timer_uses_ppi_init();
if (ret)
- return ret;
+ goto error;
/* Always-on capability */
- arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
+ arch_timer_c3stop = acpi_gtdt_c3stop(arch_timer_uses_ppi);
- arch_timer_init();
- return 0;
+ /* Get the frequency from CNTFRQ */
+ arch_timer_detect_rate(NULL, NULL);
+
+ ret = arch_timer_init();
+
+error:
+ acpi_gtdt_release();
+ return ret;
}
CLOCKSOURCE_ACPI_DECLARE(arch_timer, ACPI_SIG_GTDT, arch_timer_acpi_init);
#endif
--
2.7.4
^ permalink raw reply related
* [PATCH v15 11/13] acpi/arm64: Add memory-mapped timer support in GTDT driver
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
On platforms booting with ACPI, architected memory-mapped timers'
configuration data is provided by firmware through the ACPI GTDT
static table.
The clocksource architected timer kernel driver requires a firmware
interface to collect timer configuration and configure its driver.
this infrastructure is present for device tree systems, but it is
missing on systems booting with ACPI.
Implement the kernel infrastructure required to parse the static
ACPI GTDT table so that the architected timer clocksource driver can
make use of it on systems booting with ACPI, therefore enabling
the corresponding timers configuration.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
---
drivers/acpi/arm64/gtdt.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/acpi.h | 1 +
2 files changed, 96 insertions(+)
diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c
index 2de79aa..08d9506 100644
--- a/drivers/acpi/arm64/gtdt.c
+++ b/drivers/acpi/arm64/gtdt.c
@@ -51,6 +51,14 @@ static inline bool is_timer_block(void *platform_timer)
return gh->type == ACPI_GTDT_TYPE_TIMER_BLOCK;
}
+static inline struct acpi_gtdt_timer_block *get_timer_block(unsigned int index)
+{
+ if (index >= acpi_gtdt_desc.timer_block_count || !timer_block)
+ return NULL;
+
+ return timer_block[index];
+}
+
static inline bool is_watchdog(void *platform_timer)
{
struct acpi_gtdt_header *gh = platform_timer;
@@ -214,3 +222,90 @@ int __init acpi_gtdt_init(struct acpi_table_header *table)
acpi_gtdt_release();
return -EINVAL;
}
+
+/*
+ * Get ONE GT block info for memory-mapped timer from GTDT table.
+ * @data: the GT block data (parsing result)
+ * @index: the index number of GT block
+ * Note: we already verify @data in caller, it can't be NULL here.
+ * Returns 0 if success, -EINVAL/-ENODEV if error.
+ */
+int __init gtdt_arch_timer_mem_init(struct arch_timer_mem *data,
+ unsigned int index)
+{
+ struct acpi_gtdt_timer_block *block;
+ struct acpi_gtdt_timer_entry *frame;
+ int i;
+
+ block = get_timer_block(index);
+ if (!block)
+ return -ENODEV;
+
+ if (!block->timer_count) {
+ pr_err(FW_BUG "GT block present, but frame count is zero.");
+ return -ENODEV;
+ }
+
+ if (block->timer_count > ARCH_TIMER_MEM_MAX_FRAMES) {
+ pr_err(FW_BUG "GT block lists %d frames, ACPI spec only allows 8\n",
+ block->timer_count);
+ return -EINVAL;
+ }
+
+ data->cntctlbase = (phys_addr_t)block->block_address;
+ /*
+ * We can NOT get the size info from GTDT table,
+ * but according to "Table * CNTCTLBase memory map" of
+ * <ARM Architecture Reference Manual> for ARMv8,
+ * it should be 4KB(Offset 0x000 ? 0xFFC).
+ */
+ data->size = SZ_4K;
+ data->num_frames = block->timer_count;
+
+ frame = (void *)block + block->timer_offset;
+ if (frame + block->timer_count != (void *)block + block->header.length)
+ return -EINVAL;
+
+ /*
+ * Get the GT timer Frame data for every GT Block Timer
+ */
+ for (i = 0; i < block->timer_count; i++, frame++) {
+ if (!frame->base_address || !frame->timer_interrupt)
+ return -EINVAL;
+
+ data->frame[i].phys_irq = map_gt_gsi(frame->timer_interrupt,
+ frame->timer_flags);
+ if (data->frame[i].phys_irq <= 0) {
+ pr_warn("failed to map physical timer irq in frame %d.\n",
+ i);
+ return -EINVAL;
+ }
+
+ if (frame->virtual_timer_interrupt) {
+ data->frame[i].virt_irq =
+ map_gt_gsi(frame->virtual_timer_interrupt,
+ frame->virtual_timer_flags);
+ if (data->frame[i].virt_irq <= 0) {
+ pr_warn("failed to map virtual timer irq in frame %d.\n",
+ i);
+ return -EINVAL;
+ }
+ }
+
+ data->frame[i].frame_nr = frame->frame_number;
+ data->frame[i].cntbase = frame->base_address;
+ /*
+ * We can NOT get the size info from GTDT table,
+ * but according to "Table * CNTBaseN memory map" of
+ * <ARM Architecture Reference Manual> for ARMv8,
+ * it should be 4KB(Offset 0x000 ? 0xFFC).
+ */
+ data->frame[i].size = SZ_4K;
+ }
+
+ if (acpi_gtdt_desc.timer_block_count)
+ pr_info("parsed No.%d of %d memory-mapped timer block(s).\n",
+ index, acpi_gtdt_desc.timer_block_count);
+
+ return 0;
+}
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index a1611d1..44b8c1b 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -582,6 +582,7 @@ int acpi_gtdt_init(struct acpi_table_header *table);
int acpi_gtdt_map_ppi(int type);
bool acpi_gtdt_c3stop(int type);
void acpi_gtdt_release(void);
+int gtdt_arch_timer_mem_init(struct arch_timer_mem *data, unsigned int index);
#endif
#else /* !CONFIG_ACPI */
--
2.7.4
^ permalink raw reply related
* [PATCH v15 12/13] clocksource/drivers/arm_arch_timer: Add GTDT support for memory-mapped timer
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
The patch add memory-mapped timer register support by using the
information provided by the new GTDT driver of ACPI.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
---
drivers/clocksource/arm_arch_timer.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 653374c..350d0b9 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1053,7 +1053,28 @@ CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
arch_timer_mem_of_init);
#ifdef CONFIG_ACPI_GTDT
-/* Initialize per-processor generic timer */
+static int __init arch_timer_mem_acpi_init(void)
+{
+ struct arch_timer_mem *timer_mem;
+ int ret = 0;
+ int i = 0;
+
+ timer_mem = kzalloc(sizeof(*timer_mem), GFP_KERNEL);
+ if (!timer_mem)
+ return -ENOMEM;
+
+ while (!gtdt_arch_timer_mem_init(timer_mem, i)) {
+ ret = arch_timer_mem_init(timer_mem);
+ if (ret)
+ break;
+ i++;
+ }
+
+ kfree(timer_mem);
+ return ret;
+}
+
+/* Initialize per-processor generic timer and memory-mapped timer(if present) */
static int __init arch_timer_acpi_init(struct acpi_table_header *table)
{
int ret;
@@ -1090,6 +1111,9 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
/* Get the frequency from CNTFRQ */
arch_timer_detect_rate(NULL, NULL);
+ if (arch_timer_mem_acpi_init())
+ pr_err("Failed to initialize memory-mapped timer.\n");
+
ret = arch_timer_init();
error:
--
2.7.4
^ permalink raw reply related
* [PATCH v15 13/13] acpi/arm64: Add SBSA Generic Watchdog support in GTDT driver
From: fu.wei at linaro.org @ 2016-11-15 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479215615-26950-1-git-send-email-fu.wei@linaro.org>
From: Fu Wei <fu.wei@linaro.org>
This driver adds support for parsing SBSA Generic Watchdog timer
in GTDT, parse all info in SBSA Generic Watchdog Structure in GTDT,
and creating a platform device with that information.
This allows the operating system to obtain device data from the
resource of platform device. The platform device named "sbsa-gwdt"
can be used by the ARM SBSA Generic Watchdog driver.
Signed-off-by: Fu Wei <fu.wei@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Tested-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
---
drivers/acpi/arm64/gtdt.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/watchdog/Kconfig | 1 +
2 files changed, 101 insertions(+)
diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c
index 08d9506..69483f6 100644
--- a/drivers/acpi/arm64/gtdt.c
+++ b/drivers/acpi/arm64/gtdt.c
@@ -14,6 +14,7 @@
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/platform_device.h>
#include <clocksource/arm_arch_timer.h>
@@ -66,6 +67,14 @@ static inline bool is_watchdog(void *platform_timer)
return gh->type == ACPI_GTDT_TYPE_WATCHDOG;
}
+static inline struct acpi_gtdt_watchdog *get_watchdog(unsigned int index)
+{
+ if (index >= acpi_gtdt_desc.watchdog_count || !watchdog)
+ return NULL;
+
+ return watchdog[index];
+}
+
static int __init map_gt_gsi(u32 interrupt, u32 flags)
{
int trigger, polarity;
@@ -309,3 +318,94 @@ int __init gtdt_arch_timer_mem_init(struct arch_timer_mem *data,
return 0;
}
+
+/*
+ * Initialize a SBSA generic Watchdog platform device info from GTDT
+ */
+static int __init gtdt_import_sbsa_gwdt(struct acpi_gtdt_watchdog *wd,
+ int index)
+{
+ struct platform_device *pdev;
+ int irq = map_gt_gsi(wd->timer_interrupt, wd->timer_flags);
+ int no_irq = 1;
+
+ /*
+ * According to SBSA specification the size of refresh and control
+ * frames of SBSA Generic Watchdog is SZ_4K(Offset 0x000 ? 0xFFF).
+ */
+ struct resource res[] = {
+ DEFINE_RES_MEM(wd->control_frame_address, SZ_4K),
+ DEFINE_RES_MEM(wd->refresh_frame_address, SZ_4K),
+ DEFINE_RES_IRQ(irq),
+ };
+
+ pr_debug("found a Watchdog (0x%llx/0x%llx gsi:%u flags:0x%x).\n",
+ wd->refresh_frame_address, wd->control_frame_address,
+ wd->timer_interrupt, wd->timer_flags);
+
+ if (!(wd->refresh_frame_address && wd->control_frame_address)) {
+ pr_err(FW_BUG "failed to get the Watchdog base address.\n");
+ return -EINVAL;
+ }
+
+ if (!wd->timer_interrupt)
+ pr_warn(FW_BUG "failed to get the Watchdog interrupt.\n");
+ else if (irq <= 0)
+ pr_warn("failed to map the Watchdog interrupt.\n");
+ else
+ no_irq = 0;
+
+ /*
+ * Add a platform device named "sbsa-gwdt" to match the platform driver.
+ * "sbsa-gwdt": SBSA(Server Base System Architecture) Generic Watchdog
+ * The platform driver (like drivers/watchdog/sbsa_gwdt.c)can get device
+ * info below by matching this name.
+ */
+ pdev = platform_device_register_simple("sbsa-gwdt", index, res,
+ ARRAY_SIZE(res) - no_irq);
+ if (IS_ERR(pdev)) {
+ acpi_unregister_gsi(wd->timer_interrupt);
+ return PTR_ERR(pdev);
+ }
+
+ return 0;
+}
+
+static int __init gtdt_sbsa_gwdt_init(void)
+{
+ int i, ret;
+ struct acpi_table_header *table;
+ struct acpi_gtdt_watchdog *watchdog;
+
+ if (acpi_disabled)
+ return 0;
+
+ if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_GTDT, 0, &table)))
+ return -EINVAL;
+
+ ret = acpi_gtdt_init(table);
+ if (ret)
+ return ret;
+
+ if (!acpi_gtdt_desc.watchdog_count)
+ return 0;
+
+ for (i = 0; i < acpi_gtdt_desc.watchdog_count; i++) {
+ watchdog = get_watchdog(i);
+ if (!watchdog) {
+ ret = -ENODEV;
+ break;
+ }
+ ret = gtdt_import_sbsa_gwdt(watchdog, i);
+ if (ret)
+ break;
+ }
+
+ pr_info("found %d SBSA generic Watchdog(s), %d imported.\n",
+ acpi_gtdt_desc.watchdog_count, i);
+
+ acpi_gtdt_release();
+ return ret;
+}
+
+device_initcall(gtdt_sbsa_gwdt_init);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index fdd3228..e5ba1f0 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -218,6 +218,7 @@ config ARM_SBSA_WATCHDOG
tristate "ARM SBSA Generic Watchdog"
depends on ARM64
depends on ARM_ARCH_TIMER
+ depends on ACPI_GTDT || !ACPI
select WATCHDOG_CORE
help
ARM SBSA Generic Watchdog has two stage timeouts:
--
2.7.4
^ permalink raw reply related
* [PATCH v2 2/6] mfd: stm32-adc: Add support for stm32 ADC
From: Jonathan Cameron @ 2016-11-15 13:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <687b2001-c658-d0fb-a205-e5b6743fa9bc@st.com>
On 15 November 2016 10:47:52 GMT+00:00, Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
>On 11/14/2016 05:47 PM, Lee Jones wrote:
>> On Sat, 12 Nov 2016, Jonathan Cameron wrote:
>>
>>> On 10/11/16 16:18, Fabrice Gasnier wrote:
>>>> Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
>>>> Converter). STM32 ADC can be composed of up to 3 ADCs with shared
>>>> resources like clock prescaler, common interrupt line and analog
>>>> reference voltage.
>>>> This core driver basically manages shared resources.
>>>>
>>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
>>> Looks good to me (other than the build issue obviously ;)
>
>Hi Jonathan,
>Thanks for your review.
>I'll fix build issue, sure ;-)
>
>>>
>>> The fun bit will be trying to keep the whole thing this clean as you
>>> add the more 'interesting' functionality. *fingers crossed*
>Yes... But in the end, splitting shared resources in core driver makes
>it more simple.
>Not sure there will be more complexity here.
>
>>>
>>> Acked-by: Jonathan Cameron <jic23@kernel.org>
>> There isn't anything MFD about this driver.
>>
>> Please move it into IIO.
>
>Hmm, there is no other sub sysbtem that may be used here, ADC driver
>belongs to IIO.
>Also, of_platform_populate() is being used here. This can perfectly be
>called from within IIO.
>
>Jonathan, can this "stm32-adc-core" driver be moved to, and live in
>drivers/iio/adc ?
>(e.g. in addition to stm32-adc iio driver)
>Is it ok for you ?
Yes. That's ideal.
>
>Please advise,
>Best Regards,
>Fabrice
>
>>
>>>> ---
>>>> drivers/mfd/Kconfig | 14 ++
>>>> drivers/mfd/Makefile | 1 +
>>>> drivers/mfd/stm32-adc-core.c | 301
>+++++++++++++++++++++++++++++++++++++
>>>> include/linux/mfd/stm32-adc-core.h | 52 +++++++
>>>> 4 files changed, 368 insertions(+)
>>>> create mode 100644 drivers/mfd/stm32-adc-core.c
>>>> create mode 100644 include/linux/mfd/stm32-adc-core.h
>>>>
>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>> index c6df644..2580cee 100644
>>>> --- a/drivers/mfd/Kconfig
>>>> +++ b/drivers/mfd/Kconfig
>>>> @@ -1152,6 +1152,20 @@ config MFD_PALMAS
>>>> If you say yes here you get support for the Palmas
>>>> series of PMIC chips from Texas Instruments.
>>>>
>>>> +config MFD_STM32_ADC
>>>> + tristate "STMicroelectronics STM32 adc"
>>>> + depends on ARCH_STM32 || COMPILE_TEST
>>>> + depends on OF
>>>> + select MFD_CORE
>>>> + select REGULATOR
>>>> + select REGULATOR_FIXED_VOLTAGE
>>>> + help
>>>> + Select this option to enable the core driver for
>STMicroelectronics
>>>> + STM32 analog-to-digital converter (ADC).
>>>> +
>>>> + This driver can also be built as a module. If so, the module
>>>> + will be called stm32-adc-core.
>>>> +
>>>> config TPS6105X
>>>> tristate "TI TPS61050/61052 Boost Converters"
>>>> depends on I2C
>>>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>>>> index 9834e66..4571506 100644
>>>> --- a/drivers/mfd/Makefile
>>>> +++ b/drivers/mfd/Makefile
>>>> @@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS_PCI) +=
>intel-lpss-pci.o
>>>> obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
>>>> obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
>>>> obj-$(CONFIG_MFD_PALMAS) += palmas.o
>>>> +obj-$(CONFIG_MFD_STM32_ADC) += stm32-adc-core.o
>>>> obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
>>>> obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
>>>> obj-$(CONFIG_MFD_RK808) += rk808.o
>>>> diff --git a/drivers/mfd/stm32-adc-core.c
>b/drivers/mfd/stm32-adc-core.c
>>>> new file mode 100644
>>>> index 0000000..bcf52fb
>>>> --- /dev/null
>>>> +++ b/drivers/mfd/stm32-adc-core.c
>>>> @@ -0,0 +1,301 @@
>>>> +/*
>>>> + * This file is part of STM32 ADC driver
>>>> + *
>>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>>> + *
>>>> + * Inspired from: fsl-imx25-tsadc
>>>> + *
>>>> + * License type: GPLv2
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or
>modify it
>>>> + * under the terms of the GNU General Public License version 2 as
>published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>but
>>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>MERCHANTABILITY
>>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>>> + * See the GNU General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public
>License along with
>>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/irqchip/chained_irq.h>
>>>> +#include <linux/irqdesc.h>
>>>> +#include <linux/irqdomain.h>
>>>> +#include <linux/mfd/stm32-adc-core.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/regulator/consumer.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
>>>> +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
>>>> +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
>>>> +
>>>> +/* STM32F4_ADC_CSR - bit fields */
>>>> +#define STM32F4_EOC3 BIT(17)
>>>> +#define STM32F4_EOC2 BIT(9)
>>>> +#define STM32F4_EOC1 BIT(1)
>>>> +
>>>> +/* STM32F4_ADC_CCR - bit fields */
>>>> +#define STM32F4_ADC_ADCPRE_SHIFT 16
>>>> +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
>>>> +
>>>> +/* STM32 F4 maximum analog clock rate (from datasheet) */
>>>> +#define STM32F4_ADC_MAX_CLK_RATE 36000000
>>>> +
>>>> +/**
>>>> + * struct stm32_adc_priv - stm32 ADC core private data
>>>> + * @irq: irq for ADC block
>>>> + * @domain: irq domain reference
>>>> + * @aclk: clock reference for the analog circuitry
>>>> + * @vref: regulator reference
>>>> + * @common: common data for all ADC instances
>>>> + */
>>>> +struct stm32_adc_priv {
>>>> + int irq;
>>>> + struct irq_domain *domain;
>>>> + struct clk *aclk;
>>>> + struct regulator *vref;
>>>> + struct stm32_adc_common common;
>>>> +};
>>>> +
>>>> +static struct stm32_adc_priv *to_stm32_adc_priv(struct
>stm32_adc_common *com)
>>>> +{
>>>> + return container_of(com, struct stm32_adc_priv, common);
>>>> +}
>>>> +
>>>> +/* STM32F4 ADC internal common clock prescaler division ratios */
>>>> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
>>>> +
>>>> +/**
>>>> + * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock
>prescaler
>>>> + * @priv: stm32 ADC core private data
>>>> + * Select clock prescaler used for analog conversions, before
>using ADC.
>>>> + */
>>>> +static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + unsigned long rate;
>>>> + u32 val;
>>>> + int i;
>>>> +
>>>> + rate = clk_get_rate(priv->aclk);
>>>> + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
>>>> + if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
>>>> + break;
>>>> + }
>>>> + if (i >= ARRAY_SIZE(stm32f4_pclk_div))
>>>> + return -EINVAL;
>>>> +
>>>> + val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
>>>> + val &= ~STM32F4_ADC_ADCPRE_MASK;
>>>> + val |= i << STM32F4_ADC_ADCPRE_SHIFT;
>>>> + writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
>>>> +
>>>> + dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
>>>> + rate / (stm32f4_pclk_div[i] * 1000));
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/* ADC common interrupt for all instances */
>>>> +static void stm32_adc_irq_handler(struct irq_desc *desc)
>>>> +{
>>>> + struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
>>>> + struct irq_chip *chip = irq_desc_get_chip(desc);
>>>> + u32 status;
>>>> +
>>>> + chained_irq_enter(chip, desc);
>>>> + status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
>>>> +
>>>> + if (status & STM32F4_EOC1)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 0));
>>>> +
>>>> + if (status & STM32F4_EOC2)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 1));
>>>> +
>>>> + if (status & STM32F4_EOC3)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 2));
>>>> +
>>>> + chained_irq_exit(chip, desc);
>>>> +};
>>>> +
>>>> +static int stm32_adc_domain_map(struct irq_domain *d, unsigned int
>irq,
>>>> + irq_hw_number_t hwirq)
>>>> +{
>>>> + irq_set_chip_data(irq, d->host_data);
>>>> + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned
>int irq)
>>>> +{
>>>> + irq_set_chip_and_handler(irq, NULL, NULL);
>>>> + irq_set_chip_data(irq, NULL);
>>>> +}
>>>> +
>>>> +static const struct irq_domain_ops stm32_adc_domain_ops = {
>>>> + .map = stm32_adc_domain_map,
>>>> + .unmap = stm32_adc_domain_unmap,
>>>> + .xlate = irq_domain_xlate_onecell,
>>>> +};
>>>> +
>>>> +static int stm32_adc_irq_probe(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + struct device_node *np = pdev->dev.of_node;
>>>> +
>>>> + priv->irq = platform_get_irq(pdev, 0);
>>>> + if (priv->irq < 0) {
>>>> + dev_err(&pdev->dev, "failed to get irq\n");
>>>> + return priv->irq;
>>>> + }
>>>> +
>>>> + priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
>>>> + &stm32_adc_domain_ops,
>>>> + priv);
>>>> + if (!priv->domain) {
>>>> + dev_err(&pdev->dev, "Failed to add irq domain\n");
>>>> + return -ENOMEM;
>>>> + }
>>>> +
>>>> + irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
>>>> + irq_set_handler_data(priv->irq, priv);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static void stm32_adc_irq_remove(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + int hwirq;
>>>> +
>>>> + for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
>>>> + irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
>>>> + irq_domain_remove(priv->domain);
>>>> + irq_set_chained_handler(priv->irq, NULL);
>>>> +}
>>>> +
>>>> +static int stm32_adc_probe(struct platform_device *pdev)
>>>> +{
>>>> + struct stm32_adc_priv *priv;
>>>> + struct device_node *np = pdev->dev.of_node;
>>>> + struct resource *res;
>>>> + int ret;
>>>> +
>>>> + if (!pdev->dev.of_node)
>>>> + return -ENODEV;
>>>> +
>>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>>> + if (!priv)
>>>> + return -ENOMEM;
>>>> +
>>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> + priv->common.base = devm_ioremap_resource(&pdev->dev, res);
>>>> + if (IS_ERR(priv->common.base))
>>>> + return PTR_ERR(priv->common.base);
>>>> +
>>>> + priv->vref = devm_regulator_get(&pdev->dev, "vref");
>>>> + if (IS_ERR(priv->vref)) {
>>>> + ret = PTR_ERR(priv->vref);
>>>> + dev_err(&pdev->dev, "vref get failed, %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = regulator_enable(priv->vref);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "vref enable failed\n");
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = regulator_get_voltage(priv->vref);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
>>>> + goto err_regulator_disable;
>>>> + }
>>>> + priv->common.vref_mv = ret / 1000;
>>>> + dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
>>>> +
>>>> + priv->aclk = devm_clk_get(&pdev->dev, "adc");
>>>> + if (IS_ERR(priv->aclk)) {
>>>> + ret = PTR_ERR(priv->aclk);
>>>> + dev_err(&pdev->dev, "Can't get 'adc' clock\n");
>>>> + goto err_regulator_disable;
>>>> + }
>>>> +
>>>> + ret = clk_prepare_enable(priv->aclk);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "adc clk enable failed\n");
>>>> + goto err_regulator_disable;
>>>> + }
>>>> +
>>>> + ret = stm32f4_adc_clk_sel(pdev, priv);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "adc clk selection failed\n");
>>>> + goto err_clk_disable;
>>>> + }
>>>> +
>>>> + ret = stm32_adc_irq_probe(pdev, priv);
>>>> + if (ret < 0)
>>>> + goto err_clk_disable;
>>>> +
>>>> + platform_set_drvdata(pdev, &priv->common);
>>>> +
>>>> + ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "failed to populate DT children\n");
>>>> + goto err_irq_remove;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +
>>>> +err_irq_remove:
>>>> + stm32_adc_irq_remove(pdev, priv);
>>>> +
>>>> +err_clk_disable:
>>>> + clk_disable_unprepare(priv->aclk);
>>>> +
>>>> +err_regulator_disable:
>>>> + regulator_disable(priv->vref);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int stm32_adc_remove(struct platform_device *pdev)
>>>> +{
>>>> + struct stm32_adc_common *common = platform_get_drvdata(pdev);
>>>> + struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
>>>> +
>>>> + of_platform_depopulate(&pdev->dev);
>>>> + stm32_adc_irq_remove(pdev, priv);
>>>> + clk_disable_unprepare(priv->aclk);
>>>> + regulator_disable(priv->vref);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id stm32_adc_of_match[] = {
>>>> + { .compatible = "st,stm32f4-adc-core" },
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
>>>> +
>>>> +static struct platform_driver stm32_adc_driver = {
>>>> + .probe = stm32_adc_probe,
>>>> + .remove = stm32_adc_remove,
>>>> + .driver = {
>>>> + .name = "stm32-adc-core",
>>>> + .of_match_table = stm32_adc_of_match,
>>>> + },
>>>> +};
>>>> +module_platform_driver(stm32_adc_driver);
>>>> +
>>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC MFD driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> +MODULE_ALIAS("platform:stm32-adc-core");
>>>> diff --git a/include/linux/mfd/stm32-adc-core.h
>b/include/linux/mfd/stm32-adc-core.h
>>>> new file mode 100644
>>>> index 0000000..081fa5f
>>>> --- /dev/null
>>>> +++ b/include/linux/mfd/stm32-adc-core.h
>>>> @@ -0,0 +1,52 @@
>>>> +/*
>>>> + * This file is part of STM32 ADC driver
>>>> + *
>>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>>> + *
>>>> + * License type: GPLv2
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or
>modify it
>>>> + * under the terms of the GNU General Public License version 2 as
>published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>but
>>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>MERCHANTABILITY
>>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>>> + * See the GNU General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public
>License along with
>>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef __STM32_ADC_H
>>>> +#define __STM32_ADC_H
>>>> +
>>>> +/*
>>>> + * STM32 - ADC global register map
>>>> + * ________________________________________________________
>>>> + * | Offset | Register |
>>>> + * --------------------------------------------------------
>>>> + * | 0x000 | Master ADC1 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x100 | Slave ADC2 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x200 | Slave ADC3 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x300 | Master & Slave common regs |
>>>> + * --------------------------------------------------------
>>>> + */
>>>> +#define STM32_ADC_MAX_ADCS 3
>>>> +#define STM32_ADCX_COMN_OFFSET 0x300
>>>> +
>>>> +/**
>>>> + * struct stm32_adc_common - stm32 ADC driver common data (for all
>instances)
>>>> + * @base: control registers base cpu addr
>>>> + * @vref_mv: vref voltage (mv)
>>>> + */
>>>> +struct stm32_adc_common {
>>>> + void __iomem *base;
>>>> + int vref_mv;
>>>> +};
>>>> +
>>>> +#endif
>>>>
>
>--
>To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>the body of a message to majordomo at vger.kernel.org
>More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
^ permalink raw reply
* [PATCH v2 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 13:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4c5cd26d-97a5-f7a8-fdb6-9413975a3b10@kernel.org>
On 11/12/2016 06:08 PM, Jonathan Cameron wrote:
> On 10/11/16 16:18, Fabrice Gasnier wrote:
>> This patch adds support for STMicroelectronics STM32 MCU's analog to
>> digital converter.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Nice and clean - few minor things inline.
>
> Jonathan
>> ---
>> drivers/iio/adc/Kconfig | 10 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/stm32-adc.c | 525 ++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 536 insertions(+)
>> create mode 100644 drivers/iio/adc/stm32-adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 7edcf32..61ba674 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -419,6 +419,16 @@ config ROCKCHIP_SARADC
>> To compile this driver as a module, choose M here: the
>> module will be called rockchip_saradc.
>>
>> +config STM32_ADC
>> + tristate "STMicroelectronics STM32 adc"
>> + depends on MFD_STM32_ADC
>> + help
>> + Say yes here to build support for STMicroelectronics stm32 Analog
>> + to Digital Converter (ADC).
>> +
>> + This driver can also be built as a module. If so, the module
>> + will be called stm32-adc.
>> +
>> config STX104
>> tristate "Apex Embedded Systems STX104 driver"
>> depends on X86 && ISA_BUS_API
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 7a40c04..df7a221 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -41,6 +41,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>> obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>> obj-$(CONFIG_STX104) += stx104.o
>> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>> obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
>> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
>> new file mode 100644
>> index 0000000..2be5fee
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-adc.c
>> @@ -0,0 +1,525 @@
>> +/*
>> + * This file is part of STM32 ADC driver
>> + *
>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/mfd/stm32-adc-core.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of.h>
>> +
>> +/* STM32F4 - Registers for each ADC instance */
> Not really sure the X adds anything in these, but doesn't do much harm
> I guess ;)
You're right. I'll remove the X. I initially added that to remind this
stands for ADC1, 2 or 3...
Moreover, removing it will make register name match with reference manual.
>> +#define STM32F4_ADCX_SR 0x00
>> +#define STM32F4_ADCX_CR1 0x04
>> +#define STM32F4_ADCX_CR2 0x08
>> +#define STM32F4_ADCX_SMPR1 0x0C
>> +#define STM32F4_ADCX_SMPR2 0x10
>> +#define STM32F4_ADCX_HTR 0x24
>> +#define STM32F4_ADCX_LTR 0x28
>> +#define STM32F4_ADCX_SQR1 0x2C
>> +#define STM32F4_ADCX_SQR2 0x30
>> +#define STM32F4_ADCX_SQR3 0x34
>> +#define STM32F4_ADCX_JSQR 0x38
>> +#define STM32F4_ADCX_JDR1 0x3C
>> +#define STM32F4_ADCX_JDR2 0x40
>> +#define STM32F4_ADCX_JDR3 0x44
>> +#define STM32F4_ADCX_JDR4 0x48
>> +#define STM32F4_ADCX_DR 0x4C
>> +
>> +/* STM32F4_ADCX_SR - bit fields */
>> +#define STM32F4_OVR BIT(5)
>> +#define STM32F4_STRT BIT(4)
>> +#define STM32F4_EOC BIT(1)
>> +
>> +/* STM32F4_ADCX_CR1 - bit fields */
>> +#define STM32F4_OVRIE BIT(26)
>> +#define STM32F4_SCAN BIT(8)
>> +#define STM32F4_EOCIE BIT(5)
>> +
>> +/* STM32F4_ADCX_CR2 - bit fields */
>> +#define STM32F4_SWSTART BIT(30)
>> +#define STM32F4_EXTEN_MASK GENMASK(29, 28)
>> +#define STM32F4_EOCS BIT(10)
>> +#define STM32F4_ADON BIT(0)
>> +
>> +/* STM32F4_ADCX_SQR1 - bit fields */
>> +#define STM32F4_L_SHIFT 20
>> +#define STM32F4_L_MASK GENMASK(23, 20)
>> +
>> +/* STM32F4_ADCX_SQR3 - bit fields */
>> +#define STM32F4_SQ1_SHIFT 0
>> +#define STM32F4_SQ1_MASK GENMASK(4, 0)
>> +
>> +#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
>> +#define STM32_ADC_TIMEOUT_US 100000
>> +#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>> +
>> +/**
>> + * struct stm32_adc - private data of each ADC IIO instance
>> + * @common: reference to ADC block common data
>> + * @offset: ADC instance register offset in ADC block
>> + * @completion: end of single conversion completion
>> + * @buffer: data buffer
>> + * @clk: optional adc clock, for this adc instance
>> + * @irq: interrupt for this adc instance
>> + * @lock: spinlock
>> + */
>> +struct stm32_adc {
>> + struct stm32_adc_common *common;
>> + u32 offset;
>> + struct completion completion;
>> + u16 *buffer;
>> + struct clk *clk;
>> + int irq;
>> + spinlock_t lock; /* interrupt lock */
>> +};
>> +
>> +/**
>> + * struct stm32_adc_chan_spec - specification of stm32 adc channel
>> + * @type: IIO channel type
>> + * @channel: channel number (single ended)
>> + * @name: channel name (single ended)
>> + */
>> +struct stm32_adc_chan_spec {
>> + enum iio_chan_type type;
>> + int channel;
>> + const char *name;
>> +};
>> +
>> +/* Input definitions common for all STM32F4 instances */
>> +static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
>> + { IIO_VOLTAGE, 0, "in0" },
>> + { IIO_VOLTAGE, 1, "in1" },
>> + { IIO_VOLTAGE, 2, "in2" },
>> + { IIO_VOLTAGE, 3, "in3" },
>> + { IIO_VOLTAGE, 4, "in4" },
>> + { IIO_VOLTAGE, 5, "in5" },
>> + { IIO_VOLTAGE, 6, "in6" },
>> + { IIO_VOLTAGE, 7, "in7" },
>> + { IIO_VOLTAGE, 8, "in8" },
>> + { IIO_VOLTAGE, 9, "in9" },
>> + { IIO_VOLTAGE, 10, "in10" },
>> + { IIO_VOLTAGE, 11, "in11" },
>> + { IIO_VOLTAGE, 12, "in12" },
>> + { IIO_VOLTAGE, 13, "in13" },
>> + { IIO_VOLTAGE, 14, "in14" },
>> + { IIO_VOLTAGE, 15, "in15" },
>> +};
>> +
>> +/**
>> + * STM32 ADC registers access routines
>> + * @adc: stm32 adc instance
>> + * @reg: reg offset in adc instance
>> + *
>> + * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
>> + * for adc1, adc2 and adc3.
>> + */
>> +static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
>> +{
>> + return readl_relaxed(adc->common->base + adc->offset + reg);
>> +}
>> +
>> +static u32 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
>> +{
>> + return readw_relaxed(adc->common->base + adc->offset + reg);
>> +}
>> +
>> +static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
>> +{
>> + writel_relaxed(val, adc->common->base + adc->offset + reg);
>> +}
>> +
>> +static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>> +{
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&adc->lock, flags);
>> + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
>> + spin_unlock_irqrestore(&adc->lock, flags);
>> +}
>> +
>> +static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>> +{
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&adc->lock, flags);
>> + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
>> + spin_unlock_irqrestore(&adc->lock, flags);
>> +}
>> +
>> +/**
>> + * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt
>> + * @adc: stm32 adc instance
>> + */
>> +static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
>> +{
>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_EOCIE);
>> +};
>> +
>> +/**
>> + * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
>> + * @adc: stm32 adc instance
>> + */
>> +static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
>> +{
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_EOCIE);
>> +}
>> +
>> +/**
>> + * stm32_adc_start_conv() - Start conversions for regular channels.
>> + * @adc: stm32 adc instance
>> + */
>> +static void stm32_adc_start_conv(struct stm32_adc *adc)
>> +{
>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, STM32F4_EOCS | STM32F4_ADON);
>> +
>> + /* Wait for Power-up time (tSTAB from datasheet) */
>> + usleep_range(2, 3);
>> +
>> + /* Software start ? (e.g. trigger detection disabled ?) */
>> + if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_EXTEN_MASK))
>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, STM32F4_SWSTART);
>> +}
>> +
>> +static void stm32_adc_stop_conv(struct stm32_adc *adc)
>> +{
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
>> +
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
>> +}
>> +
>> +/**
>> + * stm32_adc_single_conv() - Performs a single conversion
>> + * @indio_dev: IIO device
>> + * @chan: IIO channel
>> + * @res: conversion result
>> + *
>> + * The function performs a single conversion on a given channel:
>> + * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
>> + * - Use SW trigger
>> + * - Start conversion, then wait for interrupt completion.
>> + */
>> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>> + const struct iio_chan_spec *chan,
>> + int *res)
>> +{
>> + struct stm32_adc *adc = iio_priv(indio_dev);
>> + long timeout;
>> + u32 val;
>> + u16 result;
>> + int ret;
>> +
>> + reinit_completion(&adc->completion);
>> +
>> + adc->buffer = &result;
>> +
>> + /* Program chan number in regular sequence */
>> + val = stm32_adc_readl(adc, STM32F4_ADCX_SQR3);
>> + val &= ~STM32F4_SQ1_MASK;
>> + val |= chan->channel << STM32F4_SQ1_SHIFT;
>> + stm32_adc_writel(adc, STM32F4_ADCX_SQR3, val);
>> +
>> + /* Set regular sequence len (0 for 1 conversion) */
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SQR1, STM32F4_L_MASK);
>> +
>> + /* Trigger detection disabled (conversion can be launched in SW) */
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>> +
>> + stm32_adc_conv_irq_enable(adc);
>> +
>> + stm32_adc_start_conv(adc);
>> +
>> + timeout = wait_for_completion_interruptible_timeout(
>> + &adc->completion, STM32_ADC_TIMEOUT);
>> + if (timeout == 0) {
>> + dev_warn(&indio_dev->dev, "Conversion timed out!\n");
>> + ret = -ETIMEDOUT;
>> + } else if (timeout < 0) {
>> + dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>> + ret = -EINTR;
>> + } else {
>> + *res = result;
>> + ret = IIO_VAL_INT;
>> + }
>> +
>> + stm32_adc_stop_conv(adc);
>> +
>> + stm32_adc_conv_irq_disable(adc);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct stm32_adc *adc = iio_priv(indio_dev);
>> + int ret = -EINVAL;
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + ret = iio_device_claim_direct_mode(indio_dev);
>> + if (ret)
>> + return ret;
>> + if (chan->type == IIO_VOLTAGE)
>> + ret = stm32_adc_single_conv(indio_dev, chan, val);
>> + else
>> + ret = -EINVAL;
>> + iio_device_release_direct_mode(indio_dev);
> return directly here. Basically always preferred to return directly if
> there is not cleanup to be done.
I will fix it.
>> + break;
>> + case IIO_CHAN_INFO_SCALE:
>> + *val = adc->common->vref_mv;
>> + *val2 = chan->scan_type.realbits;
>> + ret = IIO_VAL_FRACTIONAL_LOG2;
> return directly here.
I will fix it.
>> + break;
>> + default:
> return -EINVAL here.
I will fix it.
>> + break;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static irqreturn_t stm32_adc_isr(int irq, void *data)
>> +{
>> + struct stm32_adc *adc = data;
>> + u32 status = stm32_adc_readl(adc, STM32F4_ADCX_SR);
>> + irqreturn_t ret = IRQ_NONE;
>> +
>> + if (status & STM32F4_EOC) {
>> + *adc->buffer = stm32_adc_readw(adc, STM32F4_ADCX_DR);
>> + complete(&adc->completion);
>> + ret = IRQ_HANDLED;
> Slightly tidier to return IRQ_HANDLED here and directly return
> IRQ_NONE below.
I will fix it.
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
>> + const struct of_phandle_args *iiospec)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < indio_dev->num_channels; i++)
>> + if (indio_dev->channels[i].channel == iiospec->args[0])
>> + return i;
>> +
>> + return -EINVAL;
>> +}
>> +
>> +/**
>> + * stm32_adc_debugfs_reg_access - read or write register value
>> + *
>> + * To read a value from an ADC register:
>> + * echo [ADC reg offset] > direct_reg_access
>> + * cat direct_reg_access
>> + *
>> + * To write a value in a ADC register:
>> + * echo [ADC_reg_offset] [value] > direct_reg_access
>> + */
>> +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
>> + unsigned reg, unsigned writeval,
>> + unsigned *readval)
>> +{
>> + struct stm32_adc *adc = iio_priv(indio_dev);
>> +
>> + if (!readval)
>> + stm32_adc_writel(adc, reg, writeval);
>> + else
>> + *readval = stm32_adc_readl(adc, reg);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct iio_info stm32_adc_iio_info = {
>> + .read_raw = stm32_adc_read_raw,
>> + .debugfs_reg_access = stm32_adc_debugfs_reg_access,
>> + .of_xlate = stm32_adc_of_xlate,
>> + .driver_module = THIS_MODULE,
>> +};
>> +
>> +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>> + struct iio_chan_spec *chan,
>> + const struct stm32_adc_chan_spec *channel,
>> + int scan_index)
>> +{
>> + chan->type = channel->type;
>> + chan->channel = channel->channel;
>> + chan->datasheet_name = channel->name;
>> + chan->extend_name = channel->name;
> Don't set extend_name. That name doesn't add sufficient information to
> make it worth adding custom ABI to the userspace interface.
I will fix it.
>
>
>> + chan->scan_index = scan_index;
>> + chan->indexed = 1;
>> + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>> + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
>> + chan->scan_type.sign = 'u';
>> + chan->scan_type.realbits = 12;
>> + chan->scan_type.storagebits = 16;
>> +}
>> +
>> +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
>> +{
>> + struct device_node *node = indio_dev->dev.of_node;
>> + struct property *prop;
>> + const __be32 *cur;
>> + struct iio_chan_spec *channels;
>> + int scan_index = 0, num_channels;
>> + u32 val;
>> +
>> + num_channels = of_property_count_u32_elems(node, "st,adc-channels");
>> + if (num_channels < 0 ||
>> + num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
>> + dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>> + return num_channels < 0 ? num_channels : -EINVAL;
>> + }
>> +
>> + channels = devm_kcalloc(&indio_dev->dev, num_channels,
>> + sizeof(struct iio_chan_spec), GFP_KERNEL);
>> + if (!channels)
>> + return -ENOMEM;
>> +
>> + of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
>> + if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
>> + dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
>> + return -EINVAL;
>> + }
>> + stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
>> + &stm32f4_adc123_channels[val],
>> + scan_index);
>> + scan_index++;
>> + }
>> +
>> + indio_dev->num_channels = scan_index;
>> + indio_dev->channels = channels;
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_adc_probe(struct platform_device *pdev)
>> +{
>> + struct iio_dev *indio_dev;
>> + struct stm32_adc *adc;
>> + int ret;
>> +
>> + if (!pdev->dev.of_node)
>> + return -ENODEV;
>> +
>> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
>> + if (!indio_dev)
>> + return -ENOMEM;
>> +
>> + adc = iio_priv(indio_dev);
>> + adc->common = dev_get_drvdata(pdev->dev.parent);
>> + spin_lock_init(&adc->lock);
>> + init_completion(&adc->completion);
>> +
>> + indio_dev->name = dev_name(&pdev->dev);
>> + indio_dev->dev.parent = &pdev->dev;
>> + indio_dev->dev.of_node = pdev->dev.of_node;
>> + indio_dev->info = &stm32_adc_iio_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> + platform_set_drvdata(pdev, adc);
>> +
>> + ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
>> + if (ret != 0) {
>> + dev_err(&pdev->dev, "missing reg property\n");
>> + return -EINVAL;
>> + }
>> +
>> + adc->irq = platform_get_irq(pdev, 0);
>> + if (adc->irq < 0) {
>> + dev_err(&pdev->dev, "failed to get irq\n");
>> + return adc->irq;
>> + }
>> +
>> + ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
>> + 0, pdev->name, adc);
>> + if (ret) {
>> + dev_err(&pdev->dev, "failed to request IRQ\n");
>> + return ret;
>> + }
>> +
>> + adc->clk = devm_clk_get(&pdev->dev, NULL);
>> + if (IS_ERR(adc->clk)) {
> Could it concievably be deferred? Would be happier if this explicitly
> checked for -ENODEV or whatever gets returned when not clock has
> been specified.
I will fix it.
>> + adc->clk = NULL;
>> + dev_dbg(&pdev->dev, "No child clk found\n");
>> + } else {
>> + ret = clk_prepare_enable(adc->clk);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "clk enable failed\n");
>> + return ret;
>> + }
>> + }
>> +
>> + ret = stm32_adc_chan_of_init(indio_dev);
>> + if (ret < 0)
>> + goto err_clk_disable;
>> +
>> + ret = devm_iio_device_register(&pdev->dev, indio_dev);
> This use of devm registration is going to cause a race in the remove.
> The userspace interface will not be removed until after the remove
> function has run. That disables the clock thus leaving us a window
> where we could try and access the device with no clock enabled.
>
> Basic rule of thumb is that use of devm must not effect the ordering
> of unrolling what you do in probe when it comes to remove.
> (which more or less means that you can't use devm_iio_device_register
> unless you have no remove at all).
I will fix it.
Thanks,
Fabrice
>> + if (ret) {
>> + dev_err(&pdev->dev, "iio dev register failed\n");
>> + goto err_clk_disable;
>> + }
>> +
>> + return 0;
>> +
>> +err_clk_disable:
>> + if (adc->clk)
>> + clk_disable_unprepare(adc->clk);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_adc_remove(struct platform_device *pdev)
>> +{
>> + struct stm32_adc *adc = platform_get_drvdata(pdev);
>> +
>> + if (adc->clk)
>> + clk_disable_unprepare(adc->clk);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id stm32_adc_of_match[] = {
>> + { .compatible = "st,stm32f4-adc" },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
>> +
>> +static struct platform_driver stm32_adc_driver = {
>> + .probe = stm32_adc_probe,
>> + .remove = stm32_adc_remove,
>> + .driver = {
>> + .name = "stm32-adc",
>> + .of_match_table = stm32_adc_of_match,
>> + },
>> +};
>> +module_platform_driver(stm32_adc_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:stm32-adc");
>>
^ permalink raw reply
* [PATCH v2 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 13:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <535ffb14-a92f-1556-a4cb-f2be0508289a@metafoo.de>
On 11/14/2016 01:11 PM, Lars-Peter Clausen wrote:
> On 11/10/2016 05:18 PM, Fabrice Gasnier wrote:
> [...]
>> + static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>> + const struct iio_chan_spec *chan,
>> + int *res)
>> +{
>> + struct stm32_adc *adc = iio_priv(indio_dev);
>> + long timeout;
>> + u32 val;
>> + u16 result;
>> + int ret;
>> +
>> + reinit_completion(&adc->completion);
>> +
>> + adc->buffer = &result;
>> +
>> + /* Program chan number in regular sequence */
>> + val = stm32_adc_readl(adc, STM32F4_ADCX_SQR3);
>> + val &= ~STM32F4_SQ1_MASK;
>> + val |= chan->channel << STM32F4_SQ1_SHIFT;
>> + stm32_adc_writel(adc, STM32F4_ADCX_SQR3, val);
>> +
>> + /* Set regular sequence len (0 for 1 conversion) */
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SQR1, STM32F4_L_MASK);
>> +
>> + /* Trigger detection disabled (conversion can be launched in SW) */
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>> +
>> + stm32_adc_conv_irq_enable(adc);
>> +
>> + stm32_adc_start_conv(adc);
>> +
>> + timeout = wait_for_completion_interruptible_timeout(
>> + &adc->completion, STM32_ADC_TIMEOUT);
>> + if (timeout == 0) {
>> + dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> This should be dev_dbg() at most. This out of band reporting is not
> particular useful for applications as it is impossible to match the error to
> the action that triggered it. And you also report the error through the
> error code, so the applications knows what is going on.
>
>> + ret = -ETIMEDOUT;
>> + } else if (timeout < 0) {
>> + dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>> + ret = -EINTR;
> This should just propagate the error returned by wait_for_completion...().
> This will make sure that the right behavior occurs based on the SA_RESTART
> policy.
Hi Lars,
Thanks for reviewing.
I'll update this in next revision.
Regards,
Fabrice
>
>> + } else {
>> + *res = result;
>> + ret = IIO_VAL_INT;
>> + }
>> +
>> + stm32_adc_stop_conv(adc);
>> +
>> + stm32_adc_conv_irq_disable(adc);
>> +
>> + return ret;
^ permalink raw reply
* LEDs that change brightness "itself" -- that's a trigger. Re: PM regression with LED changes in next-20161109
From: Jacek Anaszewski @ 2016-11-15 13:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <aeccbb02-6e33-390c-d72d-2a580dd67ed8@redhat.com>
On 11/15/2016 01:06 PM, Hans de Goede wrote:
> Hi,
>
> On 15-11-16 12:48, Pavel Machek wrote:
>> Hi!
>>
>>>>>> The LED you are talking about _has_ a trigger, implemented in
>>>>>> hardware. That trigger can change LED brightness behind kernel's (and
>>>>>> userspace's) back. Don't pretend the trigger does not exist, it does.
>>>>>>
>>>>>> And when you do that, you'll have nice place to report changes to
>>>>>> userspace -- trigger can now export that information, and offer
>>>>>> poll()
>>>>>> interface.
>>>>>
>>>>> Well, that sounds interesting. It is logically justifiable.
>>>>
>>>> Thanks.
>>>>
>>>>> I initially proposed exactly this solution, with recently
>>>>> added userspace LED being a trigger listener. It seems a bit
>>>>> awkward though. How would you listen to the trigger events?
>>>>
>>>> Trigger exposes a file in sysfs, with poll() working on that file
>>>
>>> Hmm, a new file would give the advantage of making it easy for
>>> userspace to see if the trigger is poll-able, this is likely
>>> better then my own proposal I just send.
>>
>> Good.
>>
>>>> (and
>>>> probably read exposing the current brightness).
>>>
>>> If we do this, can we please make it mirror brightness, iow
>>> also make it writable, that will make it easier for userspace
>>> to deal with it. We can simply re-use the existing show / store
>>> methods for brightness for this.
>>
>> Actually, echo 0 > brightness disables the trigger, IIRC. I'd avoid
>> that here, you want to be able to turn off the backlight but still
>> keep the trigger (and be notified of future changes).
>
> True, that is easy to do the store method will just need to call
> led_set_brightness_nosleep instead of led_set_brightness, this
> will skip the checks to stop blinking in led_set_brightness and
> otherwise is equivalent.
>
>>> I suggest we call it:
>>>
>>> trigger_brightness
>>>
>>> And only register it when a poll-able trigger is present.
>>
>> I'd call it 'current_brightness', but that's no big deal. Yes, only
>> registering it for poll-able triggers makes sense.
>
> current_brightness works for me. I will take a shot a patch-set
> implementing this.
Word "current" is not precise here.
It can be thought of as either last brightness set by the
user or the brightness currently written to the device
(returned by brightness file).
There is a semantic discrepancy in our requirements -
we want the file representing both permanent brightness
set by the user and brightness set by the hardware.
The two stand in contradiction to each other since
brightness set by the user can be adjusted by the hardware.
Reading the file shouldn't update brightness property of
struct led_classdev, so it shouldn't call led_update_brightness()
but it still should allow reading brightness set by the
hardware, as a result of each POLLPRI event. So in fact in
the same time it should report both according to our requirements
which is impossible. Do we need three brightness files ?
--
Best regards,
Jacek Anaszewski
^ permalink raw reply
* [PATCH] arm: spin one more cycle in timer-based delays
From: Mason @ 2016-11-15 13:36 UTC (permalink / raw)
To: linux-arm-kernel
When polling a tick counter in a busy loop, one might sample the
counter just *before* it is updated, and then again just *after*
it is updated. In that case, while mathematically v2-v1 equals 1,
only epsilon has really passed.
So, if a caller requests an N-cycle delay, we spin until v2-v1
is strictly greater than N to avoid these random corner cases.
Signed-off-by: Mason <slash.tmp@free.fr>
---
arch/arm/lib/delay.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/lib/delay.c b/arch/arm/lib/delay.c
index 69aad80a3af4..3f1cd15ca102 100644
--- a/arch/arm/lib/delay.c
+++ b/arch/arm/lib/delay.c
@@ -60,7 +60,7 @@ static void __timer_delay(unsigned long cycles)
{
cycles_t start = get_cycles();
- while ((get_cycles() - start) < cycles)
+ while ((get_cycles() - start) <= cycles)
cpu_relax();
}
--
2.9.0
^ permalink raw reply related
* [PATCH v3 1/2] phy: rockchip-inno-usb2: support otg-port for rk3399
From: Kishon Vijay Abraham I @ 2016-11-15 13:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478520529-8869-2-git-send-email-wulf@rock-chips.com>
On Monday 07 November 2016 05:38 PM, William Wu wrote:
> The rk3399 SoC USB2 PHY is comprised of one Host port and
> one OTG port. And OTG port is for USB2.0 part of USB3.0 OTG
> controller, as a part to construct a fully feature Type-C
> subsystem.
>
> With this patch, we can support OTG port with the following
> functions:
> - Support BC1.2 charger detect, and use extcon notifier to
> send USB charger types to power driver.
> - Support PHY suspend for power management.
> - Support OTG Host only mode.
>
> Signed-off-by: William Wu <wulf@rock-chips.com>
merged.
Thanks
Kishon
> ---
> Changes in v3:
> - split the clock fix into a separate patch
>
> Changes in v2:
> - remove wakelock
>
> drivers/phy/phy-rockchip-inno-usb2.c | 591 +++++++++++++++++++++++++++++++++--
> 1 file changed, 561 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
> index ac20310..ecfd7d1 100644
> --- a/drivers/phy/phy-rockchip-inno-usb2.c
> +++ b/drivers/phy/phy-rockchip-inno-usb2.c
> @@ -17,6 +17,7 @@
> #include <linux/clk.h>
> #include <linux/clk-provider.h>
> #include <linux/delay.h>
> +#include <linux/extcon.h>
> #include <linux/interrupt.h>
> #include <linux/io.h>
> #include <linux/gpio/consumer.h>
> @@ -30,11 +31,15 @@
> #include <linux/of_platform.h>
> #include <linux/phy/phy.h>
> #include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> #include <linux/regmap.h>
> #include <linux/mfd/syscon.h>
> +#include <linux/usb/of.h>
> +#include <linux/usb/otg.h>
>
> #define BIT_WRITEABLE_SHIFT 16
> -#define SCHEDULE_DELAY (60 * HZ)
> +#define SCHEDULE_DELAY (60 * HZ)
> +#define OTG_SCHEDULE_DELAY (2 * HZ)
>
> enum rockchip_usb2phy_port_id {
> USB2PHY_PORT_OTG,
> @@ -49,6 +54,37 @@ enum rockchip_usb2phy_host_state {
> PHY_STATE_FS_LS_ONLINE = 4,
> };
>
> +/**
> + * Different states involved in USB charger detection.
> + * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection
> + * process is not yet started.
> + * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact.
> + * USB_CHG_STATE_DCD_DONE Data pin contact is detected.
> + * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects
> + * between SDP and DCP/CDP).
> + * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects
> + * between DCP and CDP).
> + * USB_CHG_STATE_DETECTED USB charger type is determined.
> + */
> +enum usb_chg_state {
> + USB_CHG_STATE_UNDEFINED = 0,
> + USB_CHG_STATE_WAIT_FOR_DCD,
> + USB_CHG_STATE_DCD_DONE,
> + USB_CHG_STATE_PRIMARY_DONE,
> + USB_CHG_STATE_SECONDARY_DONE,
> + USB_CHG_STATE_DETECTED,
> +};
> +
> +static const unsigned int rockchip_usb2phy_extcon_cable[] = {
> + EXTCON_USB,
> + EXTCON_USB_HOST,
> + EXTCON_CHG_USB_SDP,
> + EXTCON_CHG_USB_CDP,
> + EXTCON_CHG_USB_DCP,
> + EXTCON_CHG_USB_SLOW,
> + EXTCON_NONE,
> +};
> +
> struct usb2phy_reg {
> unsigned int offset;
> unsigned int bitend;
> @@ -58,19 +94,55 @@ struct usb2phy_reg {
> };
>
> /**
> + * struct rockchip_chg_det_reg: usb charger detect registers
> + * @cp_det: charging port detected successfully.
> + * @dcp_det: dedicated charging port detected successfully.
> + * @dp_det: assert data pin connect successfully.
> + * @idm_sink_en: open dm sink curren.
> + * @idp_sink_en: open dp sink current.
> + * @idp_src_en: open dm source current.
> + * @rdm_pdwn_en: open dm pull down resistor.
> + * @vdm_src_en: open dm voltage source.
> + * @vdp_src_en: open dp voltage source.
> + * @opmode: utmi operational mode.
> + */
> +struct rockchip_chg_det_reg {
> + struct usb2phy_reg cp_det;
> + struct usb2phy_reg dcp_det;
> + struct usb2phy_reg dp_det;
> + struct usb2phy_reg idm_sink_en;
> + struct usb2phy_reg idp_sink_en;
> + struct usb2phy_reg idp_src_en;
> + struct usb2phy_reg rdm_pdwn_en;
> + struct usb2phy_reg vdm_src_en;
> + struct usb2phy_reg vdp_src_en;
> + struct usb2phy_reg opmode;
> +};
> +
> +/**
> * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
> * @phy_sus: phy suspend register.
> + * @bvalid_det_en: vbus valid rise detection enable register.
> + * @bvalid_det_st: vbus valid rise detection status register.
> + * @bvalid_det_clr: vbus valid rise detection clear register.
> * @ls_det_en: linestate detection enable register.
> * @ls_det_st: linestate detection state register.
> * @ls_det_clr: linestate detection clear register.
> + * @utmi_avalid: utmi vbus avalid status register.
> + * @utmi_bvalid: utmi vbus bvalid status register.
> * @utmi_ls: utmi linestate state register.
> * @utmi_hstdet: utmi host disconnect register.
> */
> struct rockchip_usb2phy_port_cfg {
> struct usb2phy_reg phy_sus;
> + struct usb2phy_reg bvalid_det_en;
> + struct usb2phy_reg bvalid_det_st;
> + struct usb2phy_reg bvalid_det_clr;
> struct usb2phy_reg ls_det_en;
> struct usb2phy_reg ls_det_st;
> struct usb2phy_reg ls_det_clr;
> + struct usb2phy_reg utmi_avalid;
> + struct usb2phy_reg utmi_bvalid;
> struct usb2phy_reg utmi_ls;
> struct usb2phy_reg utmi_hstdet;
> };
> @@ -80,31 +152,51 @@ struct rockchip_usb2phy_port_cfg {
> * @reg: the address offset of grf for usb-phy config.
> * @num_ports: specify how many ports that the phy has.
> * @clkout_ctl: keep on/turn off output clk of phy.
> + * @chg_det: charger detection registers.
> */
> struct rockchip_usb2phy_cfg {
> unsigned int reg;
> unsigned int num_ports;
> struct usb2phy_reg clkout_ctl;
> const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS];
> + const struct rockchip_chg_det_reg chg_det;
> };
>
> /**
> * struct rockchip_usb2phy_port: usb-phy port data.
> * @port_id: flag for otg port or host port.
> * @suspended: phy suspended flag.
> + * @utmi_avalid: utmi avalid status usage flag.
> + * true - use avalid to get vbus status
> + * flase - use bvalid to get vbus status
> + * @vbus_attached: otg device vbus status.
> + * @bvalid_irq: IRQ number assigned for vbus valid rise detection.
> * @ls_irq: IRQ number assigned for linestate detection.
> * @mutex: for register updating in sm_work.
> - * @sm_work: OTG state machine work.
> + * @chg_work: charge detect work.
> + * @otg_sm_work: OTG state machine work.
> + * @sm_work: HOST state machine work.
> * @phy_cfg: port register configuration, assigned by driver data.
> + * @event_nb: hold event notification callback.
> + * @state: define OTG enumeration states before device reset.
> + * @mode: the dr_mode of the controller.
> */
> struct rockchip_usb2phy_port {
> struct phy *phy;
> unsigned int port_id;
> bool suspended;
> + bool utmi_avalid;
> + bool vbus_attached;
> + int bvalid_irq;
> int ls_irq;
> struct mutex mutex;
> + struct delayed_work chg_work;
> + struct delayed_work otg_sm_work;
> struct delayed_work sm_work;
> const struct rockchip_usb2phy_port_cfg *port_cfg;
> + struct notifier_block event_nb;
> + enum usb_otg_state state;
> + enum usb_dr_mode mode;
> };
>
> /**
> @@ -113,6 +205,11 @@ struct rockchip_usb2phy_port {
> * @clk: clock struct of phy input clk.
> * @clk480m: clock struct of phy output clk.
> * @clk_hw: clock struct of phy output clk management.
> + * @chg_state: states involved in USB charger detection.
> + * @chg_type: USB charger types.
> + * @dcd_retries: The retry count used to track Data contact
> + * detection process.
> + * @edev: extcon device for notification registration
> * @phy_cfg: phy register configuration, assigned by driver data.
> * @ports: phy port instance.
> */
> @@ -122,6 +219,10 @@ struct rockchip_usb2phy {
> struct clk *clk;
> struct clk *clk480m;
> struct clk_hw clk480m_hw;
> + enum usb_chg_state chg_state;
> + enum power_supply_type chg_type;
> + u8 dcd_retries;
> + struct extcon_dev *edev;
> const struct rockchip_usb2phy_cfg *phy_cfg;
> struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS];
> };
> @@ -263,33 +364,84 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
> return ret;
> }
>
> -static int rockchip_usb2phy_init(struct phy *phy)
> +static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
> {
> - struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
> - struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
> int ret;
> + struct device_node *node = rphy->dev->of_node;
> + struct extcon_dev *edev;
> +
> + if (of_property_read_bool(node, "extcon")) {
> + edev = extcon_get_edev_by_phandle(rphy->dev, 0);
> + if (IS_ERR(edev)) {
> + if (PTR_ERR(edev) != -EPROBE_DEFER)
> + dev_err(rphy->dev, "Invalid or missing extcon\n");
> + return PTR_ERR(edev);
> + }
> + } else {
> + /* Initialize extcon device */
> + edev = devm_extcon_dev_allocate(rphy->dev,
> + rockchip_usb2phy_extcon_cable);
>
> - if (rport->port_id == USB2PHY_PORT_HOST) {
> - /* clear linestate and enable linestate detect irq */
> - mutex_lock(&rport->mutex);
> + if (IS_ERR(edev))
> + return -ENOMEM;
>
> - ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
> + ret = devm_extcon_dev_register(rphy->dev, edev);
> if (ret) {
> - mutex_unlock(&rport->mutex);
> + dev_err(rphy->dev, "failed to register extcon device\n");
> return ret;
> }
> + }
>
> - ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
> - if (ret) {
> - mutex_unlock(&rport->mutex);
> - return ret;
> + rphy->edev = edev;
> +
> + return 0;
> +}
> +
> +static int rockchip_usb2phy_init(struct phy *phy)
> +{
> + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
> + struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
> + int ret = 0;
> +
> + mutex_lock(&rport->mutex);
> +
> + if (rport->port_id == USB2PHY_PORT_OTG) {
> + if (rport->mode != USB_DR_MODE_HOST) {
> + /* clear bvalid status and enable bvalid detect irq */
> + ret = property_enable(rphy,
> + &rport->port_cfg->bvalid_det_clr,
> + true);
> + if (ret)
> + goto out;
> +
> + ret = property_enable(rphy,
> + &rport->port_cfg->bvalid_det_en,
> + true);
> + if (ret)
> + goto out;
> +
> + schedule_delayed_work(&rport->otg_sm_work,
> + OTG_SCHEDULE_DELAY);
> + } else {
> + /* If OTG works in host only mode, do nothing. */
> + dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode);
> }
> + } else if (rport->port_id == USB2PHY_PORT_HOST) {
> + /* clear linestate and enable linestate detect irq */
> + ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
> + if (ret)
> + goto out;
> +
> + ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
> + if (ret)
> + goto out;
>
> - mutex_unlock(&rport->mutex);
> schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
> }
>
> - return 0;
> +out:
> + mutex_unlock(&rport->mutex);
> + return ret;
> }
>
> static int rockchip_usb2phy_power_on(struct phy *phy)
> @@ -340,7 +492,11 @@ static int rockchip_usb2phy_exit(struct phy *phy)
> {
> struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
>
> - if (rport->port_id == USB2PHY_PORT_HOST)
> + if (rport->port_id == USB2PHY_PORT_OTG &&
> + rport->mode != USB_DR_MODE_HOST) {
> + cancel_delayed_work_sync(&rport->otg_sm_work);
> + cancel_delayed_work_sync(&rport->chg_work);
> + } else if (rport->port_id == USB2PHY_PORT_HOST)
> cancel_delayed_work_sync(&rport->sm_work);
>
> return 0;
> @@ -354,6 +510,249 @@ static const struct phy_ops rockchip_usb2phy_ops = {
> .owner = THIS_MODULE,
> };
>
> +static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
> +{
> + struct rockchip_usb2phy_port *rport =
> + container_of(work, struct rockchip_usb2phy_port,
> + otg_sm_work.work);
> + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> + static unsigned int cable;
> + unsigned long delay;
> + bool vbus_attach, sch_work, notify_charger;
> +
> + if (rport->utmi_avalid)
> + vbus_attach =
> + property_enabled(rphy, &rport->port_cfg->utmi_avalid);
> + else
> + vbus_attach =
> + property_enabled(rphy, &rport->port_cfg->utmi_bvalid);
> +
> + sch_work = false;
> + notify_charger = false;
> + delay = OTG_SCHEDULE_DELAY;
> + dev_dbg(&rport->phy->dev, "%s otg sm work\n",
> + usb_otg_state_string(rport->state));
> +
> + switch (rport->state) {
> + case OTG_STATE_UNDEFINED:
> + rport->state = OTG_STATE_B_IDLE;
> + if (!vbus_attach)
> + rockchip_usb2phy_power_off(rport->phy);
> + /* fall through */
> + case OTG_STATE_B_IDLE:
> + if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) {
> + dev_dbg(&rport->phy->dev, "usb otg host connect\n");
> + rport->state = OTG_STATE_A_HOST;
> + rockchip_usb2phy_power_on(rport->phy);
> + return;
> + } else if (vbus_attach) {
> + dev_dbg(&rport->phy->dev, "vbus_attach\n");
> + switch (rphy->chg_state) {
> + case USB_CHG_STATE_UNDEFINED:
> + schedule_delayed_work(&rport->chg_work, 0);
> + return;
> + case USB_CHG_STATE_DETECTED:
> + switch (rphy->chg_type) {
> + case POWER_SUPPLY_TYPE_USB:
> + dev_dbg(&rport->phy->dev,
> + "sdp cable is connecetd\n");
> + rockchip_usb2phy_power_on(rport->phy);
> + rport->state = OTG_STATE_B_PERIPHERAL;
> + notify_charger = true;
> + sch_work = true;
> + cable = EXTCON_CHG_USB_SDP;
> + break;
> + case POWER_SUPPLY_TYPE_USB_DCP:
> + dev_dbg(&rport->phy->dev,
> + "dcp cable is connecetd\n");
> + rockchip_usb2phy_power_off(rport->phy);
> + notify_charger = true;
> + sch_work = true;
> + cable = EXTCON_CHG_USB_DCP;
> + break;
> + case POWER_SUPPLY_TYPE_USB_CDP:
> + dev_dbg(&rport->phy->dev,
> + "cdp cable is connecetd\n");
> + rockchip_usb2phy_power_on(rport->phy);
> + rport->state = OTG_STATE_B_PERIPHERAL;
> + notify_charger = true;
> + sch_work = true;
> + cable = EXTCON_CHG_USB_CDP;
> + break;
> + default:
> + break;
> + }
> + break;
> + default:
> + break;
> + }
> + } else {
> + notify_charger = true;
> + rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
> + }
> +
> + if (rport->vbus_attached != vbus_attach) {
> + rport->vbus_attached = vbus_attach;
> +
> + if (notify_charger && rphy->edev)
> + extcon_set_cable_state_(rphy->edev,
> + cable, vbus_attach);
> + }
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + if (!vbus_attach) {
> + dev_dbg(&rport->phy->dev, "usb disconnect\n");
> + rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
> + rport->state = OTG_STATE_B_IDLE;
> + delay = 0;
> + rockchip_usb2phy_power_off(rport->phy);
> + }
> + sch_work = true;
> + break;
> + case OTG_STATE_A_HOST:
> + if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) {
> + dev_dbg(&rport->phy->dev, "usb otg host disconnect\n");
> + rport->state = OTG_STATE_B_IDLE;
> + rockchip_usb2phy_power_off(rport->phy);
> + }
> + break;
> + default:
> + break;
> + }
> +
> + if (sch_work)
> + schedule_delayed_work(&rport->otg_sm_work, delay);
> +}
> +
> +static const char *chg_to_string(enum power_supply_type chg_type)
> +{
> + switch (chg_type) {
> + case POWER_SUPPLY_TYPE_USB:
> + return "USB_SDP_CHARGER";
> + case POWER_SUPPLY_TYPE_USB_DCP:
> + return "USB_DCP_CHARGER";
> + case POWER_SUPPLY_TYPE_USB_CDP:
> + return "USB_CDP_CHARGER";
> + default:
> + return "INVALID_CHARGER";
> + }
> +}
> +
> +static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy,
> + bool en)
> +{
> + property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en);
> + property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en);
> +}
> +
> +static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy,
> + bool en)
> +{
> + property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en);
> + property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en);
> +}
> +
> +static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy,
> + bool en)
> +{
> + property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en);
> + property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en);
> +}
> +
> +#define CHG_DCD_POLL_TIME (100 * HZ / 1000)
> +#define CHG_DCD_MAX_RETRIES 6
> +#define CHG_PRIMARY_DET_TIME (40 * HZ / 1000)
> +#define CHG_SECONDARY_DET_TIME (40 * HZ / 1000)
> +static void rockchip_chg_detect_work(struct work_struct *work)
> +{
> + struct rockchip_usb2phy_port *rport =
> + container_of(work, struct rockchip_usb2phy_port, chg_work.work);
> + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> + bool is_dcd, tmout, vout;
> + unsigned long delay;
> +
> + dev_dbg(&rport->phy->dev, "chg detection work state = %d\n",
> + rphy->chg_state);
> + switch (rphy->chg_state) {
> + case USB_CHG_STATE_UNDEFINED:
> + if (!rport->suspended)
> + rockchip_usb2phy_power_off(rport->phy);
> + /* put the controller in non-driving mode */
> + property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false);
> + /* Start DCD processing stage 1 */
> + rockchip_chg_enable_dcd(rphy, true);
> + rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
> + rphy->dcd_retries = 0;
> + delay = CHG_DCD_POLL_TIME;
> + break;
> + case USB_CHG_STATE_WAIT_FOR_DCD:
> + /* get data contact detection status */
> + is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det);
> + tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES;
> + /* stage 2 */
> + if (is_dcd || tmout) {
> + /* stage 4 */
> + /* Turn off DCD circuitry */
> + rockchip_chg_enable_dcd(rphy, false);
> + /* Voltage Source on DP, Probe on DM */
> + rockchip_chg_enable_primary_det(rphy, true);
> + delay = CHG_PRIMARY_DET_TIME;
> + rphy->chg_state = USB_CHG_STATE_DCD_DONE;
> + } else {
> + /* stage 3 */
> + delay = CHG_DCD_POLL_TIME;
> + }
> + break;
> + case USB_CHG_STATE_DCD_DONE:
> + vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det);
> + rockchip_chg_enable_primary_det(rphy, false);
> + if (vout) {
> + /* Voltage Source on DM, Probe on DP */
> + rockchip_chg_enable_secondary_det(rphy, true);
> + delay = CHG_SECONDARY_DET_TIME;
> + rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE;
> + } else {
> + if (tmout) {
> + /* floating charger found */
> + rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
> + rphy->chg_state = USB_CHG_STATE_DETECTED;
> + delay = 0;
> + } else {
> + rphy->chg_type = POWER_SUPPLY_TYPE_USB;
> + rphy->chg_state = USB_CHG_STATE_DETECTED;
> + delay = 0;
> + }
> + }
> + break;
> + case USB_CHG_STATE_PRIMARY_DONE:
> + vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det);
> + /* Turn off voltage source */
> + rockchip_chg_enable_secondary_det(rphy, false);
> + if (vout)
> + rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
> + else
> + rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP;
> + /* fall through */
> + case USB_CHG_STATE_SECONDARY_DONE:
> + rphy->chg_state = USB_CHG_STATE_DETECTED;
> + delay = 0;
> + /* fall through */
> + case USB_CHG_STATE_DETECTED:
> + /* put the controller in normal mode */
> + property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true);
> + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
> + dev_info(&rport->phy->dev, "charger = %s\n",
> + chg_to_string(rphy->chg_type));
> + return;
> + default:
> + return;
> + }
> +
> + schedule_delayed_work(&rport->chg_work, delay);
> +}
> +
> /*
> * The function manage host-phy port state and suspend/resume phy port
> * to save power.
> @@ -485,6 +884,26 @@ static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
> return IRQ_HANDLED;
> }
>
> +static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data)
> +{
> + struct rockchip_usb2phy_port *rport = data;
> + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> +
> + if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st))
> + return IRQ_NONE;
> +
> + mutex_lock(&rport->mutex);
> +
> + /* clear bvalid detect irq pending status */
> + property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true);
> +
> + mutex_unlock(&rport->mutex);
> +
> + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
> +
> + return IRQ_HANDLED;
> +}
> +
> static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
> struct rockchip_usb2phy_port *rport,
> struct device_node *child_np)
> @@ -509,13 +928,86 @@ static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
> IRQF_ONESHOT,
> "rockchip_usb2phy", rport);
> if (ret) {
> - dev_err(rphy->dev, "failed to request irq handle\n");
> + dev_err(rphy->dev, "failed to request linestate irq handle\n");
> return ret;
> }
>
> return 0;
> }
>
> +static int rockchip_otg_event(struct notifier_block *nb,
> + unsigned long event, void *ptr)
> +{
> + struct rockchip_usb2phy_port *rport =
> + container_of(nb, struct rockchip_usb2phy_port, event_nb);
> +
> + schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY);
> +
> + return NOTIFY_DONE;
> +}
> +
> +static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
> + struct rockchip_usb2phy_port *rport,
> + struct device_node *child_np)
> +{
> + int ret;
> +
> + rport->port_id = USB2PHY_PORT_OTG;
> + rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
> + rport->state = OTG_STATE_UNDEFINED;
> +
> + /*
> + * set suspended flag to true, but actually don't
> + * put phy in suspend mode, it aims to enable usb
> + * phy and clock in power_on() called by usb controller
> + * driver during probe.
> + */
> + rport->suspended = true;
> + rport->vbus_attached = false;
> +
> + mutex_init(&rport->mutex);
> +
> + rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
> + if (rport->mode == USB_DR_MODE_HOST) {
> + ret = 0;
> + goto out;
> + }
> +
> + INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
> + INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
> +
> + rport->utmi_avalid =
> + of_property_read_bool(child_np, "rockchip,utmi-avalid");
> +
> + rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
> + if (rport->bvalid_irq < 0) {
> + dev_err(rphy->dev, "no vbus valid irq provided\n");
> + ret = rport->bvalid_irq;
> + goto out;
> + }
> +
> + ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL,
> + rockchip_usb2phy_bvalid_irq,
> + IRQF_ONESHOT,
> + "rockchip_usb2phy_bvalid", rport);
> + if (ret) {
> + dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n");
> + goto out;
> + }
> +
> + if (!IS_ERR(rphy->edev)) {
> + rport->event_nb.notifier_call = rockchip_otg_event;
> +
> + ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST,
> + &rport->event_nb);
> + if (ret)
> + dev_err(rphy->dev, "register USB HOST notifier failed\n");
> + }
> +
> +out:
> + return ret;
> +}
> +
> static int rockchip_usb2phy_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> @@ -553,8 +1045,14 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
>
> rphy->dev = dev;
> phy_cfgs = match->data;
> + rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
> platform_set_drvdata(pdev, rphy);
>
> + ret = rockchip_usb2phy_extcon_register(rphy);
> + if (ret)
> + return ret;
> +
> /* find out a proper config which can be matched with dt. */
> index = 0;
> while (phy_cfgs[index].reg) {
> @@ -591,13 +1089,9 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
> struct rockchip_usb2phy_port *rport = &rphy->ports[index];
> struct phy *phy;
>
> - /*
> - * This driver aim to support both otg-port and host-port,
> - * but unfortunately, the otg part is not ready in current,
> - * so this comments and below codes are interim, which should
> - * be changed after otg-port is supplied soon.
> - */
> - if (of_node_cmp(child_np->name, "host-port"))
> + /* This driver aims to support both otg-port and host-port */
> + if (of_node_cmp(child_np->name, "host-port") &&
> + of_node_cmp(child_np->name, "otg-port"))
> goto next_child;
>
> phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
> @@ -610,9 +1104,18 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
> rport->phy = phy;
> phy_set_drvdata(rport->phy, rport);
>
> - ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np);
> - if (ret)
> - goto put_child;
> + /* initialize otg/host port separately */
> + if (!of_node_cmp(child_np->name, "host-port")) {
> + ret = rockchip_usb2phy_host_port_init(rphy, rport,
> + child_np);
> + if (ret)
> + goto put_child;
> + } else {
> + ret = rockchip_usb2phy_otg_port_init(rphy, rport,
> + child_np);
> + if (ret)
> + goto put_child;
> + }
>
> next_child:
> /* to prevent out of boundary */
> @@ -654,10 +1157,18 @@ static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
>
> static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
> {
> - .reg = 0xe450,
> + .reg = 0xe450,
> .num_ports = 2,
> .clkout_ctl = { 0xe450, 4, 4, 1, 0 },
> .port_cfgs = {
> + [USB2PHY_PORT_OTG] = {
> + .phy_sus = { 0xe454, 1, 0, 2, 1 },
> + .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 },
> + .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 },
> + .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 },
> + .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 },
> + .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 },
> + },
> [USB2PHY_PORT_HOST] = {
> .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 },
> .ls_det_en = { 0xe3c0, 6, 6, 0, 1 },
> @@ -667,12 +1178,32 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
> .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 }
> }
> },
> + .chg_det = {
> + .opmode = { 0xe454, 3, 0, 5, 1 },
> + .cp_det = { 0xe2ac, 2, 2, 0, 1 },
> + .dcp_det = { 0xe2ac, 1, 1, 0, 1 },
> + .dp_det = { 0xe2ac, 0, 0, 0, 1 },
> + .idm_sink_en = { 0xe450, 8, 8, 0, 1 },
> + .idp_sink_en = { 0xe450, 7, 7, 0, 1 },
> + .idp_src_en = { 0xe450, 9, 9, 0, 1 },
> + .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 },
> + .vdm_src_en = { 0xe450, 12, 12, 0, 1 },
> + .vdp_src_en = { 0xe450, 11, 11, 0, 1 },
> + },
> },
> {
> - .reg = 0xe460,
> + .reg = 0xe460,
> .num_ports = 2,
> .clkout_ctl = { 0xe460, 4, 4, 1, 0 },
> .port_cfgs = {
> + [USB2PHY_PORT_OTG] = {
> + .phy_sus = { 0xe464, 1, 0, 2, 1 },
> + .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 },
> + .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 },
> + .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
> + .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 },
> + .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 },
> + },
> [USB2PHY_PORT_HOST] = {
> .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 },
> .ls_det_en = { 0xe3c0, 11, 11, 0, 1 },
>
^ permalink raw reply
* [PATCH -next] phy: meson8b-usb2: fix missing clk_disable_unprepare() on error
From: Kishon Vijay Abraham I @ 2016-11-15 13:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477146822-31327-1-git-send-email-weiyj.lk@gmail.com>
On Saturday 22 October 2016 08:03 PM, Wei Yongjun wrote:
> From: Wei Yongjun <weiyongjun1@huawei.com>
>
> Fix the missing clk_disable_unprepare() before return from
> phy_meson8b_usb2_power_on() in the error handling case.
>
> Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
merged, thanks.
-Kishon
> ---
> drivers/phy/phy-meson8b-usb2.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/phy/phy-meson8b-usb2.c b/drivers/phy/phy-meson8b-usb2.c
> index 73bf632..dca3947 100644
> --- a/drivers/phy/phy-meson8b-usb2.c
> +++ b/drivers/phy/phy-meson8b-usb2.c
> @@ -158,6 +158,7 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
> ret = clk_prepare_enable(priv->clk_usb);
> if (ret) {
> dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
> + clk_disable_unprepare(priv->clk_usb_general);
> return ret;
> }
>
> @@ -190,6 +191,8 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
> if (phy_meson8b_usb2_read(priv, REG_ADP_BC) &
> REG_ADP_BC_ACA_PIN_FLOAT) {
> dev_warn(&phy->dev, "USB ID detect failed!\n");
> + clk_disable_unprepare(priv->clk_usb);
> + clk_disable_unprepare(priv->clk_usb_general);
> return -EINVAL;
> }
> }
>
^ permalink raw reply
* wdt, gpio: move arch_initcall into subsys_initcall ?
From: Guenter Roeck @ 2016-11-15 13:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <e4472c2d-effa-9583-aded-8dcdfdd57ee6@mentor.com>
On 11/15/2016 03:32 AM, Vladimir Zapolskiy wrote:
> On 11/15/2016 01:10 PM, Vladimir Zapolskiy wrote:
>> Hello Heiko,
>>
>> On 11/15/2016 12:20 PM, Heiko Schocher wrote:
>>> Hello,
>>>
>>> commit e188cbf7564f: "gpio: mxc: shift gpio_mxc_init() to subsys_initcall level"
>>> moves the gpio initialization of the mxc gpio driver
>>> from the arch_initcall level into subsys_initcall level.
>>>
>>> This leads now on mxc boards, which use a gpio wdt driver
>>> and the CONFIG_GPIO_WATCHDOG_ARCH_INITCALL option enabled,
>>> to unwanted driver probe deferrals during kernel boot.
>>>
>>> I see this currently on an imx6 based board (which has unfortunately
>>> 3 WDT: imx6 internal (disabled), gpio wdt and da9063 WDT ...).
>>>
>>> Also a side effect from above commit is, that the da9063 WDT driver
>>> is now probed before the gpio WDT driver ... so /dev/watchdog now
>>> does not point to the gpio_wdt, instead it points to the da9063 WDT.
>>>
>>> So there are 2 solutions possible:
>>>
>>> - add a CONFIG_GPIO_MCX_ARCH_INITCALL option
>>> in drivers/gpio/gpio-mxc.c like for the gpio_wdt.c driver?
>>
>> in my opinion this is overly heavy solution and it might be
>> better to avoid it if possible.
>>
>> I would rather prefer to reconsider GPIO_WATCHDOG_ARCH_INITCALL
>> usage in the watchdog driver.
>>
>> Moreover adding this proposed GPIO_MCX_ARCH_INITCALL to call
>> the driver on arch level will result in deferring the GPIO driver.
>>
>>> But how can we guarantee, that first the gpio driver and then
>>> the gpio_wdt driver gets probed?
>>>
>>> - move the arch_initcall in gpio_wdt.c into a subsys_initcall
>>> (Tested this, and the probe dereferral messages are gone ...)
>>>
>>> But this may results in problems on boards, which needs an early
>>> trigger on an gpio wdt ...
>>
>> The level of "earliness" can not be defined in absolute time value
>> in any case, why decreasing the init level of the watchdog driver
>> to subsys level can cause problems? For that there should exist
>> some kind of a dependency on IC or PCB hardware level, can you
>> name it please?
>>
>> Also please note that more than a half of all GPIO drivers settle
>> on subsys or later initcall level, this means that there is
>> an expected GPIO watchdog driver deferral for all of them.
>
> Please find two more late notes though.
>
>> I propose to send two patches for review:
>>
>> 1. remove GPIO_WATCHDOG_ARCH_INITCALL option completely and decouple
>> module_platform_driver() into arch_initcall() and module_exit()
>> unconditionally.
>>
>> 2. change arch_initcall() in the watchdog driver to subsys_initcall().
>> This change removes probe deferrals on boot, when the driver is
>> used with the most of the GPIO controllers.
>
> Alternatively commit 5e53c8ed813d ("watchdog: gpio_wdt: Add option for
> early registration") can be reverted and then module_platform_driver()
> is decoupled into subsys_initcall() and module_exit() as its replacement.
>
Sure, only the reason for that was that there are situations where
subsys_initcall() was too late. Also, when using arch_initcall() only,
we get deferrals again, which is apparently hated by many and a reason
for all those "avoid probe deferrals" patches.
> And also please note that since quite many GPIO controller drivers
> live on initcall levels after subsys_initcall(), the solution won't
> let to avoid watchdog driver deferrals totally, this should be accepted.
>
... except for others it isn't, and we are back to square one.
GPIO_WATCHDOG_ARCH_INITCALL was intended to be only used in situations
where needed. Why is it used here in the first place if that is not
the case ?
Guenter
^ 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