* Re: [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: Peter Griffin, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
kernel-F5mvAk5X5gdBDgjK7y7TUQ, patrice.chotard-qxv4g6HH51o,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1473859677-9231-3-git-send-email-peter.griffin-QSEj5FYQhm4dnm+yROfE0A@public.gmane.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-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Cc: <kishon-l0cyMroinI0@public.gmane.org>
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@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-qxv4g6HH51o@public.gmane.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/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-qxv4g6HH51o@public.gmane.org>");
> -MODULE_DESCRIPTION("STMicroelectronics USB PHY driver for STiH41x series");
> -MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v4 0/2] phy: rockchip-inno-usb2: correct 480MHz clk_ops callbacks and stable time
From: Kishon Vijay Abraham I @ 2016-11-15 13:00 UTC (permalink / raw)
To: William Wu, heiko-4mtYJXux2i+zQB+pC5nmwQ
Cc: huangtao-TNX95d0MmH7DzftRWevZcw,
devicetree-u79uwXL29TY76Z2rM5mHXA, groeck-hpIqsD4AKlfQT0dZR+AlfA,
frank.wang-TNX95d0MmH7DzftRWevZcw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dianders-hpIqsD4AKlfQT0dZR+AlfA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
briannorris-hpIqsD4AKlfQT0dZR+AlfA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1479182047-3399-1-git-send-email-wulf-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
On Tuesday 15 November 2016 09:24 AM, William Wu wrote:
> This series try to correct the 480MHz output clock of USB2 PHY
> clk_ops callback and fix the delay time. It aims to make the
> 480MHz clock gate more sensible and stable.
>
> Tested on rk3366/rk3399 EVB board.
merged to phy -next.
Thanks
Kishon
>
> William Wu (2):
> phy: rockchip-inno-usb2: correct clk_ops callback
> phy: rockchip-inno-usb2: correct 480MHz output clock stable time
>
> drivers/phy/phy-rockchip-inno-usb2.c | 16 ++++++++--------
> 1 file changed, 8 insertions(+), 8 deletions(-)
>
^ permalink raw reply
* Re: [PATCH 2/3] phy: meson8b-usb2: request a shared reset line
From: Kishon Vijay Abraham I @ 2016-11-15 12:54 UTC (permalink / raw)
To: Martin Blumenstingl, p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, khilman-rdvid1DuHRBWk0Htik3J/w,
carlo-KA+7E9HrN00dnm+yROfE0A, will.deacon-5wv7dgnIgG8,
catalin.marinas-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <20161112131305.26088-3-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
On Saturday 12 November 2016 06:43 PM, Martin Blumenstingl wrote:
> Both PHYs are sharing one reset line. With recent improvements to the
> reset framework we can now also use reset_control_reset with shared
> resets.
> This allows us to drop some workarounds where the reset was only
> specified for one PHY but not the other, to make sure that the reset it
> only executed once (as the reset framework was not able to use
> reset_control_reset with shared reset lines).
>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
merged $patch to -next.
Thanks
Kishon
> ---
> drivers/phy/phy-meson8b-usb2.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/drivers/phy/phy-meson8b-usb2.c b/drivers/phy/phy-meson8b-usb2.c
> index 73bf632..f1ee96a 100644
> --- a/drivers/phy/phy-meson8b-usb2.c
> +++ b/drivers/phy/phy-meson8b-usb2.c
> @@ -237,8 +237,7 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev)
> if (IS_ERR(priv->clk_usb))
> return PTR_ERR(priv->clk_usb);
>
> - priv->reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
> - NULL);
> + priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
> if (PTR_ERR(priv->reset) == -EPROBE_DEFER)
> return PTR_ERR(priv->reset);
>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH V8 0/6] thermal: bcm2835: add thermal driver
From: Zhang Rui @ 2016-11-15 12:50 UTC (permalink / raw)
To: Eric Anholt, kernel, Eduardo Valentin, Rob Herring, Pawel Moll,
Mark Rutland, Stephen Warren, Lee Jones, Russell King,
Florian Fainelli, Catalin Marinas, Will Deacon, linux-pm,
devicetree, linux-rpi-kernel, linux-arm-kernel
In-Reply-To: <87oa1mar27.fsf@eliezer.anholt.net>
On Fri, 2016-11-11 at 09:01 -0800, Eric Anholt wrote:
> kernel@martin.sperl.org writes:
>
> >
> > From: Martin Sperl <kernel@martin.sperl.org>
> Since Eduardo seems to be AFK, I've pulled this series to my -next
> branches.
I will take the thermal soc patches this time if we still don't have
response from Eduardo by the end of this week.
For this patches set, will you queue them up or do you prefer to go via
thermal tree?
thanks,
rui
^ permalink raw reply
* Re: [PATCH 1/3] reset: allow using reset_control_reset with shared reset
From: Philipp Zabel @ 2016-11-15 12:48 UTC (permalink / raw)
To: Martin Blumenstingl
Cc: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
will.deacon-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <20161112131305.26088-2-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
Hi Martin,
Am Samstag, den 12.11.2016, 14:13 +0100 schrieb Martin Blumenstingl:
> Some SoCs (for example Amlogic GXBB) implement a reset controller which
> only supports a reset pulse (triggered via reset_control_reset). At the
> same time multiple devices (in case of the Amlogic GXBB SoC both USB
> PHYs) are sharing the same reset line.
>
> This patch allows using reset_control_reset also for shared resets.
> There are limitations though:
> reset_control_reset can only be used if reset_control_assert was not
> used yet.
> reset_control_assert can only be used if reset_control_reset was not
> used yet.
> For shared resets the reset is only triggered once for the lifetime of
> the reset_control instance (the reset can be triggered again if all
> consumers of that specific reset_control are gone, as the reset
> framework will free the reset_control instance in that case).
>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
Looks good to me, I've applied it to the reset/next branch.
thanks
Philipp
> ---
> drivers/reset/core.c | 43 +++++++++++++++++++++++++++++++++++++------
> 1 file changed, 37 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/reset/core.c b/drivers/reset/core.c
> index b8ae1db..10368ed 100644
> --- a/drivers/reset/core.c
> +++ b/drivers/reset/core.c
> @@ -32,6 +32,9 @@ static LIST_HEAD(reset_controller_list);
> * @refcnt: Number of gets of this reset_control
> * @shared: Is this a shared (1), or an exclusive (0) reset_control?
> * @deassert_cnt: Number of times this reset line has been deasserted
> + * @triggered_count: Number of times this reset line has been reset. Currently
> + * only used for shared resets, which means that the value
> + * will be either 0 or 1.
> */
> struct reset_control {
> struct reset_controller_dev *rcdev;
> @@ -40,6 +43,7 @@ struct reset_control {
> unsigned int refcnt;
> int shared;
> atomic_t deassert_count;
> + atomic_t triggered_count;
> };
>
> /**
> @@ -134,18 +138,35 @@ EXPORT_SYMBOL_GPL(devm_reset_controller_register);
> * reset_control_reset - reset the controlled device
> * @rstc: reset controller
> *
> - * Calling this on a shared reset controller is an error.
> + * On a shared reset line the actual reset pulse is only triggered once for the
> + * lifetime of the reset_control instance: for all but the first caller this is
> + * a no-op.
> + * Consumers must not use reset_control_(de)assert on shared reset lines when
> + * reset_control_reset has been used.
> */
> int reset_control_reset(struct reset_control *rstc)
> {
> - if (WARN_ON(IS_ERR_OR_NULL(rstc)) ||
> - WARN_ON(rstc->shared))
> + int ret;
> +
> + if (WARN_ON(IS_ERR_OR_NULL(rstc)))
> return -EINVAL;
>
> - if (rstc->rcdev->ops->reset)
> - return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
> + if (!rstc->rcdev->ops->reset)
> + return -ENOTSUPP;
>
> - return -ENOTSUPP;
> + if (rstc->shared) {
> + if (WARN_ON(atomic_read(&rstc->deassert_count) != 0))
> + return -EINVAL;
> +
> + if (atomic_inc_return(&rstc->triggered_count) != 1)
> + return 0;
> + }
> +
> + ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
> + if (rstc->shared && !ret)
> + atomic_dec(&rstc->triggered_count);
> +
> + return ret;
> }
> EXPORT_SYMBOL_GPL(reset_control_reset);
>
> @@ -159,6 +180,8 @@ EXPORT_SYMBOL_GPL(reset_control_reset);
> *
> * For shared reset controls a driver cannot expect the hw's registers and
> * internal state to be reset, but must be prepared for this to happen.
> + * Consumers must not use reset_control_reset on shared reset lines when
> + * reset_control_(de)assert has been used.
> */
> int reset_control_assert(struct reset_control *rstc)
> {
> @@ -169,6 +192,9 @@ int reset_control_assert(struct reset_control *rstc)
> return -ENOTSUPP;
>
> if (rstc->shared) {
> + if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
> + return -EINVAL;
> +
> if (WARN_ON(atomic_read(&rstc->deassert_count) == 0))
> return -EINVAL;
>
> @@ -185,6 +211,8 @@ EXPORT_SYMBOL_GPL(reset_control_assert);
> * @rstc: reset controller
> *
> * After calling this function, the reset is guaranteed to be deasserted.
> + * Consumers must not use reset_control_reset on shared reset lines when
> + * reset_control_(de)assert has been used.
> */
> int reset_control_deassert(struct reset_control *rstc)
> {
> @@ -195,6 +223,9 @@ int reset_control_deassert(struct reset_control *rstc)
> return -ENOTSUPP;
>
> if (rstc->shared) {
> + if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
> + return -EINVAL;
> +
> if (atomic_inc_return(&rstc->deassert_count) != 1)
> return 0;
> }
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [RESEND PATCH v4] pwm: add pwm driver for HiSilicon BVT SOCs
From: Jian Yuan @ 2016-11-15 12:41 UTC (permalink / raw)
To: thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
Cc: linux-pwm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
xuejiancheng-C8/M+/jPZTeaMJb+Lgu22Q,
kevin.lixu-C8/M+/jPZTeaMJb+Lgu22Q,
jalen.hsu-C8/M+/jPZTeaMJb+Lgu22Q, yuanjian
From: yuanjian <yuanjian12-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
Add PWM driver for the PWM controller found on HiSilicon BVT SOCs, like Hi3519V100, Hi3516CV300, etc.
The PWM controller is primarily in charge of controlling P-Iris lens.
Reviewed-by: Jiancheng Xue <xuejiancheng-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
Signed-off-by: Jian Yuan <yuanjian12-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
---
Change Log:
v4:
Add #pwm-cells in the bindings document.
v3:
fixed issues pointed by thierry.
Add PWM compatible string for Hi3519V100.
Implement .apply() function which support atomic, instead of .enable()/.disable()/.config().
v2:
The number of PWMs is change to be probeable based on the compatible string.
.../devicetree/bindings/pwm/pwm-hibvt.txt | 23 ++
drivers/pwm/Kconfig | 9 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-hibvt.c | 270 +++++++++++++++++++++
4 files changed, 303 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/pwm-hibvt.txt
create mode 100644 drivers/pwm/pwm-hibvt.c
diff --git a/Documentation/devicetree/bindings/pwm/pwm-hibvt.txt b/Documentation/devicetree/bindings/pwm/pwm-hibvt.txt
new file mode 100644
index 0000000..609284f
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-hibvt.txt
@@ -0,0 +1,23 @@
+Hisilicon PWM controller
+
+Required properties:
+-compatible: should contain one SoC specific compatible string and one generic compatible
+string "hisilicon, hibvt-pwm". The SoC specific strings supported including:
+ "hisilicon,hi3516cv300-pwm"
+ "hisilicon,hi3519v100-pwm"
+- reg: physical base address and length of the controller's registers.
+- clocks: phandle and clock specifier of the PWM reference clock.
+- resets: phandle and reset specifier for the PWM controller reset.
+- #pwm-cells: Should be 2. See pwm.txt in this directory for a description of
+ the cells format.
+
+Example:
+ pwm: pwm@12130000 {
+
+ compatible = "hisilicon,hi3516cv300-pwm", "hisilicon,hibvt-pwm";
+ compatible = "hisilicon,hi3519v100-pwm", "hisilicon,hibvt-pwm";
+ reg = <0x12130000 0x10000>;
+ clocks = <&crg_ctrl HI3516CV300_PWM_CLK>;
+ resets = <&crg_ctrl 0x38 0>;
+ #pwm-cells = <2>;
+ };
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index c182efc..b2d7408 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -158,6 +158,15 @@ config PWM_FSL_FTM
To compile this driver as a module, choose M here: the module
will be called pwm-fsl-ftm.
+config PWM_HIBVT
+ tristate "HiSilicon BVT PWM support"
+ depends on ARCH_HISI || COMPILE_TEST
+ help
+ Generic PWM framework driver for HiSilicon BVT SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-hibvt.
+
config PWM_IMG
tristate "Imagination Technologies PWM driver"
depends on HAS_IOMEM
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index dd35bc1..37ec39e 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
obj-$(CONFIG_PWM_CRC) += pwm-crc.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
+obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
obj-$(CONFIG_PWM_IMG) += pwm-img.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c
new file mode 100644
index 0000000..1c14c21
--- /dev/null
+++ b/drivers/pwm/pwm-hibvt.c
@@ -0,0 +1,270 @@
+/*
+ * PWM Controller Driver for HiSilicon BVT SoCs
+ *
+ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/reset.h>
+
+#define PWM_CFG0_ADDR(x) (((x) * 0x20) + 0x0)
+#define PWM_CFG1_ADDR(x) (((x) * 0x20) + 0x4)
+#define PWM_CFG2_ADDR(x) (((x) * 0x20) + 0x8)
+#define PWM_CTRL_ADDR(x) (((x) * 0x20) + 0xC)
+
+#define PWM_ENABLE_SHIFT 0
+#define PWM_ENABLE_MASK BIT(0)
+
+#define PWM_POLARITY_SHIFT 1
+#define PWM_POLARITY_MASK BIT(1)
+
+#define PWM_KEEP_SHIFT 2
+#define PWM_KEEP_MASK BIT(2)
+
+#define PWM_PERIOD_MASK GENMASK(31, 0)
+#define PWM_DUTY_MASK GENMASK(31, 0)
+
+struct hibvt_pwm_chip {
+ struct pwm_chip chip;
+ struct clk *clk;
+ void __iomem *base;
+ struct reset_control *rstc;
+};
+
+struct hibvt_pwm_soc {
+ u32 num_pwms;
+};
+
+static const struct hibvt_pwm_soc pwm_soc[2] = {
+ { .num_pwms = 4 },
+ { .num_pwms = 8 },
+};
+
+static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct hibvt_pwm_chip, chip);
+}
+
+static void hibvt_pwm_set_bits(void __iomem *base, u32 offset,
+ u32 mask, u32 data)
+{
+ void __iomem *address = base + offset;
+ u32 value;
+
+ value = readl(address);
+ value &= ~mask;
+ value |= (data & mask);
+ writel(value, address);
+}
+
+static void hibvt_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm),
+ PWM_ENABLE_MASK, 0x1);
+}
+
+static void hibvt_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm),
+ PWM_ENABLE_MASK, 0x0);
+}
+
+static void hibvt_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_cycle_ns, int period_ns)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+ u32 freq, period, duty;
+
+ freq = div_u64(clk_get_rate(hi_pwm_chip->clk), 1000000);
+
+ period = div_u64(freq * period_ns, 1000);
+ duty = div_u64(period * duty_cycle_ns, period_ns);
+
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CFG0_ADDR(pwm->hwpwm),
+ PWM_PERIOD_MASK, period);
+
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CFG1_ADDR(pwm->hwpwm),
+ PWM_DUTY_MASK, duty);
+}
+
+static void hibvt_pwm_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm),
+ PWM_POLARITY_MASK, (0x1 << PWM_POLARITY_SHIFT));
+ else
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm),
+ PWM_POLARITY_MASK, (0x0 << PWM_POLARITY_SHIFT));
+}
+
+static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+ void __iomem *base;
+ u32 freq, value;
+
+ freq = div_u64(clk_get_rate(hi_pwm_chip->clk), 1000000);
+ base = hi_pwm_chip->base;
+
+ value = readl(base + PWM_CFG0_ADDR(pwm->hwpwm));
+ state->period = div_u64(value * 1000, freq);
+
+ value = readl(base + PWM_CFG1_ADDR(pwm->hwpwm));
+ state->duty_cycle = div_u64(value * 1000, freq);
+
+ value = readl(base + PWM_CTRL_ADDR(pwm->hwpwm));
+ state->enabled = (PWM_ENABLE_MASK & value);
+}
+
+static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ if (state->polarity != pwm->state.polarity)
+ hibvt_pwm_set_polarity(chip, pwm, state->polarity);
+
+ if (state->period != pwm->state.period ||
+ state->duty_cycle != pwm->state.duty_cycle)
+ hibvt_pwm_config(chip, pwm, state->duty_cycle, state->period);
+
+ if (state->enabled != pwm->state.enabled) {
+ if (state->enabled)
+ hibvt_pwm_enable(chip, pwm);
+ else
+ hibvt_pwm_disable(chip, pwm);
+ }
+
+ return 0;
+}
+
+static struct pwm_ops hibvt_pwm_ops = {
+ .get_state = hibvt_pwm_get_state,
+ .apply = hibvt_pwm_apply,
+
+ .owner = THIS_MODULE,
+};
+
+static int hibvt_pwm_probe(struct platform_device *pdev)
+{
+ const struct hibvt_pwm_soc *soc =
+ of_device_get_match_data(&pdev->dev);
+ struct hibvt_pwm_chip *pwm_chip;
+ struct resource *res;
+ int ret;
+ int i;
+
+ pwm_chip = devm_kzalloc(&pdev->dev, sizeof(*pwm_chip), GFP_KERNEL);
+ if (pwm_chip == NULL)
+ return -ENOMEM;
+
+ pwm_chip->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pwm_chip->clk)) {
+ dev_err(&pdev->dev, "getting clock failed with %ld\n",
+ PTR_ERR(pwm_chip->clk));
+ return PTR_ERR(pwm_chip->clk);
+ }
+
+ pwm_chip->chip.ops = &hibvt_pwm_ops;
+ pwm_chip->chip.dev = &pdev->dev;
+ pwm_chip->chip.base = -1;
+ pwm_chip->chip.npwm = soc->num_pwms;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pwm_chip->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pwm_chip->base))
+ return PTR_ERR(pwm_chip->base);
+
+ ret = clk_prepare_enable(pwm_chip->clk);
+ if (ret < 0)
+ return ret;
+
+ pwm_chip->rstc = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(pwm_chip->rstc)) {
+ clk_disable_unprepare(pwm_chip->clk);
+ return PTR_ERR(pwm_chip->rstc);
+ }
+
+ reset_control_assert(pwm_chip->rstc);
+ msleep(30);
+ reset_control_deassert(pwm_chip->rstc);
+
+ ret = pwmchip_add(&pwm_chip->chip);
+ if (ret < 0) {
+ clk_disable_unprepare(pwm_chip->clk);
+ return ret;
+ }
+
+ for (i = 0; i < pwm_chip->chip.npwm; i++) {
+ hibvt_pwm_set_bits(pwm_chip->base, PWM_CTRL_ADDR(i),
+ PWM_KEEP_MASK, (0x1 << PWM_KEEP_SHIFT));
+ }
+
+ platform_set_drvdata(pdev, pwm_chip);
+
+ return 0;
+}
+
+static int hibvt_pwm_remove(struct platform_device *pdev)
+{
+ struct hibvt_pwm_chip *pwm_chip;
+
+ pwm_chip = platform_get_drvdata(pdev);
+
+ reset_control_assert(pwm_chip->rstc);
+ msleep(30);
+ reset_control_deassert(pwm_chip->rstc);
+
+ clk_disable_unprepare(pwm_chip->clk);
+
+ return pwmchip_remove(&pwm_chip->chip);
+}
+
+static const struct of_device_id hibvt_pwm_of_match[] = {
+ { .compatible = "hisilicon,hibvt-pwm" },
+ { .compatible = "hisilicon,hi3516cv300-pwm", .data = &pwm_soc[0] },
+ { .compatible = "hisilicon,hi3519v100-pwm", .data = &pwm_soc[1] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match);
+
+static struct platform_driver hibvt_pwm_driver = {
+ .driver = {
+ .name = "hibvt-pwm",
+ .of_match_table = hibvt_pwm_of_match,
+ },
+ .probe = hibvt_pwm_probe,
+ .remove = hibvt_pwm_remove,
+};
+module_platform_driver(hibvt_pwm_driver);
+
+MODULE_AUTHOR("Jian Yuan");
+MODULE_DESCRIPTION("HiSilicon BVT SoCs PWM driver");
+MODULE_LICENSE("GPL");
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* RE: [upstream-release] [PATCH 1/2] drivers: usb: phy: Add qoriq usb 3.0 phy driver support
From: Sriram Dash @ 2016-11-15 12:39 UTC (permalink / raw)
To: Scott Wood, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: mark.rutland-5wv7dgnIgG8@public.gmane.org,
felipe.balbi-VuQAYsv1563Yd54FQh9/CA@public.gmane.org,
mathias.nyman-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org,
catalin.marinas-5wv7dgnIgG8@public.gmane.org,
will.deacon-5wv7dgnIgG8@public.gmane.org,
kishon-l0cyMroinI0@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz@public.gmane.org,
Suresh Gupta,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org,
pku.leo-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
In-Reply-To: <DB5PR0401MB1928CA058CC01243457F124791BC0-GXldUsIPo7Z/SeJcUcAJq43W/0Ik+aLCnBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
>From: Scott Wood
>On 11/13/2016 11:27 PM, Sriram Dash wrote:
>> diff --git a/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>> b/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>> new file mode 100644
>> index 0000000..d934c80
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/phy-qoriq-usb3.txt
>> @@ -0,0 +1,36 @@
>> +Driver for Freescale USB 3.0 PHY
>> +
>> +Required properties:
>> +
>> +- compatible : fsl,qoriq-usb3-phy
>
Hi Scott,
>This is a very vague compatible. Are there versioning registers within this register
>block?
>
There are versioning registers for the phy (1.0 and 1.1). But the current erratum
A008751 does not require the mentioning of the version numbers. Was planning
to take care of the versioning when there is code diversity on the basis of the
version number.
Shall I include the versioning in the documentation now for 1.0 and 1.1?
>> +/* Parameter control */
>> +#define USB3PRM1CR 0x000
>> +#define USB3PRM1CR_VAL 0x27672b2a
>> +
>> +/*
>> + * struct qoriq_usb3_phy - driver data for USB 3.0 PHY
>> + * @dev: pointer to device instance of this platform device
>> + * @param_ctrl: usb3 phy parameter control register base
>> + * @phy_base: usb3 phy register memory base
>> + * @has_erratum_flag: keeps track of erratum applicable on device */
>> +struct qoriq_usb3_phy {
>> + struct device *dev;
>> + void __iomem *param_ctrl;
>> + void __iomem *phy_base;
>> + u32 has_erratum_flag;
>> +};
>> +
>> +static inline u32 qoriq_usb3_phy_readl(void __iomem *addr, u32
>> +offset) {
>> + return __raw_readl(addr + offset);
>> +}
>> +
>> +static inline void qoriq_usb3_phy_writel(void __iomem *addr, u32 offset,
>> + u32 data)
>> +{
>> + __raw_writel(data, addr + offset);
>> +}
>
>Why raw? Besides missing barriers, this will cause the accesses to be native-endian
>which is not correct.
>
The only reason for __raw_writel is to make the code faster. However, shall I use
writel(with both barriers and byte swap) instead and then make appropriate changes
in the value 32'h27672B2A?
>> +/*
>> + * Erratum A008751
>> + * SCFG USB3PRM1CR has incorrect default value
>> + * SCFG USB3PRM1CR reset value should be 32'h27672B2A instead of
>32'h25E72B2A.
>
>When documenting C code, can you stick with C-style numeric constants?
>
>For that matter, just put the constant in the code instead of hiding it in an overly-
>generically-named USB3PRM1CR_VAL and then you won't need to redundantly
>state the value in a comment. Normally putting magic numbers in symbolic
>constants is a good thing, but in this case it's not actually describing anything and
>the number is of no meaning outside of this one erratum workaround (it might even
>be a different value if another chip has a similar erratum).
>
The POR value of the USB3PRM1CR should be 32'h27672B2A. So, I had named it as
such. Thanks for the info. Will remove the USB3PRM1CR_VAL and during writel,
will use 0x27672b2a directly. I will take care the next time.
>> + */
>> +static void erratum_a008751(struct qoriq_usb3_phy *phy) {
>> + qoriq_usb3_phy_writel(phy->param_ctrl, USB3PRM1CR,
>> + USB3PRM1CR_VAL);
>> +}
>> +
>> +/*
>> + * qoriq_usb3_phy_erratum - List of phy erratum
>> + * @qoriq_phy_erratum - erratum application
>> + * @compat - comapt string for erratum */
>> +
>> +struct qoriq_usb3_phy_erratum {
>> + void (*qoriq_phy_erratum)(struct qoriq_usb3_phy *phy);
>> + char *compat;
>> +};
>> +
>> +/* Erratum list */
>> +struct qoriq_usb3_phy_erratum phy_erratum_tbl[] = {
>> + {&erratum_a008751, "fsl,usb-erratum-a008751"},
>> + /* Add init time erratum here */
>> +};
>
>This needs to be static.
>
Ok. Will take care next time.
>Unnecessary & on the function pointer.
>
Ok. Will change in next version.
>> +static int qoriq_usb3_phy_init(struct phy *x) {
>> + struct qoriq_usb3_phy *phy = phy_get_drvdata(x);
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(phy_erratum_tbl); i++)
>> + if (phy->has_erratum_flag & 1 << i)
>> + phy_erratum_tbl[i].qoriq_phy_erratum(phy);
>> + return 0;
>> +}
>> +
>> +static const struct phy_ops ops = {
>> + .init = qoriq_usb3_phy_init,
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +static int qoriq_usb3_phy_probe(struct platform_device *pdev) {
>> + struct qoriq_usb3_phy *phy;
>> + struct phy *generic_phy;
>> + struct phy_provider *phy_provider;
>> + const struct of_device_id *of_id;
>> + struct device *dev = &pdev->dev;
>> + struct resource *res;
>> + int i, ret;
>> +
>> + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
>> + if (!phy)
>> + return -ENOMEM;
>> + phy->dev = dev;
>> +
>> + of_id = of_match_device(dev->driver->of_match_table, dev);
>> + if (!of_id) {
>> + dev_err(dev, "failed to get device match\n");
>> + ret = -EINVAL;
>> + goto err_out;
>> + }
>> +
>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>"param_ctrl");
>> + if (!res) {
>> + dev_err(dev, "failed to get param_ctrl memory\n");
>> + ret = -ENOENT;
>> + goto err_out;
>> + }
>> +
>> + phy->param_ctrl = devm_ioremap_resource(dev, res);
>> + if (!phy->param_ctrl) {
>> + dev_err(dev, "failed to remap param_ctrl memory\n");
>> + ret = -ENOMEM;
>> + goto err_out;
>> + }
>> +
>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>"phy_base");
>> + if (!res) {
>> + dev_err(dev, "failed to get phy_base memory\n");
>> + ret = -ENOENT;
>> + goto err_out;
>> + }
>> +
>> + phy->phy_base = devm_ioremap_resource(dev, res);
>> + if (!phy->phy_base) {
>> + dev_err(dev, "failed to remap phy_base memory\n");
>> + ret = -ENOMEM;
>> + goto err_out;
>> + }
>> +
>> + phy->has_erratum_flag = 0;
>> + for (i = 0; i < ARRAY_SIZE(phy_erratum_tbl); i++)
>> + phy->has_erratum_flag |= device_property_read_bool(dev,
>> + phy_erratum_tbl[i].compat) << i;
>
>I don't see the erratum property in either the binding or the device tree. Also, a
>property name is not a "compat".
>
Ok. Will rename "char *compat" into "char *prop".
>Is there a reason why this flag and array mechanism is needed, rather than just
>checking the erratum properties from the init function -- or, if you have a good
>reason to not want to do device tree accesses from init, just using a bool per
>erratum? How many errata are you expecting?
>
The erratum prop will be specified via the dt and parsed in the init. The idea
behind the table is that it will accommodate more errata and every time we
are adding a new erratum, and the code changes would be minimal(with this
approach, we don't have to add new variable in qoriq_usb3_phy struct
corresponding to every new erratum being added).
In my knowledge, there are more than 5 errata in pipeline, and all of them
have to be applied in the init time. So, this table will make the code more
readable and less cumbersome.
However, in future, if any other erratum comes up, and it has to be applied
at any point other than during init, then the variable has to be added in
qoriq_usb3_phy struct and the property has to be read separately.
>-Scott
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH V8 2/6] thermal: bcm2835: add thermal driver for bcm2835 soc
From: Zhang Rui @ 2016-11-15 12:29 UTC (permalink / raw)
To: kernel-TqfNSX0MhmxHKSADF0wUEw, Eduardo Valentin, Rob Herring,
Pawel Moll, Mark Rutland, Stephen Warren, Lee Jones, Eric Anholt,
Russell King, Florian Fainelli, Catalin Marinas, Will Deacon,
linux-pm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1478081906-12009-3-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
On Wed, 2016-11-02 at 10:18 +0000, kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org wrote:
> From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
>
> Add basic thermal driver for bcm2835 SOC.
>
> This driver currently relies on the firmware setting up the
> tsense HW block and does not set it up itself.
>
> Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
> Acked-by: Eric Anholt <eric-WhKQ6XTQaPysTnJN9+BGXg@public.gmane.org>
> Acked-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
>
Acked-by: Zhang Rui <rui.zhang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
thanks,
rui
> ChangeLog:
> V1 -> V2: added specific settings depending on compatiblity
> added trip point based on register
> setting up ctrl-register if HW is not enabled by firmware
> as per recommendation of Eric (untested)
> check that clock frequency is in range
> (1.9 - 5MHz - as per comment in clk-bcm2835.c)
> V2 -> V4: moved back to thermal (not using bcm sub-directory)
> set polling interval to 1second (was 0ms, so
> interrupt driven)
> V5 -> V6: added correct depends in KConfig
> removed defined default for RESET_DELAY
> removed obvious comments
> clarify HW setup comments if not set up by FW already
> move clk_prepare_enable to an earlier stage and add error
> handling
> clarify warning when TS-clock runs out of recommended range
> clk_disable_unprepare added in bcm2835_thermal_remove
> added comment on recommended temperature ranges for SOC
> V6 -> V7: removed depends on ARCH_BCM2836 || ARCH_BCM2837 in Kconfig
> V7 -> V8: rebased
> ---
> drivers/thermal/Kconfig | 8 +
> drivers/thermal/Makefile | 1 +
> drivers/thermal/bcm2835_thermal.c | 340
> ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 349 insertions(+)
> create mode 100644 drivers/thermal/bcm2835_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index a13541b..ab946ff 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -434,4 +434,12 @@ depends on (ARCH_QCOM && OF) || COMPILE_TEST
> source "drivers/thermal/qcom/Kconfig"
> endmenu
>
> +config BCM2835_THERMAL
> + tristate "Thermal sensors on bcm2835 SoC"
> + depends on ARCH_BCM2835 || COMPILE_TEST
> + depends on HAS_IOMEM
> + depends on OF
> + help
> + Support for thermal sensors on Broadcom bcm2835 SoCs.
> +
> endif
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index c92eb22..a10ebe0 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -55,3 +55,4 @@ obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/
> obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
> obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
> obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
> +obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o
> diff --git a/drivers/thermal/bcm2835_thermal.c
> b/drivers/thermal/bcm2835_thermal.c
> new file mode 100644
> index 0000000..3468c7b
> --- /dev/null
> +++ b/drivers/thermal/bcm2835_thermal.c
> @@ -0,0 +1,340 @@
> +/*
> + * Driver for Broadcom BCM2835 soc temperature sensor
> + *
> + * Copyright (C) 2016 Martin Sperl
> + *
> + * This program is free software; you can redistribute it and/or
> modify
> + * it under the terms of the GNU General Public License as published
> by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/thermal.h>
> +
> +#define BCM2835_TS_TSENSCTL 0x00
> +#define BCM2835_TS_TSENSSTAT 0x04
> +
> +#define BCM2835_TS_TSENSCTL_PRWDW BIT(0)
> +#define BCM2835_TS_TSENSCTL_RSTB BIT(1)
> +#define BCM2835_TS_TSENSCTL_CTRL_BITS 3
> +#define BCM2835_TS_TSENSCTL_CTRL_SHIFT 2
> +#define BCM2835_TS_TSENSCTL_CTRL_MASK \
> + GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS + \
> + BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \
> + BCM2835_TS_TSENSCTL_CTRL_SHIFT)
> +#define BCM2835_TS_TSENSCTL_CTRL_DEFAULT 1
> +#define BCM2835_TS_TSENSCTL_EN_INT BIT(5)
> +#define BCM2835_TS_TSENSCTL_DIRECT BIT(6)
> +#define BCM2835_TS_TSENSCTL_CLR_INT BIT(7)
> +#define BCM2835_TS_TSENSCTL_THOLD_SHIFT 8
> +#define BCM2835_TS_TSENSCTL_THOLD_BITS 10
> +#define BCM2835_TS_TSENSCTL_THOLD_MASK \
> + GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS + \
> + BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \
> + BCM2835_TS_TSENSCTL_THOLD_SHIFT)
> +#define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT 18
> +#define BCM2835_TS_TSENSCTL_RSTDELAY_BITS 8
> +#define BCM2835_TS_TSENSCTL_REGULEN BIT(26)
> +
> +#define BCM2835_TS_TSENSSTAT_DATA_BITS 10
> +#define BCM2835_TS_TSENSSTAT_DATA_SHIFT 0
> +#define BCM2835_TS_TSENSSTAT_DATA_MASK \
> + GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS + \
> + BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \
> + BCM2835_TS_TSENSSTAT_DATA_SHIFT)
> +#define BCM2835_TS_TSENSSTAT_VALID BIT(10)
> +#define BCM2835_TS_TSENSSTAT_INTERRUPT BIT(11)
> +
> +struct bcm2835_thermal_info {
> + int offset;
> + int slope;
> + int trip_temp;
> +};
> +
> +struct bcm2835_thermal_data {
> + const struct bcm2835_thermal_info *info;
> + void __iomem *regs;
> + struct clk *clk;
> + struct dentry *debugfsdir;
> +};
> +
> +static int bcm2835_thermal_adc2temp(
> + const struct bcm2835_thermal_info *info, u32 adc)
> +{
> + return info->offset + (adc * info->slope);
> +}
> +
> +static int bcm2835_thermal_temp2adc(
> + const struct bcm2835_thermal_info *info, int temp)
> +{
> + temp -= info->offset;
> + temp /= info->slope;
> +
> + if (temp < 0)
> + temp = 0;
> + if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS))
> + temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1;
> +
> + return temp;
> +}
> +
> +static int bcm2835_thermal_get_trip_type(
> + struct thermal_zone_device *tz, int trip,
> + enum thermal_trip_type *type)
> +{
> + *type = THERMAL_TRIP_CRITICAL;
> + return 0;
> +}
> +
> +static int bcm2835_thermal_get_trip_temp(
> + struct thermal_zone_device *tz, int trip, int *temp)
> +{
> + struct bcm2835_thermal_data *data = tz->devdata;
> + u32 val = readl(data->regs + BCM2835_TS_TSENSCTL);
> +
> + /* get the THOLD bits */
> + val &= BCM2835_TS_TSENSCTL_THOLD_MASK;
> + val >>= BCM2835_TS_TSENSCTL_THOLD_SHIFT;
> +
> + /* if it is zero then use the info value */
> + if (val)
> + *temp = bcm2835_thermal_adc2temp(data->info, val);
> + else
> + *temp = data->info->trip_temp;
> +
> + return 0;
> +}
> +
> +static int bcm2835_thermal_get_temp(struct thermal_zone_device *tz,
> + int *temp)
> +{
> + struct bcm2835_thermal_data *data = tz->devdata;
> + u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT);
> +
> + if (!(val & BCM2835_TS_TSENSSTAT_VALID))
> + return -EIO;
> +
> + val &= BCM2835_TS_TSENSSTAT_DATA_MASK;
> +
> + *temp = bcm2835_thermal_adc2temp(data->info, val);
> +
> + return 0;
> +}
> +
> +static const struct debugfs_reg32 bcm2835_thermal_regs[] = {
> + {
> + .name = "ctl",
> + .offset = 0
> + },
> + {
> + .name = "stat",
> + .offset = 4
> + }
> +};
> +
> +static void bcm2835_thermal_debugfs(struct platform_device *pdev)
> +{
> + struct thermal_zone_device *tz = platform_get_drvdata(pdev);
> + struct bcm2835_thermal_data *data = tz->devdata;
> + struct debugfs_regset32 *regset;
> +
> + data->debugfsdir = debugfs_create_dir("bcm2835_thermal",
> NULL);
> + if (!data->debugfsdir)
> + return;
> +
> + regset = devm_kzalloc(&pdev->dev, sizeof(*regset),
> GFP_KERNEL);
> + if (!regset)
> + return;
> +
> + regset->regs = bcm2835_thermal_regs;
> + regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs);
> + regset->base = data->regs;
> +
> + debugfs_create_regset32("regset", S_IRUGO,
> + data->debugfsdir, regset);
> +}
> +
> +static struct thermal_zone_device_ops bcm2835_thermal_ops = {
> + .get_temp = bcm2835_thermal_get_temp,
> + .get_trip_temp = bcm2835_thermal_get_trip_temp,
> + .get_trip_type = bcm2835_thermal_get_trip_type,
> +};
> +
> +static const struct of_device_id bcm2835_thermal_of_match_table[];
> +static int bcm2835_thermal_probe(struct platform_device *pdev)
> +{
> + const struct of_device_id *match;
> + struct thermal_zone_device *tz;
> + struct bcm2835_thermal_data *data;
> + struct resource *res;
> + int err;
> + u32 val;
> + unsigned long rate;
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + match = of_match_device(bcm2835_thermal_of_match_table,
> + &pdev->dev);
> + if (!match)
> + return -EINVAL;
> + data->info = match->data;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + data->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(data->regs)) {
> + err = PTR_ERR(data->regs);
> + dev_err(&pdev->dev, "Could not get registers: %d\n",
> err);
> + return err;
> + }
> +
> + data->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(data->clk)) {
> + err = PTR_ERR(data->clk);
> + if (err != -EPROBE_DEFER)
> + dev_err(&pdev->dev, "Could not get clk:
> %d\n", err);
> + return err;
> + }
> +
> + err = clk_prepare_enable(data->clk);
> + if (err)
> + return err;
> +
> + rate = clk_get_rate(data->clk);
> + if ((rate < 1920000) || (rate > 5000000))
> + dev_warn(&pdev->dev,
> + "Clock %pCn running at %pCr Hz is outside
> of the recommended range: 1.92 to 5MHz\n",
> + data->clk, data->clk);
> +
> + /*
> + * right now the FW does set up the HW-block, so we are not
> + * touching the configuration registers.
> + * But if the HW is not enabled, then set it up
> + * using "sane" values used by the firmware right now.
> + */
> + val = readl(data->regs + BCM2835_TS_TSENSCTL);
> + if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {
> + /* the basic required flags */
> + val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT <<
> + BCM2835_TS_TSENSCTL_CTRL_SHIFT) |
> + BCM2835_TS_TSENSCTL_REGULEN;
> +
> + /*
> + * reset delay using the current firmware value of
> 14
> + * - units of time are unknown.
> + */
> + val |= (14 << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);
> +
> + /* trip_adc value from info */
> + val |= bcm2835_thermal_temp2adc(data->info,
> + data->info-
> >trip_temp) <<
> + BCM2835_TS_TSENSCTL_THOLD_SHIFT;
> +
> + /* write the value back to the register as 2 steps
> */
> + writel(val, data->regs + BCM2835_TS_TSENSCTL);
> + val |= BCM2835_TS_TSENSCTL_RSTB;
> + writel(val, data->regs + BCM2835_TS_TSENSCTL);
> + }
> +
> + /* register thermal zone with 1 trip point an 1s polling */
> + tz = thermal_zone_device_register("bcm2835_thermal",
> + 1, 0, data,
> + &bcm2835_thermal_ops,
> + NULL,
> + 0, 1000);
> + if (IS_ERR(tz)) {
> + clk_disable_unprepare(data->clk);
> + err = PTR_ERR(tz);
> + dev_err(&pdev->dev,
> + "Failed to register the thermal device:
> %d\n",
> + err);
> + return err;
> + }
> +
> + platform_set_drvdata(pdev, tz);
> +
> + bcm2835_thermal_debugfs(pdev);
> +
> + return 0;
> +}
> +
> +static int bcm2835_thermal_remove(struct platform_device *pdev)
> +{
> + struct thermal_zone_device *tz = platform_get_drvdata(pdev);
> + struct bcm2835_thermal_data *data = tz->devdata;
> +
> + debugfs_remove_recursive(data->debugfsdir);
> + thermal_zone_device_unregister(tz);
> + clk_disable_unprepare(data->clk);
> +
> + return 0;
> +}
> +
> +/*
> + * Note: as per Raspberry Foundation FAQ
> + * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTempe
> rature)
> + * the recommended temperature range for the SOC -40C to +85C
> + * so the trip limit is set to 80C.
> + * this applies to all the BCM283X SOC
> + */
> +
> +static const struct of_device_id bcm2835_thermal_of_match_table[] =
> {
> + {
> + .compatible = "brcm,bcm2835-thermal",
> + .data = &(struct bcm2835_thermal_info) {
> + .offset = 407000,
> + .slope = -538,
> + .trip_temp = 80000
> + }
> + },
> + {
> + .compatible = "brcm,bcm2836-thermal",
> + .data = &(struct bcm2835_thermal_info) {
> + .offset = 407000,
> + .slope = -538,
> + .trip_temp = 80000
> + }
> + },
> + {
> + .compatible = "brcm,bcm2837-thermal",
> + .data = &(struct bcm2835_thermal_info) {
> + /* the bcm2837 needs adjustment of +5C */
> + .offset = 407000 + 5000,
> + .slope = -538,
> + .trip_temp = 80000
> + }
> + },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table);
> +
> +static struct platform_driver bcm2835_thermal_driver = {
> + .probe = bcm2835_thermal_probe,
> + .remove = bcm2835_thermal_remove,
> + .driver = {
> + .name = "bcm2835_thermal",
> + .of_match_table = bcm2835_thermal_of_match_table,
> + },
> +};
> +module_platform_driver(bcm2835_thermal_driver);
> +
> +MODULE_AUTHOR("Martin Sperl");
> +MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
> +MODULE_LICENSE("GPL");
> --
> 2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v4 2/3] PCI: qcom: add support to msm8996 PCIE controller
From: Stanimir Varbanov @ 2016-11-15 12:24 UTC (permalink / raw)
To: Srinivas Kandagatla, linux-pci, bhelgaas
Cc: robh+dt, linux-arm-msm, devicetree
In-Reply-To: <1479122155-13393-3-git-send-email-srinivas.kandagatla@linaro.org>
Hi Srini,
On 11/14/2016 01:15 PM, Srinivas Kandagatla wrote:
> This patch adds support to msm8996/apq8096 pcie, MSM8996 supports
> Gen 1/2, One lane, 3 pcie root-complex with support to MSI and
> legacy interrupts and it conforms to PCI Express Base 2.1 specification.
>
> This patch adds post_init callback to qcom_pcie_ops, as this is pcie
> pipe clocks are only setup after the phy is powered on.
> It also adds ltssm_enable callback as it is very much different to other
> supported SOCs in the driver.
>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
With below comments addressed:
Acked-by: Stanimir Varbanov <svarbanov@mm-sol.com>
> ---
> .../devicetree/bindings/pci/qcom,pcie.txt | 67 +++++++-
> drivers/pci/host/pcie-qcom.c | 177 ++++++++++++++++++++-
> 2 files changed, 238 insertions(+), 6 deletions(-)
>
<snip>
> diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/host/pcie-qcom.c
> index 3593640..03ba6b1 100644
> --- a/drivers/pci/host/pcie-qcom.c
> +++ b/drivers/pci/host/pcie-qcom.c
> @@ -36,11 +36,19 @@
>
> #include "pcie-designware.h"
>
> +#define PCIE20_PARF_DBI_BASE_ADDR 0x168
This is already defined few rows below, please drop it.
> +
> +#define PCIE20_PARF_SYS_CTRL 0x00
> #define PCIE20_PARF_PHY_CTRL 0x40
> #define PCIE20_PARF_PHY_REFCLK 0x4C
> #define PCIE20_PARF_DBI_BASE_ADDR 0x168
> #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
> +#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174
> #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
> +#define MSM8996_PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x1A8
I don't like MSM8996_ prefix. Could you invent a macro which depending
on controller selects proper offset?
> +#define PCIE20_PARF_LTSSM 0x1B0
> +#define PCIE20_PARF_SID_OFFSET 0x234
> +#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C
>
> #define PCIE20_ELBI_SYS_CTRL 0x04
> #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0)
> @@ -72,9 +80,18 @@ struct qcom_pcie_resources_v1 {
> struct regulator *vdda;
> };
>
> +struct qcom_pcie_resources_v2 {
> + struct clk *aux_clk;
> + struct clk *master_clk;
> + struct clk *slave_clk;
> + struct clk *cfg_clk;
> + struct clk *pipe_clk;
> +};
<snip>
regards,
Stan
^ permalink raw reply
* [PATCH v4 6/6] arm64: arch_timer: acpi: add hisi timer errata data
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1479212167-5812-1-git-send-email-dingtianhong@huawei.com>
From: Hanjun Guo <hanjun.guo@linaro.org>
Add hisi timer specific erratum fixes.
v3: add hisilicon erratum 161601 for ACPI mode.
v4: update some data structures.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
drivers/clocksource/arm_arch_timer.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index d4f4a0d..649f7fe 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1078,10 +1078,28 @@ struct gtdt_arch_timer_fixup {
void *context;
};
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+static void __init erratum_workaround_enable(void *context)
+{
+ u64 erratum = (u64) context;
+
+ if (erratum & HISILICON_161601) {
+ timer_unstable_counter_workaround = &arch_timer_hisi_161601;
+ static_branch_enable(&arch_timer_read_ool_enabled);
+ pr_info("Enabling workaround for HISILICON ERRATUM 161601\n");
+ }
+}
+#endif
+
/* note: this needs to be updated according to the doc of OEM ID
* and TABLE ID for different board.
*/
struct gtdt_arch_timer_fixup arch_timer_quirks[] __initdata = {
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+ {"HISI", "hip05", 0, &erratum_workaround_enable, (void *) HISILICON_161601},
+ {"HISI", "hip06", 0, &erratum_workaround_enable, (void *) HISILICON_161601},
+ {"HISI", "hip07", 0, &erratum_workaround_enable, (void *) HISILICON_161601},
+#endif
};
void __init arch_timer_acpi_quirks_handler(char *oem_id,
--
1.9.0
^ permalink raw reply related
* [PATCH v4 5/6] arm64: arch_timer: apci: Introduce a generic aquirk framework for erratum
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1479212167-5812-1-git-send-email-dingtianhong@huawei.com>
From: Hanjun Guo <hanjun.guo@linaro.org>
Introduce a general quirk framework for each timer erratum in ACPI,
which use the oem information in GTDT table for platform specific erratums.
The struct gtdt_arch_timer_fixup is introduced to record the oem
information to match the quirk and handle the erratum.
v3: Introduce a generic aquick framework for erratum in ACPI mode.
v4: rename the quirk handler parameter to make it more generic, and
avoid break loop when handling the quirk becasue it need to
support multi quirks handler.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
drivers/clocksource/arm_arch_timer.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 3d59af1..d4f4a0d 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1068,6 +1068,39 @@ CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
arch_timer_mem_init);
#ifdef CONFIG_ACPI
+struct gtdt_arch_timer_fixup {
+ char oem_id[ACPI_OEM_ID_SIZE];
+ char oem_table_id[ACPI_OEM_TABLE_ID_SIZE];
+ u32 oem_revision;
+
+ /* quirk handler for arch timer erratum */
+ void (*handler)(void *context);
+ void *context;
+};
+
+/* note: this needs to be updated according to the doc of OEM ID
+ * and TABLE ID for different board.
+ */
+struct gtdt_arch_timer_fixup arch_timer_quirks[] __initdata = {
+};
+
+void __init arch_timer_acpi_quirks_handler(char *oem_id,
+ char *oem_table_id,
+ u32 oem_revision)
+{
+ struct gtdt_arch_timer_fixup *quirks = arch_timer_quirks;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arch_timer_quirks); i++, quirks++) {
+ if (!memcmp(quirks->oem_id, oem_id, ACPI_OEM_ID_SIZE) &&
+ !memcmp(quirks->oem_table_id, oem_table_id, ACPI_OEM_TABLE_ID_SIZE) &&
+ quirks->oem_revision == oem_revision) {
+ if (quirks->handler && quirks->context)
+ quirks->handler(quirks->context);
+ }
+ }
+}
+
static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
{
int trigger, polarity;
@@ -1094,6 +1127,9 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
return -EINVAL;
}
+ arch_timer_acpi_quirks_handler(table->oem_id, table->oem_table_id,
+ table->oem_revision);
+
gtdt = container_of(table, struct acpi_table_gtdt, header);
arch_timers_present |= ARCH_CP15_TIMER;
--
1.9.0
^ permalink raw reply related
* [PATCH v4 4/6] arm64: arch timer: Add timer erratum property for Hip05-d02 and Hip06-d03
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
marc.zyngier-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
oss-fOR+EgIDQEHk1uMJSBkQmQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
shawnguo-DgEjT+Ai2ygdnm+yROfE0A, stuart.yoder-3arQi8VN3Tc,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linuxarm-hv44wF8Li93QT0dZR+AlfA,
hanjun.guo-QSEj5FYQhm4dnm+yROfE0A
Cc: Ding Tianhong
In-Reply-To: <1479212167-5812-1-git-send-email-dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
Enable workaround for hisilicon erratum 161601 on Hip05-d02 and Hip06-d03 board.
Signed-off-by: Ding Tianhong <dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
---
arch/arm64/boot/dts/hisilicon/hip05.dtsi | 1 +
arch/arm64/boot/dts/hisilicon/hip06.dtsi | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hip05.dtsi b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
index bf322ed..f815d94 100644
--- a/arch/arm64/boot/dts/hisilicon/hip05.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
@@ -281,6 +281,7 @@
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ hisilicon,erratum-161601;
};
pmu {
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index 5927bc4..d63990b 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -260,6 +260,7 @@
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ hisilicon,erratum-161601;
};
pmu {
--
1.9.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 3/6] arm64: arch_timer: Work around Erratum Hisilicon-161601
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1479212167-5812-1-git-send-email-dingtianhong@huawei.com>
Erratum Hisilicon-161601 says that the ARM generic timer counter "has the
potential to contain an erroneous value when the timer value changes".
Accesses to TVAL (both read and write) are also affected due to the implicit counter
read. Accesses to CVAL are not affected.
The workaround is to reread the system count registers until the value of the second
read is larger than the first one by less than 32, the system counter can be guaranteed
not to return wrong value twice by back-to-back read and the error value is always larger
than the correct one by 32. Writes to TVAL are replaced with an equivalent write to CVAL.
The workaround is enabled if the hisilicon,erratum-161601 property is found in
the timer node in the device tree. This can be overridden with the
clocksource.arm_arch_timer.hisilicon-161601 boot parameter, which allows KVM
users to enable the workaround until a mechanism is implemented to
automatically communicate this information.
Fix some description for fsl erratum a008585.
v2: Significant rework based on feedback, including seperate the fsl erratum a008585
to another patch, update the erratum name and remove unwanted code.
v3: Significant rework based on feedback, including fix some alignment problem, make the
#define __hisi_161601_read_reg to be private to the .c file instead of being globally
visible, add more accurate annotation and modify a bit of logical format to enable
arch_timer_read_ool_enabled, remove the kernel commandline parameter
clocksource.arm_arch_timer.hisilicon-161601.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
Documentation/arm64/silicon-errata.txt | 1 +
arch/arm64/include/asm/arch_timer.h | 2 +-
drivers/clocksource/Kconfig | 9 +++++
drivers/clocksource/arm_arch_timer.c | 67 +++++++++++++++++++++++++++++++---
4 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index 405da11..1c1a95f 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -63,3 +63,4 @@ stable kernels.
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
| | | | |
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
+| Hisilicon | Hip0{5,6,7} | #161601 | HISILICON_ERRATUM_161601|
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index f882c7c..ebf4cde 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -29,7 +29,7 @@
#include <clocksource/arm_arch_timer.h>
-#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
extern struct static_key_false arch_timer_read_ool_enabled;
#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 8a753fd..6c03ed0 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -315,6 +315,15 @@ config FSL_ERRATUM_A008585
value"). The workaround will only be active if the
fsl,erratum-a008585 property is found in the timer node.
+config HISILICON_ERRATUM_161601
+ bool "Workaround for Hisilicon Erratum 161601"
+ default y
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a workaround for Hisilicon Erratum
+ 161601. The workaround will be active if the hisilicon,erratum-161601
+ property is found in the timer node.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select CLKSRC_OF if OF
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 696386f..3d59af1 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -94,15 +94,18 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
* Architected system timer support.
*/
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if CONFIG_FSL_ERRATUM_A008585 || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
#define FSL_A008585 0x0001
+#define HISILICON_161601 0x0002
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+#endif
+#ifdef CONFIG_FSL_ERRATUM_A008585
/*
* The number of retries is an arbitrary value well beyond the highest number
* of iterations the loop has been observed to take.
@@ -144,6 +147,51 @@ static struct arch_timer_erratum_workaround arch_timer_fsl_a008585 = {
};
#endif /* CONFIG_FSL_ERRATUM_A008585 */
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+/*
+ * Theoretically the erratum should not occur more than twice in succession,
+ * so set the retry count to 2 is sufficient here.
+ * Verify whether the value of the second read is larger than the first by
+ * less than 32 is the only way to confirm the value is correct, so clear the
+ * lower 5 bits to check whether the difference is greater than 32 or not.
+ */
+#define __hisi_161601_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 2; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely((_new - _old) >> 5) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 hisi_161601_read_cntp_tval_el0(void)
+{
+ return __hisi_161601_read_reg(cntp_tval_el0);
+}
+
+static u32 hisi_161601_read_cntv_tval_el0(void)
+{
+ return __hisi_161601_read_reg(cntv_tval_el0);
+}
+
+static u64 hisi_161601_read_cntvct_el0(void)
+{
+ return __hisi_161601_read_reg(cntvct_el0);
+}
+
+static struct arch_timer_erratum_workaround arch_timer_hisi_161601 = {
+ .erratum = HISILICON_161601,
+ .read_cntp_tval_el0 = hisi_161601_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = hisi_161601_read_cntv_tval_el0,
+ .read_cntvct_el0 = hisi_161601_read_cntvct_el0,
+};
+#endif /* CONFIG_HISILICON_ERRATUM_161601 */
+
static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
struct clock_event_device *clk)
@@ -293,7 +341,7 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
@@ -357,7 +405,7 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
@@ -617,7 +665,7 @@ static void __init arch_counter_register(unsigned type)
clocksource_counter.archdata.vdso_direct = true;
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
/*
* Don't use the vdso fastpath if errata require using
* the out-of-line counter accessor.
@@ -906,10 +954,19 @@ static int __init arch_timer_of_init(struct device_node *np)
#ifdef CONFIG_FSL_ERRATUM_A008585
if (!timer_unstable_counter_workaround && of_property_read_bool(np, "fsl,erratum-a008585"))
timer_unstable_counter_workaround = &arch_timer_fsl_a008585;
+#endif
+
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+ if (!timer_unstable_counter_workaround && of_property_read_bool(np, "hisilicon,erratum-161601"))
+ timer_unstable_counter_workaround = &arch_timer_hisi_161601;
+#endif
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
if (timer_unstable_counter_workaround) {
static_branch_enable(&arch_timer_read_ool_enabled);
- pr_info("Enabling workaround for FSL erratum A-008585\n");
+ pr_info("Enabling workaround for %s\n",
+ timer_unstable_counter_workaround->erratum == FSL_A008585 ?
+ "FSL ERRATUM A-008585" : "HISILICON ERRATUM 161601");
}
#endif
--
1.9.0
^ permalink raw reply related
* [PATCH v4 2/6] arm64: arch_timer: Introduce a generic erratum handing mechanism for fsl-a008585
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
In-Reply-To: <1479212167-5812-1-git-send-email-dingtianhong@huawei.com>
The workaround for hisilicon,161601 will check the return value of the system counter
by different way, in order to distinguish with the fsl-a008585 workaround, introduce
a new generic erratum handing mechanism for fsl-a008585 and rename some functions.
v2: Introducing a new generic erratum handling mechanism for fsl erratum a008585.
v3: Introducing the erratum_workaround_set_sne generic function for fsl erratum a008585
and make the #define __fsl_a008585_read_reg to be private to the .c file instead of
being globally visible. After discussion with Marc and Will, a consensus decision was
made to remove the commandline parameter for enabling fsl,erratum-a008585 erratum,
and make some generic name more specific, export timer_unstable_counter_workaround
for module access.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
Documentation/kernel-parameters.txt | 9 -----
arch/arm64/include/asm/arch_timer.h | 36 ++++++-----------
drivers/clocksource/arm_arch_timer.c | 78 +++++++++++++++++++++---------------
3 files changed, 58 insertions(+), 65 deletions(-)
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 6fa1d8a..738de4e 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -698,15 +698,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
loops can be debugged more effectively on production
systems.
- clocksource.arm_arch_timer.fsl-a008585=
- [ARM64]
- Format: <bool>
- Enable/disable the workaround of Freescale/NXP
- erratum A-008585. This can be useful for KVM
- guests, if the guest device tree doesn't show the
- erratum. If unspecified, the workaround is
- enabled based on the device tree.
-
clearcpuid=BITNUM [X86]
Disable CPUID feature X for the kernel. See
arch/x86/include/asm/cpufeatures.h for the valid bit
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index eaa5bbe..f882c7c 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -31,39 +31,27 @@
#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
extern struct static_key_false arch_timer_read_ool_enabled;
-#define needs_fsl_a008585_workaround() \
+#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
#else
-#define needs_fsl_a008585_workaround() false
+#define needs_unstable_timer_counter_workaround() false
#endif
-u32 __fsl_a008585_read_cntp_tval_el0(void);
-u32 __fsl_a008585_read_cntv_tval_el0(void);
-u64 __fsl_a008585_read_cntvct_el0(void);
-/*
- * The number of retries is an arbitrary value well beyond the highest number
- * of iterations the loop has been observed to take.
- */
-#define __fsl_a008585_read_reg(reg) ({ \
- u64 _old, _new; \
- int _retries = 200; \
- \
- do { \
- _old = read_sysreg(reg); \
- _new = read_sysreg(reg); \
- _retries--; \
- } while (unlikely(_old != _new) && _retries); \
- \
- WARN_ON_ONCE(!_retries); \
- _new; \
-})
+struct arch_timer_erratum_workaround {
+ int erratum; /* Indicate the Erratum ID */
+ u32 (*read_cntp_tval_el0)(void);
+ u32 (*read_cntv_tval_el0)(void);
+ u64 (*read_cntvct_el0)(void);
+};
+
+extern struct arch_timer_erratum_workaround *timer_unstable_counter_workaround;
#define arch_timer_reg_read_stable(reg) \
({ \
u64 _val; \
- if (needs_fsl_a008585_workaround()) \
- _val = __fsl_a008585_read_##reg(); \
+ if (needs_unstable_timer_counter_workaround()) \
+ _val = timer_unstable_counter_workaround->read_##reg();\
else \
_val = read_sysreg(reg); \
_val; \
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 73c487d..696386f 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -95,40 +95,53 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
*/
#ifdef CONFIG_FSL_ERRATUM_A008585
-DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
-EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
-
-static int fsl_a008585_enable = -1;
+struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
+EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
-static int __init early_fsl_a008585_cfg(char *buf)
-{
- int ret;
- bool val;
+#define FSL_A008585 0x0001
- ret = strtobool(buf, &val);
- if (ret)
- return ret;
-
- fsl_a008585_enable = val;
- return 0;
-}
-early_param("clocksource.arm_arch_timer.fsl-a008585", early_fsl_a008585_cfg);
+DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
+EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
-u32 __fsl_a008585_read_cntp_tval_el0(void)
+/*
+ * The number of retries is an arbitrary value well beyond the highest number
+ * of iterations the loop has been observed to take.
+ */
+#define __fsl_a008585_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 200; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely(_old != _new) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 fsl_a008585_read_cntp_tval_el0(void)
{
return __fsl_a008585_read_reg(cntp_tval_el0);
}
-u32 __fsl_a008585_read_cntv_tval_el0(void)
+static u32 fsl_a008585_read_cntv_tval_el0(void)
{
return __fsl_a008585_read_reg(cntv_tval_el0);
}
-u64 __fsl_a008585_read_cntvct_el0(void)
+static u64 fsl_a008585_read_cntvct_el0(void)
{
return __fsl_a008585_read_reg(cntvct_el0);
}
-EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
+
+static struct arch_timer_erratum_workaround arch_timer_fsl_a008585 = {
+ .erratum = FSL_A008585,
+ .read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0,
+ .read_cntvct_el0 = fsl_a008585_read_cntvct_el0,
+};
#endif /* CONFIG_FSL_ERRATUM_A008585 */
static __always_inline
@@ -281,7 +294,7 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
}
#ifdef CONFIG_FSL_ERRATUM_A008585
-static __always_inline void fsl_a008585_set_next_event(const int access,
+static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
unsigned long ctrl;
@@ -299,17 +312,17 @@ static __always_inline void fsl_a008585_set_next_event(const int access,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-static int fsl_a008585_set_next_event_virt(unsigned long evt,
+static int erratum_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_VIRT_ACCESS, evt, clk);
return 0;
}
-static int fsl_a008585_set_next_event_phys(unsigned long evt,
+static int erratum_set_next_event_phys(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_PHYS_ACCESS, evt, clk);
return 0;
}
#endif /* CONFIG_FSL_ERRATUM_A008585 */
@@ -342,16 +355,16 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
return 0;
}
-static void fsl_a008585_set_sne(struct clock_event_device *clk)
+static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
#ifdef CONFIG_FSL_ERRATUM_A008585
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
if (arch_timer_uses_ppi == VIRT_PPI)
- clk->set_next_event = fsl_a008585_set_next_event_virt;
+ clk->set_next_event = erratum_set_next_event_virt;
else
- clk->set_next_event = fsl_a008585_set_next_event_phys;
+ clk->set_next_event = erratum_set_next_event_phys;
#endif
}
@@ -384,7 +397,7 @@ static void __arch_timer_setup(unsigned type,
BUG();
}
- fsl_a008585_set_sne(clk);
+ erratum_workaround_set_sne(clk);
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
@@ -891,9 +904,10 @@ static int __init arch_timer_of_init(struct device_node *np)
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
#ifdef CONFIG_FSL_ERRATUM_A008585
- if (fsl_a008585_enable < 0)
- fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
- if (fsl_a008585_enable) {
+ if (!timer_unstable_counter_workaround && of_property_read_bool(np, "fsl,erratum-a008585"))
+ timer_unstable_counter_workaround = &arch_timer_fsl_a008585;
+
+ if (timer_unstable_counter_workaround) {
static_branch_enable(&arch_timer_read_ool_enabled);
pr_info("Enabling workaround for FSL erratum A-008585\n");
}
--
1.9.0
^ permalink raw reply related
* [PATCH v4 1/6] arm64: arch_timer: Add device tree binding for hisilicon-161601 erratum
From: Ding Tianhong @ 2016-11-15 12:16 UTC (permalink / raw)
To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
hanjun.guo
Cc: Ding Tianhong
This erratum describes a bug in logic outside the core, so MIDR can't be
used to identify its presence, and reading an SoC-specific revision
register from common arch timer code would be awkward. So, describe it
in the device tree.
v2: Use the new erratum name and update the description.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/arm/arch_timer.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index ef5fbe9..c27b2c4 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -31,6 +31,14 @@ to deliver its interrupts via SPIs.
This also affects writes to the tval register, due to the implicit
counter read.
+- hisilicon,erratum-161601 : A boolean property. Indicates the presence of
+ erratum 161601, which says that reading the counter is unreliable unless
+ reading twice on the register and the value of the second read is larger
+ than the first by less than 32. If the verification is unsuccessful, then
+ discard the value of this read and repeat this procedure until the verification
+ is successful. This also affects writes to the tval register, due to the
+ implicit counter read.
+
** Optional properties:
- arm,cpu-registers-not-fw-configured : Firmware does not initialize
--
1.9.0
^ permalink raw reply related
* Re: [PATCH v4 2/2] Add support for OV5647 sensor
From: Pavel Machek @ 2016-11-15 12:10 UTC (permalink / raw)
To: Ramiro Oliveira
Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-media-u79uwXL29TY76Z2rM5mHXA,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
davem-fT/PcQaiUtIeIZ0/mPfg9Q,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
linux-0h96xk9xTtrk1uMJSBkQmQ, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
dheitmueller-eb9eJ82Ua7k9XoPSrs7Ehg,
slongerbeam-Re5JQEeQqe8AvxtiuMwx3w, lars-Qo5EllUWu/uELgA04lAiVw,
robert.jarzmik-GANU6spQydw, pali.rohar-Re5JQEeQqe8AvxtiuMwx3w,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA, mark.rutland-5wv7dgnIgG8,
CARLOS.PALMINHA-HKixBCOQz3hWk0Htik3J/w
In-Reply-To: <36447f1f102f648057eb9038a693941794a6c344.1479129004.git.roliveir-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 3423 bytes --]
Hi!
> Add support for OV5647 sensor.
>
> +static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
> +{
> + int ret;
> + unsigned char data[3] = { reg >> 8, reg & 0xff, val};
> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> + ret = i2c_master_send(client, data, 3);
> + if (ret != 3) {
> + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
> + __func__, reg);
> + return ret < 0 ? ret : -EIO;
> + }
> + return 0;
> +}
Sorry, this is wrong. It should something <0 any time error is detected.
> +static int ov5647_write_array(struct v4l2_subdev *sd,
> + struct regval_list *regs, int array_size)
> +{
> + int i = 0;
> + int ret = 0;
> +
> + if (!regs)
> + return -EINVAL;
> +
> + while (i < array_size) {
> + ret = ov5647_write(sd, regs->addr, regs->data);
> + if (ret < 0)
> + return ret;
> + i++;
> + regs++;
> + }
> + return 0;
> +}
For example this expects <0 on error.
> +static int set_sw_standby(struct v4l2_subdev *sd, bool standby)
> +{
> + int ret;
> + unsigned char rdval;
> +
> + ret = ov5647_read(sd, 0x0100, &rdval);
> + if (ret != 0)
> + return ret;
> +
> + if (standby)
> + ret = ov5647_write(sd, 0x0100, rdval&0xfe);
> + else
> + ret = ov5647_write(sd, 0x0100, rdval|0x01);
> +
> + return ret;
if (standby)
rdval &= 0xfe;
else
rdval |= 0x01;
ret = ov5647_write(sd, 0x0100, rdval);
?
> +/**
> + * @short Store information about the video data format.
> + */
> +static struct sensor_format_struct {
> + __u8 *desc;
> + u32 mbus_code;
u8 is suitable here.
> + ov5647_read(sd, 0x0100, &resetval);
> + if (!resetval&0x01) {
add ()s here.
> +static int sensor_power(struct v4l2_subdev *sd, int on)
> +{
> + int ret;
> + struct ov5647 *ov5647 = to_state(sd);
> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> + ret = 0;
> + mutex_lock(&ov5647->lock);
> +
> + if (on) {
> + dev_dbg(&client->dev, "OV5647 power on!\n");
> +
> + ret = ov5647_write_array(sd, sensor_oe_enable_regs,
> + ARRAY_SIZE(sensor_oe_enable_regs));
> +
> + ret = __sensor_init(sd);
> +
> + if (ret < 0)
> + dev_err(&client->dev,
> + "Camera not available! Check Power!\n");
> + } else {
> + dev_dbg(&client->dev, "OV5647 power off!\n");
> +
> + dev_dbg(&client->dev, "disable oe\n");
> + ret = ov5647_write_array(sd, sensor_oe_disable_regs,
> + ARRAY_SIZE(sensor_oe_disable_regs));
> +
> + if (ret < 0)
> + dev_dbg(&client->dev, "disable oe failed!\n");
> +
> + ret = set_sw_standby(sd, true);
> +
> + if (ret < 0)
> + dev_dbg(&client->dev, "soft stby failed!\n");
dev_err for errors? Little less "!"s in the output?
> +static int sensor_get_register(struct v4l2_subdev *sd,
> + struct v4l2_dbg_register *reg)
> +{
> + unsigned char val = 0;
> + int ret;
> +
> + ret = ov5647_read(sd, reg->reg & 0xff, &val);
> + reg->val = val;
> + reg->size = 1;
> + return ret;
> +}
Filling reg->* when read failed is strange.
> +static int sensor_set_register(struct v4l2_subdev *sd,
> + const struct v4l2_dbg_register *reg)
> +{
> + ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff);
> + return 0;
> +}
error handling?
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply
* [PATCH v3 2/2] ARM: dts: apq8064: add support to pm8821
From: Srinivas Kandagatla @ 2016-11-15 12:01 UTC (permalink / raw)
To: Lee Jones
Cc: bjorn.andersson, Rob Herring, Andy Gross, devicetree,
linux-kernel, linux-arm-msm, linux-soc, linux-arm-kernel,
Srinivas Kandagatla
In-Reply-To: <1479211312-1431-1-git-send-email-srinivas.kandagatla@linaro.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
arch/arm/boot/dts/qcom-apq8064.dtsi | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index 268bd47..f725e31 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -627,6 +627,33 @@
clock-names = "core";
};
+ ssbi@c00000 {
+ compatible = "qcom,ssbi";
+ reg = <0x00c00000 0x1000>;
+ qcom,controller-type = "pmic-arbiter";
+
+ pm8821: pmic@1 {
+ compatible = "qcom,pm8821";
+ interrupt-parent = <&tlmm_pinmux>;
+ interrupts = <76 IRQ_TYPE_LEVEL_LOW>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pm8821_mpps: mpps@50 {
+ compatible = "qcom,pm8821-mpp", "qcom,ssbi-mpp";
+ reg = <0x50>;
+ interrupts = <24 IRQ_TYPE_NONE>,
+ <25 IRQ_TYPE_NONE>,
+ <26 IRQ_TYPE_NONE>,
+ <27 IRQ_TYPE_NONE>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+ };
+
qcom,ssbi@500000 {
compatible = "qcom,ssbi";
reg = <0x00500000 0x1000>;
--
2.10.1
^ permalink raw reply related
* [PATCH v3 1/2] mfd: pm8xxx: add support to pm8821
From: Srinivas Kandagatla @ 2016-11-15 12:01 UTC (permalink / raw)
To: Lee Jones
Cc: bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A, Rob Herring, Andy Gross,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-soc-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Srinivas Kandagatla
This patch adds support to PM8821 PMIC and interrupt support.
PM8821 is companion device that supplements primary PMIC PM8921 IC.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes since v2:
- removed few empty lines spotted by Bjorn
- rephrased some debug prints suggested by Bjorn
.../devicetree/bindings/mfd/qcom-pm8xxx.txt | 1 +
drivers/mfd/qcom-pm8xxx.c | 231 ++++++++++++++++++++-
2 files changed, 222 insertions(+), 10 deletions(-)
diff --git a/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt b/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
index 37a088f..9e5eba4 100644
--- a/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
+++ b/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt
@@ -10,6 +10,7 @@ voltages and other various functionality to Qualcomm SoCs.
Value type: <string>
Definition: must be one of:
"qcom,pm8058"
+ "qcom,pm8821"
"qcom,pm8921"
- #address-cells:
diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c
index 7f9620e..f08758f 100644
--- a/drivers/mfd/qcom-pm8xxx.c
+++ b/drivers/mfd/qcom-pm8xxx.c
@@ -39,6 +39,20 @@
#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
+#define PM8821_SSBI_REG_ADDR_IRQ_BASE 0x100
+#define PM8821_SSBI_REG_ADDR_IRQ_MASTER0 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0x30)
+#define PM8821_SSBI_REG_ADDR_IRQ_MASTER1 (PM8821_SSBI_REG_ADDR_IRQ_BASE + 0xb0)
+#define PM8821_SSBI_REG(m, b, offset) \
+ ((m == 0) ? \
+ (PM8821_SSBI_REG_ADDR_IRQ_MASTER0 + b + offset) : \
+ (PM8821_SSBI_REG_ADDR_IRQ_MASTER1 + b + offset))
+#define PM8821_SSBI_ADDR_IRQ_ROOT(m, b) PM8821_SSBI_REG(m, b, 0x0)
+#define PM8821_SSBI_ADDR_IRQ_CLEAR(m, b) PM8821_SSBI_REG(m, b, 0x01)
+#define PM8821_SSBI_ADDR_IRQ_MASK(m, b) PM8821_SSBI_REG(m, b, 0x08)
+#define PM8821_SSBI_ADDR_IRQ_RT_STATUS(m, b) PM8821_SSBI_REG(m, b, 0x0f)
+
+#define PM8821_BLOCKS_PER_MASTER 7
+
#define PM_IRQF_LVL_SEL 0x01 /* level select */
#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
@@ -54,6 +68,7 @@
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
#define PM8XXX_NR_IRQS 256
+#define PM8821_NR_IRQS 112
struct pm_irq_chip {
struct regmap *regmap;
@@ -65,6 +80,12 @@ struct pm_irq_chip {
u8 config[0];
};
+struct pm_irq_data {
+ int num_irqs;
+ const struct irq_domain_ops *irq_domain_ops;
+ void (*irq_handler)(struct irq_desc *desc);
+};
+
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
unsigned int *ip)
{
@@ -182,6 +203,78 @@ static void pm8xxx_irq_handler(struct irq_desc *desc)
chained_irq_exit(irq_chip, desc);
}
+static void pm8821_irq_block_handler(struct pm_irq_chip *chip,
+ int master, int block)
+{
+ int pmirq, irq, i, ret;
+ unsigned int bits;
+
+ ret = regmap_read(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_ROOT(master, block), &bits);
+ if (ret) {
+ pr_err("Reading block %d failed ret=%d", block, ret);
+ return;
+ }
+
+ /* Convert block offset to global block number */
+ block += (master * PM8821_BLOCKS_PER_MASTER) - 1;
+
+ /* Check IRQ bits */
+ for (i = 0; i < 8; i++) {
+ if (bits & BIT(i)) {
+ pmirq = block * 8 + i;
+ irq = irq_find_mapping(chip->irqdomain, pmirq);
+ generic_handle_irq(irq);
+ }
+ }
+}
+
+static inline void pm8821_irq_master_handler(struct pm_irq_chip *chip,
+ int master, u8 master_val)
+{
+ int block;
+
+ for (block = 1; block < 8; block++)
+ if (master_val & BIT(block))
+ pm8821_irq_block_handler(chip, master, block);
+}
+
+static void pm8821_irq_handler(struct irq_desc *desc)
+{
+ struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ unsigned int master;
+ int ret;
+
+ chained_irq_enter(irq_chip, desc);
+ ret = regmap_read(chip->regmap,
+ PM8821_SSBI_REG_ADDR_IRQ_MASTER0, &master);
+ if (ret) {
+ pr_err("Failed to read master 0 ret=%d\n", ret);
+ goto done;
+ }
+
+ /* bits 1 through 7 marks the first 7 blocks in master 0 */
+ if (master & GENMASK(7, 1))
+ pm8821_irq_master_handler(chip, 0, master);
+
+ /* bit 0 marks if master 1 contains any bits */
+ if (!(master & BIT(0)))
+ goto done;
+
+ ret = regmap_read(chip->regmap,
+ PM8821_SSBI_REG_ADDR_IRQ_MASTER1, &master);
+ if (ret) {
+ pr_err("Failed to read master 1 ret=%d\n", ret);
+ goto done;
+ }
+
+ pm8821_irq_master_handler(chip, 1, master);
+
+done:
+ chained_irq_exit(irq_chip, desc);
+}
+
static void pm8xxx_irq_mask_ack(struct irq_data *d)
{
struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
@@ -299,6 +392,104 @@ static const struct irq_domain_ops pm8xxx_irq_domain_ops = {
.map = pm8xxx_irq_domain_map,
};
+static void pm8821_irq_mask_ack(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ u8 block, master;
+ int irq_bit, rc;
+
+ block = pmirq / 8;
+ master = block / PM8821_BLOCKS_PER_MASTER;
+ irq_bit = pmirq % 8;
+ block %= PM8821_BLOCKS_PER_MASTER;
+
+ rc = regmap_update_bits(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_MASK(master, block),
+ BIT(irq_bit), BIT(irq_bit));
+ if (rc) {
+ pr_err("Failed to mask IRQ:%d rc=%d\n", pmirq, rc);
+ return;
+ }
+
+ rc = regmap_update_bits(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_CLEAR(master, block),
+ BIT(irq_bit), BIT(irq_bit));
+ if (rc)
+ pr_err("Failed to CLEAR IRQ:%d rc=%d\n", pmirq, rc);
+}
+
+static void pm8821_irq_unmask(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ int irq_bit, rc;
+ u8 block, master;
+
+ block = pmirq / 8;
+ master = block / PM8821_BLOCKS_PER_MASTER;
+ irq_bit = pmirq % 8;
+ block %= PM8821_BLOCKS_PER_MASTER;
+
+ rc = regmap_update_bits(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_MASK(master, block),
+ BIT(irq_bit), ~BIT(irq_bit));
+ if (rc)
+ pr_err("Failed to read/write unmask IRQ:%d rc=%d\n", pmirq, rc);
+
+}
+
+static int pm8821_irq_get_irqchip_state(struct irq_data *d,
+ enum irqchip_irq_state which,
+ bool *state)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ int rc, pmirq = irqd_to_hwirq(d);
+ u8 block, irq_bit, master;
+ unsigned int bits;
+
+ block = pmirq / 8;
+ master = block / PM8821_BLOCKS_PER_MASTER;
+ irq_bit = pmirq % 8;
+ block %= PM8821_BLOCKS_PER_MASTER;
+
+ rc = regmap_read(chip->regmap,
+ PM8821_SSBI_ADDR_IRQ_RT_STATUS(master, block), &bits);
+ if (rc) {
+ pr_err("Reading Status of IRQ %d failed rc=%d\n", pmirq, rc);
+ return rc;
+ }
+
+ *state = !!(bits & BIT(irq_bit));
+
+ return rc;
+}
+
+static struct irq_chip pm8821_irq_chip = {
+ .name = "pm8821",
+ .irq_mask_ack = pm8821_irq_mask_ack,
+ .irq_unmask = pm8821_irq_unmask,
+ .irq_get_irqchip_state = pm8821_irq_get_irqchip_state,
+ .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int pm8821_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct pm_irq_chip *chip = d->host_data;
+
+ irq_set_chip_and_handler(irq, &pm8821_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, chip);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops pm8821_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .map = pm8821_irq_domain_map,
+};
+
static const struct regmap_config ssbi_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
@@ -308,22 +499,41 @@ static const struct regmap_config ssbi_regmap_config = {
.reg_write = ssbi_reg_write
};
+static const struct pm_irq_data pm8xxx_data = {
+ .num_irqs = PM8XXX_NR_IRQS,
+ .irq_domain_ops = &pm8xxx_irq_domain_ops,
+ .irq_handler = pm8xxx_irq_handler,
+};
+
+static const struct pm_irq_data pm8821_data = {
+ .num_irqs = PM8821_NR_IRQS,
+ .irq_domain_ops = &pm8821_irq_domain_ops,
+ .irq_handler = pm8821_irq_handler,
+};
+
static const struct of_device_id pm8xxx_id_table[] = {
- { .compatible = "qcom,pm8018", },
- { .compatible = "qcom,pm8058", },
- { .compatible = "qcom,pm8921", },
+ { .compatible = "qcom,pm8018", .data = &pm8xxx_data},
+ { .compatible = "qcom,pm8058", .data = &pm8xxx_data},
+ { .compatible = "qcom,pm8821", .data = &pm8821_data},
+ { .compatible = "qcom,pm8921", .data = &pm8xxx_data},
{ }
};
MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
static int pm8xxx_probe(struct platform_device *pdev)
{
+ const struct pm_irq_data *data;
struct regmap *regmap;
int irq, rc;
unsigned int val;
u32 rev;
struct pm_irq_chip *chip;
- unsigned int nirqs = PM8XXX_NR_IRQS;
+
+ data = of_device_get_match_data(&pdev->dev);
+ if (!data) {
+ dev_err(&pdev->dev, "No matching driver data found\n");
+ return -EINVAL;
+ }
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -354,25 +564,26 @@ static int pm8xxx_probe(struct platform_device *pdev)
rev |= val << BITS_PER_BYTE;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip) +
- sizeof(chip->config[0]) * nirqs,
- GFP_KERNEL);
+ sizeof(chip->config[0]) * data->num_irqs,
+ GFP_KERNEL);
if (!chip)
return -ENOMEM;
platform_set_drvdata(pdev, chip);
chip->regmap = regmap;
- chip->num_irqs = nirqs;
+ chip->num_irqs = data->num_irqs;
chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
spin_lock_init(&chip->pm_irq_lock);
- chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs,
- &pm8xxx_irq_domain_ops,
+ chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
+ data->num_irqs,
+ data->irq_domain_ops,
chip);
if (!chip->irqdomain)
return -ENODEV;
- irq_set_chained_handler_and_data(irq, pm8xxx_irq_handler, chip);
+ irq_set_chained_handler_and_data(irq, data->irq_handler, chip);
irq_set_irq_wake(irq, 1);
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v2 08/10] ARM: dts: add basic support for Rockchip RK1108 SOC
From: Heiko Stuebner @ 2016-11-15 11:45 UTC (permalink / raw)
To: Andy Yan
Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1479125688-24528-1-git-send-email-andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Am Montag, 14. November 2016, 20:14:48 CET schrieb Andy Yan:
> RK1108 is embedded with an ARM Cortex-A7 single core and a DSP core.
> It is designed for varies application scenario such as car DVR, sports
> DV, secure camera and UAV camera.
>
> This patch add basic support for it with DMAC / UART / CRU / pinctrl / MMC
> enabled.
>
> Signed-off-by: Andy Yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> Tested-by: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> ---
>
> Changes in v2:
> - fix timer and gic dt description
> - ordering devices by register address
>
> arch/arm/boot/dts/rk1108.dtsi | 428
> ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 428
> insertions(+)
> create mode 100644 arch/arm/boot/dts/rk1108.dtsi
>
> diff --git a/arch/arm/boot/dts/rk1108.dtsi b/arch/arm/boot/dts/rk1108.dtsi
> new file mode 100644
> index 0000000..636c294
> --- /dev/null
> +++ b/arch/arm/boot/dts/rk1108.dtsi
> @@ -0,0 +1,428 @@
> +/*
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + * a) This file is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * This file 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.
> + *
> + * Or, alternatively,
> + *
> + * b) Permission is hereby granted, free of charge, to any person
> + * obtaining a copy of this software and associated documentation
> + * files (the "Software"), to deal in the Software without
> + * restriction, including without limitation the rights to use,
> + * copy, modify, merge, publish, distribute, sublicense, and/or
> + * sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following
> + * conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> + * included in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/interrupt-controller/irq.h>
> +#include <dt-bindings/interrupt-controller/arm-gic.h>
> +#include <dt-bindings/clock/rk1108-cru.h>
> +#include <dt-bindings/pinctrl/rockchip.h>
> +/ {
> + #address-cells = <1>;
> + #size-cells = <1>;
> +
> + compatible = "rockchip,rk1108";
> +
> + interrupt-parent = <&gic>;
> +
> + aliases {
> + serial0 = &uart0;
> + serial1 = &uart1;
> + serial2 = &uart2;
> + };
> +
> + cpus {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + cpu0: cpu@f00 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a7";
> + reg = <0xf00>;
> + };
> + };
> +
> + arm-pmu {
> + compatible = "arm,cortex-a7-pmu";
> + interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> +
unnecessary empty line (only 1 please)
otherwise looks fine now, just needs to wait for fixed clock ids now.
Heiko
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH V2 1/2] pinctrl: tegra: Add DT binding for io pads control
From: Laxman Dewangan @ 2016-11-15 11:45 UTC (permalink / raw)
To: Rob Herring
Cc: linus.walleij-QSEj5FYQhm4dnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
swarren-3lzwWm7+Weoh9ZMKESR00Q,
thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
gnurou-Re5JQEeQqe8AvxtiuMwx3w,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A,
jonathanh-DDmLM1+adcrQT0dZR+AlfA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-tegra-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161114193432.wi5ljtp3t46wt2c7@rob-hp-laptop>
On Tuesday 15 November 2016 01:04 AM, Rob Herring wrote:
> On Wed, Nov 09, 2016 at 06:36:21PM +0530, Laxman Dewangan wrote:
> +
> + audio {
> + pins = "audio", "dmic", "sdmmc3";
> What's the purpose of grouping these?
Just to show that multiple IO pads can be provided here as this is from
pinctrl dt binding.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] regulator: pwm: DT: Add ramp delay for exponential voltage transition
From: Laxman Dewangan @ 2016-11-15 11:42 UTC (permalink / raw)
To: Rob Herring
Cc: broonie-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Douglas Anderson,
Aleksandr Frid
In-Reply-To: <20161114154807.j4oo4veslhknauxy@rob-hp-laptop>
On Monday 14 November 2016 09:18 PM, Rob Herring wrote:
> On Fri, Nov 04, 2016 at 11:07:54PM +0530, Laxman Dewangan wrote:
>> Some PWM regulator has the exponential transition in voltage change as
>> opposite to fixed slew-rate linear transition on other regulators.
>> For such PWM regulators, add the property for providing the delay
>> from DT node.
>>
>> Add DT binding details of the new property
>> "pwm-regulator-voltage-ramp-time-us" added for providing voltage
>> transition delay.
>>
>> Signed-off-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>> CC: Douglas Anderson <dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
>> CC: Aleksandr Frid <afrid-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>>
>> ---
>> This patch is continuation of discussion on patch
>> regulator: pwm: Fix regulator ramp delay for continuous mode
>> https://patchwork.kernel.org/patch/9216857/
>> where is it discussed to have separate property for PWM which has
>> exponential voltage transition.
>> ---
>> Documentation/devicetree/bindings/regulator/pwm-regulator.txt | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
>> index 3aeba9f..a163f42 100644
>> --- a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
>> +++ b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
>> @@ -54,6 +54,16 @@ Optional properties:
>> --------------------
>> - enable-gpios: GPIO to use to enable/disable the regulator
>>
>> +- pwm-regulator-voltage-ramp-time-us: Integer, voltage ramp time in
> This is a really long name. Drop the 'pwm-regulator-' part as it is
> redundant. The fact that it is PWM reg specific is captured as it is
> documented that way.
>
We already have the regulator-ramp-delay from the regulator core.
Just wanted to make this (pwm-regulator-voltage-ramp-time-us) for pwm
specific.
Can we have "pwm-regulator-ramp-delay" or "pwm-regulator-settling-time-us"?
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 0/3] usb: dwc2: params revert and rework
From: Felipe Balbi @ 2016-11-15 11:32 UTC (permalink / raw)
To: John Youn
In-Reply-To: <cover.1479176826.git.johnyoun-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 537 bytes --]
Hi,
John Youn <johnyoun-HKixBCOQz3hWk0Htik3J/w@public.gmane.org> writes:
> Hi Felipe,
>
> This reverts and fixes a few commits that are queued on your
> testing/next, removing the previously added DT bindings, and the code
> that reads them in.
>
> The feedback was that IP validation is not reason enough to add these.
> So we'll leave them out for now.
these are still in testing/next, so I can actually still drop
them. I'm applying only patch 3/3, please make sure it all looks good on
testing/next.
--
balbi
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply
* Re: [PATCH v4 1/3] bus: simple-pm: add support to pm clocks
From: Srinivas Kandagatla @ 2016-11-15 11:25 UTC (permalink / raw)
To: Geert Uytterhoeven, Bjorn Helgaas
Cc: svarbanov, linux-pci, Bjorn Helgaas, Rob Herring,
linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org,
Geert Uytterhoeven, Kevin Hilman, Simon Horman, Linux PM list,
Rajendra Nayak, Nayak, Rajendra
In-Reply-To: <CAMuHMdUJ8Qn=dR_OMob4BO_4RmY5XemTf_UGM_oJ2VYtBa7Jiw@mail.gmail.com>
+ Rajendra (qcom,gdsc author)
On 15/11/16 08:23, Geert Uytterhoeven wrote:
> +cc linux-pm
>
> On Mon, Nov 14, 2016 at 11:14 PM, Bjorn Helgaas <helgaas@kernel.org> wrote:
>> [+cc Geert, Kevin, Simon]
>>
>> On Mon, Nov 14, 2016 at 11:15:53AM +0000, Srinivas Kandagatla wrote:
>>> This patch adds support to pm clocks via device tree, so that the clocks
>>> can be turned on and off during runtime pm. This patch is required for
>>> Qualcomm msm8996 pcie controller which sits on a bus with its own
>>> power-domain and clocks.
>>>
>>> Without this patch the clock associated with the bus are never turned on.
>>>
>>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>>
>> I don't see a formal maintainer for drivers/bus/simple-pm-bus.c, but I'd
>> like an ack or at least a review from Geert or Simon.
>
> Thanks for letting me know!
>
>>> ---
>>> drivers/bus/simple-pm-bus.c | 13 ++++++++++++-
>>> 1 file changed, 12 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c
>>> index c5eb46c..63b7e8c 100644
>>> --- a/drivers/bus/simple-pm-bus.c
>>> +++ b/drivers/bus/simple-pm-bus.c
>>> @@ -11,6 +11,7 @@
>>> #include <linux/module.h>
>>> #include <linux/of_platform.h>
>>> #include <linux/platform_device.h>
>>> +#include <linux/pm_clock.h>
>>> #include <linux/pm_runtime.h>
>>>
>>>
>>> @@ -22,17 +23,26 @@ static int simple_pm_bus_probe(struct platform_device *pdev)
>>>
>>> pm_runtime_enable(&pdev->dev);
>>>
>>> - if (np)
>>> + if (np) {
>>> + of_pm_clk_add_clks(&pdev->dev);
>
> This should work out-of-the-box (that's the actual purpose of this driver),
> if the platform code that registers your PM Domain would take care
> of registering the clocks needed for PM management of the bus.
Yep, if the pm domain provider takes care of the bus clks, then it would
work.
Am guessing that the clocks property in the DT node would be read by the
PM domain provider and enable/disable during attach/detach callbacks.
If that is true, then any device tree nodes which are not children of
"simple-pm-bus" and consumers of power-domain provider would enable all
(including non-bus clks) clks twice. Once in the power-domain provider
and once in the actual driver. Is this expected behavior from
power-domains in general?
>
> Adding of_pm_clk_add_clks() here will start managing all clocks of the bus,
> which may not be wanted on all platforms.
>
That was the purpose.
Rajendra,
Looks like qcom gdsc pm domain provider driver does not handle bus clks
along with power-domain, Is this something we should do? Or the bus
driver take care of it?
Thanks,
srini
>>> of_platform_populate(np, NULL, NULL, &pdev->dev);
>>> + }
>>>
>>> return 0;
>>> }
>>>
>>> +static const struct dev_pm_ops simple_pm_bus_pm_ops = {
>>> + SET_RUNTIME_PM_OPS(pm_clk_suspend,
>>> + pm_clk_resume, NULL)
>>> +};
>>> +
>>> static int simple_pm_bus_remove(struct platform_device *pdev)
>>> {
>>> dev_dbg(&pdev->dev, "%s\n", __func__);
>>>
>>> pm_runtime_disable(&pdev->dev);
>>> + pm_clk_destroy(&pdev->dev);
>>> +
>>> return 0;
>>> }
>>>
>>> @@ -48,6 +58,7 @@ static struct platform_driver simple_pm_bus_driver = {
>>> .driver = {
>>> .name = "simple-pm-bus",
>>> .of_match_table = simple_pm_bus_of_match,
>>> + .pm = &simple_pm_bus_pm_ops,
>>> },
>>> };
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
>
^ permalink raw reply
* Re: [PATCH v4 1/2] i2c: aspeed: added driver for Aspeed I2C
From: Joel Stanley @ 2016-11-15 11:23 UTC (permalink / raw)
To: Brendan Higgins
Cc: Wolfram Sang, Rob Herring, Mark Rutland, linux-i2c, devicetree,
OpenBMC Maillist
In-Reply-To: <1478311099-6771-2-git-send-email-brendanhiggins@google.com>
On Sat, Nov 5, 2016 at 12:28 PM, Brendan Higgins
<brendanhiggins@google.com> wrote:
> Added initial master and slave support for Aspeed I2C controller.
> Supports fourteen busses present in ast24xx and ast25xx BMC SoCs by
> Aspeed.
>
> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
I've tested this on an OpenPower Palmetto BMC system and on the Qemu
machine for the same board.
Reviewed-by: Joel Stanley <joel@jms.id.au>
Cheers,
Joel
> ---
> Changes for v2:
> - Added single module_init (multiple was breaking some builds).
> Changes for v3:
> - Removed "bus" device tree param; now extracted from bus address offset
> Changes for v4:
> - I2C adapter number is now generated dynamically unless specified in alias.
> ---
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-aspeed.c | 807 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 818 insertions(+)
> create mode 100644 drivers/i2c/busses/i2c-aspeed.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index d252276..b6caa5d 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -1009,6 +1009,16 @@ config I2C_RCAR
> This driver can also be built as a module. If so, the module
> will be called i2c-rcar.
>
> +config I2C_ASPEED
> + tristate "Aspeed AST2xxx SoC I2C Controller"
> + depends on ARCH_ASPEED
> + help
> + If you say yes to this option, support will be included for the
> + Aspeed AST2xxx SoC I2C controller.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-aspeed.
> +
> comment "External I2C/SMBus adapter drivers"
>
> config I2C_DIOLAN_U2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 29764cc..826e780 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -99,6 +99,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
> obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
> +obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
>
> # External I2C/SMBus adapter drivers
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
> new file mode 100644
> index 0000000..88e078a
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-aspeed.c
> @@ -0,0 +1,807 @@
> +/*
> + * I2C adapter for the ASPEED I2C bus.
> + *
> + * Copyright (C) 2012-2020 ASPEED Technology Inc.
> + * Copyright 2016 IBM Corporation
> + * Copyright 2016 Google, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/completion.h>
> +#include <linux/slab.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +
> +/* I2C Register */
> +#define ASPEED_I2C_FUN_CTRL_REG 0x00
> +#define ASPEED_I2C_AC_TIMING_REG1 0x04
> +#define ASPEED_I2C_AC_TIMING_REG2 0x08
> +#define ASPEED_I2C_INTR_CTRL_REG 0x0c
> +#define ASPEED_I2C_INTR_STS_REG 0x10
> +#define ASPEED_I2C_CMD_REG 0x14
> +#define ASPEED_I2C_DEV_ADDR_REG 0x18
> +#define ASPEED_I2C_BYTE_BUF_REG 0x20
> +#define ASPEED_I2C_OFFSET_START 0x40
> +#define ASPEED_I2C_OFFSET_INCREMENT 0x40
> +
> +#define ASPEED_I2C_NUM_BUS 14
> +
> +/* Global Register Definition */
> +/* 0x00 : I2C Interrupt Status Register */
> +/* 0x08 : I2C Interrupt Target Assignment */
> +
> +/* Device Register Definition */
> +/* 0x00 : I2CD Function Control Register */
> +#define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15)
> +#define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8)
> +#define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7)
> +#define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6)
> +#define ASPEED_I2CD_SLAVE_EN BIT(1)
> +#define ASPEED_I2CD_MASTER_EN BIT(0)
> +
> +/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
> +#define ASPEED_NO_TIMEOUT_CTRL 0
> +
> +
> +/* 0x0c : I2CD Interrupt Control Register &
> + * 0x10 : I2CD Interrupt Status Register
> + *
> + * These share bit definitions, so use the same values for the enable &
> + * status bits.
> + */
> +#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
> +#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
> +#define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7)
> +#define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6)
> +#define ASPEED_I2CD_INTR_ABNORMAL BIT(5)
> +#define ASPEED_I2CD_INTR_NORMAL_STOP BIT(4)
> +#define ASPEED_I2CD_INTR_ARBIT_LOSS BIT(3)
> +#define ASPEED_I2CD_INTR_RX_DONE BIT(2)
> +#define ASPEED_I2CD_INTR_TX_NAK BIT(1)
> +#define ASPEED_I2CD_INTR_TX_ACK BIT(0)
> +
> +/* 0x14 : I2CD Command/Status Register */
> +#define ASPEED_I2CD_SCL_LINE_STS BIT(18)
> +#define ASPEED_I2CD_SDA_LINE_STS BIT(17)
> +#define ASPEED_I2CD_BUS_BUSY_STS BIT(16)
> +#define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11)
> +
> +/* Command Bit */
> +#define ASPEED_I2CD_M_STOP_CMD BIT(5)
> +#define ASPEED_I2CD_M_S_RX_CMD_LAST BIT(4)
> +#define ASPEED_I2CD_M_RX_CMD BIT(3)
> +#define ASPEED_I2CD_S_TX_CMD BIT(2)
> +#define ASPEED_I2CD_M_TX_CMD BIT(1)
> +#define ASPEED_I2CD_M_START_CMD BIT(0)
> +
> +/* 0x18 : I2CD Slave Device Address Register */
> +#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
> +
> +enum aspeed_i2c_slave_state {
> + ASPEED_I2C_SLAVE_START,
> + ASPEED_I2C_SLAVE_READ_REQUESTED,
> + ASPEED_I2C_SLAVE_READ_PROCESSED,
> + ASPEED_I2C_SLAVE_WRITE_REQUESTED,
> + ASPEED_I2C_SLAVE_WRITE_RECEIVED,
> + ASPEED_I2C_SLAVE_STOP,
> +};
> +
> +struct aspeed_i2c_bus {
> + struct i2c_adapter adap;
> + struct device *dev;
> + void __iomem *base;
> + spinlock_t lock;
> + struct completion cmd_complete;
> + int irq;
> + /* Transaction state. */
> + struct i2c_msg *msg;
> + int msg_pos;
> + u32 cmd_err;
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + struct i2c_client *slave;
> + enum aspeed_i2c_slave_state slave_state;
> +#endif
> +};
> +
> +struct aspeed_i2c_controller {
> + struct device *dev;
> + void __iomem *base;
> + int irq;
> + struct irq_domain *irq_domain;
> +};
> +
> +static inline void aspeed_i2c_write(struct aspeed_i2c_bus *bus, u32 val,
> + u32 reg)
> +{
> + writel(val, bus->base + reg);
> +}
> +
> +static inline u32 aspeed_i2c_read(struct aspeed_i2c_bus *bus, u32 reg)
> +{
> + return readl(bus->base + reg);
> +}
> +
> +static u8 aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
> +{
> + u32 command;
> + unsigned long time_left;
> + unsigned long flags;
> + int ret = 0;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
> + /* Bus is idle: no recovery needed. */
> + if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
> + (command & ASPEED_I2CD_SCL_LINE_STS))
> + goto out;
> +
> + dev_dbg(bus->dev, "bus hung (state %x), attempting recovery\n",
> + command);
> +
> + /* Bus held: put bus in stop state. */
> + if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
> + !(command & ASPEED_I2CD_SCL_LINE_STS)) {
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (time_left == 0)
> + ret = -ETIMEDOUT;
> + else if (bus->cmd_err)
> + ret = -EIO;
> + /* Bus error. */
> + } else if (!(command & ASPEED_I2CD_SDA_LINE_STS)) {
> + aspeed_i2c_write(bus, ASPEED_I2CD_BUS_RECOVER_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (time_left == 0)
> + ret = -ETIMEDOUT;
> + else if (bus->cmd_err)
> + ret = -EIO;
> + /* Recovery failed. */
> + else if (!(aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
> + ASPEED_I2CD_SDA_LINE_STS))
> + ret = -EIO;
> + }
> +
> +out:
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return ret;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
> +{
> + bool irq_handled = true;
> + u32 command;
> + u32 irq_status;
> + u32 status_ack = 0;
> + u8 value;
> + struct i2c_client *slave = bus->slave;
> +
> + spin_lock(&bus->lock);
> + if (!slave) {
> + irq_handled = false;
> + goto out;
> + }
> + command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
> + irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
> +
> + /* Slave was requested, restart state machine. */
> + if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
> + status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH;
> + bus->slave_state = ASPEED_I2C_SLAVE_START;
> + }
> + /* Slave is not currently active, irq was for someone else. */
> + if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
> + irq_handled = false;
> + goto out;
> + }
> +
> + dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
> + irq_status, command);
> +
> + /* Slave was sent something. */
> + if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
> + value = aspeed_i2c_read(bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
> + /* Handle address frame. */
> + if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
> + if (value & 0x1)
> + bus->slave_state =
> + ASPEED_I2C_SLAVE_READ_REQUESTED;
> + else
> + bus->slave_state =
> + ASPEED_I2C_SLAVE_WRITE_REQUESTED;
> + }
> + status_ack |= ASPEED_I2CD_INTR_RX_DONE;
> + }
> +
> + /* Slave was asked to stop. */
> + if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
> + status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + }
> + if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
> + status_ack |= ASPEED_I2CD_INTR_TX_NAK;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + }
> +
> + if (bus->slave_state == ASPEED_I2C_SLAVE_READ_REQUESTED) {
> + if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
> + dev_err(bus->dev, "Unexpected ACK on read request.\n");
> + bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
> +
> + i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
> + aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
> + status_ack |= ASPEED_I2CD_INTR_TX_ACK;
> + if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
> + dev_err(bus->dev,
> + "Expected ACK after processed read.\n");
> + i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
> + aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_REQUESTED) {
> + bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
> + i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED) {
> + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
> + i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
> + }
> +
> + if (status_ack != irq_status)
> + dev_err(bus->dev,
> + "irq handled != irq. expected %x, but was %x\n",
> + irq_status, status_ack);
> + aspeed_i2c_write(bus, status_ack, ASPEED_I2C_INTR_STS_REG);
> +
> +out:
> + spin_unlock(&bus->lock);
> + return irq_handled;
> +}
> +#endif
> +
> +static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
> +{
> + const u32 errs = ASPEED_I2CD_INTR_ARBIT_LOSS |
> + ASPEED_I2CD_INTR_ABNORMAL |
> + ASPEED_I2CD_INTR_SCL_TIMEOUT |
> + ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
> + ASPEED_I2CD_INTR_TX_NAK;
> + u32 irq_status;
> +
> + spin_lock(&bus->lock);
> + irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
> + bus->cmd_err = irq_status & errs;
> +
> + dev_dbg(bus->dev, "master irq status 0x%08x\n", irq_status);
> +
> + /* No message to transfer. */
> + if (bus->cmd_err ||
> + (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) ||
> + (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE)) {
> + complete(&bus->cmd_complete);
> + goto out;
> + } else if (!bus->msg || bus->msg_pos >= bus->msg->len)
> + goto out;
> +
> + if ((bus->msg->flags & I2C_M_RD) &&
> + (irq_status & ASPEED_I2CD_INTR_RX_DONE)) {
> + bus->msg->buf[bus->msg_pos++] = aspeed_i2c_read(
> + bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
> + if (bus->msg_pos + 1 < bus->msg->len)
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD,
> + ASPEED_I2C_CMD_REG);
> + else if (bus->msg_pos < bus->msg->len)
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD |
> + ASPEED_I2CD_M_S_RX_CMD_LAST,
> + ASPEED_I2C_CMD_REG);
> + } else if (!(bus->msg->flags & I2C_M_RD) &&
> + (irq_status & ASPEED_I2CD_INTR_TX_ACK)) {
> + aspeed_i2c_write(bus, bus->msg->buf[bus->msg_pos++],
> + ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_TX_CMD, ASPEED_I2C_CMD_REG);
> + }
> +
> + /* Transmission complete: notify caller. */
> + if (bus->msg_pos >= bus->msg->len)
> + complete(&bus->cmd_complete);
> +out:
> + aspeed_i2c_write(bus, irq_status, ASPEED_I2C_INTR_STS_REG);
> + spin_unlock(&bus->lock);
> + return true;
> +}
> +
> +static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
> +{
> + struct aspeed_i2c_bus *bus = dev_id;
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + if (aspeed_i2c_slave_irq(bus)) {
> + dev_dbg(bus->dev, "irq handled by slave.\n");
> + return IRQ_HANDLED;
> + }
> +#endif
> + if (aspeed_i2c_master_irq(bus)) {
> + dev_dbg(bus->dev, "irq handled by master.\n");
> + return IRQ_HANDLED;
> + }
> + dev_err(bus->dev, "irq not handled properly!\n");
> + return IRQ_HANDLED;
> +}
> +
> +static int aspeed_i2c_master_single_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msg)
> +{
> + struct aspeed_i2c_bus *bus = adap->algo_data;
> + unsigned long flags;
> + u8 slave_addr;
> + u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
> + int ret = msg->len;
> + unsigned long time_left;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + bus->msg = msg;
> + bus->msg_pos = 0;
> + slave_addr = msg->addr << 1;
> + if (msg->flags & I2C_M_RD) {
> + slave_addr |= 1;
> + command |= ASPEED_I2CD_M_RX_CMD;
> + if (msg->len == 1)
> + command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
> + }
> + aspeed_i2c_write(bus, slave_addr, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, command, ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ * msg->len);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (bus->cmd_err)
> + ret = -EIO;
> + bus->msg = NULL;
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + return ret;
> +}
> +
> +static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs, int num)
> +{
> + struct aspeed_i2c_bus *bus = adap->algo_data;
> + int ret;
> + int i;
> + unsigned long flags;
> + unsigned long time_left;
> +
> + /* If bus is busy, attempt recovery. We assume a single master
> + * environment.
> + */
> + if (aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
> + ASPEED_I2CD_BUS_BUSY_STS) {
> + ret = aspeed_i2c_recover_bus(bus);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < num; i++) {
> + ret = aspeed_i2c_master_single_xfer(adap, &msgs[i]);
> + if (ret < 0)
> + break;
> + /* TODO: Support other forms of I2C protocol mangling. */
> + if (msgs[i].flags & I2C_M_STOP) {
> + spin_lock_irqsave(&bus->lock, flags);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete,
> + bus->adap.timeout * HZ);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> + }
> + }
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD, ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> +
> + /* If nothing went wrong, return number of messages transferred. */
> + if (ret < 0)
> + return ret;
> + else
> + return i;
> +}
> +
> +static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static int aspeed_i2c_reg_slave(struct i2c_client *client)
> +{
> + struct aspeed_i2c_bus *bus;
> + unsigned long flags;
> + u32 addr_reg_val;
> + u32 func_ctrl_reg_val;
> +
> + bus = client->adapter->algo_data;
> + spin_lock_irqsave(&bus->lock, flags);
> + if (bus->slave) {
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return -EINVAL;
> + }
> +
> + /* Set slave addr. */
> + addr_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_DEV_ADDR_REG);
> + addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
> + addr_reg_val |= client->addr & ASPEED_I2CD_DEV_ADDR_MASK;
> + aspeed_i2c_write(bus, addr_reg_val, ASPEED_I2C_DEV_ADDR_REG);
> +
> + /* Switch from master mode to slave mode. */
> + func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
> + func_ctrl_reg_val &= ~ASPEED_I2CD_MASTER_EN;
> + func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
> + aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
> +
> + bus->slave = client;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return 0;
> +}
> +
> +static int aspeed_i2c_unreg_slave(struct i2c_client *client)
> +{
> + struct aspeed_i2c_bus *bus = client->adapter->algo_data;
> + unsigned long flags;
> + u32 func_ctrl_reg_val;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (!bus->slave) {
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return -EINVAL;
> + }
> +
> + /* Switch from slave mode to master mode. */
> + func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
> + func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
> + func_ctrl_reg_val |= ASPEED_I2CD_MASTER_EN;
> + aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
> +
> + bus->slave = NULL;
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return 0;
> +}
> +#endif
> +
> +static const struct i2c_algorithm aspeed_i2c_algo = {
> + .master_xfer = aspeed_i2c_master_xfer,
> + .functionality = aspeed_i2c_functionality,
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + .reg_slave = aspeed_i2c_reg_slave,
> + .unreg_slave = aspeed_i2c_unreg_slave,
> +#endif
> +};
> +
> +static u32 aspeed_i2c_get_clk_reg_val(u32 divider_ratio)
> +{
> + unsigned int inc = 0, div;
> + u32 scl_low, scl_high, data;
> +
> + for (div = 0; divider_ratio >= 16; div++) {
> + inc |= (divider_ratio & 1);
> + divider_ratio >>= 1;
> + }
> + divider_ratio += inc;
> + scl_low = (divider_ratio >> 1) - 1;
> + scl_high = divider_ratio - scl_low - 2;
> + data = 0x77700300 | (scl_high << 16) | (scl_low << 12) | div;
> + return data;
> +}
> +
> +static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus,
> + struct platform_device *pdev)
> +{
> + struct clk *pclk;
> + u32 clk_freq;
> + u32 divider_ratio;
> + int ret;
> +
> + pclk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(pclk)) {
> + dev_err(&pdev->dev, "clk_get failed\n");
> + return PTR_ERR(pclk);
> + }
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "clock-frequency", &clk_freq);
> + if (ret < 0) {
> + dev_err(&pdev->dev,
> + "Could not read clock-frequency property\n");
> + clk_freq = 100000;
> + }
> + divider_ratio = clk_get_rate(pclk) / clk_freq;
> + /* We just need the clock rate, we don't actually use the clk object. */
> + devm_clk_put(&pdev->dev, pclk);
> +
> + /* Set AC Timing */
> + if (clk_freq / 1000 > 400) {
> + aspeed_i2c_write(bus, aspeed_i2c_read(bus,
> + ASPEED_I2C_FUN_CTRL_REG) |
> + ASPEED_I2CD_M_HIGH_SPEED_EN |
> + ASPEED_I2CD_M_SDA_DRIVE_1T_EN |
> + ASPEED_I2CD_SDA_DRIVE_1T_EN,
> + ASPEED_I2C_FUN_CTRL_REG);
> +
> + aspeed_i2c_write(bus, 0x3, ASPEED_I2C_AC_TIMING_REG2);
> + aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
> + ASPEED_I2C_AC_TIMING_REG1);
> + } else {
> + aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
> + ASPEED_I2C_AC_TIMING_REG1);
> + aspeed_i2c_write(bus, ASPEED_NO_TIMEOUT_CTRL,
> + ASPEED_I2C_AC_TIMING_REG2);
> + }
> +
> + return 0;
> +}
> +
> +static void noop(struct irq_data *data) { }
> +
> +static struct irq_chip aspeed_i2c_irqchip = {
> + .name = "ast-i2c",
> + .irq_unmask = noop,
> + .irq_mask = noop,
> +};
> +
> +static int aspeed_i2c_probe_bus(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_bus *bus;
> + struct aspeed_i2c_controller *controller =
> + dev_get_drvdata(pdev->dev.parent);
> + struct resource *res;
> + int ret, irq;
> + u32 hwirq;
> +
> + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
> + if (!bus)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + bus->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(bus->base))
> + return PTR_ERR(bus->base);
> +
> + bus->irq = platform_get_irq(pdev, 0);
> + if (bus->irq < 0)
> + return -ENXIO;
> + ret = of_property_read_u32(pdev->dev.of_node, "interrupts", &hwirq);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "no I2C 'interrupts' property\n");
> + return -ENXIO;
> + }
> + irq = irq_create_mapping(controller->irq_domain, hwirq);
> + irq_set_chip_data(irq, controller);
> + irq_set_chip_and_handler(irq, &aspeed_i2c_irqchip, handle_simple_irq);
> + ret = devm_request_irq(&pdev->dev, bus->irq, aspeed_i2c_bus_irq,
> + 0, dev_name(&pdev->dev), bus);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to request interrupt\n");
> + return -ENXIO;
> + }
> +
> + /* Initialize the I2C adapter */
> + spin_lock_init(&bus->lock);
> + init_completion(&bus->cmd_complete);
> + bus->adap.owner = THIS_MODULE;
> + bus->adap.retries = 0;
> + bus->adap.timeout = 5;
> + bus->adap.algo = &aspeed_i2c_algo;
> + bus->adap.algo_data = bus;
> + bus->adap.dev.parent = &pdev->dev;
> + bus->adap.dev.of_node = pdev->dev.of_node;
> + snprintf(bus->adap.name, sizeof(bus->adap.name), "Aspeed i2c");
> +
> + bus->dev = &pdev->dev;
> +
> + /* reset device: disable master & slave functions */
> + aspeed_i2c_write(bus, 0, ASPEED_I2C_FUN_CTRL_REG);
> +
> + ret = aspeed_i2c_init_clk(bus, pdev);
> + if (ret < 0)
> + return ret;
> +
> + /* Enable Master Mode */
> + aspeed_i2c_write(bus, aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG) |
> + ASPEED_I2CD_MASTER_EN |
> + ASPEED_I2CD_MULTI_MASTER_DIS, ASPEED_I2C_FUN_CTRL_REG);
> +
> + /* Set interrupt generation of I2C controller */
> + aspeed_i2c_write(bus, ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
> + ASPEED_I2CD_INTR_BUS_RECOVER_DONE |
> + ASPEED_I2CD_INTR_SCL_TIMEOUT |
> + ASPEED_I2CD_INTR_ABNORMAL |
> + ASPEED_I2CD_INTR_NORMAL_STOP |
> + ASPEED_I2CD_INTR_ARBIT_LOSS |
> + ASPEED_I2CD_INTR_RX_DONE |
> + ASPEED_I2CD_INTR_TX_NAK |
> + ASPEED_I2CD_INTR_TX_ACK,
> + ASPEED_I2C_INTR_CTRL_REG);
> +
> + ret = i2c_add_adapter(&bus->adap);
> + if (ret < 0)
> + return -ENXIO;
> +
> + platform_set_drvdata(pdev, bus);
> +
> + dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
> + bus->adap.nr, bus->irq);
> +
> + return 0;
> +}
> +
> +static int aspeed_i2c_remove_bus(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
> +
> + i2c_del_adapter(&bus->adap);
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_i2c_bus_of_table[] = {
> + { .compatible = "aspeed,ast2400-i2c-bus", },
> + { .compatible = "aspeed,ast2500-i2c-bus", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
> +
> +static struct platform_driver aspeed_i2c_bus_driver = {
> + .probe = aspeed_i2c_probe_bus,
> + .remove = aspeed_i2c_remove_bus,
> + .driver = {
> + .name = "ast-i2c-bus",
> + .of_match_table = aspeed_i2c_bus_of_table,
> + },
> +};
> +
> +static void aspeed_i2c_controller_irq(struct irq_desc *desc)
> +{
> + struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
> + unsigned long p, status;
> + unsigned int bus_irq;
> +
> + status = readl(c->base);
> + for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
> + bus_irq = irq_find_mapping(c->irq_domain, p);
> + generic_handle_irq(bus_irq);
> + }
> +}
> +
> +static int aspeed_i2c_probe_controller(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_controller *controller;
> + struct device_node *np;
> + struct resource *res;
> +
> + controller = kzalloc(sizeof(*controller), GFP_KERNEL);
> + if (!controller)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + controller->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(controller->base))
> + return PTR_ERR(controller->base);
> +
> + controller->irq = platform_get_irq(pdev, 0);
> + if (controller->irq < 0)
> + return -ENXIO;
> +
> + controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
> + ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
> + if (!controller->irq_domain)
> + return -ENXIO;
> + controller->irq_domain->name = "ast-i2c-domain";
> +
> + irq_set_chained_handler_and_data(controller->irq,
> + aspeed_i2c_controller_irq, controller);
> +
> + controller->dev = &pdev->dev;
> +
> + platform_set_drvdata(pdev, controller);
> +
> + dev_info(controller->dev, "i2c controller registered, irq %d\n",
> + controller->irq);
> +
> + for_each_child_of_node(pdev->dev.of_node, np) {
> + of_platform_device_create(np, NULL, &pdev->dev);
> + of_node_put(np);
> + }
> +
> + return 0;
> +}
> +
> +static int aspeed_i2c_remove_controller(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
> +
> + irq_domain_remove(controller->irq_domain);
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_i2c_controller_of_table[] = {
> + { .compatible = "aspeed,ast2400-i2c-controller", },
> + { .compatible = "aspeed,ast2500-i2c-controller", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_i2c_controller_of_table);
> +
> +static struct platform_driver aspeed_i2c_controller_driver = {
> + .probe = aspeed_i2c_probe_controller,
> + .remove = aspeed_i2c_remove_controller,
> + .driver = {
> + .name = "ast-i2c-controller",
> + .of_match_table = aspeed_i2c_controller_of_table,
> + },
> +};
> +
> +static int __init aspeed_i2c_driver_init(void)
> +{
> + int ret;
> +
> + ret = platform_driver_register(&aspeed_i2c_controller_driver);
> + if (ret < 0)
> + return ret;
> + return platform_driver_register(&aspeed_i2c_bus_driver);
> +}
> +module_init(aspeed_i2c_driver_init);
> +
> +static void __exit aspeed_i2c_driver_exit(void)
> +{
> + platform_driver_unregister(&aspeed_i2c_bus_driver);
> + platform_driver_unregister(&aspeed_i2c_controller_driver);
> +}
> +module_exit(aspeed_i2c_driver_exit);
> +
> +MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
> +MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
> +MODULE_LICENSE("GPL");
> --
> 2.8.0.rc3.226.g39d4020
>
^ permalink raw reply
* Re: [PATCH v4 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
From: Joel Stanley @ 2016-11-15 11:16 UTC (permalink / raw)
To: Rob Herring
Cc: Brendan Higgins, Wolfram Sang, Mark Rutland,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, OpenBMC Maillist
In-Reply-To: <20161114155731.p4kxmefqevplphtd@rob-hp-laptop>
On Tue, Nov 15, 2016 at 2:27 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Fri, Nov 04, 2016 at 06:58:19PM -0700, Brendan Higgins wrote:
>> Added device tree binding documentation for Aspeed I2C controller and
>> busses.
>>
>> Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
>> ---
>> Changes for v2:
>> - None
>> Changes for v3:
>> - Removed reference to "bus" device tree param
>> Changes for v4:
>> - None
>> ---
>> .../devicetree/bindings/i2c/i2c-aspeed.txt | 61 ++++++++++++++++++++++
>> 1 file changed, 61 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Thanks Rob.
Acked-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
Cheers,
Joel
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
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