* [PATCH v2 3/4] usb: musb: da8xx: Add DT support for the DA8xx driver
From: Alexandre Bailon @ 2016-10-26 15:03 UTC (permalink / raw)
To: khilman, robh+dt, b-liu
Cc: linux-kernel, linux-usb, devicetree, linux-arm-kernel,
Petr Kulhavy, Alexandre Bailon
In-Reply-To: <1477494237-22831-1-git-send-email-abailon@baylibre.com>
From: Petr Kulhavy <petr@barix.com>
This adds DT support for TI DA8xx/OMAP-L1x/AM17xx/AM18xx MUSB driver
Signed-off-by: Petr Kulhavy <petr@barix.com>
Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
---
drivers/usb/musb/da8xx.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 210b7e4..bfa571d 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -6,6 +6,9 @@
* Based on the DaVinci "glue layer" code.
* Copyright (C) 2005-2006 by Texas Instruments
*
+ * DT support
+ * Copyright (c) 2016 Petr Kulhavy <petr@barix.com>
+ *
* This file is part of the Inventra Controller Driver for Linux.
*
* The Inventra Controller Driver for Linux is free software; you
@@ -433,6 +436,21 @@ static int da8xx_musb_exit(struct musb *musb)
return 0;
}
+static inline u8 get_vbus_power(struct device *dev)
+{
+ struct regulator *vbus_supply;
+ int current_uA;
+
+ vbus_supply = regulator_get_optional(dev, "vbus");
+ if (IS_ERR(vbus_supply))
+ return 255;
+ current_uA = regulator_get_current_limit(vbus_supply);
+ regulator_put(vbus_supply);
+ if (current_uA <= 0 || current_uA > 510000)
+ return 255;
+ return current_uA / 1000 / 2;
+}
+
static const struct musb_platform_ops da8xx_ops = {
.quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP,
.init = da8xx_musb_init,
@@ -458,6 +476,12 @@ static const struct platform_device_info da8xx_dev_info = {
.dma_mask = DMA_BIT_MASK(32),
};
+static const struct musb_hdrc_config da8xx_config = {
+ .ram_bits = 10,
+ .num_eps = 5,
+ .multipoint = 1,
+};
+
static int da8xx_probe(struct platform_device *pdev)
{
struct resource musb_resources[2];
@@ -465,7 +489,9 @@ static int da8xx_probe(struct platform_device *pdev)
struct da8xx_glue *glue;
struct platform_device_info pinfo;
struct clk *clk;
+ struct device_node *np = pdev->dev.of_node;
int ret;
+ struct resource *res;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
@@ -486,6 +512,18 @@ static int da8xx_probe(struct platform_device *pdev)
glue->dev = &pdev->dev;
glue->clk = clk;
+ if (IS_ENABLED(CONFIG_OF) && np) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ /* FIXME */
+ return -ENOMEM;
+ }
+
+ pdata->config = &da8xx_config;
+ pdata->mode = musb_get_mode(&pdev->dev);
+ pdata->power = get_vbus_power(&pdev->dev);
+ }
+
pdata->platform_ops = &da8xx_ops;
glue->usb_phy = usb_phy_generic_register();
@@ -536,11 +574,20 @@ static int da8xx_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id da8xx_id_table[] = {
+ {
+ .compatible = "ti,da830-musb",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, da8xx_id_table);
+
static struct platform_driver da8xx_driver = {
.probe = da8xx_probe,
.remove = da8xx_remove,
.driver = {
.name = "musb-da8xx",
+ .of_match_table = of_match_ptr(da8xx_id_table),
},
};
--
2.7.3
^ permalink raw reply related
* [PATCH v2 2/4] usb: musb: core: added helper function for parsing DT
From: Alexandre Bailon @ 2016-10-26 15:03 UTC (permalink / raw)
To: khilman, robh+dt, b-liu
Cc: linux-kernel, linux-usb, devicetree, linux-arm-kernel,
Petr Kulhavy, Alexandre Bailon
In-Reply-To: <1477494237-22831-1-git-send-email-abailon@baylibre.com>
From: Petr Kulhavy <petr@barix.com>
This adds the function musb_get_mode() to get the DT property "dr_mode"
Signed-off-by: Petr Kulhavy <petr@barix.com>
Acked-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
---
drivers/usb/musb/musb_core.c | 19 +++++++++++++++++++
drivers/usb/musb/musb_core.h | 5 +++++
2 files changed, 24 insertions(+)
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 27dadc0..bba07e7 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -100,6 +100,7 @@
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
+#include <linux/usb/of.h>
#include "musb_core.h"
#include "musb_trace.h"
@@ -130,6 +131,24 @@ static inline struct musb *dev_to_musb(struct device *dev)
return dev_get_drvdata(dev);
}
+enum musb_mode musb_get_mode(struct device *dev)
+{
+ enum usb_dr_mode mode;
+
+ mode = usb_get_dr_mode(dev);
+ switch (mode) {
+ case USB_DR_MODE_HOST:
+ return MUSB_HOST;
+ case USB_DR_MODE_PERIPHERAL:
+ return MUSB_PERIPHERAL;
+ case USB_DR_MODE_OTG:
+ case USB_DR_MODE_UNKNOWN:
+ default:
+ return MUSB_OTG;
+ }
+}
+EXPORT_SYMBOL_GPL(musb_get_mode);
+
/*-------------------------------------------------------------------------*/
#ifndef CONFIG_BLACKFIN
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 2cb88a49..a406468 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -617,4 +617,9 @@ static inline void musb_platform_post_root_reset_end(struct musb *musb)
musb->ops->post_root_reset_end(musb);
}
+/* gets the "dr_mode" property from DT and converts it into musb_mode
+ * if the property is not found or not recognized returns MUSB_OTG
+ */
+extern enum musb_mode musb_get_mode(struct device *dev);
+
#endif /* __MUSB_CORE_H__ */
--
2.7.3
^ permalink raw reply related
* [PATCH v2 1/4] dt/bindings: Add binding for the DA8xx MUSB driver
From: Alexandre Bailon @ 2016-10-26 15:03 UTC (permalink / raw)
To: khilman, robh+dt, b-liu
Cc: linux-kernel, linux-usb, devicetree, linux-arm-kernel,
Petr Kulhavy, Alexandre Bailon
In-Reply-To: <1477494237-22831-1-git-send-email-abailon@baylibre.com>
From: Petr Kulhavy <petr@barix.com>
DT binding for the TI DA8xx/OMAP-L1x/AM17xx/AM18xx MUSB driver.
Signed-off-by: Petr Kulhavy <petr@barix.com>
Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
---
.../devicetree/bindings/usb/da8xx-usb.txt | 43 ++++++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/da8xx-usb.txt
diff --git a/Documentation/devicetree/bindings/usb/da8xx-usb.txt b/Documentation/devicetree/bindings/usb/da8xx-usb.txt
new file mode 100644
index 0000000..5663d79
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/da8xx-usb.txt
@@ -0,0 +1,43 @@
+TI DA8xx MUSB
+~~~~~~~~~~~~~
+For DA8xx/OMAP-L1x/AM17xx/AM18xx platforms.
+
+Required properties:
+~~~~~~~~~~~~~~~~~~~~
+ - compatible : Should be set to "ti,da830-musb".
+
+ - reg: Offset and length of the USB controller register set.
+
+ - interrupts: The USB interrupt number.
+
+ - interrupt-names: Should be set to "mc".
+
+ - dr_mode: The USB operation mode. Should be one of "host", "peripheral" or "otg".
+
+ - phys: Phandle for the PHY device
+
+ - phy-names: Should be "usb-phy"
+
+Optional properties:
+~~~~~~~~~~~~~~~~~~~~
+ - vbus-supply: Phandle to a regulator providing the USB bus power.
+
+Example:
+ usb_phy: usb-phy {
+ compatible = "ti,da830-usb-phy";
+ #phy-cells = <0>;
+ status = "okay";
+ };
+ usb20: usb@1e00000 {
+ compatible = "ti,da830-musb";
+ reg = <0x00200000 0x10000>;
+ interrupts = <58>;
+ interrupt-names = "mc";
+
+ dr_mode = "host";
+ vbus-supply = <&usb_vbus>;
+ phys = <&usb_phy 0>;
+ phy-names = "usb-phy";
+
+ status = "okay";
+ };
--
2.7.3
^ permalink raw reply related
* [PATCH v2 0/4] Add DT support for DA8xx
From: Alexandre Bailon @ 2016-10-26 15:03 UTC (permalink / raw)
To: khilman-rdvid1DuHRBWk0Htik3J/w, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
b-liu-l0cyMroinI0
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Alexandre Bailon
The purpose of this series is to add DT support to the da8xx USB OTG.
This series should apply and build without any issues but it has
some dependencies on "Add DT support for ohci-da8xx" series.
Without it, the phy init will fail and then the da8xx driver will also fail.
Changes in v2:
* Remove unrelated changes in patch 3
* Rename the device node in patch 4
Alexandre Bailon (1):
ARM: dts: da850: Add the usb otg device node
Petr Kulhavy (3):
dt/bindings: Add binding for the DA8xx MUSB driver
usb: musb: core: added helper function for parsing DT
usb: musb: da8xx: Add DT support for the DA8xx driver
.../devicetree/bindings/usb/da8xx-usb.txt | 43 ++++++++++++++++++++
arch/arm/boot/dts/da850-lcdk.dts | 8 ++++
arch/arm/boot/dts/da850.dtsi | 15 +++++++
drivers/usb/musb/da8xx.c | 47 ++++++++++++++++++++++
drivers/usb/musb/musb_core.c | 19 +++++++++
drivers/usb/musb/musb_core.h | 5 +++
6 files changed, 137 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/da8xx-usb.txt
--
2.7.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [RFC PATCH 5/5] of: overlay-mgr: add a detector for headers stored on a ds2431 eeprom over w1
From: Antoine Tenart @ 2016-10-26 14:57 UTC (permalink / raw)
To: maxime.ripard, pantelis.antoniou, mark.rutland, sboyd
Cc: Antoine Tenart, thomas.petazzoni, linux-arm-kernel, linux-kernel,
devicetree
In-Reply-To: <20161026145756.21689-1-antoine.tenart@free-electrons.com>
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
drivers/of/overlay-manager/Kconfig | 10 ++++++++++
drivers/w1/slaves/w1_ds2431.c | 39 ++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git a/drivers/of/overlay-manager/Kconfig b/drivers/of/overlay-manager/Kconfig
index 1a36613c0c53..ad0a5b8e9e5e 100644
--- a/drivers/of/overlay-manager/Kconfig
+++ b/drivers/of/overlay-manager/Kconfig
@@ -16,4 +16,14 @@ config OF_OVERLAY_MGR_FORMAT_CHIP
endmenu
+menu "Overlay Manager detectors"
+
+config OF_OVERLAY_MGR_DETECTOR_DS2431
+ bool "Dip header on a DS2431 EEPROM"
+ depends on W1_SLAVE_DS2431
+ help
+ Enable dip header DS2431 EEPROM support.
+
+endmenu
+
endif
diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c
index 80572cb63ba8..760325f9a2bd 100644
--- a/drivers/w1/slaves/w1_ds2431.c
+++ b/drivers/w1/slaves/w1_ds2431.c
@@ -15,6 +15,9 @@
#include <linux/device.h>
#include <linux/types.h>
#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <linux/overlay-manager.h>
#include "../w1.h"
#include "../w1_int.h"
@@ -280,7 +283,43 @@ static const struct attribute_group *w1_f2d_groups[] = {
NULL,
};
+#if IS_ENABLED(CONFIG_OF_OVERLAY_MGR_DETECTOR_DS2431)
+static int chip_dip_callback(struct w1_slave *sl)
+{
+ char **candidates = NULL;
+ int i, n, err = 0;
+ u8 *data;
+
+ data = kzalloc(OVERLAY_MGR_DIP_MAX_SZ, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* sizeof(struct chip_header) is a mulitple of 8 */
+ for (i = 0; i < OVERLAY_MGR_DIP_MAX_SZ; i += 8) {
+ if (w1_f2d_readblock(sl, i, 8, &data[i])) {
+ err = -EIO;
+ goto end;
+ }
+ }
+
+ overlay_mgr_parse(&sl->dev, data, &candidates, &n);
+ if (!n) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ err = overlay_mgr_apply(&sl->dev, candidates, n);
+
+end:
+ kfree(data);
+ return err;
+}
+#endif
+
static struct w1_family_ops w1_f2d_fops = {
+#if IS_ENABLED(CONFIG_OF_OVERLAY_MGR_DETECTOR_DS2431)
+ .callback = chip_dip_callback,
+#endif
.groups = w1_f2d_groups,
};
--
2.10.1
^ permalink raw reply related
* [RFC PATCH 4/5] w1: add a callback to call slave when a new device is connected
From: Antoine Tenart @ 2016-10-26 14:57 UTC (permalink / raw)
To: maxime.ripard, pantelis.antoniou, mark.rutland, sboyd
Cc: Antoine Tenart, thomas.petazzoni, linux-arm-kernel, linux-kernel,
devicetree
In-Reply-To: <20161026145756.21689-1-antoine.tenart@free-electrons.com>
This patch adds the possibility for slave drivers to register a
callback, to be called whenever a new device matching the slave ID
is connected.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
drivers/w1/w1.c | 10 ++++++++++
drivers/w1/w1_family.h | 2 ++
2 files changed, 12 insertions(+)
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 80d0cc4e6e7f..7010ffd1ea93 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -659,6 +659,16 @@ static int w1_family_notify(unsigned long action, struct w1_slave *sl)
return err;
}
}
+ if (fops->callback) {
+ err = fops->callback(sl);
+ /*
+ * Do not return an error as the slave driver correctly
+ * probed.
+ */
+ if (err)
+ dev_err(&sl->dev,
+ "callback call failed. err=%d\n", err);
+ }
break;
case BUS_NOTIFY_DEL_DEVICE:
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 10a7a0767187..5e165babc6f3 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -55,11 +55,13 @@ struct w1_slave;
* @add_slave: add_slave
* @remove_slave: remove_slave
* @groups: sysfs group
+ * @callback: called when a new device is discovered
*/
struct w1_family_ops
{
int (* add_slave)(struct w1_slave *);
void (* remove_slave)(struct w1_slave *);
+ int (* callback)(struct w1_slave *);
const struct attribute_group **groups;
};
--
2.10.1
^ permalink raw reply related
* [RFC PATCH 3/5] w1: report errors returned by w1_family_notify
From: Antoine Tenart @ 2016-10-26 14:57 UTC (permalink / raw)
To: maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w,
mark.rutland-5wv7dgnIgG8, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
Cc: Antoine Tenart, thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161026145756.21689-1-antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Signed-off-by: Antoine Tenart <antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/w1/w1.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index bb34362e930a..80d0cc4e6e7f 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -702,7 +702,9 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
dev_name(&sl->dev), err);
return err;
}
- w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl);
+ err = w1_family_notify(BUS_NOTIFY_ADD_DEVICE, sl);
+ if (err)
+ return err;
dev_set_uevent_suppress(&sl->dev, false);
kobject_uevent(&sl->dev.kobj, KOBJ_ADD);
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC PATCH 2/5] of: overlay-mgr: add the CHIP format
From: Antoine Tenart @ 2016-10-26 14:57 UTC (permalink / raw)
To: maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w,
mark.rutland-5wv7dgnIgG8, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
Cc: Antoine Tenart, thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161026145756.21689-1-antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Support parsing the header used by capes compatible with Nextthing's
C.H.I.P.
Signed-off-by: Antoine Tenart <antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/of/overlay-manager/Kconfig | 13 ++++++
drivers/of/overlay-manager/Makefile | 1 +
drivers/of/overlay-manager/format-chip.c | 72 ++++++++++++++++++++++++++++++++
include/linux/overlay-manager.h | 13 ++++++
4 files changed, 99 insertions(+)
create mode 100644 drivers/of/overlay-manager/format-chip.c
diff --git a/drivers/of/overlay-manager/Kconfig b/drivers/of/overlay-manager/Kconfig
index eeb76054dcb8..1a36613c0c53 100644
--- a/drivers/of/overlay-manager/Kconfig
+++ b/drivers/of/overlay-manager/Kconfig
@@ -4,3 +4,16 @@ config OF_OVERLAY_MGR
help
Enable the overlay manager to handle automatic overlay loading when
devices are detected.
+
+if OF_OVERLAY_MGR
+
+menu "Dips header formats"
+
+config OF_OVERLAY_MGR_FORMAT_CHIP
+ bool "Nextthing's C.H.I.P. dip header format"
+ help
+ Enable Nextthing's C.H.I.P. dip header format support.
+
+endmenu
+
+endif
diff --git a/drivers/of/overlay-manager/Makefile b/drivers/of/overlay-manager/Makefile
index 86d2b53950e7..637cc7ba20c2 100644
--- a/drivers/of/overlay-manager/Makefile
+++ b/drivers/of/overlay-manager/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_OF_OVERLAY_MGR) += overlay-manager.o
+obj-$(CONFIG_OF_OVERLAY_MGR_FORMAT_CHIP) += format-chip.o
diff --git a/drivers/of/overlay-manager/format-chip.c b/drivers/of/overlay-manager/format-chip.c
new file mode 100644
index 000000000000..3a3d315dcb5c
--- /dev/null
+++ b/drivers/of/overlay-manager/format-chip.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 - Antoine Tenart <antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/device.h>
+#include <linux/overlay-manager.h>
+#include <linux/slab.h>
+
+#define CAPE_CHIP_MAGIC 0x43484950
+#define CAPE_CHIP_VERSION 1
+#define CAPE_CHIP_CANDIDATES 2
+
+static int cape_chip_parse(struct device *dev, void *data, char ***candidates,
+ unsigned *n)
+{
+ struct chip_header *header = (struct chip_header *)data;
+ char **tmp;
+ int err;
+
+ if (dip_convert(header->magic) != CAPE_CHIP_MAGIC)
+ return -EINVAL;
+
+ if (dip_convert(header->version) > CAPE_CHIP_VERSION)
+ return -EINVAL;
+
+ tmp = devm_kzalloc(dev, CAPE_CHIP_CANDIDATES * sizeof(char *), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ tmp[0] = devm_kasprintf(dev, GFP_KERNEL, "%x-%x-%x",
+ dip_convert(header->vendor_id),
+ dip_convert(header->product_id),
+ dip_convert(header->product_version));
+ if (!tmp[0]) {
+ err = -ENOMEM;
+ goto err_free_list;
+ }
+
+ tmp[1] = devm_kasprintf(dev, GFP_KERNEL, "%x-%x",
+ dip_convert(header->vendor_id),
+ dip_convert(header->product_id));
+ if (!tmp[1]) {
+ err = -ENOMEM;
+ goto err_free_0;
+ }
+
+ *candidates = tmp;
+ *n = CAPE_CHIP_CANDIDATES;
+
+ return 0;
+
+err_free_0:
+ devm_kfree(dev, tmp[0]);
+err_free_list:
+ devm_kfree(dev, tmp);
+ return err;
+}
+
+static struct overlay_mgr_format format_chip = {
+ .name = "Nextthing C.H.I.P. dip header format",
+ .parse = &cape_chip_parse,
+};
+
+static int __init cape_chip_init(void)
+{
+ return overlay_mgr_register_format(&format_chip);
+}
+device_initcall(cape_chip_init);
diff --git a/include/linux/overlay-manager.h b/include/linux/overlay-manager.h
index 8adcc4f5ddf6..d76c3c9fd863 100644
--- a/include/linux/overlay-manager.h
+++ b/include/linux/overlay-manager.h
@@ -35,4 +35,17 @@ int overlay_mgr_apply(struct device *dev, char **candidates, unsigned n);
-1 \
)
+/* Nextthing's C.H.I.P. dip header */
+struct chip_header {
+ u32 magic; /* rsvd */
+ u8 version; /* spec version */
+ u32 vendor_id;
+ u16 product_id;
+ u8 product_version;
+ char vendor_name[32];
+ char product_name[32];
+ u8 rsvd[36]; /* rsvd for futre spec versions */
+ u8 data[16]; /* user data, per-cape specific */
+} __packed;
+
#endif /* __OVERLAY_MGR_H__ */
--
2.10.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [RFC PATCH 1/5] of: introduce the overlay manager
From: Antoine Tenart @ 2016-10-26 14:57 UTC (permalink / raw)
To: maxime.ripard, pantelis.antoniou, mark.rutland, sboyd
Cc: Antoine Tenart, thomas.petazzoni, linux-arm-kernel, linux-kernel,
devicetree
In-Reply-To: <20161026145756.21689-1-antoine.tenart@free-electrons.com>
The overlay manager is an in-kernel library helping to handle dt overlay
loading when using capes.
Signed-off-by: Antoine Tenart <antoine.tenart@free-electrons.com>
---
drivers/of/Kconfig | 2 +
drivers/of/Makefile | 1 +
drivers/of/overlay-manager/Kconfig | 6 +
drivers/of/overlay-manager/Makefile | 1 +
drivers/of/overlay-manager/overlay-manager.c | 199 +++++++++++++++++++++++++++
include/linux/overlay-manager.h | 38 +++++
6 files changed, 247 insertions(+)
create mode 100644 drivers/of/overlay-manager/Kconfig
create mode 100644 drivers/of/overlay-manager/Makefile
create mode 100644 drivers/of/overlay-manager/overlay-manager.c
create mode 100644 include/linux/overlay-manager.h
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index bc07ad30c9bf..e57aeaf0bf4f 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -116,4 +116,6 @@ config OF_OVERLAY
config OF_NUMA
bool
+source "drivers/of/overlay-manager/Kconfig"
+
endif # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index d7efd9d458aa..d738fd41271f 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.o
obj-$(CONFIG_OF_NUMA) += of_numa.o
obj-$(CONFIG_OF_UNITTEST) += unittest-data/
+obj-y += overlay-manager/
diff --git a/drivers/of/overlay-manager/Kconfig b/drivers/of/overlay-manager/Kconfig
new file mode 100644
index 000000000000..eeb76054dcb8
--- /dev/null
+++ b/drivers/of/overlay-manager/Kconfig
@@ -0,0 +1,6 @@
+config OF_OVERLAY_MGR
+ bool "Device Tree Overlay Manager"
+ depends on OF_OVERLAY
+ help
+ Enable the overlay manager to handle automatic overlay loading when
+ devices are detected.
diff --git a/drivers/of/overlay-manager/Makefile b/drivers/of/overlay-manager/Makefile
new file mode 100644
index 000000000000..86d2b53950e7
--- /dev/null
+++ b/drivers/of/overlay-manager/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_OF_OVERLAY_MGR) += overlay-manager.o
diff --git a/drivers/of/overlay-manager/overlay-manager.c b/drivers/of/overlay-manager/overlay-manager.c
new file mode 100644
index 000000000000..a725d7e24d38
--- /dev/null
+++ b/drivers/of/overlay-manager/overlay-manager.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 - Antoine Tenart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/firmware.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/overlay-manager.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+struct overlay_mgr_overlay {
+ struct list_head list;
+ char *name;
+};
+
+LIST_HEAD(overlay_mgr_overlays);
+LIST_HEAD(overlay_mgr_formats);
+DEFINE_SPINLOCK(overlay_mgr_lock);
+DEFINE_SPINLOCK(overlay_mgr_format_lock);
+
+/*
+ * overlay_mgr_register_format()
+ *
+ * Adds a new format candidate to the list of supported formats. The registered
+ * formats are used to parse the headers stored on the dips.
+ */
+int overlay_mgr_register_format(struct overlay_mgr_format *candidate)
+{
+ struct overlay_mgr_format *format;
+ int err = 0;
+
+ spin_lock(&overlay_mgr_format_lock);
+
+ /* Check if the format is already registered */
+ list_for_each_entry(format, &overlay_mgr_formats, list) {
+ if (!strcpy(format->name, candidate->name)) {
+ err = -EEXIST;
+ goto err;
+ }
+ }
+
+ list_add_tail(&candidate->list, &overlay_mgr_formats);
+
+err:
+ spin_unlock(&overlay_mgr_format_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(overlay_mgr_register_format);
+
+/*
+ * overlay_mgr_parse()
+ *
+ * Parse raw data with registered format parsers. Fills the candidate string if
+ * one parser understood the raw data format.
+ */
+int overlay_mgr_parse(struct device *dev, void *data, char ***candidates,
+ unsigned *n)
+{
+ struct list_head *pos, *tmp;
+ struct overlay_mgr_format *format;
+
+ list_for_each_safe(pos, tmp, &overlay_mgr_formats) {
+ format = list_entry(pos, struct overlay_mgr_format, list);
+
+ format->parse(dev, data, candidates, n);
+ if (n > 0)
+ return 0;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(overlay_mgr_parse);
+
+static int overlay_mgr_check_overlay(struct device_node *node)
+{
+ struct property *p;
+ const char *str = NULL;
+
+ p = of_find_property(node, "compatible", NULL);
+ if (!p)
+ return -EINVAL;
+
+ do {
+ str = of_prop_next_string(p, str);
+ if (of_machine_is_compatible(str))
+ return 0;
+ } while (str);
+
+ return -EINVAL;
+}
+
+/*
+ * _overlay_mgr_insert()
+ *
+ * Try to request and apply an overlay given a candidate name.
+ */
+static int _overlay_mgr_apply(struct device *dev, char *candidate)
+{
+ struct overlay_mgr_overlay *overlay;
+ struct device_node *node;
+ const struct firmware *firmware;
+ char *firmware_name;
+ int err = 0;
+
+ spin_lock(&overlay_mgr_lock);
+
+ list_for_each_entry(overlay, &overlay_mgr_overlays, list) {
+ if (!strcmp(overlay->name, candidate)) {
+ dev_err(dev, "overlay already loaded\n");
+ err = -EEXIST;
+ goto err_lock;
+ }
+ }
+
+ overlay = devm_kzalloc(dev, sizeof(*overlay), GFP_KERNEL);
+ if (!overlay) {
+ err = -ENOMEM;
+ goto err_lock;
+ }
+
+ overlay->name = candidate;
+
+ firmware_name = kasprintf(GFP_KERNEL, "overlay-%s.dtbo", candidate);
+ if (!firmware_name) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ dev_info(dev, "requesting firmware '%s'\n", firmware_name);
+
+ err = request_firmware_direct(&firmware, firmware_name, dev);
+ if (err) {
+ dev_info(dev, "failed to request firmware '%s'\n",
+ firmware_name);
+ goto err_free;
+ }
+
+ of_fdt_unflatten_tree((unsigned long *)firmware->data, NULL, &node);
+ if (!node) {
+ dev_err(dev, "failed to unflatted tree\n");
+ err = -EINVAL;
+ goto err_fw;
+ }
+
+ of_node_set_flag(node, OF_DETACHED);
+
+ err = of_resolve_phandles(node);
+ if (err) {
+ dev_err(dev, "failed to resolve phandles: %d\n", err);
+ goto err_fw;
+ }
+
+ err = overlay_mgr_check_overlay(node);
+ if (err) {
+ dev_err(dev, "overlay checks failed: %d\n", err);
+ goto err_fw;
+ }
+
+ err = of_overlay_create(node);
+ if (err < 0) {
+ dev_err(dev, "failed to create overlay: %d\n", err);
+ goto err_fw;
+ }
+
+ list_add_tail(&overlay->list, &overlay_mgr_overlays);
+
+ dev_info(dev, "loaded firmware '%s'\n", firmware_name);
+
+ spin_unlock(&overlay_mgr_lock);
+ return 0;
+
+err_fw:
+ release_firmware(firmware);
+err_free:
+ devm_kfree(dev, overlay);
+err_lock:
+ spin_unlock(&overlay_mgr_lock);
+ return err;
+}
+
+int overlay_mgr_apply(struct device *dev, char **candidates, unsigned n)
+{
+ int i, ret;
+
+ for (i=0; i < n; i++) {
+ ret = _overlay_mgr_apply(dev, candidates[i]);
+ if (!ret)
+ return 0;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(overlay_mgr_apply);
diff --git a/include/linux/overlay-manager.h b/include/linux/overlay-manager.h
new file mode 100644
index 000000000000..8adcc4f5ddf6
--- /dev/null
+++ b/include/linux/overlay-manager.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 - Antoine Tenart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __OVERLAY_MGR_H__
+#define __OVERLAY_MGR_H__
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/sizes.h>
+
+#define OVERLAY_MGR_DIP_MAX_SZ SZ_128
+
+struct overlay_mgr_format {
+ struct list_head list;
+ char *name;
+ int (*parse)(struct device *dev, void *data, char ***candidates,
+ unsigned *n);
+};
+
+int overlay_mgr_register_format(struct overlay_mgr_format *candidate);
+int overlay_mgr_parse(struct device *dev, void *data, char ***candidates,
+ unsigned *n);
+int overlay_mgr_apply(struct device *dev, char **candidates, unsigned n);
+
+#define dip_convert(field) \
+ ( \
+ (sizeof(field) == 1) ? field : \
+ (sizeof(field) == 2) ? be16_to_cpu(field) : \
+ (sizeof(field) == 4) ? be32_to_cpu(field) : \
+ -1 \
+ )
+
+#endif /* __OVERLAY_MGR_H__ */
--
2.10.1
^ permalink raw reply related
* [RFC PATCH 0/5] Add an overlay manager to handle board capes
From: Antoine Tenart @ 2016-10-26 14:57 UTC (permalink / raw)
To: maxime.ripard, pantelis.antoniou, mark.rutland, sboyd
Cc: thomas.petazzoni, devicetree, Antoine Tenart, linux-kernel,
linux-arm-kernel
Hi all,
Many boards now come with dips and compatible capes; among others the
C.H.I.P, or Beaglebones. All these boards have a kernel implementing an
out-of-tree "cape manager" which is used to detected capes, retrieve
their description and apply a corresponding overlay. This series is an
attempt to start a discussion, with an implementation of such a manager
which is somehow generic (i.e. formats or cape detectors can be added).
Other use cases could make use of this manager to dynamically load dt
overlays based on some input / hw presence.
The proposed design is a library which can be used by detector drivers
to parse headers and load the corresponding overlay. Helpers are
provided for this purpose. The whole thing is divided into 3 entities:
- The parser which is project-specific (to allow supporting headers
already into the wild). It registers a function parsing an header's
data and filling one or more strings which will be used to find
matching dtbo on the fs.
- The overlay manager helpers allowing to parse a header to retrieve
the previously mentioned strings and to load a compatible overlay.
- The detectors which are used to detect capes and get their description
(to be parsed).
An example of parser and detector is given, compatible with what's done
for the C.H.I.P. As the w1 framework is really bad (and we should
probably do something about that) the detector code is far from being
perfect; but that's not related to what we try to achieve here.
The actual implementation has a limitation: the detectors cannot be
built-in the kernel image as they would likely detect capes at boot time
but will fail to get their corresponding dt overlays as the fs isn't
mounted yet. The only case this can work is when dt overlays are
built-in firmwares. This isn't an issue for the C.H.I.P. use case right
now. There was a discussion about making an helper to wait for the
rootfs to be mount but the answer was "this is the driver's problem".
I'd like to get comments, specifically from people using custom cape
managers, to see if this could fill their needs (with I guess some
modifications).
Thanks!
Antoine
Antoine Tenart (5):
of: introduce the overlay manager
of: overlay-mgr: add the CHIP format
w1: report errors returned by w1_family_notify
w1: add a callback to call slave when a new device is connected
of: overlay-mgr: add a detector for headers stored on a ds2431 eeprom
over w1
drivers/of/Kconfig | 2 +
drivers/of/Makefile | 1 +
drivers/of/overlay-manager/Kconfig | 29 ++++
drivers/of/overlay-manager/Makefile | 2 +
drivers/of/overlay-manager/format-chip.c | 72 ++++++++++
drivers/of/overlay-manager/overlay-manager.c | 199 +++++++++++++++++++++++++++
drivers/w1/slaves/w1_ds2431.c | 39 ++++++
drivers/w1/w1.c | 14 +-
drivers/w1/w1_family.h | 2 +
include/linux/overlay-manager.h | 51 +++++++
10 files changed, 410 insertions(+), 1 deletion(-)
create mode 100644 drivers/of/overlay-manager/Kconfig
create mode 100644 drivers/of/overlay-manager/Makefile
create mode 100644 drivers/of/overlay-manager/format-chip.c
create mode 100644 drivers/of/overlay-manager/overlay-manager.c
create mode 100644 include/linux/overlay-manager.h
--
2.10.1
^ permalink raw reply
* Re: [PATCH v7 2/4] soc: mediatek: Init MT8173 scpsys driver earlier
From: Matthias Brugger @ 2016-10-26 14:54 UTC (permalink / raw)
To: Yong Wu
Cc: James Liao, Rob Herring, srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
devicetree-u79uwXL29TY76Z2rM5mHXA, Kevin Hilman,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sascha Hauer,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
joro-zLv9SwRftAIdnm+yROfE0A,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
In-Reply-To: <1468314071.24705.4.camel@mhfsdcap03>
Hi Yong,
On 07/12/2016 11:01 AM, Yong Wu wrote:
> Hi Matthias,
>
> On Fri, 2016-07-08 at 14:47 +0200, Matthias Brugger wrote:
>>
>> On 06/07/16 07:22, James Liao wrote:
>>> On Sat, 2016-07-02 at 18:35 +0200, Matthias Brugger wrote:
>>>>
>>>> On 05/16/2016 11:28 AM, James Liao wrote:
>>>>> Some power domain comsumers may init before module_init.
>>>>> So the power domain provider (scpsys) need to be initialized
>>>>> earlier too.
>>>>>
>>>>> Take an example for our IOMMU (M4U) and SMI. SMI is a bridge
>>>>> between IOMMU and multimedia HW. SMI is responsible to
>>>>> enable/disable iommu and help transfer data for each multimedia
>>>>> HW. Both of them have to wait until the power and clocks are
>>>>> enabled.
>>>>>
>>>>> So scpsys driver should be initialized before SMI, and SMI should
>>>>> be initialized before IOMMU, and then init IOMMU consumers
>>>>> (display/vdec/venc/camera etc.).
>>>>>
>>>>> IOMMU is subsys_init by default. So we need to init scpsys driver
>>>>> before subsys_init.
>>>>>
>>>>> Signed-off-by: James Liao <jamesjj.liao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
>>>>> Reviewed-by: Kevin Hilman <khilman-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
>>>>> ---
>>>>> drivers/soc/mediatek/mtk-scpsys.c | 19 ++++++++++++++++++-
>>>>> 1 file changed, 18 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c
>>>>> index 5870a24..00c0adb 100644
>>>>> --- a/drivers/soc/mediatek/mtk-scpsys.c
>>>>> +++ b/drivers/soc/mediatek/mtk-scpsys.c
>>>>> @@ -617,4 +617,21 @@ static struct platform_driver scpsys_drv = {
>>>>> .of_match_table = of_match_ptr(of_scpsys_match_tbl),
>>>>> },
>>>>> };
>>>>> -builtin_platform_driver(scpsys_drv);
>>>>> +
>>>>> +static int __init scpsys_drv_init(void)
>>>>> +{
>>>>> + return platform_driver_register(&scpsys_drv);
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * There are some Mediatek drivers which depend on the power domain driver need
>>>>> + * to probe in earlier initcall levels. So scpsys driver also need to probe
>>>>> + * earlier.
>>>>> + *
>>>>> + * IOMMU(M4U) and SMI drivers for example. SMI is a bridge between IOMMU and
>>>>> + * multimedia HW. IOMMU depends on SMI, and SMI is a power domain consumer,
>>>>> + * so the proper probe sequence should be scpsys -> SMI -> IOMMU driver.
>>>>> + * IOMMU drivers are initialized during subsys_init by default, so we need to
>>>>> + * move SMI and scpsys drivers to subsys_init or earlier init levels.
>>>>> + */
>>>>> +subsys_initcall(scpsys_drv_init);
>>>>>
>>>>
>>>> Can't we achieve this with probe deferring? I'm not really keen on
>>>> coding the order of the different drivers like this.
>>>
>>> Hi Matthias,
>>>
>>> Some drivers such as IOMMU don't support probe deferring. So scpsys need
>>> to init before them by changing init level.
>>>
>>
>> SMI larbs return EPROBE_DEFER if it can't find the PM provider for it's
>> domain, so this part should not be the problem. The larbs get added as
>> components in the probe, when the PM provider is present.
>>
>> The iommu driver uses the larbs as components. As long as not all
>> components are present the iommu does not bind them. So from what I see
>> this should work without any problem.
>>
>> Can you please specify where the iommu dirver has a problem. Maybe we
>> need to fix the driver rather then scpsys.
>
> We got a problem while bootup, the hang backtrace is like below(Sorry, I
> can't find the full backtrace now):
>
> [ 7.832359] Call trace:
> [ 7.834778] [<ffffffc000764424>] mtk_smi_larb_get+0x24/0xa8
> [ 7.840300] [<ffffffc0005a1390>] mtk_drm_crtc_enable+0x6c/0x450
>
> The reason is that "larb->mmu" is NULL while DRM call mtk_smi_larb_get.
>
> DRM call iommu_present to wait for whether IOMMU is ready[1].
> But in the MTK-IOMMU, It will call
> bus_set_iommu->mtk_iommu_add_device->mtk_iommu_attach_device, then it's
> able to transfer "struct mtk_smi_iommu" to SMI whose probe maybe delayed
> by power-domain, then SMI could finish its probe.
>
> So If DRM probe is called after the time of calling bus_set_iommu and
> before the time of SMI probe finish, this problem can be reproduced.
>
> The root cause is that iommu_present cann't indicate that MTK-IOMMU and
> SMI is ready actually. in other words, the DRM cann't detect
> whether the SMI has probed done.
>
> If we move scpsys_init from module_initcall to subsys_initcall, the
> IOMMU and SMI could be probe done during the subsys_initcall period.
> this issue can be avoid.
> And as we grep before, there are also some examples whose power-domain
> is also not module_init[2].
> core_initcall(exynos4_pm_init_power_domain);
> subsys_initcall(imx_pgc_init);
>
> If you think that this patch will hide the problem above, then I have to
> add a new function, like below:
> //==================
> bool mtk_smi_larb_ready(struct device *larbdev)
> {
> struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
>
> return larb && !!larb->mmu;
> }
> //=================
>
> And in the probe of all the MTK-IOMMU consumer, we have to add this:
> //====================
> if (!mtk_smi_larb_ready(&larb_pdev->dev))
> return -EPROBE_DEFER;
> //====================
>
> The sequence is a little complex, If I don't explain clearly, please
> tell me.
> Any other suggestion about this? Thanks.
I'm still trying to find a different way to solve this. Unfortunately
I'm not able to reproduce this on my mt8173-evb...
...but: iommu_present checkes if the bus->iommu_ops is set. If we set
these option in the iommu after all components got initialized, we
should be fine. Or did I miss something?
Would you mind trying the patch below (against v4.9-rc1)?
If you know of any way to reproduce this issue, I would be happy to know.
I'm adding Joerg, the iommu maintainer, maybe he has an idea.
Thanks a lot,
Matthias
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index b12c12d..9249011 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -602,10 +602,12 @@ static int mtk_iommu_probe(struct platform_device
*pdev)
if (ret)
return ret;
- if (!iommu_present(&platform_bus_type))
+ ret = component_master_add_with_match(dev, &mtk_iommu_com_ops,
match);
+
+ if (ret && !iommu_present(&platform_bus_type))
bus_set_iommu(&platform_bus_type, &mtk_iommu_ops);
- return component_master_add_with_match(dev, &mtk_iommu_com_ops,
match);
+ return ret;
}
static int mtk_iommu_remove(struct platform_device *pdev)
--
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] soc: qcom: Add SoC info driver
From: Arnd Bergmann @ 2016-10-26 14:46 UTC (permalink / raw)
To: Imran Khan
Cc: andy.gross, David Brown, Rob Herring, Mark Rutland,
open list:ARM/QUALCOMM SUPPORT, open list:ARM/QUALCOMM SUPPORT,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <2ae082c4-27c1-fbdd-cb7a-986716e15d18@codeaurora.org>
On Wednesday, October 26, 2016 7:42:08 PM CEST Imran Khan wrote:
> On 10/26/2016 7:35 PM, Arnd Bergmann wrote:
> >> > As we are talking about generic soc_device_attribute fields, I was hoping that
> >> > having a vendor field would be helpful as along with family it would provide
> >> > a more thorough information. Also as more than one foundries may be used for
> >> > a soc, can we have a field say foundry_id to provide this information.
> > My first feeling is that this 'vendor' information can should be
> > derived from the family. It's also not clear what would happen
> > to this when a company gets bought. E.g. the Oxnas product family
> > was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
> > mxs family was Sigmatel, Freescale, now NXP and might soon be
> > Qualcomm. What would you put in there in this case?
>
> Okay, not having vendor field is fine for me. Could you also suggest
> something about the foundry_id field.
This one seems more well-defined, so it's probably ok to add. What
would be the use case of reading this? Would you want to read it
just from user space or also from the kernel?
Maybe this can be combined with a manufacturing process, which probably
falls into a similar category, so we could have something like
"TSMC 28ULP" as a string in there.
Arnd
^ permalink raw reply
* Re: [PATCH 4/9] pinctrl: meson: allow gpio to request irq
From: Linus Walleij @ 2016-10-26 14:44 UTC (permalink / raw)
To: Jerome Brunet
Cc: Marc Zyngier, Carlo Caione, Kevin Hilman,
open list:ARM/Amlogic Meson...,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Thomas Gleixner, Jason Cooper, Rob Herring, Catalin Marinas,
Will Deacon, Russell King
In-Reply-To: <1477491816.2482.160.camel-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
On Wed, Oct 26, 2016 at 4:23 PM, Jerome Brunet <jbrunet-rdvid1DuHRBWk0Htik3J/w@public.gmane.org> wrote:
> [Me]
>> We usually refer to the local numberspace on the GPIO controller
>> as "offsets", so line offsets 0...31 on a gpiochip with 31 lines.
>>
>> The ngpio in struct gpio_chip is the number of lines on that
>> controller,
>> and should nominally map 1:1 to hwirq sources.
>
> Indeed it should be the the case, and for meson, it is pretty close.
> The irqchip controller provide a number of hwirq. Each hwirq maps to
> one, and only one, pin. But since not every pins are connected to the
> irqchip controller, the opposite is not true.
>
> Taking an example with 16 gpios, here is what it could look like with
> the exception we have on meson :
>
> gpio offset [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ]
> hwirq num [ 0 1 2 3] NC NC[4 5 6 7 8 9 10]NC NC NC
>
> Like gpio offset are used (internally) in the driver to find
> appropriate gpio registers and bit, the hwirq has a meaning too.
> It is the setting you put in the channel multiplexer of the controller
> to select the proper pin to spy on.
>
> In the end, these gpio offset and hwirq number are different. I would
> prefer to have hwirq == gpio and go your way, it would make my life
> easier, but I don't see how it would work.
>
> The irqchip controller cares only about the hwirq number. You can
> actually request an interrupt directly to the controller by asking the
> proper hwirq number (in DT for example), without involving the gpio
> driver (tested).
>
> The relation between the pins and the interrupt number is provided by
> the manufacturer in the Datasheet [1], in the section GPIO Interrupt.
I think I kind of get it.
This reminds me of recent patches to the generic GPIOLIB_IRQCHIP
where we made it possible to "mask" set of IRQ lines, saying
"these are in the irqdomain, but you cannot map them".
See
commit 79b804cb6af4f128b2c53f0887c02537a7eb5824
"gpiolib: Make it possible to exclude GPIOs from IRQ domain"
commit 96b2cca64fa3e1a31b573bb308b2944c802aacf3
"gpio: stmpe: forbid unused lines to be mapped as IRQs"
So what we do in the generic case is put a linear map over all
the lines/offsets, then punch holes in that map and say
"this and this and this can not be mapped as IRQ".
As you can see in _gpiochip_irqchip_add() we pre-map all
except those with irq_create_mapping().
Does this scheme fit with your usecase? I would guess so,
just that your domain is hierarchic, not simple/linear.
Maybe the takeaway is that your map does not need to
be "dense", i.e. every hwirq is in use. It can be sparse.
It is stored in a radix tree anyways.
> Looking at other gpio drivers, it is not uncommon to have some simple
> calculation to get from gpio offset to the hwirq number. I don't get
> what is the specific problem here ?
It's OK to use the offset vs hwirq.
I just misunderstand it as the global GPIO number, that is
not OK.
Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH V3 2/2] iio: adc: spmi-vadc: Changes to support different scaling
From: Rama Krishna Phani A @ 2016-10-26 14:41 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: robh-DgEjT+Ai2ygdnm+yROfE0A, linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
smohanad-sgV2jX0FEOL9JmXXK+q4OQ, mgautam-sgV2jX0FEOL9JmXXK+q4OQ,
sivaa-sgV2jX0FEOL9JmXXK+q4OQ, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
Julia.Lawall-L2FTfq7BK8M, Rama Krishna Phani A
In-Reply-To: <1477492887-1663-1-git-send-email-rphani-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Polling can also be used for End of conversion completion. Implement logic
to choose either polling or interrupt for End of conversion completion.
Scaling can be done on the voltage to report adc code in physical units.
Add changes to support different scale functions to convert adc code to
physical units.
Signed-off-by: Rama Krishna Phani A <rphani-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
.../devicetree/bindings/iio/adc/qcom,spmi-vadc.txt | 14 ++
drivers/iio/adc/qcom-spmi-vadc.c | 263 +++++++++++++++++----
2 files changed, 236 insertions(+), 41 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
index 0fb4613..39e31c0e 100644
--- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
@@ -37,6 +37,12 @@ VADC node:
Value type: <prop-encoded-array>
Definition: End of conversion interrupt.
+- qcom,vadc-poll-eoc:
+ Usage: optional
+ Value type: <bool>
+ Definition: Use polling instead of interrupts for End of Conversion
+ completion.
+
Channel node properties:
- reg:
@@ -92,6 +98,14 @@ Channel node properties:
Valid values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
If property is not found, 1 sample will be used.
+- qcom,scale-function:
+ Usage: optional
+ Value type: <u32>
+ Definition: Scaling function used to convert raw ADC code to
+ units specific to a given channel. Scaled units can be
+ microvolts, millidegC.Valid values are: 0, 1, 2, 3, 4.
+ If property is not found, 0 scaling will be used.
+
NOTE:
Following channels, also known as reference point channels, are used for
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index ff4d549..6e521a9 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -92,6 +92,8 @@
#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
+#define VADC_DEF_SCALE_FN SCALE_DEFAULT
+
#define VADC_DECIMATION_MIN 512
#define VADC_DECIMATION_MAX 4096
@@ -100,9 +102,43 @@
#define KELVINMIL_CELSIUSMIL 273150
+#define PMI_CHG_SCALE_1 -138890
+#define PMI_CHG_SCALE_2 391750000000
+
#define VADC_CHAN_MIN VADC_USBIN
#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
+/**
+ * enum vadc_scale_fn_type - Scaling function to convert ADC code to
+ * physical scaled units for the channel.
+ * %SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
+ * %SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
+ * Uses a mapping table with 100K pullup.
+ * %SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
+ * %SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
+ * %SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
+ * %SCALE_NONE: Do not use this scaling type.
+ */
+enum vadc_scale_fn_type {
+ SCALE_DEFAULT = 0,
+ SCALE_THERM_100K_PULLUP,
+ SCALE_PMIC_THERM,
+ SCALE_XOTHERM,
+ SCALE_PMI_CHG_TEMP,
+ SCALE_NONE,
+};
+
+/**
+ * struct vadc_map_pt - Map the graph representation for ADC channel
+ * @x: Represent the ADC digitized code.
+ * @y: Represent the physical data which can be temperature, voltage,
+ * resistance.
+ */
+struct vadc_map_pt {
+ s32 x;
+ s32 y;
+};
+
/*
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
@@ -148,6 +184,9 @@ struct vadc_prescale_ratio {
* start of conversion.
* @avg_samples: ability to provide single result from the ADC
* that is an average of multiple measurements.
+ *@scale_function: Represents the scaling function to convert voltage
+ * physical units desired by the client for the channel.
+ * Referenced from enum vadc_scale_fn_type.
*/
struct vadc_channel_prop {
unsigned int channel;
@@ -156,6 +195,7 @@ struct vadc_channel_prop {
unsigned int prescale;
unsigned int hw_settle_time;
unsigned int avg_samples;
+ unsigned int scale_function;
};
/**
@@ -197,6 +237,44 @@ struct vadc_priv {
{.num = 1, .den = 10}
};
+/* Voltage to temperature */
+static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
+ {1758, -40},
+ {1742, -35},
+ {1719, -30},
+ {1691, -25},
+ {1654, -20},
+ {1608, -15},
+ {1551, -10},
+ {1483, -5},
+ {1404, 0},
+ {1315, 5},
+ {1218, 10},
+ {1114, 15},
+ {1007, 20},
+ {900, 25},
+ {795, 30},
+ {696, 35},
+ {605, 40},
+ {522, 45},
+ {448, 50},
+ {383, 55},
+ {327, 60},
+ {278, 65},
+ {237, 70},
+ {202, 75},
+ {172, 80},
+ {146, 85},
+ {125, 90},
+ {107, 95},
+ {92, 100},
+ {79, 105},
+ {68, 110},
+ {59, 115},
+ {51, 120},
+ {44, 125}
+};
+
static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
{
return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
@@ -468,6 +546,51 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
return ret;
}
+static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
+ u32 tablesize, s32 input, s64 *output)
+{
+ bool descending = 1;
+ u32 i = 0;
+
+ if (!pts)
+ return -EINVAL;
+
+ /* Check if table is descending or ascending */
+ if (tablesize > 1) {
+ if (pts[0].x < pts[1].x)
+ descending = 0;
+ }
+
+ while (i < tablesize) {
+ if ((descending) && (pts[i].x < input)) {
+ /* table entry is less than measured*/
+ /* value and table is descending, stop */
+ break;
+ } else if ((!descending) &&
+ (pts[i].x > input)) {
+ /* table entry is greater than measured*/
+ /*value and table is ascending, stop */
+ break;
+ }
+ i++;
+ }
+
+ if (i == 0) {
+ *output = pts[0].y;
+ } else if (i == tablesize) {
+ *output = pts[tablesize - 1].y;
+ } else {
+ /* result is between search_index and search_index-1 */
+ /* interpolate linearly */
+ *output = (((s32)((pts[i].y - pts[i - 1].y) *
+ (input - pts[i - 1].x)) /
+ (pts[i].x - pts[i - 1].x)) +
+ pts[i - 1].y);
+ }
+
+ return 0;
+}
+
static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
const struct vadc_channel_prop *prop,
s64 *scale_voltage)
@@ -489,15 +612,61 @@ static s64 vadc_scale_fn(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop, u16 adc_code)
{
const struct vadc_prescale_ratio *prescale;
- s64 voltage = 0;
+ s64 voltage = 0, result = 0;
+ int ret;
+
+ switch (prop->scale_function) {
+ case SCALE_DEFAULT:
+ vadc_scale_calib(vadc, adc_code, prop, &voltage);
+
+ prescale = &vadc_prescale_ratios[prop->prescale];
+ voltage = voltage * prescale->den;
+ return div64_s64(voltage, prescale->num);
+
+ case SCALE_THERM_100K_PULLUP:
+ case SCALE_XOTHERM:
+ vadc_scale_calib(vadc, adc_code, prop, &voltage);
+
+ if (prop->calibration == VADC_CALIB_ABSOLUTE)
+ voltage /= 1000;
+
+ vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb),
+ voltage, &result);
+ result *= 1000;
+ return result;
+
+ case SCALE_PMIC_THERM:
+ vadc_scale_calib(vadc, adc_code, prop, &voltage);
+
+ if (voltage > 0) {
+ prescale = &vadc_prescale_ratios[prop->prescale];
+ voltage = voltage * prescale->den;
+ voltage /= (prescale->num * 2);
+ } else {
+ voltage = 0;
+ }
+
+ voltage -= KELVINMIL_CELSIUSMIL;
- vadc_scale_calib(vadc, adc_code, prop, &voltage);
+ return voltage;
- prescale = &vadc_prescale_ratios[prop->prescale];
+ case SCALE_PMI_CHG_TEMP:
+ vadc_scale_calib(vadc, adc_code, prop, &voltage);
+ prescale = &vadc_prescale_ratios[prop->prescale];
+ voltage = voltage * prescale->den;
- voltage = voltage * prescale->den;
+ voltage = div64_s64(voltage, prescale->num);
+ voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
+ voltage = (voltage + PMI_CHG_SCALE_2);
+ return div64_s64(voltage, 1000000);
- return div64_s64(voltage, prescale->num);
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
}
static int vadc_decimation_from_dt(u32 value)
@@ -615,7 +784,9 @@ struct vadc_channels {
}, \
#define VADC_CHAN_TEMP(_dname, _pre) \
- VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \
+ VADC_CHAN(_dname, IIO_TEMP, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre) \
#define VADC_CHAN_VOLT(_dname, _pre) \
VADC_CHAN(_dname, IIO_VOLTAGE, \
@@ -639,7 +810,7 @@ struct vadc_channels {
VADC_CHAN_TEMP(DIE_TEMP, 0)
VADC_CHAN_VOLT(REF_625MV, 0)
VADC_CHAN_VOLT(REF_1250MV, 0)
- VADC_CHAN_VOLT(CHG_TEMP, 0)
+ VADC_CHAN_TEMP(CHG_TEMP, 0)
VADC_CHAN_VOLT(SPARE1, 0)
VADC_CHAN_VOLT(SPARE2, 0)
VADC_CHAN_VOLT(GND_REF, 0)
@@ -693,41 +864,41 @@ struct vadc_channels {
VADC_CHAN_VOLT(AMUX_PU2, 0)
VADC_CHAN_VOLT(LR_MUX3_BUF_XO_THERM, 0)
- VADC_CHAN_VOLT(LR_MUX1_PU1_BAT_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX1_PU1_BAT_THERM, 0)
VADC_CHAN_VOLT(LR_MUX2_PU1_BAT_ID, 0)
- VADC_CHAN_VOLT(LR_MUX3_PU1_XO_THERM, 0)
- VADC_CHAN_VOLT(LR_MUX4_PU1_AMUX_THM1, 0)
- VADC_CHAN_VOLT(LR_MUX5_PU1_AMUX_THM2, 0)
- VADC_CHAN_VOLT(LR_MUX6_PU1_AMUX_THM3, 0)
+ VADC_CHAN_TEMP(LR_MUX3_PU1_XO_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0)
+ VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0)
+ VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0)
VADC_CHAN_VOLT(LR_MUX7_PU1_AMUX_HW_ID, 0)
- VADC_CHAN_VOLT(LR_MUX8_PU1_AMUX_THM4, 0)
- VADC_CHAN_VOLT(LR_MUX9_PU1_AMUX_THM5, 0)
+ VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0)
+ VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0)
VADC_CHAN_VOLT(LR_MUX10_PU1_AMUX_USB_ID, 0)
- VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_XO_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0)
- VADC_CHAN_VOLT(LR_MUX1_PU2_BAT_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX1_PU2_BAT_THERM, 0)
VADC_CHAN_VOLT(LR_MUX2_PU2_BAT_ID, 0)
- VADC_CHAN_VOLT(LR_MUX3_PU2_XO_THERM, 0)
- VADC_CHAN_VOLT(LR_MUX4_PU2_AMUX_THM1, 0)
- VADC_CHAN_VOLT(LR_MUX5_PU2_AMUX_THM2, 0)
- VADC_CHAN_VOLT(LR_MUX6_PU2_AMUX_THM3, 0)
+ VADC_CHAN_TEMP(LR_MUX3_PU2_XO_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX4_PU2_AMUX_THM1, 0)
+ VADC_CHAN_TEMP(LR_MUX5_PU2_AMUX_THM2, 0)
+ VADC_CHAN_TEMP(LR_MUX6_PU2_AMUX_THM3, 0)
VADC_CHAN_VOLT(LR_MUX7_PU2_AMUX_HW_ID, 0)
- VADC_CHAN_VOLT(LR_MUX8_PU2_AMUX_THM4, 0)
- VADC_CHAN_VOLT(LR_MUX9_PU2_AMUX_THM5, 0)
+ VADC_CHAN_TEMP(LR_MUX8_PU2_AMUX_THM4, 0)
+ VADC_CHAN_TEMP(LR_MUX9_PU2_AMUX_THM5, 0)
VADC_CHAN_VOLT(LR_MUX10_PU2_AMUX_USB_ID, 0)
- VADC_CHAN_VOLT(LR_MUX3_BUF_PU2_XO_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX3_BUF_PU2_XO_THERM, 0)
- VADC_CHAN_VOLT(LR_MUX1_PU1_PU2_BAT_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX1_PU1_PU2_BAT_THERM, 0)
VADC_CHAN_VOLT(LR_MUX2_PU1_PU2_BAT_ID, 0)
- VADC_CHAN_VOLT(LR_MUX3_PU1_PU2_XO_THERM, 0)
- VADC_CHAN_VOLT(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
- VADC_CHAN_VOLT(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
- VADC_CHAN_VOLT(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
+ VADC_CHAN_TEMP(LR_MUX3_PU1_PU2_XO_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
+ VADC_CHAN_TEMP(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
+ VADC_CHAN_TEMP(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
VADC_CHAN_VOLT(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
- VADC_CHAN_VOLT(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
- VADC_CHAN_VOLT(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
+ VADC_CHAN_TEMP(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
+ VADC_CHAN_TEMP(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
VADC_CHAN_VOLT(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
- VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
};
static int vadc_get_dt_channel_data(struct device *dev,
@@ -804,6 +975,11 @@ static int vadc_get_dt_channel_data(struct device *dev,
prop->avg_samples = VADC_DEF_AVG_SAMPLES;
}
+ ret = of_property_read_u32(node, "qcom,scale-function",
+ &prop->scale_function);
+ if (ret)
+ prop->scale_function = SCALE_DEFAULT;
+
if (of_property_read_bool(node, "qcom,ratiometric"))
prop->calibration = VADC_CALIB_RATIOMETRIC;
else
@@ -966,16 +1142,21 @@ static int vadc_probe(struct platform_device *pdev)
if (ret)
return ret;
- irq_eoc = platform_get_irq(pdev, 0);
- if (irq_eoc < 0) {
- if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
- return irq_eoc;
- vadc->poll_eoc = true;
- } else {
- ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0,
- "spmi-vadc", vadc);
- if (ret)
- return ret;
+ vadc->poll_eoc = of_property_read_bool(node,
+ "qcom,vadc-poll-eoc");
+
+ if (!vadc->poll_eoc) {
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc < 0) {
+ if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
+ return irq_eoc;
+ vadc->poll_eoc = true;
+ } else {
+ ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0,
+ "spmi-vadc", vadc);
+ if (ret)
+ return ret;
+ }
}
ret = vadc_reset(vadc);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH V3 1/2] iio: adc: spmi-vadc: Update changes to support reporting of Raw adc code.
From: Rama Krishna Phani A @ 2016-10-26 14:41 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: robh-DgEjT+Ai2ygdnm+yROfE0A, linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
smohanad-sgV2jX0FEOL9JmXXK+q4OQ, mgautam-sgV2jX0FEOL9JmXXK+q4OQ,
sivaa-sgV2jX0FEOL9JmXXK+q4OQ, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
Julia.Lawall-L2FTfq7BK8M, Rama Krishna Phani A
In-Reply-To: <1477492887-1663-1-git-send-email-rphani-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Logic to convert adc code to voltage is generic across all channels.
Different scaling can be done on the obtained voltage to report in physical
units. Implement separate function for generic conversion logic.
Scaling functionality can be changed per channel. Update changes to support
reporting of Raw adc code.
Signed-off-by: Rama Krishna Phani A <rphani-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
drivers/iio/adc/qcom-spmi-vadc.c | 54 +++++++++++++++++++++-------------------
1 file changed, 28 insertions(+), 26 deletions(-)
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index c2babe5..ff4d549 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -84,7 +84,7 @@
#define VADC_MAX_ADC_CODE 0xa800
#define VADC_ABSOLUTE_RANGE_UV 625000
-#define VADC_RATIOMETRIC_RANGE_UV 1800000
+#define VADC_RATIOMETRIC_RANGE 1800
#define VADC_DEF_PRESCALING 0 /* 1:1 */
#define VADC_DEF_DECIMATION 0 /* 512 */
@@ -418,7 +418,7 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
u16 read_1, read_2;
int ret;
- vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE_UV;
+ vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE;
vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
prop = vadc_get_channel(vadc, VADC_REF_1250MV);
@@ -468,21 +468,30 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
return ret;
}
-static s32 vadc_calibrate(struct vadc_priv *vadc,
- const struct vadc_channel_prop *prop, u16 adc_code)
+static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
+ const struct vadc_channel_prop *prop,
+ s64 *scale_voltage)
{
- const struct vadc_prescale_ratio *prescale;
- s64 voltage;
+ *scale_voltage = (adc_code -
+ vadc->graph[prop->calibration].gnd);
+ *scale_voltage *= vadc->graph[prop->calibration].dx;
+ *scale_voltage = div64_s64(*scale_voltage,
+ vadc->graph[prop->calibration].dy);
+ if (prop->calibration == VADC_CALIB_ABSOLUTE)
+ *scale_voltage +=
+ vadc->graph[prop->calibration].dx;
- voltage = adc_code - vadc->graph[prop->calibration].gnd;
- voltage *= vadc->graph[prop->calibration].dx;
- voltage = div64_s64(voltage, vadc->graph[prop->calibration].dy);
+ if (*scale_voltage < 0)
+ *scale_voltage = 0;
+}
- if (prop->calibration == VADC_CALIB_ABSOLUTE)
- voltage += vadc->graph[prop->calibration].dx;
+static s64 vadc_scale_fn(struct vadc_priv *vadc,
+ const struct vadc_channel_prop *prop, u16 adc_code)
+{
+ const struct vadc_prescale_ratio *prescale;
+ s64 voltage = 0;
- if (voltage < 0)
- voltage = 0;
+ vadc_scale_calib(vadc, adc_code, prop, &voltage);
prescale = &vadc_prescale_ratios[prop->prescale];
@@ -552,11 +561,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
if (ret)
break;
- *val = vadc_calibrate(vadc, prop, adc_code);
+ *val = vadc_scale_fn(vadc, prop, adc_code);
- /* 2mV/K, return milli Celsius */
- *val /= 2;
- *val -= KELVINMIL_CELSIUSMIL;
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
prop = &vadc->chan_props[chan->address];
@@ -564,12 +570,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
if (ret)
break;
- *val = vadc_calibrate(vadc, prop, adc_code);
+ *val = (int)adc_code;
return IIO_VAL_INT;
- case IIO_CHAN_INFO_SCALE:
- *val = 0;
- *val2 = 1000;
- return IIO_VAL_INT_PLUS_MICRO;
default:
ret = -EINVAL;
break;
@@ -616,8 +618,8 @@ struct vadc_channels {
VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \
#define VADC_CHAN_VOLT(_dname, _pre) \
- VADC_CHAN(_dname, IIO_VOLTAGE, \
- BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
+ VADC_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
_pre) \
/*
@@ -850,9 +852,9 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
iio_chan->channel = prop.channel;
iio_chan->datasheet_name = vadc_chan->datasheet_name;
+ iio_chan->extend_name = child->name;
iio_chan->info_mask_separate = vadc_chan->info_mask;
iio_chan->type = vadc_chan->type;
- iio_chan->indexed = 1;
iio_chan->address = index++;
iio_chan++;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH V3 0/2] iio: adc: spmi-vadc: Changes to support different scaling
From: Rama Krishna Phani A @ 2016-10-26 14:41 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA, jic23-DgEjT+Ai2ygdnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: robh-DgEjT+Ai2ygdnm+yROfE0A, linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
smohanad-sgV2jX0FEOL9JmXXK+q4OQ, mgautam-sgV2jX0FEOL9JmXXK+q4OQ,
sivaa-sgV2jX0FEOL9JmXXK+q4OQ, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
Julia.Lawall-L2FTfq7BK8M, Rama Krishna Phani A
VADC is a 15 bit ADC that measures signals through the Main analog
multiplexer (AMUX). The driver arbitrates the request to issue ADC
read requests. These patches adds different scaling functionality
to convert adc code to physical units.
[V3]
1. Update the commit text to give more information on the
functionality implemented in the patches.
2. Added Dt maintainers.
[V2]
1. Replaced the do_div with normal division.
2. Split the functionality implemented in v1 patch.
3. Abstracted out common logic and updated change to report
Raw adc code properly.
4. Change to support different scaling functionality.
5. Added support for introducing new bindings in driver.
[V1]
https://lkml.org/lkml/2016/10/25/26
Rama Krishna Phani A (2):
iio: adc: spmi-vadc: Update changes to support reporting of Raw adc
code.
iio: adc: spmi-vadc: Changes to support different scaling
.../devicetree/bindings/iio/adc/qcom,spmi-vadc.txt | 14 +
drivers/iio/adc/qcom-spmi-vadc.c | 311 ++++++++++++++++-----
2 files changed, 261 insertions(+), 64 deletions(-)
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* Re: [PATCH 4/9] pinctrl: meson: allow gpio to request irq
From: Linus Walleij @ 2016-10-26 14:32 UTC (permalink / raw)
To: Jerome Brunet
Cc: Marc Zyngier, Carlo Caione, Kevin Hilman,
open list:ARM/Amlogic Meson...,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Thomas Gleixner, Jason Cooper, Rob Herring, Catalin Marinas,
Will Deacon, Russell King
In-Reply-To: <1477491766.2482.159.camel-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
On Wed, Oct 26, 2016 at 4:22 PM, Jerome Brunet <jbrunet-rdvid1DuHRBWk0Htik3J/w@public.gmane.org> wrote:
> On Tue, 2016-10-25 at 20:20 +0200, Linus Walleij wrote:
>> However the semantic is such, that it is not necessary to call
>> to_irq()
>> before using an IRQ: the irqchip and gpiochip abstractions should be
>> orthogonal.
>
> Linus,
>
> They are orthogonal. You can request an irq from the irqchip controller
> without the gpiochip, like any other irq controller.
OK good, sorry if I'm stating the obvious.
> irq_create_mapping (and irq_create_fwspec_mapping) internally calls
> irq_find_mapping. So if the mapping already exist (the irq is already
> used before calling to_irq), the existing mapping will be returned. The
> mapping will be actually created only if needed. It seems to be in line
> with your explanation, no ?
Yes, but you want to call irq_create_mapping() in slowpath (irq setup)
and irq_find_mapping() in fastpath (irq handler). Else the first IRQ
may result in unwelcomed surprises.
> There is really a *lot* of gpio drivers which use irq_create_mapping in
> the to_irq callback, are these all wrong ?
Yes they are all wrong. They should all be using irq_find_mapping().
> If this should not be used, what should we all do instead ?
Call irq_create_mapping() in some other place.
Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 4/9] pinctrl: meson: allow gpio to request irq
From: Jerome Brunet @ 2016-10-26 14:23 UTC (permalink / raw)
To: Linus Walleij, Marc Zyngier
Cc: Carlo Caione, Kevin Hilman, open list:ARM/Amlogic Meson...,
linux-arm-kernel@lists.infradead.org, linux-gpio@vger.kernel.org,
linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
Thomas Gleixner, Jason Cooper, Rob Herring, Catalin Marinas,
Will Deacon, Russell King
In-Reply-To: <CACRpkdba_iaOK61KiQGvJZYuz7-PiBgtN4vxv__tJLsZ8ZVL=w@mail.gmail.com>
On Tue, 2016-10-25 at 20:10 +0200, Linus Walleij wrote:
> On Tue, Oct 25, 2016 at 4:47 PM, Marc Zyngier <marc.zyngier@arm.com>
> wrote:
> >
> > On 25/10/16 15:22, Jerome Brunet wrote:
>
> >
> > >
> > > There is a few problems to guarantee that gpio == hwirq.
> > > 1. We have 2 instances of pinctrl, to guarantee that the linux
> > > gpio
> > > number == hwirq, we would have to guarantee the order in which
> > > they are
> > > probed. At least this my understanding
> >
> > Maybe I wasn't clear enough, and my use of gpio is probably wrong.
> > So
> > Linux has a gpio number, which is obviously an abstract number
> > (just
> > like the Linux irq number). But the pad number, in the context of
> > given
> > SoC, is constant. So we have:
> >
> > pad->gpio
> > hwirq->irq
> >
> > Why can't you have pad == hwirq, always? This is already what you
> > have
> > in the irqchip driver. This would simplify a lot of things.
>
> My thought as well.
>
> We usually refer to the local numberspace on the GPIO controller
> as "offsets", so line offsets 0...31 on a gpiochip with 31 lines.
>
> The ngpio in struct gpio_chip is the number of lines on that
> controller,
> and should nominally map 1:1 to hwirq sources.
Indeed it should be the the case, and for meson, it is pretty close.
The irqchip controller provide a number of hwirq. Each hwirq maps to
one, and only one, pin. But since not every pins are connected to the
irqchip controller, the opposite is not true.
Taking an example with 16 gpios, here is what it could look like with
the exception we have on meson :
gpio offset [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ]
hwirq num [ 0 1 2 3] NC NC[4 5 6 7 8 9 10]NC NC NC
Like gpio offset are used (internally) in the driver to find
appropriate gpio registers and bit, the hwirq has a meaning too.
It is the setting you put in the channel multiplexer of the controller
to select the proper pin to spy on.
In the end, these gpio offset and hwirq number are different. I would
prefer to have hwirq == gpio and go your way, it would make my life
easier, but I don't see how it would work.
The irqchip controller cares only about the hwirq number. You can
actually request an interrupt directly to the controller by asking the
proper hwirq number (in DT for example), without involving the gpio
driver (tested).
The relation between the pins and the interrupt number is provided by
the manufacturer in the Datasheet [1], in the section GPIO Interrupt.
Looking at other gpio drivers, it is not uncommon to have some simple
calculation to get from gpio offset to the hwirq number. I don't get
what is the specific problem here ?
If I missed something, feel free to point it out.
[1] http://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%202015012
6.pdf
>
> Yours,
> Linus Walleij
^ permalink raw reply
* Re: [PATCH 4/9] pinctrl: meson: allow gpio to request irq
From: Jerome Brunet @ 2016-10-26 14:22 UTC (permalink / raw)
To: Linus Walleij
Cc: Marc Zyngier, Carlo Caione, Kevin Hilman,
open list:ARM/Amlogic Meson...,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Thomas Gleixner, Jason Cooper, Rob Herring, Catalin Marinas,
Will Deacon, Russell King
In-Reply-To: <CACRpkdbGo4BJOdzkgBrE9jT-rKodd4zssCnOtOuGS+OqV-Uc6Q-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Tue, 2016-10-25 at 20:20 +0200, Linus Walleij wrote:
> On Tue, Oct 25, 2016 at 5:31 PM, Jerome Brunet <jbrunet-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> wrote:
> >
> > On Tue, 2016-10-25 at 15:47 +0100, Marc Zyngier wrote:
>
> >
> > >
> > > Is gpio_to_irq() supposed to allocate an interrupt? Or merely to
> > > report the existence of a mapping?
>
> It should provide an IRQ corresponding to the gpio line, if possible.
>
> However the semantic is such, that it is not necessary to call
> to_irq()
> before using an IRQ: the irqchip and gpiochip abstractions should be
> orthogonal.
Linus,
They are orthogonal. You can request an irq from the irqchip controller
without the gpiochip, like any other irq controller.
>
> This goes especially when using device tree or ACPI, where you
> may reference an IRQ from something modeled as irqchip, which
> is simultaneously a gpiochip.
>
> >
> > Linus, please correct me if I'm wrong,
> > .to_irq gets the linux gpio number and returns the linux virtual
> > irq
> > numbers, 0 if there is no interrupt.
>
> Yes. But it may *or may not* be called before using the IRQ.
>
> So it should look up or try to create a mapping on request, but not
> assume to have been called before using some line as IRQ.
>
> The only thing you should assume to be called before an interrupt
> is put to use is the stuff in irqchip. So you have to do your
> dynamic irqdomain mapping elsewhere than .to_irq().
irq_create_mapping (and irq_create_fwspec_mapping) internally calls
irq_find_mapping. So if the mapping already exist (the irq is already
used before calling to_irq), the existing mapping will be returned. The
mapping will be actually created only if needed. It seems to be in line
with your explanation, no ?
There is really a *lot* of gpio drivers which use irq_create_mapping in
the to_irq callback, are these all wrong ?
If this should not be used, what should we all do instead ?
>
> Yours,
> Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 2/4] pinctrl: single: Use generic parser and #pinctrl-cells for pinctrl-single,pins
From: Tony Lindgren @ 2016-10-26 14:16 UTC (permalink / raw)
To: Linus Walleij
Cc: Jon Hunter, Mark Rutland, Rob Herring, Grygorii Strashko,
Nishanth Menon, linux-gpio, devicetree, linux-kernel, linux-omap
In-Reply-To: <20161025164538.453-3-tony@atomide.com>
* Tony Lindgren <tony@atomide.com> [161025 09:51]:
> We can now use generic parser. To support the legacy binding without
> #pinctrl-cells, add pcs_quirk_missing_pinctrl_cells() and warn about
> missing #pinctrl-cells.
...
> +/**
> + * pcs_quirk_missing_pinctrl_cells - handle legacy binding
> + * @pcs: pinctrl driver instance
> + * @np: device tree node
> + * @cells: number of cells
> + *
> + * Handle legacy binding with no #pinctrl-cells. This should be
> + * always two pinctrl-single,bit-per-mux and one for others.
> + * At some point we may want to consider removing this.
> + */
> +static int pcs_quirk_missing_pinctrl_cells(struct pcs_device *pcs,
> + struct device_node *np,
> + int cells)
> +{
> + struct property *p;
> + const char *name = "#pinctrl-cells";
> + int error;
> + u32 val;
> +
> + error = of_property_read_u32(np, name, &val);
> + if (!error)
> + return 0;
> +
> + dev_warn(pcs->dev, "please update dts to use %s = <%i>\n",
> + name, cells);
> +
> + p = devm_kzalloc(pcs->dev, sizeof(*p), GFP_KERNEL);
> + if (!p)
> + return -ENOMEM;
> +
> + p->length = sizeof(__be32);
> + p->value = devm_kzalloc(pcs->dev, sizeof(__be32), GFP_KERNEL);
> + if (!p->value)
> + return -ENOMEM;
> + *(__be32 *)p->value = cpu_to_be32(cells);
> +
> + p->name = devm_kstrdup(pcs->dev, name, GFP_KERNEL);
> + if (!p->name)
> + return -ENOMEM;
> +
> + pcs->missing_nr_pinctrl_cells = p;
> +
> + return of_add_property(np, pcs->missing_nr_pinctrl_cells);
> +}
Looking at some make randconfig results, looks like we don't have
of_add_property() and of_remove_property() exported. Is there some
reason not to export them?
Regards,
Tony
^ permalink raw reply
* [PATCH] arm64: dts: mt8173: Fix auxadc node
From: Matthias Brugger @ 2016-10-26 14:15 UTC (permalink / raw)
To: robh+dt, mark.rutland, will.deacon, zhiyong.tao, s.hauer
Cc: djkurtz, p.zabel, hans.verkuil, andrew-ct.chen, ck.hu, bayi.cheng,
yong.wu, dawei.chien, chunfeng.yun, tiffany.lin, fan.chen,
devicetree, linux-arm-kernel, linux-kernel, linux-mediatek,
Matthias Brugger
The devicetree node for mt8173-auxadc lacks the clock and
io-channel-cells property. This leads to a non-working driver.
mt6577-auxadc 11001000.auxadc: failed to get auxadc clock
mt6577-auxadc: probe of 11001000.auxadc failed with error -2
Fix these fields to get the device up and running.
Fixes: 748c7d4de46a ("ARM64: dts: mt8173: Add thermal/auxadc device
nodes")
Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com>
---
arch/arm64/boot/dts/mediatek/mt8173.dtsi | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 1c71e25..6c03c17 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -450,6 +450,9 @@
auxadc: auxadc@11001000 {
compatible = "mediatek,mt8173-auxadc";
reg = <0 0x11001000 0 0x1000>;
+ clocks = <&pericfg CLK_PERI_AUXADC>;
+ clock-names = "main";
+ #io-channel-cells = <1>;
};
uart0: serial@11002000 {
--
2.10.1
^ permalink raw reply related
* Re: [PATCH] soc: qcom: Add SoC info driver
From: Imran Khan @ 2016-10-26 14:12 UTC (permalink / raw)
To: Arnd Bergmann
Cc: andy.gross, David Brown, Rob Herring, Mark Rutland,
open list:ARM/QUALCOMM SUPPORT, open list:ARM/QUALCOMM SUPPORT,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <3809309.oOnjdjqnN9@wuerfel>
On 10/26/2016 7:35 PM, Arnd Bergmann wrote:
>> > As we are talking about generic soc_device_attribute fields, I was hoping that
>> > having a vendor field would be helpful as along with family it would provide
>> > a more thorough information. Also as more than one foundries may be used for
>> > a soc, can we have a field say foundry_id to provide this information.
> My first feeling is that this 'vendor' information can should be
> derived from the family. It's also not clear what would happen
> to this when a company gets bought. E.g. the Oxnas product family
> was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
> mxs family was Sigmatel, Freescale, now NXP and might soon be
> Qualcomm. What would you put in there in this case?
Okay, not having vendor field is fine for me. Could you also suggest
something about the foundry_id field.
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a\nmember of the Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply
* Re: [PATCH] soc: qcom: Add SoC info driver
From: Arnd Bergmann @ 2016-10-26 14:05 UTC (permalink / raw)
To: Imran Khan
Cc: andy.gross, David Brown, Rob Herring, Mark Rutland,
open list:ARM/QUALCOMM SUPPORT, open list:ARM/QUALCOMM SUPPORT,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <aec67ab3-8e89-fe06-b385-988f8eea5f46@codeaurora.org>
On Wednesday, October 26, 2016 7:20:42 PM CEST Imran Khan wrote:
> On 10/26/2016 2:19 AM, Arnd Bergmann wrote:
> > On Tuesday, October 25, 2016 3:23:34 PM CEST Imran Khan wrote:
> >> On 10/21/2016 4:03 PM, Arnd Bergmann wrote:
> >>>> +/* socinfo: sysfs functions */
> >>>
> >>> This seems overly verbose, having both raw and human-readable
> >>> IDs is generally not necessary, pick one of the two. If you
> >>> need any fields that we don't already support in soc_device,
> >>> let's talk about adding them to the generic structure.
> >>>
> >>>
> >>
> >> Okay. I will go for human readable IDs. Can we add 2 more fields
> >> in the generic structure.
> >> These 2 fields would be:
> >>
> >> vendor: A string for vendor name
> >> serial_number: A string containing serial number for the platform
> >
> >
> > serial_number seems straightforward, adding this seems like a good
> > idea. I don't understand yet what would go into the vendor field
> > though. For this particular driver, is it always "Qualcomm", or
> > would it be a third-party that makes a device based on that chip?
> >
>
> As we are talking about generic soc_device_attribute fields, I was hoping that
> having a vendor field would be helpful as along with family it would provide
> a more thorough information. Also as more than one foundries may be used for
> a soc, can we have a field say foundry_id to provide this information.
My first feeling is that this 'vendor' information can should be
derived from the family. It's also not clear what would happen
to this when a company gets bought. E.g. the Oxnas product family
was subsequently owned by Oxford, PLX, Avago and Broadcom, and the
mxs family was Sigmatel, Freescale, now NXP and might soon be
Qualcomm. What would you put in there in this case?
Arnd
^ permalink raw reply
* Re: [PATCH 05/14] mfd: sun6i-prcm: Add sun8i analog codec as subnode
From: Lee Jones @ 2016-10-26 14:05 UTC (permalink / raw)
To: Mylène Josserand
Cc: vinod.koul-ral2JQCrhuEAvxtiuMwx3w,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, wens-jdAy2FN1RRM,
mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k, mark.rutland-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dmaengine-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-clk-u79uwXL29TY76Z2rM5mHXA,
alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
In-Reply-To: <c74aea452bd1831f439dd67884cb2879e0849f03.1475571575.git.mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
On Tue, 04 Oct 2016, Mylène Josserand wrote:
> The sun8i audio codec is using PRCM registers to configure all the
> analog part of the audio codec. It is added as a subnode of the PRCM
> with his resource (offset of 0x1c0).
>
> Signed-off-by: Mylène Josserand <mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
> drivers/mfd/sun6i-prcm.c | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/drivers/mfd/sun6i-prcm.c b/drivers/mfd/sun6i-prcm.c
> index 011fcc5..e0c6bf5 100644
> --- a/drivers/mfd/sun6i-prcm.c
> +++ b/drivers/mfd/sun6i-prcm.c
> @@ -12,6 +12,8 @@
> #include <linux/init.h>
> #include <linux/of.h>
>
> +#define SUN6I_AUDIO_CODEC_ANALOG 0x1c0
> +
> struct prcm_data {
> int nsubdevs;
> const struct mfd_cell *subdevs;
> @@ -57,6 +59,14 @@ static const struct resource sun6i_a31_apb0_rstc_res[] = {
> },
> };
>
> +static const struct resource sun8i_adda_res[] = {
> + {
> + .start = SUN6I_AUDIO_CODEC_ANALOG,
> + .end = 0x1c3,
This also needs defining. No magic numbers please.
> + .flags = IORESOURCE_MEM,
> + },
> +};
> +
> static const struct mfd_cell sun6i_a31_prcm_subdevs[] = {
> {
> .name = "sun6i-a31-ar100-clk",
> @@ -109,6 +119,12 @@ static const struct mfd_cell sun8i_a23_prcm_subdevs[] = {
> .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res),
> .resources = sun6i_a31_apb0_rstc_res,
> },
> + {
> + .name = "sun8i-codec-analog",
> + .of_compatible = "allwinner,sun8i-codec-analog",
> + .num_resources = ARRAY_SIZE(sun8i_adda_res),
> + .resources = sun8i_adda_res,
> + },
> };
>
> static const struct prcm_data sun6i_a31_prcm_data = {
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH 2/2] phy: qcom-qmp: new qmp phy driver for qcom-chipsets
From: Srinivas Kandagatla @ 2016-10-26 13:57 UTC (permalink / raw)
To: Vivek Gautam, kishon-l0cyMroinI0, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1476873827-7191-3-git-send-email-vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Hi Vivek,
Thanks for consolidating qmp phy support for both usb and pcie.
On 19/10/16 11:43, Vivek Gautam wrote:
> Qualcomm SOCs have QMP phy controller that provides support
> to a number of controller, viz. PCIe, UFS, and USB.
> Add a new driver, based on generic phy framework, for this
> phy controller.
>
> USB3-phy changes: Based on phy-msm-ssusb-qmp driver available on
> msm-4.4 kernel @codeaurora[1].
> PCIe-phy changes: Based on msm8996-pcie-phy driver posted by
> Srinivas [2].
>
> [1] https://source.codeaurora.org/quic/la/kernel/msm-4.4/log/?h=caf/3.18/msm-3.18
> [2] https://patchwork.kernel.org/patch/9318947/
>
> Signed-off-by: Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Cc: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>
very few minor nits which needs fixing in next version.
I have tested this patch on DB820c PCIE with ethernet and SATA ports.
Tested-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> .../devicetree/bindings/phy/qcom-qmp-phy.txt | 61 ++
> drivers/phy/Kconfig | 8 +
> drivers/phy/Makefile | 1 +
> drivers/phy/phy-qcom-qmp.c | 1154 ++++++++++++++++++++
> 4 files changed, 1224 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> create mode 100644 drivers/phy/phy-qcom-qmp.c
>
> diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> new file mode 100644
> index 0000000..90214aa
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
> @@ -0,0 +1,61 @@
> +Qualcomm QMP PHY
> +----------------
> +
> +QMP phy controller supports physical layer functionality for a number of
> +controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
> +
> +Required properties:
> + - compatible: compatible list, contains:
> + "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
> + "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
> + - reg: offset and length of the PHY register set.
> + - #phy-cells: must be 1
> + - Cell after phy phandle should be the port (lane) number.
> + - clocks: a list of phandles and clock-specifier pairs,
> + one for each entry in clock-names.
> + - clock-names: must be "cfg_ahb" for phy config clock,
> + "aux" for phy aux clock,
> + "ref_clk" for 19.2 MHz ref clk,
> + "ref_clk_src" for reference clock source,
> + "pipe<port-number>" for pipe clock specific to
> + each port/lane (Optional).
> + - resets: a list of phandles and reset controller specifier pairs,
> + one for each entry in reset-names.
> + - reset-names: must be "phy" for reset of phy block,
> + "common" for phy common block reset,
> + "cfg" for phy's ahb cfg block reset (Optional).
> + "port<port-number>" for reset specific to
> + each port/lane. (Optional)
> + - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
> + - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
> +
> +Optional properties:
> + - vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
> + pll block.
> +
> +Example:
> + pcie_phy: pciephy@34000 {
> + compatible = "qcom,qmp-14nm-pcie-phy";
Fix this according to the compatibilities listed above.
> + reg = <0x034000 0x3fff>;
> + #phy-cells = <1>;
> +
> + clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
> + <&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
> + <&gcc GCC_PCIE_0_PIPE_CLK>,
> + <&gcc GCC_PCIE_1_PIPE_CLK>,
> + <&gcc GCC_PCIE_2_PIPE_CLK>;
> + clock-names = "aux", "cfg_ahb",
> + "pipe0", "pipe1", "pipe2";
> +
> + vdda-phy-supply = <&pm8994_l28>;
> + vdda-pll-supply = <&pm8994_l12>;
> +
> + resets = <&gcc GCC_PCIE_PHY_BCR>,
> + <&gcc GCC_PCIE_PHY_COM_BCR>,
> + <&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>,
> + <&gcc GCC_PCIE_0_PHY_BCR>,
> + <&gcc GCC_PCIE_1_PHY_BCR>,
> + <&gcc GCC_PCIE_2_PHY_BCR>;
> + reset-names = "phy", "common", "cfg",
> + "lane0", "lane1", "lane2";
> + };
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 5547984..d5e2b50f 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -446,6 +446,14 @@ config PHY_STIH41X_USB
> Enable this to support the USB transceiver that is part of
> STMicroelectronics STiH41x SoC series.
>
> +config PHY_QCOM_QMP
> + tristate "Qualcomm QMP PHY Driver"
> + depends on OF && (ARCH_QCOM || COMPILE_TEST)
> + select GENERIC_PHY
> + help
> + Enable this to support the QMP PHY transceiver that is used
> + with controllers such as PCIe, UFS, and USB on Qualcomm chips.
> +
> config PHY_QCOM_QUSB2
> tristate "Qualcomm QUSB2 PHY Driver"
> depends on OF && (ARCH_QCOM || COMPILE_TEST)
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 848489d..fde9fba 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -51,6 +51,7 @@ 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_QMP) += phy-qcom-qmp.o
> obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
> obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
> obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
> diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c
> new file mode 100644
> index 0000000..7e89179
> --- /dev/null
> +++ b/drivers/phy/phy-qcom-qmp.c
> @@ -0,0 +1,1154 @@
> +/*
> + * Copyright (c) 2016, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include <dt-bindings/phy/phy.h>
We would not need this once you have proper flags in cfg.
> +
> +
> +static inline void qphy_setbits(u32 bitmask, void __iomem *reg)
> +{
> + u32 val;
> +
> + val = readl_relaxed(reg);
> + val |= bitmask;
> + writel_relaxed(val, reg);
> +}
> +
> +static inline void qphy_clrbits(u32 bitmask, void __iomem *reg)
> +{
> + u32 val;
> +
> + val = readl_relaxed(reg);
> + val &= ~bitmask;
> + writel_relaxed(val, reg);
> +}
> +
> +unsigned int msm8996_pciephy_tx_offsets[] = { 0x1000, 0x2000, 0x3000 };
> +unsigned int msm8996_pciephy_rx_offsets[] = { 0x1200, 0x2200, 0x3200 };
> +unsigned int msm8996_pciephy_pcs_offsets[] = { 0x1400, 0x2400, 0x3400 };
> +
> +unsigned int msm8996_usb3phy_tx_offsets[] = { 0x200 };
> +unsigned int msm8996_usb3phy_rx_offsets[] = { 0x400 };
> +unsigned int msm8996_usb3phy_pcs_offsets[] = { 0x600 };
> +
> +const struct qmp_phy_init_cfg pciephy_init_cfg = {
> + .type = PHY_TYPE_PCIE,
> + .nlanes = 3,
> + .tx_offsets = msm8996_pciephy_tx_offsets,
> + .rx_offsets = msm8996_pciephy_rx_offsets,
> + .pcs_offsets = msm8996_pciephy_pcs_offsets,
> +
> + .phy_init_serdes_tbl = pciephy_serdes_init_tbl,
> + .phy_init_serdes_tbl_sz = ARRAY_SIZE(pciephy_serdes_init_tbl),
> + .phy_init_tx_tbl = pciephy_tx_init_tbl,
> + .phy_init_tx_tbl_sz = ARRAY_SIZE(pciephy_tx_init_tbl),
> + .phy_init_rx_tbl = pciephy_rx_init_tbl,
> + .phy_init_rx_tbl_sz = ARRAY_SIZE(pciephy_rx_init_tbl),
> + .phy_init_pcs_tbl = pciephy_pcs_init_tbl,
> + .phy_init_pcs_tbl_sz = ARRAY_SIZE(pciephy_pcs_init_tbl),
> + .has_phy_com_ctrl = true,
> + .regs = pciephy_regs_layout,
> + .mask_start_ctrl = (PHY_PCS_START | PHY_PLL_READY_GATE_EN),
> + .mask_pwr_dn_ctrl = (PHY_SW_PWRDN | PHY_REFCLK_DRV_DSBL),
> +};
> +
> +const struct qmp_phy_init_cfg usb3phy_init_cfg = {
> + .type = PHY_TYPE_USB3,
> + .nlanes = 1,
> + .tx_offsets = msm8996_usb3phy_tx_offsets,
> + .rx_offsets = msm8996_usb3phy_rx_offsets,
> + .pcs_offsets = msm8996_usb3phy_pcs_offsets,
> +
> + .phy_init_serdes_tbl = usb3phy_serdes_init_tbl,
> + .phy_init_serdes_tbl_sz = ARRAY_SIZE(usb3phy_serdes_init_tbl),
> + .phy_init_tx_tbl = usb3phy_tx_init_tbl,
> + .phy_init_tx_tbl_sz = ARRAY_SIZE(usb3phy_tx_init_tbl),
> + .phy_init_rx_tbl = usb3phy_rx_init_tbl,
> + .phy_init_rx_tbl_sz = ARRAY_SIZE(usb3phy_rx_init_tbl),
> + .phy_init_pcs_tbl = usb3phy_pcs_init_tbl,
> + .phy_init_pcs_tbl_sz = ARRAY_SIZE(usb3phy_pcs_init_tbl),
> + .regs = usb3phy_regs_layout,
> + .mask_start_ctrl = (PHY_SERDES_START | PHY_PCS_START),
> + .mask_pwr_dn_ctrl = PHY_SW_PWRDN,
> +};
> +
> +static void qcom_qmp_phy_configure(void __iomem *base,
> + unsigned int *regs_layout,
> + struct qmp_phy_init_tbl init_tbl[],
> + int init_tbl_sz)
> +{
> + int i;
> +
> + for (i = 0; i < init_tbl_sz; i++) {
> + if (init_tbl[i].in_layout)
> + writel_relaxed(init_tbl[i].cfg_val,
> + base + regs_layout[init_tbl[i].reg_offset]);
> + else
> + writel_relaxed(init_tbl[i].cfg_val,
> + base + init_tbl[i].reg_offset);
> + }
> +
> + /* flush buffered writes */
> + mb();
> +}
> +
> +static int qcom_qmp_phy_poweron(struct phy *phy)
> +{
> + struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> + struct qcom_qmp_phy *qphy = phydesc->qphy;
> + int ret;
> +
> + dev_info(&phy->dev, "Powering on QMP phy\n");
> +
> + ret = regulator_enable(qphy->vdda_phy);
> + if (ret) {
> + dev_err(qphy->dev, "%s: vdda-phy enable failed, err=%d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + ret = regulator_enable(qphy->vdda_pll);
> + if (ret) {
> + dev_err(qphy->dev, "%s: vdda-pll enable failed, err=%d\n",
> + __func__, ret);
> + goto err_vdda_pll;
> + }
> +
> + if (qphy->vddp_ref_clk) {
> + ret = regulator_enable(qphy->vddp_ref_clk);
> + if (ret) {
> + dev_err(qphy->dev, "%s: vdda-ref-clk enable failed, err=%d\n",
> + __func__, ret);
> + goto err_vddp_refclk;
> + }
> + }
> +
> + if (!qphy->clk_enabled) {
> + clk_prepare_enable(qphy->ref_clk_src);
> + clk_prepare_enable(qphy->ref_clk);
> + clk_prepare_enable(phydesc->pipe_clk);
> + qphy->clk_enabled = true;
> + }
> +
> + return 0;
> +
> +err_vddp_refclk:
> + regulator_disable(qphy->vdda_pll);
> +err_vdda_pll:
> + regulator_disable(qphy->vdda_phy);
> + return ret;
> +}
> +
> +static int qcom_qmp_phy_poweroff(struct phy *phy)
> +{
> + struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> + struct qcom_qmp_phy *qphy = phydesc->qphy;
> +
> + if (qphy->clk_enabled) {
> + clk_disable_unprepare(qphy->ref_clk_src);
> + clk_disable_unprepare(qphy->ref_clk);
> + clk_disable_unprepare(phydesc->pipe_clk);
> + qphy->clk_enabled = false;
> + }
> +
> + if (qphy->vddp_ref_clk)
> + regulator_disable(qphy->vddp_ref_clk);
> +
> + regulator_disable(qphy->vdda_pll);
> + regulator_disable(qphy->vdda_phy);
> +
> + return 0;
> +}
> +
> +static int qcom_qmp_phy_is_ready(struct qcom_qmp_phy *qphy,
> + void __iomem *pcs_status, u32 mask)
> +{
> + unsigned int init_timeout;
> +
> + init_timeout = PHY_READY_TIMEOUT_COUNT;
> + do {
> + if (readl_relaxed(pcs_status) & mask)
> + break;
> +
> + usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
> + REFCLK_STABILIZATION_DELAY_US_MAX);
> + } while (--init_timeout);
> +
> + if (!init_timeout)
> + return -EBUSY;
> +
> + return 0;
> +}
> +
> +static int qcom_qmp_phy_com_init(struct qcom_qmp_phy *qphy)
> +{
> + const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> + void __iomem *serdes = qphy->serdes;
> + int ret;
> +
> + mutex_lock(&qphy->phy_mutex);
> + if (qphy->init_count++) {
> + mutex_unlock(&qphy->phy_mutex);
> + return 0;
> + }
> +
> + ret = reset_control_deassert(qphy->phy_rst);
> + if (ret) {
> + dev_err(qphy->dev, "phy reset deassert failed\n");
mutex unlock
> + return ret;
> + }
> +
> + ret = reset_control_deassert(qphy->phycom_rst);
> + if (ret) {
> + dev_err(qphy->dev, "common reset deassert failed\n");
> + goto err_phycom_rst;
> + }
> +
> + if (qphy->phycfg_rst) {
> + ret = reset_control_deassert(qphy->phycfg_rst);
> + if (ret) {
> + dev_err(qphy->dev, "common reset deassert failed\n");
> + goto err_phycfg_rst;
> + }
> + }
> +
> + if (cfg->has_phy_com_ctrl) {
> + qphy_setbits(PHY_SW_PWRDN,
> + serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
> + /* Make sure that above write is completed */
> + mb();
> + }
> +
> + /* Serdes configuration */
> + qcom_qmp_phy_configure(serdes, cfg->regs, cfg->phy_init_serdes_tbl,
> + cfg->phy_init_serdes_tbl_sz);
> +
> + if (cfg->has_phy_com_ctrl) {
> + qphy_clrbits(PHY_SW_RESET, serdes +
> + cfg->regs[QPHY_COM_SW_RESET]);
> + qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
> + serdes + cfg->regs[QPHY_COM_START_CONTROL]);
> + /* Make sure that above write is completed */
> + mb();
> +
> + ret = qcom_qmp_phy_is_ready(qphy, serdes +
> + cfg->regs[QPHY_COM_PCS_READY_STATUS],
> + MASK_COM_PCS_READY);
> + if (ret) {
> + dev_err(qphy->dev,
> + "common control block init timed-out\n");
> + goto err_phy_comctrl;
> + }
> + }
> +
> + mutex_unlock(&qphy->phy_mutex);
> +
> + return 0;
> +
> +err_phy_comctrl:
mutex unlock
> + if (qphy->phycfg_rst)
> + reset_control_assert(qphy->phycfg_rst);
> +err_phycfg_rst:
> + reset_control_assert(qphy->phycom_rst);
> +err_phycom_rst:
> + reset_control_assert(qphy->phy_rst);
> + return ret;
> +}
> +
> +static int qcom_qmp_phy_com_exit(struct qcom_qmp_phy *qphy)
> +{
> + const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> + void __iomem *serdes = qphy->serdes;
> +
> + mutex_lock(&qphy->phy_mutex);
> + if (--qphy->init_count) {
> + mutex_unlock(&qphy->phy_mutex);
> + return 0;
> + }
> +
> + if (cfg->has_phy_com_ctrl) {
> + qphy_setbits(PHY_SERDES_START | PHY_PCS_START,
> + serdes + cfg->regs[QPHY_COM_START_CONTROL]);
> + qphy_clrbits(PHY_SW_RESET, serdes +
> + cfg->regs[QPHY_COM_SW_RESET]);
> + qphy_setbits(PHY_SW_PWRDN,
> + serdes + cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]);
> +
> + /* Make sure that above writes are completed */
> + mb();
> + }
> +
> + reset_control_assert(qphy->phy_rst);
> + reset_control_assert(qphy->phycom_rst);
> + if (qphy->phycfg_rst)
> + reset_control_assert(qphy->phycfg_rst);
> +
> + mutex_unlock(&qphy->phy_mutex);
> +
> + return 0;
> +}
> +
> +/* PHY Initialization */
> +static int qcom_qmp_phy_init(struct phy *phy)
> +{
> + struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> + struct qcom_qmp_phy *qphy = phydesc->qphy;
> + const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> + void __iomem *tx = phydesc->tx;
> + void __iomem *rx = phydesc->rx;
> + void __iomem *pcs = phydesc->pcs;
> + int ret;
> +
> + dev_info(qphy->dev, "Initializing QMP phy\n");
> +
> + /* enable interface clocks to program phy */
> + clk_prepare_enable(qphy->aux_clk);
> + clk_prepare_enable(qphy->cfg_ahb_clk);
> +
> + ret = qcom_qmp_phy_com_init(qphy);
> + if (ret)
> + goto err;
> +
> + if (phydesc->lane_rst) {
> + ret = reset_control_deassert(phydesc->lane_rst);
> + if (ret) {
> + dev_err(qphy->dev, "lane<%d> reset deassert failed\n",
> + phydesc->index);
> + goto err_lane_rst;
> + }
> + }
> +
> + /* Tx, Rx, and PCS configurations */
> + qcom_qmp_phy_configure(tx, cfg->regs, cfg->phy_init_tx_tbl,
> + cfg->phy_init_tx_tbl_sz);
> + qcom_qmp_phy_configure(rx, cfg->regs, cfg->phy_init_rx_tbl,
> + cfg->phy_init_rx_tbl_sz);
> + qcom_qmp_phy_configure(pcs, cfg->regs, cfg->phy_init_pcs_tbl,
> + cfg->phy_init_pcs_tbl_sz);
> +
> + /*
> + * Pull out PHY from POWER DOWN state:
> + * This is active low enable signal to power-down PHY.
> + */
> + qphy_setbits(cfg->mask_pwr_dn_ctrl,
> + pcs + QPHY_POWER_DOWN_CONTROL);
> + /* XXX: 10 us delay; given in PCIE HPG only */
> + usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
> +
> + /* start SerDes and Phy-Coding-Sublayer */
> + qphy_setbits(cfg->mask_start_ctrl, pcs + QPHY_START_CTRL);
> +
> + /* Pull PHY out of reset state */
> + qphy_clrbits(PHY_SW_RESET, pcs + QPHY_SW_RESET);
> + /* Make sure that above writes are completed */
> + mb();
> +
> + ret = qcom_qmp_phy_is_ready(qphy, pcs +
> + cfg->regs[QPHY_PCS_READY_STATUS],
> + MASK_PHYSTATUS);
> + if (ret) {
> + dev_err(qphy->dev, "phy initialization timed-out\n");
> + goto err_pcs_ready;
> + }
> +
> + return 0;
> +
> +err_pcs_ready:
> + if (phydesc->lane_rst)
> + reset_control_assert(phydesc->lane_rst);
> +err_lane_rst:
> + qcom_qmp_phy_com_exit(qphy);
> +err:
> + clk_disable_unprepare(qphy->cfg_ahb_clk);
> + clk_disable_unprepare(qphy->aux_clk);
> + return ret;
> +}
> +
> +static int qcom_qmp_phy_exit(struct phy *phy)
> +{
> + struct qmp_phy_desc *phydesc = phy_get_drvdata(phy);
> + struct qcom_qmp_phy *qphy = phydesc->qphy;
> + const struct qmp_phy_init_cfg *cfg = qphy->cfg;
> +
> + /* PHY reset */
> + qphy_setbits(PHY_SW_RESET, phydesc->pcs + QPHY_SW_RESET);
> +
> + /* stop SerDes and Phy-Coding-Sublayer */
> + qphy_clrbits(cfg->mask_start_ctrl, phydesc->pcs + QPHY_START_CTRL);
> +
> + /* Put PHY into POWER DOWN state: active low */
> + qphy_clrbits(cfg->mask_pwr_dn_ctrl,
> + phydesc->pcs + QPHY_POWER_DOWN_CONTROL);
> +
> + /* Make sure that above writes are completed */
> + mb();
> +
> + if (phydesc->lane_rst)
> + reset_control_assert(phydesc->lane_rst);
> +
> + qcom_qmp_phy_com_exit(qphy);
> +
> + clk_disable_unprepare(qphy->aux_clk);
> + clk_disable_unprepare(qphy->cfg_ahb_clk);
> +
> + return 0;
> +}
> +
> +
> +static int qcom_qmp_phy_regulator_init(struct device *dev)
> +{
> + struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> + int ret = 0;
> +
> + qphy->vdda_phy = devm_regulator_get(dev, "vdda-phy");
> + if (IS_ERR(qphy->vdda_phy)) {
> + ret = PTR_ERR(qphy->vdda_phy);
> + dev_err(dev, "failed to get vdda-phy, %d\n", ret);
> + return ret;
> + }
> +
> + qphy->vdda_pll = devm_regulator_get(dev, "vdda-pll");
> + if (IS_ERR(qphy->vdda_pll)) {
> + ret = PTR_ERR(qphy->vdda_pll);
> + dev_err(dev, "failed to get vdda-pll, %d\n", ret);
> + return ret;
> + }
> +
> + /* optional regulator */
> + qphy->vddp_ref_clk = devm_regulator_get(dev, "vddp-ref-clk");
Should mention this in bindings too.
> + if (IS_ERR(qphy->vddp_ref_clk)) {
> + ret = PTR_ERR(qphy->vddp_ref_clk);
> + dev_info(dev, "failed to get vddp-ref-clk, %d\n", ret);
> + qphy->vddp_ref_clk = NULL;
> + }
> +
> + return 0;
> +}
> +
> +static int qcom_qmp_phy_clk_init(struct device *dev)
> +{
> + struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> + int ret;
> +
> + qphy->aux_clk = devm_clk_get(dev, "aux");
> + if (IS_ERR(qphy->aux_clk)) {
> + ret = PTR_ERR(qphy->aux_clk);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "failed to get aux_clk\n");
> + return ret;
> + }
> +
> + qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
> + if (IS_ERR(qphy->cfg_ahb_clk)) {
> + ret = PTR_ERR(qphy->cfg_ahb_clk);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "failed to get cfg_ahb_clk\n");
> + return ret;
> + }
> +
> + /*
> + * ref_clk and ref_clk_src handles may not be available in
> + * all hardwares. So we don't return error in these cases.
> + */
This thing has to be mentioned in the bindings too.
> + qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
> + if (IS_ERR(qphy->ref_clk_src)) {
> + ret = PTR_ERR(qphy->ref_clk_src);
> + if (ret != -EPROBE_DEFER) {
> + qphy->ref_clk_src = NULL;
> + dev_dbg(dev, "failed to get ref_clk_src\n");
> + } else {
> + return ret;
> + }
> + }
> +
> + qphy->ref_clk = devm_clk_get(dev, "ref_clk");
> + if (IS_ERR(qphy->ref_clk)) {
> + ret = PTR_ERR(qphy->ref_clk);
> + if (ret != -EPROBE_DEFER) {
> + qphy->ref_clk = NULL;
> + dev_dbg(dev, "failed to get ref_clk\n");
> + } else {
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static struct phy *qcom_qmp_phy_xlate(struct device *dev,
> + struct of_phandle_args *args)
> +{
> + struct qcom_qmp_phy *qphy = dev_get_drvdata(dev);
> + int i;
> +
> + if (WARN_ON(args->args[0] >= qphy->cfg->nlanes))
> + return ERR_PTR(-ENODEV);
> +
> + for (i = 0; i < qphy->cfg->nlanes; i++) {
> + if (qphy->phys[i]->index == args->args[0])
> + break;
> + }
> +
> + if (i == qphy->cfg->nlanes)
> + return ERR_PTR(-ENODEV);
> +
> + return qphy->phys[i]->phy;
> +}
> +
> +static const struct phy_ops qcom_qmp_phy_gen_ops = {
> + .init = qcom_qmp_phy_init,
> + .exit = qcom_qmp_phy_exit,
> + .power_on = qcom_qmp_phy_poweron,
> + .power_off = qcom_qmp_phy_poweroff,
> + .owner = THIS_MODULE,
> +};
> +
> +static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
> + {
> + .compatible = "qcom,msm8996-qmp-pcie-phy",
> + .data = &pciephy_init_cfg,
> + }, {
> + .compatible = "qcom,msm8996-qmp-usb3-phy",
> + .data = &usb3phy_init_cfg,
> + },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
> +
> +static int qcom_qmp_phy_probe(struct platform_device *pdev)
> +{
> + struct qcom_qmp_phy *qphy;
> + struct device *dev = &pdev->dev;
> + struct device_node *child;
> + struct phy_provider *phy_provider;
> + struct resource *res;
> + const struct of_device_id *match;
> + void __iomem *base;
> + int ret = 0;
> + int id;
> +
> + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
> + if (!qphy)
> + return -ENOMEM;
new line
> + qphy->dev = dev;
> + dev_set_drvdata(dev, qphy);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -ENODEV;
new line
> + base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + /* per PHY serdes; usually located at base address */
> + qphy->serdes = base;
> +
> + mutex_init(&qphy->phy_mutex);
> +
> + /* Get the specific init parameters of QMP phy */
> + match = of_match_node(qcom_qmp_phy_of_match_table, dev->of_node);
> + qphy->cfg = match->data;
> +
> + ret = qcom_qmp_phy_clk_init(dev);
> + if (ret) {
> + dev_err(dev, "clock init failed\n");
> + return ret;
> + }
> +
> + ret = qcom_qmp_phy_regulator_init(dev);
> + if (ret) {
> + dev_err(dev, "regulator init failed\n");
> + return ret;
> + }
> +
> + qphy->phy_rst = devm_reset_control_get(dev, "phy");
> + if (IS_ERR(qphy->phy_rst)) {
> + dev_err(dev, "failed to get phy core reset\n");
> + return PTR_ERR(qphy->phy_rst);
> + }
> +
> + qphy->phycom_rst = devm_reset_control_get(dev, "common");
> + if (IS_ERR(qphy->phycom_rst)) {
> + dev_err(dev, "failed to get phy common reset\n");
> + return PTR_ERR(qphy->phycom_rst);
> + }
> +
> + qphy->phycfg_rst = devm_reset_control_get(dev, "cfg");
> + if (IS_ERR(qphy->phycfg_rst)) {
> + dev_err(dev, "failed to get phy ahb cfg reset\n");
> + qphy->phycfg_rst = NULL;
> + }
> +
> + qphy->phys = devm_kcalloc(dev, qphy->cfg->nlanes,
> + sizeof(*qphy->phys), GFP_KERNEL);
> + if (!qphy->phys)
> + return -ENOMEM;
> +
> + for (id = 0; id < qphy->cfg->nlanes; id++) {
> + struct phy *generic_phy;
> + struct qmp_phy_desc *phy_desc;
> + char prop_name[MAX_PROP_NAME];
> +
> + phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
> + if (!phy_desc) {
> + ret = -ENOMEM;
> + goto put_child;
> + }
> +
> + phy_desc->tx = base + qphy->cfg->tx_offsets[id];
> + phy_desc->rx = base + qphy->cfg->rx_offsets[id];
> + phy_desc->pcs = base + qphy->cfg->pcs_offsets[id];
> +
> + /*
> + * Get PHY's Pipe clock, if any; USB3 and PCIe are PIPE
> + * based phys, so they essentially have pipe clock
> + */
> + memset(&prop_name, 0, sizeof(prop_name));
> + snprintf(prop_name, MAX_PROP_NAME, "pipe%d", id);
> + phy_desc->pipe_clk = devm_clk_get(dev, prop_name);
> + if (IS_ERR(phy_desc->pipe_clk)) {
> + if (qphy->cfg->type == PHY_TYPE_PCIE ||
> + qphy->cfg->type == PHY_TYPE_USB3) {
> + ret = PTR_ERR(phy_desc->pipe_clk);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev,
> + "failed to get lane%d pipe_clk\n", id);
> + return ret;
> + } else {
> + phy_desc->pipe_clk = NULL;
> + }
> + }
Do we need to really need phy type for this?
You should proably make a flag in cfg to mark this rather then using phy
type. Hopefully it will also simplify the logic.
> +
> + /* Get lane reset, if any */
> + memset(&prop_name, 0, sizeof(prop_name));
> + snprintf(prop_name, MAX_PROP_NAME, "lane%d", id);
> + phy_desc->lane_rst = devm_reset_control_get(dev, prop_name);
> + if (IS_ERR(phy_desc->lane_rst)) {
> + if (qphy->cfg->type == PHY_TYPE_PCIE) {
> + dev_err(dev, "failed to get lane%d reset\n",
> + id);
> + ret = PTR_ERR(phy_desc->lane_rst);
> + goto put_child;
> + } else {
> + phy_desc->lane_rst = NULL;
> + }
> + }
Same comment as above.
> +
> + generic_phy = devm_phy_create(dev, NULL, &qcom_qmp_phy_gen_ops);
> + if (IS_ERR(generic_phy)) {
> + ret = PTR_ERR(generic_phy);
> + dev_err(dev, "failed to create qphy %d\n", ret);
> + goto put_child;
> + }
> +
> + phy_desc->phy = generic_phy;
> + phy_desc->index = id;
> + phy_desc->qphy = qphy;
> + phy_set_drvdata(generic_phy, phy_desc);
> + qphy->phys[id] = phy_desc;
> + }
> +
> + phy_provider = devm_of_phy_provider_register(dev, qcom_qmp_phy_xlate);
> + if (IS_ERR(phy_provider)) {
> + ret = PTR_ERR(phy_provider);
> + dev_err(dev, "failed to register qphy %d\n", ret);
> + goto put_child;
> + }
> +
> +put_child:
> + of_node_put(child);
> + return ret;
> +}
> +
> +static struct platform_driver qcom_qmp_phy_driver = {
> + .probe = qcom_qmp_phy_probe,
> + .driver = {
> + .name = "qcom_qmp_phy",
> + .of_match_table = of_match_ptr(qcom_qmp_phy_of_match_table),
> + },
> +};
> +
> +module_platform_driver(qcom_qmp_phy_driver);
> +
> +MODULE_AUTHOR("Vivek Gautam <vivek.gautam-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>");
> +MODULE_DESCRIPTION("Qualcomm QMP PHY driver");
> +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
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