* [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 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 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
* 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
* 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: [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
* [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: [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
* 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 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 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 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 v2 2/6] mfd: stm32-adc: Add support for stm32 ADC
From: Jonathan Cameron @ 2016-11-15 13:17 UTC (permalink / raw)
To: Fabrice Gasnier, Lee Jones, Jonathan Cameron
Cc: linux-iio, linux-arm-kernel, devicetree, linux-kernel, linux,
robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue, lars,
knaack.h, pmeerw
In-Reply-To: <687b2001-c658-d0fb-a205-e5b6743fa9bc@st.com>
On 15 November 2016 10:47:52 GMT+00:00, Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
>On 11/14/2016 05:47 PM, Lee Jones wrote:
>> On Sat, 12 Nov 2016, Jonathan Cameron wrote:
>>
>>> On 10/11/16 16:18, Fabrice Gasnier wrote:
>>>> Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
>>>> Converter). STM32 ADC can be composed of up to 3 ADCs with shared
>>>> resources like clock prescaler, common interrupt line and analog
>>>> reference voltage.
>>>> This core driver basically manages shared resources.
>>>>
>>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
>>> Looks good to me (other than the build issue obviously ;)
>
>Hi Jonathan,
>Thanks for your review.
>I'll fix build issue, sure ;-)
>
>>>
>>> The fun bit will be trying to keep the whole thing this clean as you
>>> add the more 'interesting' functionality. *fingers crossed*
>Yes... But in the end, splitting shared resources in core driver makes
>it more simple.
>Not sure there will be more complexity here.
>
>>>
>>> Acked-by: Jonathan Cameron <jic23@kernel.org>
>> There isn't anything MFD about this driver.
>>
>> Please move it into IIO.
>
>Hmm, there is no other sub sysbtem that may be used here, ADC driver
>belongs to IIO.
>Also, of_platform_populate() is being used here. This can perfectly be
>called from within IIO.
>
>Jonathan, can this "stm32-adc-core" driver be moved to, and live in
>drivers/iio/adc ?
>(e.g. in addition to stm32-adc iio driver)
>Is it ok for you ?
Yes. That's ideal.
>
>Please advise,
>Best Regards,
>Fabrice
>
>>
>>>> ---
>>>> drivers/mfd/Kconfig | 14 ++
>>>> drivers/mfd/Makefile | 1 +
>>>> drivers/mfd/stm32-adc-core.c | 301
>+++++++++++++++++++++++++++++++++++++
>>>> include/linux/mfd/stm32-adc-core.h | 52 +++++++
>>>> 4 files changed, 368 insertions(+)
>>>> create mode 100644 drivers/mfd/stm32-adc-core.c
>>>> create mode 100644 include/linux/mfd/stm32-adc-core.h
>>>>
>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>> index c6df644..2580cee 100644
>>>> --- a/drivers/mfd/Kconfig
>>>> +++ b/drivers/mfd/Kconfig
>>>> @@ -1152,6 +1152,20 @@ config MFD_PALMAS
>>>> If you say yes here you get support for the Palmas
>>>> series of PMIC chips from Texas Instruments.
>>>>
>>>> +config MFD_STM32_ADC
>>>> + tristate "STMicroelectronics STM32 adc"
>>>> + depends on ARCH_STM32 || COMPILE_TEST
>>>> + depends on OF
>>>> + select MFD_CORE
>>>> + select REGULATOR
>>>> + select REGULATOR_FIXED_VOLTAGE
>>>> + help
>>>> + Select this option to enable the core driver for
>STMicroelectronics
>>>> + STM32 analog-to-digital converter (ADC).
>>>> +
>>>> + This driver can also be built as a module. If so, the module
>>>> + will be called stm32-adc-core.
>>>> +
>>>> config TPS6105X
>>>> tristate "TI TPS61050/61052 Boost Converters"
>>>> depends on I2C
>>>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>>>> index 9834e66..4571506 100644
>>>> --- a/drivers/mfd/Makefile
>>>> +++ b/drivers/mfd/Makefile
>>>> @@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS_PCI) +=
>intel-lpss-pci.o
>>>> obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
>>>> obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
>>>> obj-$(CONFIG_MFD_PALMAS) += palmas.o
>>>> +obj-$(CONFIG_MFD_STM32_ADC) += stm32-adc-core.o
>>>> obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
>>>> obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
>>>> obj-$(CONFIG_MFD_RK808) += rk808.o
>>>> diff --git a/drivers/mfd/stm32-adc-core.c
>b/drivers/mfd/stm32-adc-core.c
>>>> new file mode 100644
>>>> index 0000000..bcf52fb
>>>> --- /dev/null
>>>> +++ b/drivers/mfd/stm32-adc-core.c
>>>> @@ -0,0 +1,301 @@
>>>> +/*
>>>> + * This file is part of STM32 ADC driver
>>>> + *
>>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>>> + *
>>>> + * Inspired from: fsl-imx25-tsadc
>>>> + *
>>>> + * License type: GPLv2
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or
>modify it
>>>> + * under the terms of the GNU General Public License version 2 as
>published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>but
>>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>MERCHANTABILITY
>>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>>> + * See the GNU General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public
>License along with
>>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/irqchip/chained_irq.h>
>>>> +#include <linux/irqdesc.h>
>>>> +#include <linux/irqdomain.h>
>>>> +#include <linux/mfd/stm32-adc-core.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/regulator/consumer.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
>>>> +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
>>>> +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
>>>> +
>>>> +/* STM32F4_ADC_CSR - bit fields */
>>>> +#define STM32F4_EOC3 BIT(17)
>>>> +#define STM32F4_EOC2 BIT(9)
>>>> +#define STM32F4_EOC1 BIT(1)
>>>> +
>>>> +/* STM32F4_ADC_CCR - bit fields */
>>>> +#define STM32F4_ADC_ADCPRE_SHIFT 16
>>>> +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
>>>> +
>>>> +/* STM32 F4 maximum analog clock rate (from datasheet) */
>>>> +#define STM32F4_ADC_MAX_CLK_RATE 36000000
>>>> +
>>>> +/**
>>>> + * struct stm32_adc_priv - stm32 ADC core private data
>>>> + * @irq: irq for ADC block
>>>> + * @domain: irq domain reference
>>>> + * @aclk: clock reference for the analog circuitry
>>>> + * @vref: regulator reference
>>>> + * @common: common data for all ADC instances
>>>> + */
>>>> +struct stm32_adc_priv {
>>>> + int irq;
>>>> + struct irq_domain *domain;
>>>> + struct clk *aclk;
>>>> + struct regulator *vref;
>>>> + struct stm32_adc_common common;
>>>> +};
>>>> +
>>>> +static struct stm32_adc_priv *to_stm32_adc_priv(struct
>stm32_adc_common *com)
>>>> +{
>>>> + return container_of(com, struct stm32_adc_priv, common);
>>>> +}
>>>> +
>>>> +/* STM32F4 ADC internal common clock prescaler division ratios */
>>>> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
>>>> +
>>>> +/**
>>>> + * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock
>prescaler
>>>> + * @priv: stm32 ADC core private data
>>>> + * Select clock prescaler used for analog conversions, before
>using ADC.
>>>> + */
>>>> +static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + unsigned long rate;
>>>> + u32 val;
>>>> + int i;
>>>> +
>>>> + rate = clk_get_rate(priv->aclk);
>>>> + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
>>>> + if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
>>>> + break;
>>>> + }
>>>> + if (i >= ARRAY_SIZE(stm32f4_pclk_div))
>>>> + return -EINVAL;
>>>> +
>>>> + val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
>>>> + val &= ~STM32F4_ADC_ADCPRE_MASK;
>>>> + val |= i << STM32F4_ADC_ADCPRE_SHIFT;
>>>> + writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
>>>> +
>>>> + dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
>>>> + rate / (stm32f4_pclk_div[i] * 1000));
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/* ADC common interrupt for all instances */
>>>> +static void stm32_adc_irq_handler(struct irq_desc *desc)
>>>> +{
>>>> + struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
>>>> + struct irq_chip *chip = irq_desc_get_chip(desc);
>>>> + u32 status;
>>>> +
>>>> + chained_irq_enter(chip, desc);
>>>> + status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
>>>> +
>>>> + if (status & STM32F4_EOC1)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 0));
>>>> +
>>>> + if (status & STM32F4_EOC2)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 1));
>>>> +
>>>> + if (status & STM32F4_EOC3)
>>>> + generic_handle_irq(irq_find_mapping(priv->domain, 2));
>>>> +
>>>> + chained_irq_exit(chip, desc);
>>>> +};
>>>> +
>>>> +static int stm32_adc_domain_map(struct irq_domain *d, unsigned int
>irq,
>>>> + irq_hw_number_t hwirq)
>>>> +{
>>>> + irq_set_chip_data(irq, d->host_data);
>>>> + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned
>int irq)
>>>> +{
>>>> + irq_set_chip_and_handler(irq, NULL, NULL);
>>>> + irq_set_chip_data(irq, NULL);
>>>> +}
>>>> +
>>>> +static const struct irq_domain_ops stm32_adc_domain_ops = {
>>>> + .map = stm32_adc_domain_map,
>>>> + .unmap = stm32_adc_domain_unmap,
>>>> + .xlate = irq_domain_xlate_onecell,
>>>> +};
>>>> +
>>>> +static int stm32_adc_irq_probe(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + struct device_node *np = pdev->dev.of_node;
>>>> +
>>>> + priv->irq = platform_get_irq(pdev, 0);
>>>> + if (priv->irq < 0) {
>>>> + dev_err(&pdev->dev, "failed to get irq\n");
>>>> + return priv->irq;
>>>> + }
>>>> +
>>>> + priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
>>>> + &stm32_adc_domain_ops,
>>>> + priv);
>>>> + if (!priv->domain) {
>>>> + dev_err(&pdev->dev, "Failed to add irq domain\n");
>>>> + return -ENOMEM;
>>>> + }
>>>> +
>>>> + irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
>>>> + irq_set_handler_data(priv->irq, priv);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static void stm32_adc_irq_remove(struct platform_device *pdev,
>>>> + struct stm32_adc_priv *priv)
>>>> +{
>>>> + int hwirq;
>>>> +
>>>> + for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
>>>> + irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
>>>> + irq_domain_remove(priv->domain);
>>>> + irq_set_chained_handler(priv->irq, NULL);
>>>> +}
>>>> +
>>>> +static int stm32_adc_probe(struct platform_device *pdev)
>>>> +{
>>>> + struct stm32_adc_priv *priv;
>>>> + struct device_node *np = pdev->dev.of_node;
>>>> + struct resource *res;
>>>> + int ret;
>>>> +
>>>> + if (!pdev->dev.of_node)
>>>> + return -ENODEV;
>>>> +
>>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>>> + if (!priv)
>>>> + return -ENOMEM;
>>>> +
>>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>> + priv->common.base = devm_ioremap_resource(&pdev->dev, res);
>>>> + if (IS_ERR(priv->common.base))
>>>> + return PTR_ERR(priv->common.base);
>>>> +
>>>> + priv->vref = devm_regulator_get(&pdev->dev, "vref");
>>>> + if (IS_ERR(priv->vref)) {
>>>> + ret = PTR_ERR(priv->vref);
>>>> + dev_err(&pdev->dev, "vref get failed, %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = regulator_enable(priv->vref);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "vref enable failed\n");
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = regulator_get_voltage(priv->vref);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
>>>> + goto err_regulator_disable;
>>>> + }
>>>> + priv->common.vref_mv = ret / 1000;
>>>> + dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
>>>> +
>>>> + priv->aclk = devm_clk_get(&pdev->dev, "adc");
>>>> + if (IS_ERR(priv->aclk)) {
>>>> + ret = PTR_ERR(priv->aclk);
>>>> + dev_err(&pdev->dev, "Can't get 'adc' clock\n");
>>>> + goto err_regulator_disable;
>>>> + }
>>>> +
>>>> + ret = clk_prepare_enable(priv->aclk);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "adc clk enable failed\n");
>>>> + goto err_regulator_disable;
>>>> + }
>>>> +
>>>> + ret = stm32f4_adc_clk_sel(pdev, priv);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "adc clk selection failed\n");
>>>> + goto err_clk_disable;
>>>> + }
>>>> +
>>>> + ret = stm32_adc_irq_probe(pdev, priv);
>>>> + if (ret < 0)
>>>> + goto err_clk_disable;
>>>> +
>>>> + platform_set_drvdata(pdev, &priv->common);
>>>> +
>>>> + ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
>>>> + if (ret < 0) {
>>>> + dev_err(&pdev->dev, "failed to populate DT children\n");
>>>> + goto err_irq_remove;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +
>>>> +err_irq_remove:
>>>> + stm32_adc_irq_remove(pdev, priv);
>>>> +
>>>> +err_clk_disable:
>>>> + clk_disable_unprepare(priv->aclk);
>>>> +
>>>> +err_regulator_disable:
>>>> + regulator_disable(priv->vref);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int stm32_adc_remove(struct platform_device *pdev)
>>>> +{
>>>> + struct stm32_adc_common *common = platform_get_drvdata(pdev);
>>>> + struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
>>>> +
>>>> + of_platform_depopulate(&pdev->dev);
>>>> + stm32_adc_irq_remove(pdev, priv);
>>>> + clk_disable_unprepare(priv->aclk);
>>>> + regulator_disable(priv->vref);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id stm32_adc_of_match[] = {
>>>> + { .compatible = "st,stm32f4-adc-core" },
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
>>>> +
>>>> +static struct platform_driver stm32_adc_driver = {
>>>> + .probe = stm32_adc_probe,
>>>> + .remove = stm32_adc_remove,
>>>> + .driver = {
>>>> + .name = "stm32-adc-core",
>>>> + .of_match_table = stm32_adc_of_match,
>>>> + },
>>>> +};
>>>> +module_platform_driver(stm32_adc_driver);
>>>> +
>>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC MFD driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> +MODULE_ALIAS("platform:stm32-adc-core");
>>>> diff --git a/include/linux/mfd/stm32-adc-core.h
>b/include/linux/mfd/stm32-adc-core.h
>>>> new file mode 100644
>>>> index 0000000..081fa5f
>>>> --- /dev/null
>>>> +++ b/include/linux/mfd/stm32-adc-core.h
>>>> @@ -0,0 +1,52 @@
>>>> +/*
>>>> + * This file is part of STM32 ADC driver
>>>> + *
>>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
>>>> + *
>>>> + * License type: GPLv2
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or
>modify it
>>>> + * under the terms of the GNU General Public License version 2 as
>published by
>>>> + * the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>but
>>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>MERCHANTABILITY
>>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>>> + * See the GNU General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public
>License along with
>>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef __STM32_ADC_H
>>>> +#define __STM32_ADC_H
>>>> +
>>>> +/*
>>>> + * STM32 - ADC global register map
>>>> + * ________________________________________________________
>>>> + * | Offset | Register |
>>>> + * --------------------------------------------------------
>>>> + * | 0x000 | Master ADC1 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x100 | Slave ADC2 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x200 | Slave ADC3 |
>>>> + * --------------------------------------------------------
>>>> + * | 0x300 | Master & Slave common regs |
>>>> + * --------------------------------------------------------
>>>> + */
>>>> +#define STM32_ADC_MAX_ADCS 3
>>>> +#define STM32_ADCX_COMN_OFFSET 0x300
>>>> +
>>>> +/**
>>>> + * struct stm32_adc_common - stm32 ADC driver common data (for all
>instances)
>>>> + * @base: control registers base cpu addr
>>>> + * @vref_mv: vref voltage (mv)
>>>> + */
>>>> +struct stm32_adc_common {
>>>> + void __iomem *base;
>>>> + int vref_mv;
>>>> +};
>>>> +
>>>> +#endif
>>>>
>
>--
>To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
^ permalink raw reply
* Re: [PATCH v4 2/3] PCI: qcom: add support to msm8996 PCIE controller
From: Srinivas Kandagatla @ 2016-11-15 13:22 UTC (permalink / raw)
To: Stanimir Varbanov, linux-pci-u79uwXL29TY76Z2rM5mHXA,
bhelgaas-hpIqsD4AKlfQT0dZR+AlfA
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <aa135735-4ff4-06e3-7899-1255a21edfb4-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
On 15/11/16 12:24, Stanimir Varbanov wrote:
> 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-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>
> With below comments addressed:
>
> Acked-by: Stanimir Varbanov <svarbanov-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
Thanks for the ack.
>
>> ---
>> .../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.
>
Yep, will remove this.
>> +
>> +#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?
maybe some like this ??
#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8
--srini
>
>> +#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
>
--
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 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 13:24 UTC (permalink / raw)
To: Jonathan Cameron, linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg
In-Reply-To: <4c5cd26d-97a5-f7a8-fdb6-9413975a3b10-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 11/12/2016 06:08 PM, Jonathan Cameron wrote:
> On 10/11/16 16:18, Fabrice Gasnier wrote:
>> This patch adds support for STMicroelectronics STM32 MCU's analog to
>> digital converter.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
> Nice and clean - few minor things inline.
>
> Jonathan
>> ---
>> drivers/iio/adc/Kconfig | 10 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/stm32-adc.c | 525 ++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 536 insertions(+)
>> create mode 100644 drivers/iio/adc/stm32-adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 7edcf32..61ba674 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -419,6 +419,16 @@ config ROCKCHIP_SARADC
>> To compile this driver as a module, choose M here: the
>> module will be called rockchip_saradc.
>>
>> +config STM32_ADC
>> + tristate "STMicroelectronics STM32 adc"
>> + depends on MFD_STM32_ADC
>> + help
>> + Say yes here to build support for STMicroelectronics stm32 Analog
>> + to Digital Converter (ADC).
>> +
>> + This driver can also be built as a module. If so, the module
>> + will be called stm32-adc.
>> +
>> config STX104
>> tristate "Apex Embedded Systems STX104 driver"
>> depends on X86 && ISA_BUS_API
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 7a40c04..df7a221 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -41,6 +41,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>> obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>> obj-$(CONFIG_STX104) += stx104.o
>> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>> obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
>> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
>> new file mode 100644
>> index 0000000..2be5fee
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-adc.c
>> @@ -0,0 +1,525 @@
>> +/*
>> + * This file is part of STM32 ADC driver
>> + *
>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>> + * Author: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>.
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/mfd/stm32-adc-core.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of.h>
>> +
>> +/* STM32F4 - Registers for each ADC instance */
> Not really sure the X adds anything in these, but doesn't do much harm
> I guess ;)
You're right. I'll remove the X. I initially added that to remind this
stands for ADC1, 2 or 3...
Moreover, removing it will make register name match with reference manual.
>> +#define STM32F4_ADCX_SR 0x00
>> +#define STM32F4_ADCX_CR1 0x04
>> +#define STM32F4_ADCX_CR2 0x08
>> +#define STM32F4_ADCX_SMPR1 0x0C
>> +#define STM32F4_ADCX_SMPR2 0x10
>> +#define STM32F4_ADCX_HTR 0x24
>> +#define STM32F4_ADCX_LTR 0x28
>> +#define STM32F4_ADCX_SQR1 0x2C
>> +#define STM32F4_ADCX_SQR2 0x30
>> +#define STM32F4_ADCX_SQR3 0x34
>> +#define STM32F4_ADCX_JSQR 0x38
>> +#define STM32F4_ADCX_JDR1 0x3C
>> +#define STM32F4_ADCX_JDR2 0x40
>> +#define STM32F4_ADCX_JDR3 0x44
>> +#define STM32F4_ADCX_JDR4 0x48
>> +#define STM32F4_ADCX_DR 0x4C
>> +
>> +/* STM32F4_ADCX_SR - bit fields */
>> +#define STM32F4_OVR BIT(5)
>> +#define STM32F4_STRT BIT(4)
>> +#define STM32F4_EOC BIT(1)
>> +
>> +/* STM32F4_ADCX_CR1 - bit fields */
>> +#define STM32F4_OVRIE BIT(26)
>> +#define STM32F4_SCAN BIT(8)
>> +#define STM32F4_EOCIE BIT(5)
>> +
>> +/* STM32F4_ADCX_CR2 - bit fields */
>> +#define STM32F4_SWSTART BIT(30)
>> +#define STM32F4_EXTEN_MASK GENMASK(29, 28)
>> +#define STM32F4_EOCS BIT(10)
>> +#define STM32F4_ADON BIT(0)
>> +
>> +/* STM32F4_ADCX_SQR1 - bit fields */
>> +#define STM32F4_L_SHIFT 20
>> +#define STM32F4_L_MASK GENMASK(23, 20)
>> +
>> +/* STM32F4_ADCX_SQR3 - bit fields */
>> +#define STM32F4_SQ1_SHIFT 0
>> +#define STM32F4_SQ1_MASK GENMASK(4, 0)
>> +
>> +#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
>> +#define STM32_ADC_TIMEOUT_US 100000
>> +#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>> +
>> +/**
>> + * struct stm32_adc - private data of each ADC IIO instance
>> + * @common: reference to ADC block common data
>> + * @offset: ADC instance register offset in ADC block
>> + * @completion: end of single conversion completion
>> + * @buffer: data buffer
>> + * @clk: optional adc clock, for this adc instance
>> + * @irq: interrupt for this adc instance
>> + * @lock: spinlock
>> + */
>> +struct stm32_adc {
>> + struct stm32_adc_common *common;
>> + u32 offset;
>> + struct completion completion;
>> + u16 *buffer;
>> + struct clk *clk;
>> + int irq;
>> + spinlock_t lock; /* interrupt lock */
>> +};
>> +
>> +/**
>> + * struct stm32_adc_chan_spec - specification of stm32 adc channel
>> + * @type: IIO channel type
>> + * @channel: channel number (single ended)
>> + * @name: channel name (single ended)
>> + */
>> +struct stm32_adc_chan_spec {
>> + enum iio_chan_type type;
>> + int channel;
>> + const char *name;
>> +};
>> +
>> +/* Input definitions common for all STM32F4 instances */
>> +static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
>> + { IIO_VOLTAGE, 0, "in0" },
>> + { IIO_VOLTAGE, 1, "in1" },
>> + { IIO_VOLTAGE, 2, "in2" },
>> + { IIO_VOLTAGE, 3, "in3" },
>> + { IIO_VOLTAGE, 4, "in4" },
>> + { IIO_VOLTAGE, 5, "in5" },
>> + { IIO_VOLTAGE, 6, "in6" },
>> + { IIO_VOLTAGE, 7, "in7" },
>> + { IIO_VOLTAGE, 8, "in8" },
>> + { IIO_VOLTAGE, 9, "in9" },
>> + { IIO_VOLTAGE, 10, "in10" },
>> + { IIO_VOLTAGE, 11, "in11" },
>> + { IIO_VOLTAGE, 12, "in12" },
>> + { IIO_VOLTAGE, 13, "in13" },
>> + { IIO_VOLTAGE, 14, "in14" },
>> + { IIO_VOLTAGE, 15, "in15" },
>> +};
>> +
>> +/**
>> + * STM32 ADC registers access routines
>> + * @adc: stm32 adc instance
>> + * @reg: reg offset in adc instance
>> + *
>> + * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
>> + * for adc1, adc2 and adc3.
>> + */
>> +static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
>> +{
>> + return readl_relaxed(adc->common->base + adc->offset + reg);
>> +}
>> +
>> +static u32 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
>> +{
>> + return readw_relaxed(adc->common->base + adc->offset + reg);
>> +}
>> +
>> +static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
>> +{
>> + writel_relaxed(val, adc->common->base + adc->offset + reg);
>> +}
>> +
>> +static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>> +{
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&adc->lock, flags);
>> + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
>> + spin_unlock_irqrestore(&adc->lock, flags);
>> +}
>> +
>> +static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>> +{
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&adc->lock, flags);
>> + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
>> + spin_unlock_irqrestore(&adc->lock, flags);
>> +}
>> +
>> +/**
>> + * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt
>> + * @adc: stm32 adc instance
>> + */
>> +static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
>> +{
>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_EOCIE);
>> +};
>> +
>> +/**
>> + * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
>> + * @adc: stm32 adc instance
>> + */
>> +static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
>> +{
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_EOCIE);
>> +}
>> +
>> +/**
>> + * stm32_adc_start_conv() - Start conversions for regular channels.
>> + * @adc: stm32 adc instance
>> + */
>> +static void stm32_adc_start_conv(struct stm32_adc *adc)
>> +{
>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, STM32F4_EOCS | STM32F4_ADON);
>> +
>> + /* Wait for Power-up time (tSTAB from datasheet) */
>> + usleep_range(2, 3);
>> +
>> + /* Software start ? (e.g. trigger detection disabled ?) */
>> + if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_EXTEN_MASK))
>> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, STM32F4_SWSTART);
>> +}
>> +
>> +static void stm32_adc_stop_conv(struct stm32_adc *adc)
>> +{
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
>> +
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
>> +}
>> +
>> +/**
>> + * stm32_adc_single_conv() - Performs a single conversion
>> + * @indio_dev: IIO device
>> + * @chan: IIO channel
>> + * @res: conversion result
>> + *
>> + * The function performs a single conversion on a given channel:
>> + * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
>> + * - Use SW trigger
>> + * - Start conversion, then wait for interrupt completion.
>> + */
>> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>> + const struct iio_chan_spec *chan,
>> + int *res)
>> +{
>> + struct stm32_adc *adc = iio_priv(indio_dev);
>> + long timeout;
>> + u32 val;
>> + u16 result;
>> + int ret;
>> +
>> + reinit_completion(&adc->completion);
>> +
>> + adc->buffer = &result;
>> +
>> + /* Program chan number in regular sequence */
>> + val = stm32_adc_readl(adc, STM32F4_ADCX_SQR3);
>> + val &= ~STM32F4_SQ1_MASK;
>> + val |= chan->channel << STM32F4_SQ1_SHIFT;
>> + stm32_adc_writel(adc, STM32F4_ADCX_SQR3, val);
>> +
>> + /* Set regular sequence len (0 for 1 conversion) */
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SQR1, STM32F4_L_MASK);
>> +
>> + /* Trigger detection disabled (conversion can be launched in SW) */
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>> +
>> + stm32_adc_conv_irq_enable(adc);
>> +
>> + stm32_adc_start_conv(adc);
>> +
>> + timeout = wait_for_completion_interruptible_timeout(
>> + &adc->completion, STM32_ADC_TIMEOUT);
>> + if (timeout == 0) {
>> + dev_warn(&indio_dev->dev, "Conversion timed out!\n");
>> + ret = -ETIMEDOUT;
>> + } else if (timeout < 0) {
>> + dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>> + ret = -EINTR;
>> + } else {
>> + *res = result;
>> + ret = IIO_VAL_INT;
>> + }
>> +
>> + stm32_adc_stop_conv(adc);
>> +
>> + stm32_adc_conv_irq_disable(adc);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct stm32_adc *adc = iio_priv(indio_dev);
>> + int ret = -EINVAL;
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + ret = iio_device_claim_direct_mode(indio_dev);
>> + if (ret)
>> + return ret;
>> + if (chan->type == IIO_VOLTAGE)
>> + ret = stm32_adc_single_conv(indio_dev, chan, val);
>> + else
>> + ret = -EINVAL;
>> + iio_device_release_direct_mode(indio_dev);
> return directly here. Basically always preferred to return directly if
> there is not cleanup to be done.
I will fix it.
>> + break;
>> + case IIO_CHAN_INFO_SCALE:
>> + *val = adc->common->vref_mv;
>> + *val2 = chan->scan_type.realbits;
>> + ret = IIO_VAL_FRACTIONAL_LOG2;
> return directly here.
I will fix it.
>> + break;
>> + default:
> return -EINVAL here.
I will fix it.
>> + break;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static irqreturn_t stm32_adc_isr(int irq, void *data)
>> +{
>> + struct stm32_adc *adc = data;
>> + u32 status = stm32_adc_readl(adc, STM32F4_ADCX_SR);
>> + irqreturn_t ret = IRQ_NONE;
>> +
>> + if (status & STM32F4_EOC) {
>> + *adc->buffer = stm32_adc_readw(adc, STM32F4_ADCX_DR);
>> + complete(&adc->completion);
>> + ret = IRQ_HANDLED;
> Slightly tidier to return IRQ_HANDLED here and directly return
> IRQ_NONE below.
I will fix it.
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
>> + const struct of_phandle_args *iiospec)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < indio_dev->num_channels; i++)
>> + if (indio_dev->channels[i].channel == iiospec->args[0])
>> + return i;
>> +
>> + return -EINVAL;
>> +}
>> +
>> +/**
>> + * stm32_adc_debugfs_reg_access - read or write register value
>> + *
>> + * To read a value from an ADC register:
>> + * echo [ADC reg offset] > direct_reg_access
>> + * cat direct_reg_access
>> + *
>> + * To write a value in a ADC register:
>> + * echo [ADC_reg_offset] [value] > direct_reg_access
>> + */
>> +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
>> + unsigned reg, unsigned writeval,
>> + unsigned *readval)
>> +{
>> + struct stm32_adc *adc = iio_priv(indio_dev);
>> +
>> + if (!readval)
>> + stm32_adc_writel(adc, reg, writeval);
>> + else
>> + *readval = stm32_adc_readl(adc, reg);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct iio_info stm32_adc_iio_info = {
>> + .read_raw = stm32_adc_read_raw,
>> + .debugfs_reg_access = stm32_adc_debugfs_reg_access,
>> + .of_xlate = stm32_adc_of_xlate,
>> + .driver_module = THIS_MODULE,
>> +};
>> +
>> +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>> + struct iio_chan_spec *chan,
>> + const struct stm32_adc_chan_spec *channel,
>> + int scan_index)
>> +{
>> + chan->type = channel->type;
>> + chan->channel = channel->channel;
>> + chan->datasheet_name = channel->name;
>> + chan->extend_name = channel->name;
> Don't set extend_name. That name doesn't add sufficient information to
> make it worth adding custom ABI to the userspace interface.
I will fix it.
>
>
>> + chan->scan_index = scan_index;
>> + chan->indexed = 1;
>> + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>> + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
>> + chan->scan_type.sign = 'u';
>> + chan->scan_type.realbits = 12;
>> + chan->scan_type.storagebits = 16;
>> +}
>> +
>> +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
>> +{
>> + struct device_node *node = indio_dev->dev.of_node;
>> + struct property *prop;
>> + const __be32 *cur;
>> + struct iio_chan_spec *channels;
>> + int scan_index = 0, num_channels;
>> + u32 val;
>> +
>> + num_channels = of_property_count_u32_elems(node, "st,adc-channels");
>> + if (num_channels < 0 ||
>> + num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
>> + dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>> + return num_channels < 0 ? num_channels : -EINVAL;
>> + }
>> +
>> + channels = devm_kcalloc(&indio_dev->dev, num_channels,
>> + sizeof(struct iio_chan_spec), GFP_KERNEL);
>> + if (!channels)
>> + return -ENOMEM;
>> +
>> + of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
>> + if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
>> + dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
>> + return -EINVAL;
>> + }
>> + stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
>> + &stm32f4_adc123_channels[val],
>> + scan_index);
>> + scan_index++;
>> + }
>> +
>> + indio_dev->num_channels = scan_index;
>> + indio_dev->channels = channels;
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_adc_probe(struct platform_device *pdev)
>> +{
>> + struct iio_dev *indio_dev;
>> + struct stm32_adc *adc;
>> + int ret;
>> +
>> + if (!pdev->dev.of_node)
>> + return -ENODEV;
>> +
>> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
>> + if (!indio_dev)
>> + return -ENOMEM;
>> +
>> + adc = iio_priv(indio_dev);
>> + adc->common = dev_get_drvdata(pdev->dev.parent);
>> + spin_lock_init(&adc->lock);
>> + init_completion(&adc->completion);
>> +
>> + indio_dev->name = dev_name(&pdev->dev);
>> + indio_dev->dev.parent = &pdev->dev;
>> + indio_dev->dev.of_node = pdev->dev.of_node;
>> + indio_dev->info = &stm32_adc_iio_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> + platform_set_drvdata(pdev, adc);
>> +
>> + ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
>> + if (ret != 0) {
>> + dev_err(&pdev->dev, "missing reg property\n");
>> + return -EINVAL;
>> + }
>> +
>> + adc->irq = platform_get_irq(pdev, 0);
>> + if (adc->irq < 0) {
>> + dev_err(&pdev->dev, "failed to get irq\n");
>> + return adc->irq;
>> + }
>> +
>> + ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
>> + 0, pdev->name, adc);
>> + if (ret) {
>> + dev_err(&pdev->dev, "failed to request IRQ\n");
>> + return ret;
>> + }
>> +
>> + adc->clk = devm_clk_get(&pdev->dev, NULL);
>> + if (IS_ERR(adc->clk)) {
> Could it concievably be deferred? Would be happier if this explicitly
> checked for -ENODEV or whatever gets returned when not clock has
> been specified.
I will fix it.
>> + adc->clk = NULL;
>> + dev_dbg(&pdev->dev, "No child clk found\n");
>> + } else {
>> + ret = clk_prepare_enable(adc->clk);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "clk enable failed\n");
>> + return ret;
>> + }
>> + }
>> +
>> + ret = stm32_adc_chan_of_init(indio_dev);
>> + if (ret < 0)
>> + goto err_clk_disable;
>> +
>> + ret = devm_iio_device_register(&pdev->dev, indio_dev);
> This use of devm registration is going to cause a race in the remove.
> The userspace interface will not be removed until after the remove
> function has run. That disables the clock thus leaving us a window
> where we could try and access the device with no clock enabled.
>
> Basic rule of thumb is that use of devm must not effect the ordering
> of unrolling what you do in probe when it comes to remove.
> (which more or less means that you can't use devm_iio_device_register
> unless you have no remove at all).
I will fix it.
Thanks,
Fabrice
>> + if (ret) {
>> + dev_err(&pdev->dev, "iio dev register failed\n");
>> + goto err_clk_disable;
>> + }
>> +
>> + return 0;
>> +
>> +err_clk_disable:
>> + if (adc->clk)
>> + clk_disable_unprepare(adc->clk);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_adc_remove(struct platform_device *pdev)
>> +{
>> + struct stm32_adc *adc = platform_get_drvdata(pdev);
>> +
>> + if (adc->clk)
>> + clk_disable_unprepare(adc->clk);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id stm32_adc_of_match[] = {
>> + { .compatible = "st,stm32f4-adc" },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
>> +
>> +static struct platform_driver stm32_adc_driver = {
>> + .probe = stm32_adc_probe,
>> + .remove = stm32_adc_remove,
>> + .driver = {
>> + .name = "stm32-adc",
>> + .of_match_table = stm32_adc_of_match,
>> + },
>> +};
>> +module_platform_driver(stm32_adc_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:stm32-adc");
>>
--
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 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-15 13:26 UTC (permalink / raw)
To: Lars-Peter Clausen, linux-iio, linux-arm-kernel, devicetree,
linux-kernel
Cc: jic23, lee.jones, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, knaack.h, pmeerw
In-Reply-To: <535ffb14-a92f-1556-a4cb-f2be0508289a@metafoo.de>
On 11/14/2016 01:11 PM, Lars-Peter Clausen wrote:
> On 11/10/2016 05:18 PM, Fabrice Gasnier wrote:
> [...]
>> + static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>> + const struct iio_chan_spec *chan,
>> + int *res)
>> +{
>> + struct stm32_adc *adc = iio_priv(indio_dev);
>> + long timeout;
>> + u32 val;
>> + u16 result;
>> + int ret;
>> +
>> + reinit_completion(&adc->completion);
>> +
>> + adc->buffer = &result;
>> +
>> + /* Program chan number in regular sequence */
>> + val = stm32_adc_readl(adc, STM32F4_ADCX_SQR3);
>> + val &= ~STM32F4_SQ1_MASK;
>> + val |= chan->channel << STM32F4_SQ1_SHIFT;
>> + stm32_adc_writel(adc, STM32F4_ADCX_SQR3, val);
>> +
>> + /* Set regular sequence len (0 for 1 conversion) */
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SQR1, STM32F4_L_MASK);
>> +
>> + /* Trigger detection disabled (conversion can be launched in SW) */
>> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>> +
>> + stm32_adc_conv_irq_enable(adc);
>> +
>> + stm32_adc_start_conv(adc);
>> +
>> + timeout = wait_for_completion_interruptible_timeout(
>> + &adc->completion, STM32_ADC_TIMEOUT);
>> + if (timeout == 0) {
>> + dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> This should be dev_dbg() at most. This out of band reporting is not
> particular useful for applications as it is impossible to match the error to
> the action that triggered it. And you also report the error through the
> error code, so the applications knows what is going on.
>
>> + ret = -ETIMEDOUT;
>> + } else if (timeout < 0) {
>> + dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>> + ret = -EINTR;
> This should just propagate the error returned by wait_for_completion...().
> This will make sure that the right behavior occurs based on the SA_RESTART
> policy.
Hi Lars,
Thanks for reviewing.
I'll update this in next revision.
Regards,
Fabrice
>
>> + } else {
>> + *res = result;
>> + ret = IIO_VAL_INT;
>> + }
>> +
>> + stm32_adc_stop_conv(adc);
>> +
>> + stm32_adc_conv_irq_disable(adc);
>> +
>> + return ret;
^ permalink raw reply
* Re: [PATCH v3 1/2] phy: rockchip-inno-usb2: support otg-port for rk3399
From: Kishon Vijay Abraham I @ 2016-11-15 13:39 UTC (permalink / raw)
To: 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: <1478520529-8869-2-git-send-email-wulf-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
On Monday 07 November 2016 05:38 PM, William Wu wrote:
> The rk3399 SoC USB2 PHY is comprised of one Host port and
> one OTG port. And OTG port is for USB2.0 part of USB3.0 OTG
> controller, as a part to construct a fully feature Type-C
> subsystem.
>
> With this patch, we can support OTG port with the following
> functions:
> - Support BC1.2 charger detect, and use extcon notifier to
> send USB charger types to power driver.
> - Support PHY suspend for power management.
> - Support OTG Host only mode.
>
> Signed-off-by: William Wu <wulf-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
merged.
Thanks
Kishon
> ---
> Changes in v3:
> - split the clock fix into a separate patch
>
> Changes in v2:
> - remove wakelock
>
> drivers/phy/phy-rockchip-inno-usb2.c | 591 +++++++++++++++++++++++++++++++++--
> 1 file changed, 561 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
> index ac20310..ecfd7d1 100644
> --- a/drivers/phy/phy-rockchip-inno-usb2.c
> +++ b/drivers/phy/phy-rockchip-inno-usb2.c
> @@ -17,6 +17,7 @@
> #include <linux/clk.h>
> #include <linux/clk-provider.h>
> #include <linux/delay.h>
> +#include <linux/extcon.h>
> #include <linux/interrupt.h>
> #include <linux/io.h>
> #include <linux/gpio/consumer.h>
> @@ -30,11 +31,15 @@
> #include <linux/of_platform.h>
> #include <linux/phy/phy.h>
> #include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> #include <linux/regmap.h>
> #include <linux/mfd/syscon.h>
> +#include <linux/usb/of.h>
> +#include <linux/usb/otg.h>
>
> #define BIT_WRITEABLE_SHIFT 16
> -#define SCHEDULE_DELAY (60 * HZ)
> +#define SCHEDULE_DELAY (60 * HZ)
> +#define OTG_SCHEDULE_DELAY (2 * HZ)
>
> enum rockchip_usb2phy_port_id {
> USB2PHY_PORT_OTG,
> @@ -49,6 +54,37 @@ enum rockchip_usb2phy_host_state {
> PHY_STATE_FS_LS_ONLINE = 4,
> };
>
> +/**
> + * Different states involved in USB charger detection.
> + * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection
> + * process is not yet started.
> + * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact.
> + * USB_CHG_STATE_DCD_DONE Data pin contact is detected.
> + * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects
> + * between SDP and DCP/CDP).
> + * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects
> + * between DCP and CDP).
> + * USB_CHG_STATE_DETECTED USB charger type is determined.
> + */
> +enum usb_chg_state {
> + USB_CHG_STATE_UNDEFINED = 0,
> + USB_CHG_STATE_WAIT_FOR_DCD,
> + USB_CHG_STATE_DCD_DONE,
> + USB_CHG_STATE_PRIMARY_DONE,
> + USB_CHG_STATE_SECONDARY_DONE,
> + USB_CHG_STATE_DETECTED,
> +};
> +
> +static const unsigned int rockchip_usb2phy_extcon_cable[] = {
> + EXTCON_USB,
> + EXTCON_USB_HOST,
> + EXTCON_CHG_USB_SDP,
> + EXTCON_CHG_USB_CDP,
> + EXTCON_CHG_USB_DCP,
> + EXTCON_CHG_USB_SLOW,
> + EXTCON_NONE,
> +};
> +
> struct usb2phy_reg {
> unsigned int offset;
> unsigned int bitend;
> @@ -58,19 +94,55 @@ struct usb2phy_reg {
> };
>
> /**
> + * struct rockchip_chg_det_reg: usb charger detect registers
> + * @cp_det: charging port detected successfully.
> + * @dcp_det: dedicated charging port detected successfully.
> + * @dp_det: assert data pin connect successfully.
> + * @idm_sink_en: open dm sink curren.
> + * @idp_sink_en: open dp sink current.
> + * @idp_src_en: open dm source current.
> + * @rdm_pdwn_en: open dm pull down resistor.
> + * @vdm_src_en: open dm voltage source.
> + * @vdp_src_en: open dp voltage source.
> + * @opmode: utmi operational mode.
> + */
> +struct rockchip_chg_det_reg {
> + struct usb2phy_reg cp_det;
> + struct usb2phy_reg dcp_det;
> + struct usb2phy_reg dp_det;
> + struct usb2phy_reg idm_sink_en;
> + struct usb2phy_reg idp_sink_en;
> + struct usb2phy_reg idp_src_en;
> + struct usb2phy_reg rdm_pdwn_en;
> + struct usb2phy_reg vdm_src_en;
> + struct usb2phy_reg vdp_src_en;
> + struct usb2phy_reg opmode;
> +};
> +
> +/**
> * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
> * @phy_sus: phy suspend register.
> + * @bvalid_det_en: vbus valid rise detection enable register.
> + * @bvalid_det_st: vbus valid rise detection status register.
> + * @bvalid_det_clr: vbus valid rise detection clear register.
> * @ls_det_en: linestate detection enable register.
> * @ls_det_st: linestate detection state register.
> * @ls_det_clr: linestate detection clear register.
> + * @utmi_avalid: utmi vbus avalid status register.
> + * @utmi_bvalid: utmi vbus bvalid status register.
> * @utmi_ls: utmi linestate state register.
> * @utmi_hstdet: utmi host disconnect register.
> */
> struct rockchip_usb2phy_port_cfg {
> struct usb2phy_reg phy_sus;
> + struct usb2phy_reg bvalid_det_en;
> + struct usb2phy_reg bvalid_det_st;
> + struct usb2phy_reg bvalid_det_clr;
> struct usb2phy_reg ls_det_en;
> struct usb2phy_reg ls_det_st;
> struct usb2phy_reg ls_det_clr;
> + struct usb2phy_reg utmi_avalid;
> + struct usb2phy_reg utmi_bvalid;
> struct usb2phy_reg utmi_ls;
> struct usb2phy_reg utmi_hstdet;
> };
> @@ -80,31 +152,51 @@ struct rockchip_usb2phy_port_cfg {
> * @reg: the address offset of grf for usb-phy config.
> * @num_ports: specify how many ports that the phy has.
> * @clkout_ctl: keep on/turn off output clk of phy.
> + * @chg_det: charger detection registers.
> */
> struct rockchip_usb2phy_cfg {
> unsigned int reg;
> unsigned int num_ports;
> struct usb2phy_reg clkout_ctl;
> const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS];
> + const struct rockchip_chg_det_reg chg_det;
> };
>
> /**
> * struct rockchip_usb2phy_port: usb-phy port data.
> * @port_id: flag for otg port or host port.
> * @suspended: phy suspended flag.
> + * @utmi_avalid: utmi avalid status usage flag.
> + * true - use avalid to get vbus status
> + * flase - use bvalid to get vbus status
> + * @vbus_attached: otg device vbus status.
> + * @bvalid_irq: IRQ number assigned for vbus valid rise detection.
> * @ls_irq: IRQ number assigned for linestate detection.
> * @mutex: for register updating in sm_work.
> - * @sm_work: OTG state machine work.
> + * @chg_work: charge detect work.
> + * @otg_sm_work: OTG state machine work.
> + * @sm_work: HOST state machine work.
> * @phy_cfg: port register configuration, assigned by driver data.
> + * @event_nb: hold event notification callback.
> + * @state: define OTG enumeration states before device reset.
> + * @mode: the dr_mode of the controller.
> */
> struct rockchip_usb2phy_port {
> struct phy *phy;
> unsigned int port_id;
> bool suspended;
> + bool utmi_avalid;
> + bool vbus_attached;
> + int bvalid_irq;
> int ls_irq;
> struct mutex mutex;
> + struct delayed_work chg_work;
> + struct delayed_work otg_sm_work;
> struct delayed_work sm_work;
> const struct rockchip_usb2phy_port_cfg *port_cfg;
> + struct notifier_block event_nb;
> + enum usb_otg_state state;
> + enum usb_dr_mode mode;
> };
>
> /**
> @@ -113,6 +205,11 @@ struct rockchip_usb2phy_port {
> * @clk: clock struct of phy input clk.
> * @clk480m: clock struct of phy output clk.
> * @clk_hw: clock struct of phy output clk management.
> + * @chg_state: states involved in USB charger detection.
> + * @chg_type: USB charger types.
> + * @dcd_retries: The retry count used to track Data contact
> + * detection process.
> + * @edev: extcon device for notification registration
> * @phy_cfg: phy register configuration, assigned by driver data.
> * @ports: phy port instance.
> */
> @@ -122,6 +219,10 @@ struct rockchip_usb2phy {
> struct clk *clk;
> struct clk *clk480m;
> struct clk_hw clk480m_hw;
> + enum usb_chg_state chg_state;
> + enum power_supply_type chg_type;
> + u8 dcd_retries;
> + struct extcon_dev *edev;
> const struct rockchip_usb2phy_cfg *phy_cfg;
> struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS];
> };
> @@ -263,33 +364,84 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
> return ret;
> }
>
> -static int rockchip_usb2phy_init(struct phy *phy)
> +static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
> {
> - struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
> - struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
> int ret;
> + struct device_node *node = rphy->dev->of_node;
> + struct extcon_dev *edev;
> +
> + if (of_property_read_bool(node, "extcon")) {
> + edev = extcon_get_edev_by_phandle(rphy->dev, 0);
> + if (IS_ERR(edev)) {
> + if (PTR_ERR(edev) != -EPROBE_DEFER)
> + dev_err(rphy->dev, "Invalid or missing extcon\n");
> + return PTR_ERR(edev);
> + }
> + } else {
> + /* Initialize extcon device */
> + edev = devm_extcon_dev_allocate(rphy->dev,
> + rockchip_usb2phy_extcon_cable);
>
> - if (rport->port_id == USB2PHY_PORT_HOST) {
> - /* clear linestate and enable linestate detect irq */
> - mutex_lock(&rport->mutex);
> + if (IS_ERR(edev))
> + return -ENOMEM;
>
> - ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
> + ret = devm_extcon_dev_register(rphy->dev, edev);
> if (ret) {
> - mutex_unlock(&rport->mutex);
> + dev_err(rphy->dev, "failed to register extcon device\n");
> return ret;
> }
> + }
>
> - ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
> - if (ret) {
> - mutex_unlock(&rport->mutex);
> - return ret;
> + rphy->edev = edev;
> +
> + return 0;
> +}
> +
> +static int rockchip_usb2phy_init(struct phy *phy)
> +{
> + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
> + struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
> + int ret = 0;
> +
> + mutex_lock(&rport->mutex);
> +
> + if (rport->port_id == USB2PHY_PORT_OTG) {
> + if (rport->mode != USB_DR_MODE_HOST) {
> + /* clear bvalid status and enable bvalid detect irq */
> + ret = property_enable(rphy,
> + &rport->port_cfg->bvalid_det_clr,
> + true);
> + if (ret)
> + goto out;
> +
> + ret = property_enable(rphy,
> + &rport->port_cfg->bvalid_det_en,
> + true);
> + if (ret)
> + goto out;
> +
> + schedule_delayed_work(&rport->otg_sm_work,
> + OTG_SCHEDULE_DELAY);
> + } else {
> + /* If OTG works in host only mode, do nothing. */
> + dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode);
> }
> + } else if (rport->port_id == USB2PHY_PORT_HOST) {
> + /* clear linestate and enable linestate detect irq */
> + ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
> + if (ret)
> + goto out;
> +
> + ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
> + if (ret)
> + goto out;
>
> - mutex_unlock(&rport->mutex);
> schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
> }
>
> - return 0;
> +out:
> + mutex_unlock(&rport->mutex);
> + return ret;
> }
>
> static int rockchip_usb2phy_power_on(struct phy *phy)
> @@ -340,7 +492,11 @@ static int rockchip_usb2phy_exit(struct phy *phy)
> {
> struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
>
> - if (rport->port_id == USB2PHY_PORT_HOST)
> + if (rport->port_id == USB2PHY_PORT_OTG &&
> + rport->mode != USB_DR_MODE_HOST) {
> + cancel_delayed_work_sync(&rport->otg_sm_work);
> + cancel_delayed_work_sync(&rport->chg_work);
> + } else if (rport->port_id == USB2PHY_PORT_HOST)
> cancel_delayed_work_sync(&rport->sm_work);
>
> return 0;
> @@ -354,6 +510,249 @@ static const struct phy_ops rockchip_usb2phy_ops = {
> .owner = THIS_MODULE,
> };
>
> +static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
> +{
> + struct rockchip_usb2phy_port *rport =
> + container_of(work, struct rockchip_usb2phy_port,
> + otg_sm_work.work);
> + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> + static unsigned int cable;
> + unsigned long delay;
> + bool vbus_attach, sch_work, notify_charger;
> +
> + if (rport->utmi_avalid)
> + vbus_attach =
> + property_enabled(rphy, &rport->port_cfg->utmi_avalid);
> + else
> + vbus_attach =
> + property_enabled(rphy, &rport->port_cfg->utmi_bvalid);
> +
> + sch_work = false;
> + notify_charger = false;
> + delay = OTG_SCHEDULE_DELAY;
> + dev_dbg(&rport->phy->dev, "%s otg sm work\n",
> + usb_otg_state_string(rport->state));
> +
> + switch (rport->state) {
> + case OTG_STATE_UNDEFINED:
> + rport->state = OTG_STATE_B_IDLE;
> + if (!vbus_attach)
> + rockchip_usb2phy_power_off(rport->phy);
> + /* fall through */
> + case OTG_STATE_B_IDLE:
> + if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) {
> + dev_dbg(&rport->phy->dev, "usb otg host connect\n");
> + rport->state = OTG_STATE_A_HOST;
> + rockchip_usb2phy_power_on(rport->phy);
> + return;
> + } else if (vbus_attach) {
> + dev_dbg(&rport->phy->dev, "vbus_attach\n");
> + switch (rphy->chg_state) {
> + case USB_CHG_STATE_UNDEFINED:
> + schedule_delayed_work(&rport->chg_work, 0);
> + return;
> + case USB_CHG_STATE_DETECTED:
> + switch (rphy->chg_type) {
> + case POWER_SUPPLY_TYPE_USB:
> + dev_dbg(&rport->phy->dev,
> + "sdp cable is connecetd\n");
> + rockchip_usb2phy_power_on(rport->phy);
> + rport->state = OTG_STATE_B_PERIPHERAL;
> + notify_charger = true;
> + sch_work = true;
> + cable = EXTCON_CHG_USB_SDP;
> + break;
> + case POWER_SUPPLY_TYPE_USB_DCP:
> + dev_dbg(&rport->phy->dev,
> + "dcp cable is connecetd\n");
> + rockchip_usb2phy_power_off(rport->phy);
> + notify_charger = true;
> + sch_work = true;
> + cable = EXTCON_CHG_USB_DCP;
> + break;
> + case POWER_SUPPLY_TYPE_USB_CDP:
> + dev_dbg(&rport->phy->dev,
> + "cdp cable is connecetd\n");
> + rockchip_usb2phy_power_on(rport->phy);
> + rport->state = OTG_STATE_B_PERIPHERAL;
> + notify_charger = true;
> + sch_work = true;
> + cable = EXTCON_CHG_USB_CDP;
> + break;
> + default:
> + break;
> + }
> + break;
> + default:
> + break;
> + }
> + } else {
> + notify_charger = true;
> + rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
> + }
> +
> + if (rport->vbus_attached != vbus_attach) {
> + rport->vbus_attached = vbus_attach;
> +
> + if (notify_charger && rphy->edev)
> + extcon_set_cable_state_(rphy->edev,
> + cable, vbus_attach);
> + }
> + break;
> + case OTG_STATE_B_PERIPHERAL:
> + if (!vbus_attach) {
> + dev_dbg(&rport->phy->dev, "usb disconnect\n");
> + rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
> + rport->state = OTG_STATE_B_IDLE;
> + delay = 0;
> + rockchip_usb2phy_power_off(rport->phy);
> + }
> + sch_work = true;
> + break;
> + case OTG_STATE_A_HOST:
> + if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) {
> + dev_dbg(&rport->phy->dev, "usb otg host disconnect\n");
> + rport->state = OTG_STATE_B_IDLE;
> + rockchip_usb2phy_power_off(rport->phy);
> + }
> + break;
> + default:
> + break;
> + }
> +
> + if (sch_work)
> + schedule_delayed_work(&rport->otg_sm_work, delay);
> +}
> +
> +static const char *chg_to_string(enum power_supply_type chg_type)
> +{
> + switch (chg_type) {
> + case POWER_SUPPLY_TYPE_USB:
> + return "USB_SDP_CHARGER";
> + case POWER_SUPPLY_TYPE_USB_DCP:
> + return "USB_DCP_CHARGER";
> + case POWER_SUPPLY_TYPE_USB_CDP:
> + return "USB_CDP_CHARGER";
> + default:
> + return "INVALID_CHARGER";
> + }
> +}
> +
> +static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy,
> + bool en)
> +{
> + property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en);
> + property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en);
> +}
> +
> +static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy,
> + bool en)
> +{
> + property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en);
> + property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en);
> +}
> +
> +static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy,
> + bool en)
> +{
> + property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en);
> + property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en);
> +}
> +
> +#define CHG_DCD_POLL_TIME (100 * HZ / 1000)
> +#define CHG_DCD_MAX_RETRIES 6
> +#define CHG_PRIMARY_DET_TIME (40 * HZ / 1000)
> +#define CHG_SECONDARY_DET_TIME (40 * HZ / 1000)
> +static void rockchip_chg_detect_work(struct work_struct *work)
> +{
> + struct rockchip_usb2phy_port *rport =
> + container_of(work, struct rockchip_usb2phy_port, chg_work.work);
> + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> + bool is_dcd, tmout, vout;
> + unsigned long delay;
> +
> + dev_dbg(&rport->phy->dev, "chg detection work state = %d\n",
> + rphy->chg_state);
> + switch (rphy->chg_state) {
> + case USB_CHG_STATE_UNDEFINED:
> + if (!rport->suspended)
> + rockchip_usb2phy_power_off(rport->phy);
> + /* put the controller in non-driving mode */
> + property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false);
> + /* Start DCD processing stage 1 */
> + rockchip_chg_enable_dcd(rphy, true);
> + rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
> + rphy->dcd_retries = 0;
> + delay = CHG_DCD_POLL_TIME;
> + break;
> + case USB_CHG_STATE_WAIT_FOR_DCD:
> + /* get data contact detection status */
> + is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det);
> + tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES;
> + /* stage 2 */
> + if (is_dcd || tmout) {
> + /* stage 4 */
> + /* Turn off DCD circuitry */
> + rockchip_chg_enable_dcd(rphy, false);
> + /* Voltage Source on DP, Probe on DM */
> + rockchip_chg_enable_primary_det(rphy, true);
> + delay = CHG_PRIMARY_DET_TIME;
> + rphy->chg_state = USB_CHG_STATE_DCD_DONE;
> + } else {
> + /* stage 3 */
> + delay = CHG_DCD_POLL_TIME;
> + }
> + break;
> + case USB_CHG_STATE_DCD_DONE:
> + vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det);
> + rockchip_chg_enable_primary_det(rphy, false);
> + if (vout) {
> + /* Voltage Source on DM, Probe on DP */
> + rockchip_chg_enable_secondary_det(rphy, true);
> + delay = CHG_SECONDARY_DET_TIME;
> + rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE;
> + } else {
> + if (tmout) {
> + /* floating charger found */
> + rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
> + rphy->chg_state = USB_CHG_STATE_DETECTED;
> + delay = 0;
> + } else {
> + rphy->chg_type = POWER_SUPPLY_TYPE_USB;
> + rphy->chg_state = USB_CHG_STATE_DETECTED;
> + delay = 0;
> + }
> + }
> + break;
> + case USB_CHG_STATE_PRIMARY_DONE:
> + vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det);
> + /* Turn off voltage source */
> + rockchip_chg_enable_secondary_det(rphy, false);
> + if (vout)
> + rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
> + else
> + rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP;
> + /* fall through */
> + case USB_CHG_STATE_SECONDARY_DONE:
> + rphy->chg_state = USB_CHG_STATE_DETECTED;
> + delay = 0;
> + /* fall through */
> + case USB_CHG_STATE_DETECTED:
> + /* put the controller in normal mode */
> + property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true);
> + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
> + dev_info(&rport->phy->dev, "charger = %s\n",
> + chg_to_string(rphy->chg_type));
> + return;
> + default:
> + return;
> + }
> +
> + schedule_delayed_work(&rport->chg_work, delay);
> +}
> +
> /*
> * The function manage host-phy port state and suspend/resume phy port
> * to save power.
> @@ -485,6 +884,26 @@ static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
> return IRQ_HANDLED;
> }
>
> +static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data)
> +{
> + struct rockchip_usb2phy_port *rport = data;
> + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> +
> + if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st))
> + return IRQ_NONE;
> +
> + mutex_lock(&rport->mutex);
> +
> + /* clear bvalid detect irq pending status */
> + property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true);
> +
> + mutex_unlock(&rport->mutex);
> +
> + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
> +
> + return IRQ_HANDLED;
> +}
> +
> static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
> struct rockchip_usb2phy_port *rport,
> struct device_node *child_np)
> @@ -509,13 +928,86 @@ static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
> IRQF_ONESHOT,
> "rockchip_usb2phy", rport);
> if (ret) {
> - dev_err(rphy->dev, "failed to request irq handle\n");
> + dev_err(rphy->dev, "failed to request linestate irq handle\n");
> return ret;
> }
>
> return 0;
> }
>
> +static int rockchip_otg_event(struct notifier_block *nb,
> + unsigned long event, void *ptr)
> +{
> + struct rockchip_usb2phy_port *rport =
> + container_of(nb, struct rockchip_usb2phy_port, event_nb);
> +
> + schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY);
> +
> + return NOTIFY_DONE;
> +}
> +
> +static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
> + struct rockchip_usb2phy_port *rport,
> + struct device_node *child_np)
> +{
> + int ret;
> +
> + rport->port_id = USB2PHY_PORT_OTG;
> + rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
> + rport->state = OTG_STATE_UNDEFINED;
> +
> + /*
> + * set suspended flag to true, but actually don't
> + * put phy in suspend mode, it aims to enable usb
> + * phy and clock in power_on() called by usb controller
> + * driver during probe.
> + */
> + rport->suspended = true;
> + rport->vbus_attached = false;
> +
> + mutex_init(&rport->mutex);
> +
> + rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
> + if (rport->mode == USB_DR_MODE_HOST) {
> + ret = 0;
> + goto out;
> + }
> +
> + INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
> + INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
> +
> + rport->utmi_avalid =
> + of_property_read_bool(child_np, "rockchip,utmi-avalid");
> +
> + rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
> + if (rport->bvalid_irq < 0) {
> + dev_err(rphy->dev, "no vbus valid irq provided\n");
> + ret = rport->bvalid_irq;
> + goto out;
> + }
> +
> + ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL,
> + rockchip_usb2phy_bvalid_irq,
> + IRQF_ONESHOT,
> + "rockchip_usb2phy_bvalid", rport);
> + if (ret) {
> + dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n");
> + goto out;
> + }
> +
> + if (!IS_ERR(rphy->edev)) {
> + rport->event_nb.notifier_call = rockchip_otg_event;
> +
> + ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST,
> + &rport->event_nb);
> + if (ret)
> + dev_err(rphy->dev, "register USB HOST notifier failed\n");
> + }
> +
> +out:
> + return ret;
> +}
> +
> static int rockchip_usb2phy_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> @@ -553,8 +1045,14 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
>
> rphy->dev = dev;
> phy_cfgs = match->data;
> + rphy->chg_state = USB_CHG_STATE_UNDEFINED;
> + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
> platform_set_drvdata(pdev, rphy);
>
> + ret = rockchip_usb2phy_extcon_register(rphy);
> + if (ret)
> + return ret;
> +
> /* find out a proper config which can be matched with dt. */
> index = 0;
> while (phy_cfgs[index].reg) {
> @@ -591,13 +1089,9 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
> struct rockchip_usb2phy_port *rport = &rphy->ports[index];
> struct phy *phy;
>
> - /*
> - * This driver aim to support both otg-port and host-port,
> - * but unfortunately, the otg part is not ready in current,
> - * so this comments and below codes are interim, which should
> - * be changed after otg-port is supplied soon.
> - */
> - if (of_node_cmp(child_np->name, "host-port"))
> + /* This driver aims to support both otg-port and host-port */
> + if (of_node_cmp(child_np->name, "host-port") &&
> + of_node_cmp(child_np->name, "otg-port"))
> goto next_child;
>
> phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
> @@ -610,9 +1104,18 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
> rport->phy = phy;
> phy_set_drvdata(rport->phy, rport);
>
> - ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np);
> - if (ret)
> - goto put_child;
> + /* initialize otg/host port separately */
> + if (!of_node_cmp(child_np->name, "host-port")) {
> + ret = rockchip_usb2phy_host_port_init(rphy, rport,
> + child_np);
> + if (ret)
> + goto put_child;
> + } else {
> + ret = rockchip_usb2phy_otg_port_init(rphy, rport,
> + child_np);
> + if (ret)
> + goto put_child;
> + }
>
> next_child:
> /* to prevent out of boundary */
> @@ -654,10 +1157,18 @@ static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
>
> static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
> {
> - .reg = 0xe450,
> + .reg = 0xe450,
> .num_ports = 2,
> .clkout_ctl = { 0xe450, 4, 4, 1, 0 },
> .port_cfgs = {
> + [USB2PHY_PORT_OTG] = {
> + .phy_sus = { 0xe454, 1, 0, 2, 1 },
> + .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 },
> + .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 },
> + .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 },
> + .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 },
> + .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 },
> + },
> [USB2PHY_PORT_HOST] = {
> .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 },
> .ls_det_en = { 0xe3c0, 6, 6, 0, 1 },
> @@ -667,12 +1178,32 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
> .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 }
> }
> },
> + .chg_det = {
> + .opmode = { 0xe454, 3, 0, 5, 1 },
> + .cp_det = { 0xe2ac, 2, 2, 0, 1 },
> + .dcp_det = { 0xe2ac, 1, 1, 0, 1 },
> + .dp_det = { 0xe2ac, 0, 0, 0, 1 },
> + .idm_sink_en = { 0xe450, 8, 8, 0, 1 },
> + .idp_sink_en = { 0xe450, 7, 7, 0, 1 },
> + .idp_src_en = { 0xe450, 9, 9, 0, 1 },
> + .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 },
> + .vdm_src_en = { 0xe450, 12, 12, 0, 1 },
> + .vdp_src_en = { 0xe450, 11, 11, 0, 1 },
> + },
> },
> {
> - .reg = 0xe460,
> + .reg = 0xe460,
> .num_ports = 2,
> .clkout_ctl = { 0xe460, 4, 4, 1, 0 },
> .port_cfgs = {
> + [USB2PHY_PORT_OTG] = {
> + .phy_sus = { 0xe464, 1, 0, 2, 1 },
> + .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 },
> + .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 },
> + .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
> + .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 },
> + .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 },
> + },
> [USB2PHY_PORT_HOST] = {
> .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 },
> .ls_det_en = { 0xe3c0, 11, 11, 0, 1 },
>
^ permalink raw reply
* Re: [PATCH 3/4] dt: bindings: add new dt entry for BTCOEX feature in qcom, ath10k.txt
From: Valo, Kalle @ 2016-11-15 13:39 UTC (permalink / raw)
To: Raja, Tamizh Chelvam
Cc: ath10k-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
tamizhchelvam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org,
linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <1478617462-28188-1-git-send-email-c_traja-Rm6X0d1/PG5y9aJCnZT0Uw@public.gmane.org>
(Adding devicetree list)
<c_traja-Rm6X0d1/PG5y9aJCnZT0Uw@public.gmane.org> writes:
> From: Tamizh chelvam <tamizhchelvam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>
> There two things done in this patch.
>
> 1) 'btcoex_support' flag for BTCOEX feature support by the hardware.
> 2) 'wlan_btcoex_gpio' is used to fill wlan priority pin number for
> BTCOEX priority feature support.
>
> Signed-off-by: Tamizh chelvam <tamizhchelvam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> ---
> .../bindings/net/wireless/qcom,ath10k.txt | 4 ++++
> 1 file changed, 4 insertions(+)
As this changes the device tree bindings you need to CC the device tree
list. Please resend the whole patchset (and mark it as v2).
--
Kalle Valo--
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/2] Add support for OV5647 sensor
From: Guenter Roeck @ 2016-11-15 13:50 UTC (permalink / raw)
To: Pavel Machek, Ramiro Oliveira
Cc: mchehab, linux-kernel, linux-media, robh+dt, devicetree, davem,
gregkh, geert+renesas, akpm, hverkuil, dheitmueller, slongerbeam,
lars, robert.jarzmik, pali.rohar, sakari.ailus, mark.rutland,
CARLOS.PALMINHA
In-Reply-To: <20161115121032.GB7018@amd>
On 11/15/2016 04:10 AM, Pavel Machek wrote:
> 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.
>
It seems to me that it does return a value < 0 each time an error is detected.
Guenter
^ permalink raw reply
* [PATCH v2 0/3] ARM: dts: sun7i: BPI-M1+ USB support
From: Chen-Yu Tsai @ 2016-11-15 13:51 UTC (permalink / raw)
To: Maxime Ripard; +Cc: Chen-Yu Tsai, devicetree, linux-arm-kernel, linux-kernel
Hi Maxime,
These are the remaining patches of my BPI-M1+ fixes series from July.
Changes since v1:
- Split out USB PHY enable patch.
- Dropped custom OPP table. Tested the A20 default one with
cpufreq-ljt-stress-test and it seemed stable.
- Dropped voltage range for cpu supply regulator to normal 1.0V ~ 1.4V.
- Dropped pinmux setting for OTG ID pin.
Please have a look.
Regards
ChenYu
Chen-Yu Tsai (3):
ARM: dts: sun7i: bananapi-m1-plus: Enable USB PHY for USB host support
ARM: dts: sun7i: bananapi-m1-plus: Add PMIC regulators
ARM: dts: sun7i: bananapi-m1-plus: Enable USB OTG
arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 60 ++++++++++++++++++++++--
1 file changed, 56 insertions(+), 4 deletions(-)
--
2.10.2
^ permalink raw reply
* [PATCH v2 1/3] ARM: dts: sun7i: bananapi-m1-plus: Enable USB PHY for USB host support
From: Chen-Yu Tsai @ 2016-11-15 13:51 UTC (permalink / raw)
To: Maxime Ripard; +Cc: Chen-Yu Tsai, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20161115135106.438-1-wens@csie.org>
The 2 USB host ports are directly tied to the 2 USB hosts in the SoC.
The 2 host pairs were already enabled, but the USB PHY wasn't.
VBUS on the 2 ports are always on.
Enable the USB PHY.
Fixes: 04c85ecad32a ("ARM: dts: sun7i: Add dts file for Bananapi M1 Plus
board")
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
index ba5bca0fe997..44377a98cc89 100644
--- a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
@@ -227,3 +227,8 @@
pinctrl-0 = <&uart0_pins_a>;
status = "okay";
};
+
+&usbphy {
+ /* VBUS on usb host ports are tied to DC5V and therefore always on */
+ status = "okay";
+};
--
2.10.2
^ permalink raw reply related
* [PATCH v2 2/3] ARM: dts: sun7i: bananapi-m1-plus: Add PMIC regulators
From: Chen-Yu Tsai @ 2016-11-15 13:51 UTC (permalink / raw)
To: Maxime Ripard; +Cc: Chen-Yu Tsai, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20161115135106.438-1-wens@csie.org>
The Bananapi M1+, like other Allwinner A20 based boards, uses the
AXP209 PMIC to supply its power.
Add the AXP209 regulators.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 35 +++++++++++++++++++++---
1 file changed, 31 insertions(+), 4 deletions(-)
diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
index 44377a98cc89..ac19630c1c23 100644
--- a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
@@ -105,6 +105,10 @@
status = "okay";
};
+&cpu0 {
+ cpu-supply = <®_dcdc2>;
+};
+
&ehci0 {
status = "okay";
};
@@ -132,16 +136,14 @@
status = "okay";
axp209: pmic@34 {
- compatible = "x-powers,axp209";
reg = <0x34>;
interrupt-parent = <&nmi_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
-
- interrupt-controller;
- #interrupt-cells = <1>;
};
};
+#include "axp209.dtsi"
+
&ir0 {
pinctrl-names = "default";
pinctrl-0 = <&ir0_rx_pins_a>;
@@ -222,6 +224,31 @@
};
};
+®_dcdc2 {
+ regulator-always-on;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-name = "vdd-cpu";
+};
+
+®_dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-name = "vdd-int-dll";
+};
+
+®_ldo1 {
+ regulator-name = "vdd-rtc";
+};
+
+®_ldo2 {
+ regulator-always-on;
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-name = "avcc";
+};
+
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins_a>;
--
2.10.2
^ permalink raw reply related
* [PATCH v2 3/3] ARM: dts: sun7i: bananapi-m1-plus: Enable USB OTG
From: Chen-Yu Tsai @ 2016-11-15 13:51 UTC (permalink / raw)
To: Maxime Ripard; +Cc: Chen-Yu Tsai, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20161115135106.438-1-wens@csie.org>
The Bananapi M1+ supports USB OTG, with the PMIC doing VBUS sensing.
Enable the USB OTG related functions.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
index ac19630c1c23..5f7114e13850 100644
--- a/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
@@ -194,6 +194,10 @@
status = "okay";
};
+&otg_sram {
+ status = "okay";
+};
+
&pio {
gmac_power_pin_bpi_m1p: gmac_power_pin@0 {
allwinner,pins = "PH23";
@@ -249,13 +253,29 @@
regulator-name = "avcc";
};
+®_usb0_vbus {
+ status = "okay";
+};
+
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins_a>;
status = "okay";
};
+&usb_otg {
+ dr_mode = "otg";
+ status = "okay";
+};
+
+&usb_power_supply {
+ status = "okay";
+};
+
&usbphy {
+ usb0_id_det-gpios = <&pio 7 4 GPIO_ACTIVE_HIGH>; /* PH4 */
+ usb0_vbus_power-supply = <&usb_power_supply>;
+ usb0_vbus-supply = <®_usb0_vbus>;
/* VBUS on usb host ports are tied to DC5V and therefore always on */
status = "okay";
};
--
2.10.2
^ permalink raw reply related
* Re: [PATCH v4 0/8] IIO wrapper drivers, dpot-dac and envelope-detector
From: Peter Rosin @ 2016-11-15 14:03 UTC (permalink / raw)
To: Jonathan Cameron, linux-kernel
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, Daniel Baluta, Slawomir Stepien,
Thomas Gleixner, linux-iio, devicetree
In-Reply-To: <9d0ddee1-6f50-0fe9-2efa-a31f1e239aa6@kernel.org>
On 2016-11-12 18:15, Jonathan Cameron wrote:
> On 08/11/16 11:58, Peter Rosin wrote:
>> I also wonder if the "new" *_available ABI should perhaps be documented
>> for all variants directly in sysfs-bus-iio instead of doing it in a driver
>> specific maner that I did? But that can be fixed later by someone more
>> capable than me :-)
> You doubt yourself too much ;) Some one with fewer inhibitions you mean!
Maybe so, I just find that file long and confusing. I do not have a mental
picture of what fits where...
> Anyhow, just thought I'd add that I like this series very much.
> It's a nice interesting use of the infrastructures. Good to see people
> are getting more adventurous all the time.
Thanks for the confidence boost! And also thanks for taking care of the
bot fallout...
Cheers,
Peter
^ permalink raw reply
* [PATCH] ARM64: zynqmp: Fix W=1 dtc 1.4 warnings
From: Michal Simek @ 2016-11-15 14:04 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Sören Brinkmann, Punnaiah Choudary Kalluri, monstr,
Alexander Graf, Carlo Caione, devicetree, Bharat Kumar Gogada,
linux-kernel, Marc Zyngier, Rob Herring, Will Deacon,
Catalin Marinas, Mark Rutland
The patch removes these warnings reported by dtc 1.4:
Warning (unit_address_vs_reg): Node /amba_apu has a reg or ranges
property, but no unit name
Warning (unit_address_vs_reg): Node /memory has a reg or ranges
property, but no unit name
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts | 2 +-
arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
index 358089687a69..ef1b9e573af0 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-ep108.dts
@@ -27,7 +27,7 @@
stdout-path = "serial0:115200n8";
};
- memory {
+ memory@0 {
device_type = "memory";
reg = <0x0 0x0 0x0 0x40000000>;
};
diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 68a908334c7b..83791eadff41 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -72,7 +72,7 @@
<1 10 0xf08>;
};
- amba_apu {
+ amba_apu: amba_apu@0 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
--
1.9.1
^ permalink raw reply related
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