* [PATCH next v11 02/11] dt-bindings: connector: add optional properties for Type-B
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
Add id-gpios, vbus-gpios, vbus-supply and pinctrl properties for
usb-b-connector
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
v11 changes:
1. add Reviewed-by Linus
v6~v10 no changes
v5 changes:
1. add reviewed by Rob
v4 no changes
v3 changes:
1. add GPIO direction, and use fixed-regulator for GPIO controlled
VBUS regulator suggested by Rob;
v2 changes:
1. describe more clear for vbus-gpios and vbus-supply suggested by Hans
---
.../bindings/connector/usb-connector.txt | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt
index cef556d4e5ee..d357987181ee 100644
--- a/Documentation/devicetree/bindings/connector/usb-connector.txt
+++ b/Documentation/devicetree/bindings/connector/usb-connector.txt
@@ -17,6 +17,20 @@ Optional properties:
- self-powered: Set this property if the usb device that has its own power
source.
+Optional properties for usb-b-connector:
+- id-gpios: an input gpio for USB ID pin.
+- vbus-gpios: an input gpio for USB VBUS pin, used to detect presence of
+ VBUS 5V.
+ see gpio/gpio.txt.
+- vbus-supply: a phandle to the regulator for USB VBUS if needed when host
+ mode or dual role mode is supported.
+ Particularly, if use an output GPIO to control a VBUS regulator, should
+ model it as a regulator.
+ see regulator/fixed-regulator.yaml
+- pinctrl-names : a pinctrl state named "default" is optional
+- pinctrl-0 : pin control group
+ see pinctrl/pinctrl-bindings.txt
+
Optional properties for usb-c-connector:
- power-role: should be one of "source", "sink" or "dual"(DRP) if typec
connector has power support.
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 03/11] dt-bindings: usb: add binding for USB GPIO based connection detection driver
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
It's used to support dual role switch via GPIO when use Type-B
receptacle, typically the USB ID pin is connected to an input
GPIO, and also used to enable/disable device when the USB Vbus
pin is connected to an input GPIO.
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
v11 changes:
1. add Reviewed-by Linus
2. change compatible as "gpio-usb-b-connector", and remove label
in example suggested by Rob
v9~v10 no changes
v8 changes:
1. rename the title
2. change the compatible as "linux,usb-conn-gpio" instead of
"linux,typeb-conn-gpio"
v7 changes:
1. add description for device only mode
v6 changes:
1. remove status and port nodes in example
2. make vbus-supply as optional property
v5 changes:
1. treat type-B connector as child device of USB controller's, but not
as a separate virtual device, suggested by Rob
2. put connector's port node under connector node, suggested by Rob
v4 no changes
v3 changes:
1. treat type-B connector as a virtual device, but not child device of
USB controller's
v2 changes:
1. new patch to make binding clear suggested by Hans
---
.../devicetree/bindings/usb/usb-conn-gpio.txt | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/usb-conn-gpio.txt
diff --git a/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt b/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt
new file mode 100644
index 000000000000..3d05ae56cb0d
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt
@@ -0,0 +1,30 @@
+USB GPIO Based Connection Detection
+
+This is typically used to switch dual role mode from the USB ID pin connected
+to an input GPIO, and also used to enable/disable device mode from the USB
+Vbus pin connected to an input GPIO.
+
+Required properties:
+- compatible : should include "gpio-usb-b-connector" and "usb-b-connector".
+- id-gpios, vbus-gpios : input gpios, either one of them must be present,
+ and both can be present as well.
+ see connector/usb-connector.txt
+
+Optional properties:
+- vbus-supply : can be present if needed when supports dual role mode.
+ see connector/usb-connector.txt
+
+- Sub-nodes:
+ - port : can be present.
+ see graph.txt
+
+Example:
+
+&mtu3 {
+ connector {
+ compatible = "gpio-usb-b-connector", "usb-b-connector";
+ type = "micro";
+ id-gpios = <&pio 12 GPIO_ACTIVE_HIGH>;
+ vbus-supply = <&usb_p0_vbus>;
+ };
+};
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 04/11] dt-bindings: usb: mtu3: add properties about USB Role Switch
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
Now the USB Role Switch is supported, so add properties about it,
and modify some description related.
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
v6~v11 no changes
v5 changes:
1. modify decription about extcon and vbus-supply properties
2. make this patch depend on [1]
[1]: [v3] dt-binding: usb: add usb-role-switch property
https://patchwork.kernel.org/patch/10934835/
v4 no changes
v3 no changes
v2 changes:
1. fix typo
2. refer new binding about connector property
---
.../devicetree/bindings/usb/mediatek,mtu3.txt | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt
index 3382b5cb471d..3a8300205cdb 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt
@@ -28,8 +28,13 @@ Optional properties:
parent's address space
- extcon : external connector for vbus and idpin changes detection, needed
when supports dual-role mode.
+ it's considered valid for compatibility reasons, not allowed for
+ new bindings, and use "usb-role-switch" property instead.
- vbus-supply : reference to the VBUS regulator, needed when supports
dual-role mode.
+ it's considered valid for compatibility reasons, not allowed for
+ new bindings, and put into a usb-connector node.
+ see connector/usb-connector.txt.
- pinctrl-names : a pinctrl state named "default" is optional, and need be
defined if auto drd switch is enabled, that means the property dr_mode
is set as "otg", and meanwhile the property "mediatek,enable-manual-drd"
@@ -39,6 +44,8 @@ Optional properties:
- maximum-speed : valid arguments are "super-speed", "high-speed" and
"full-speed"; refer to usb/generic.txt
+ - usb-role-switch : use USB Role Switch to support dual-role switch, but
+ not extcon; see usb/generic.txt.
- enable-manual-drd : supports manual dual-role switch via debugfs; usually
used when receptacle is TYPE-A and also wants to support dual-role
mode.
@@ -61,6 +68,9 @@ The xhci should be added as subnode to mtu3 as shown in the following example
if host mode is enabled. The DT binding details of xhci can be found in:
Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
+The port would be added as subnode if use "usb-role-switch" property.
+ see graph.txt
+
Example:
ssusb: usb@11271000 {
compatible = "mediatek,mt8173-mtu3";
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 05/11] usb: roles: Introduce stubs for the exiting functions in role.h
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
John Stultz, Badhri Jagan Sridharan, Linus Walleij, linux-usb,
Yu Chen, linux-kernel, Matthias Brugger, Andy Shevchenko,
linux-mediatek, Min Guo, Chunfeng Yun, Nagarjuna Kristam,
Adam Thomson, linux-arm-kernel, Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
From: Yu Chen <chenyu56@huawei.com>
This patch adds stubs for the exiting functions while
CONFIG_USB_ROLE_SWITCH does not enabled.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Cc: Hans de Goede <hdegoede@redhat.com>
Cc: Andy Shevchenko <andy.shevchenko@gmail.com>
Cc: John Stultz <john.stultz@linaro.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Yu Chen <chenyu56@huawei.com>
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
v8~v11 no changes
v7:
add Signed-off-by Chunfeng
v6:
merge this patch [1] into this series to add new API
[1] https://patchwork.kernel.org/patch/10909971/
---
include/linux/usb/role.h | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h
index c05ffa6abda9..da2b9641b877 100644
--- a/include/linux/usb/role.h
+++ b/include/linux/usb/role.h
@@ -42,6 +42,8 @@ struct usb_role_switch_desc {
bool allow_userspace_control;
};
+
+#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role);
enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw);
struct usb_role_switch *usb_role_switch_get(struct device *dev);
@@ -51,5 +53,33 @@ struct usb_role_switch *
usb_role_switch_register(struct device *parent,
const struct usb_role_switch_desc *desc);
void usb_role_switch_unregister(struct usb_role_switch *sw);
+#else
+static inline int usb_role_switch_set_role(struct usb_role_switch *sw,
+ enum usb_role role)
+{
+ return 0;
+}
+
+static inline enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw)
+{
+ return USB_ROLE_NONE;
+}
+
+static inline struct usb_role_switch *usb_role_switch_get(struct device *dev)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void usb_role_switch_put(struct usb_role_switch *sw) { }
+
+static inline struct usb_role_switch *
+usb_role_switch_register(struct device *parent,
+ const struct usb_role_switch_desc *desc)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void usb_role_switch_unregister(struct usb_role_switch *sw) { }
+#endif
#endif /* __LINUX_USB_ROLE_H */
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 06/11] device connection: Add fwnode_connection_find_match()
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
The fwnode_connection_find_match() function is exactly the
same as device_connection_find_match(), except it takes
struct fwnode_handle as parameter instead of struct device.
That allows locating device connections before the device
entries have been created.
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
v11: no changes
v10:
revert changes of v9
v9:
replace signed-off-by by suggested-by Heikki
v8: no changes
v7:
rebased on Rafael's tree [1] (after rc4), provided by Heikki
[1] https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git/log/?h=linux-next
v6:
new patch
---
drivers/base/devcon.c | 43 ++++++++++++++++++++++++++++++------------
include/linux/device.h | 10 +++++++---
2 files changed, 38 insertions(+), 15 deletions(-)
diff --git a/drivers/base/devcon.c b/drivers/base/devcon.c
index 1d488dc5dd0c..14e2178e09f8 100644
--- a/drivers/base/devcon.c
+++ b/drivers/base/devcon.c
@@ -12,9 +12,6 @@
static DEFINE_MUTEX(devcon_lock);
static LIST_HEAD(devcon_list);
-typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep,
- void *data);
-
static void *
fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
void *data, devcon_match_fn_t match)
@@ -60,6 +57,34 @@ fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
return NULL;
}
+/**
+ * fwnode_connection_find_match - Find connection from a device node
+ * @fwnode: Device node with the connection
+ * @con_id: Identifier for the connection
+ * @data: Data for the match function
+ * @match: Function to check and convert the connection description
+ *
+ * Find a connection with unique identifier @con_id between @fwnode and another
+ * device node. @match will be used to convert the connection description to
+ * data the caller is expecting to be returned.
+ */
+void *fwnode_connection_find_match(struct fwnode_handle *fwnode,
+ const char *con_id, void *data,
+ devcon_match_fn_t match)
+{
+ void *ret;
+
+ if (!fwnode || !match)
+ return NULL;
+
+ ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
+ if (ret)
+ return ret;
+
+ return fwnode_devcon_match(fwnode, con_id, data, match);
+}
+EXPORT_SYMBOL_GPL(fwnode_connection_find_match);
+
/**
* device_connection_find_match - Find physical connection to a device
* @dev: Device with the connection
@@ -83,15 +108,9 @@ void *device_connection_find_match(struct device *dev, const char *con_id,
if (!match)
return NULL;
- if (fwnode) {
- ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
- if (ret)
- return ret;
-
- ret = fwnode_devcon_match(fwnode, con_id, data, match);
- if (ret)
- return ret;
- }
+ ret = fwnode_connection_find_match(fwnode, con_id, data, match);
+ if (ret)
+ return ret;
mutex_lock(&devcon_lock);
diff --git a/include/linux/device.h b/include/linux/device.h
index 8ae3f4b47293..93626476c9ae 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -1009,10 +1009,14 @@ struct device_connection {
struct list_head list;
};
+typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep,
+ void *data);
+
+void *fwnode_connection_find_match(struct fwnode_handle *fwnode,
+ const char *con_id, void *data,
+ devcon_match_fn_t match);
void *device_connection_find_match(struct device *dev, const char *con_id,
- void *data,
- void *(*match)(struct device_connection *con,
- int ep, void *data));
+ void *data, devcon_match_fn_t match);
struct device *device_connection_find(struct device *dev, const char *con_id);
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 08/11] usb: roles: get usb-role-switch from parent
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
when the USB host controller is the parent of the connector,
usually type-B, sometimes don't need the graph, so we should
check whether it's parent registers usb-role-switch or not
firstly, and get it if exists.
Suggested-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
v10~v11: no changes
v9:
1. replace signed-off-by by suggested-by Heikki
2. use class_find_device_by_fwnode()
v8: no changes
v7:
add signed-off-by Chunfeng
v6:
new patch
---
drivers/usb/roles/class.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index 2abb6fe384ca..94b4e7db2b94 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -102,6 +102,19 @@ static void *usb_role_switch_match(struct device_connection *con, int ep,
return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
}
+static struct usb_role_switch *
+usb_role_switch_is_parent(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *parent = fwnode_get_parent(fwnode);
+ struct device *dev;
+
+ if (!parent || !fwnode_property_present(parent, "usb-role-switch"))
+ return NULL;
+
+ dev = class_find_device_by_fwnode(role_class, parent);
+ return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
+}
+
/**
* usb_role_switch_get - Find USB role switch linked with the caller
* @dev: The caller device
@@ -113,8 +126,10 @@ struct usb_role_switch *usb_role_switch_get(struct device *dev)
{
struct usb_role_switch *sw;
- sw = device_connection_find_match(dev, "usb-role-switch", NULL,
- usb_role_switch_match);
+ sw = usb_role_switch_is_parent(dev_fwnode(dev));
+ if (!sw)
+ sw = device_connection_find_match(dev, "usb-role-switch", NULL,
+ usb_role_switch_match);
if (!IS_ERR_OR_NULL(sw))
WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
@@ -134,8 +149,10 @@ struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode)
{
struct usb_role_switch *sw;
- sw = fwnode_connection_find_match(fwnode, "usb-role-switch", NULL,
- usb_role_switch_match);
+ sw = usb_role_switch_is_parent(fwnode);
+ if (!sw)
+ sw = fwnode_connection_find_match(fwnode, "usb-role-switch",
+ NULL, usb_role_switch_match);
if (!IS_ERR_OR_NULL(sw))
WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 07/11] usb: roles: Add fwnode_usb_role_switch_get() function
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
The fwnode_usb_role_switch_get() function is exactly the
same as usb_role_switch_get(), except that it takes struct
fwnode_handle as parameter instead of struct device.
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Tested-by: Biju Das <biju.das@bp.renesas.com>
---
v11: no changes
v10:
revert changes of v9
v9:
replace signed-off-by by suggested-by Heikki
v8: no changes
v7:
add signed-off-by Chunfeng and tested-by Biju
v6:
new patch
---
drivers/usb/roles/class.c | 20 ++++++++++++++++++++
include/linux/usb/role.h | 7 +++++++
2 files changed, 27 insertions(+)
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index 0526efbc4922..2abb6fe384ca 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -123,6 +123,26 @@ struct usb_role_switch *usb_role_switch_get(struct device *dev)
}
EXPORT_SYMBOL_GPL(usb_role_switch_get);
+/**
+ * fwnode_usb_role_switch_get - Find USB role switch linked with the caller
+ * @fwnode: The caller device node
+ *
+ * This is similar to the usb_role_switch_get() function above, but it searches
+ * the switch using fwnode instead of device entry.
+ */
+struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode)
+{
+ struct usb_role_switch *sw;
+
+ sw = fwnode_connection_find_match(fwnode, "usb-role-switch", NULL,
+ usb_role_switch_match);
+ if (!IS_ERR_OR_NULL(sw))
+ WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
+
+ return sw;
+}
+EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get);
+
/**
* usb_role_switch_put - Release handle to a switch
* @sw: USB Role Switch
diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h
index da2b9641b877..2d77f97df72d 100644
--- a/include/linux/usb/role.h
+++ b/include/linux/usb/role.h
@@ -47,6 +47,7 @@ struct usb_role_switch_desc {
int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role);
enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw);
struct usb_role_switch *usb_role_switch_get(struct device *dev);
+struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *node);
void usb_role_switch_put(struct usb_role_switch *sw);
struct usb_role_switch *
@@ -70,6 +71,12 @@ static inline struct usb_role_switch *usb_role_switch_get(struct device *dev)
return ERR_PTR(-ENODEV);
}
+static inline struct usb_role_switch *
+fwnode_usb_role_switch_get(struct fwnode_handle *node)
+{
+ return ERR_PTR(-ENODEV);
+}
+
static inline void usb_role_switch_put(struct usb_role_switch *sw) { }
static inline struct usb_role_switch *
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 09/11] usb: common: create Kconfig file
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
Create Kconfig file for USB common core, and move USB_LED_TRIG
and USB_ULPI_BUS configs into the new file from the parent Kconfig,
it will help to add new configs later.
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
v9~v11: no changes
v8:
new patch
---
drivers/usb/Kconfig | 35 +----------------------------------
drivers/usb/common/Kconfig | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+), 34 deletions(-)
create mode 100644 drivers/usb/common/Kconfig
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 6e59d370ef81..7bf94e65ed2f 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -36,8 +36,7 @@ menuconfig USB_SUPPORT
if USB_SUPPORT
-config USB_COMMON
- tristate
+source "drivers/usb/common/Kconfig"
config USB_ARCH_HAS_HCD
def_bool y
@@ -175,36 +174,4 @@ source "drivers/usb/typec/Kconfig"
source "drivers/usb/roles/Kconfig"
-config USB_LED_TRIG
- bool "USB LED Triggers"
- depends on LEDS_CLASS && LEDS_TRIGGERS
- select USB_COMMON
- help
- This option adds LED triggers for USB host and/or gadget activity.
-
- Say Y here if you are working on a system with led-class supported
- LEDs and you want to use them as activity indicators for USB host or
- gadget.
-
-config USB_ULPI_BUS
- tristate "USB ULPI PHY interface support"
- select USB_COMMON
- help
- UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
- USB 2.0 PHY interface. The ULPI specification defines a standard set
- of registers that can be used to detect the vendor and product which
- allows ULPI to be handled as a bus. This module is the driver for that
- bus.
-
- The ULPI interfaces (the buses) are registered by the drivers for USB
- controllers which support ULPI register access and have ULPI PHY
- attached to them. The ULPI PHY drivers themselves are normal PHY
- drivers.
-
- ULPI PHYs provide often functions such as ADP sensing/probing (OTG
- protocol) and USB charger detection.
-
- To compile this driver as a module, choose M here: the module will
- be called ulpi.
-
endif # USB_SUPPORT
diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig
new file mode 100644
index 000000000000..848545b099cf
--- /dev/null
+++ b/drivers/usb/common/Kconfig
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config USB_COMMON
+ tristate
+
+
+config USB_LED_TRIG
+ bool "USB LED Triggers"
+ depends on LEDS_CLASS && LEDS_TRIGGERS
+ select USB_COMMON
+ help
+ This option adds LED triggers for USB host and/or gadget activity.
+
+ Say Y here if you are working on a system with led-class supported
+ LEDs and you want to use them as activity indicators for USB host or
+ gadget.
+
+config USB_ULPI_BUS
+ tristate "USB ULPI PHY interface support"
+ select USB_COMMON
+ help
+ UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
+ USB 2.0 PHY interface. The ULPI specification defines a standard set
+ of registers that can be used to detect the vendor and product which
+ allows ULPI to be handled as a bus. This module is the driver for that
+ bus.
+
+ The ULPI interfaces (the buses) are registered by the drivers for USB
+ controllers which support ULPI register access and have ULPI PHY
+ attached to them. The ULPI PHY drivers themselves are normal PHY
+ drivers.
+
+ ULPI PHYs provide often functions such as ADP sensing/probing (OTG
+ protocol) and USB charger detection.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ulpi.
+
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 11/11] usb: mtu3: register a USB Role Switch for dual role mode
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
Because extcon is not allowed for new bindings, and the
dual role switch is supported by USB Role Switch,
especially for Type-C drivers, so register a USB Role
Switch to support the new way
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
v5~v11 no changes
v4 changes:
1. assign fwnode member of usb_role_switch struct suggested by Heikki
v3 changes:
1. select USB_ROLE_SWITCH in Kconfig suggested by Heikki
2. rename ssusb_mode_manual_switch() to ssusb_mode_switch()
v2 no changes
---
drivers/usb/mtu3/Kconfig | 1 +
drivers/usb/mtu3/mtu3.h | 5 ++++
drivers/usb/mtu3/mtu3_debugfs.c | 4 +--
drivers/usb/mtu3/mtu3_dr.c | 48 ++++++++++++++++++++++++++++++++-
drivers/usb/mtu3/mtu3_dr.h | 6 ++---
drivers/usb/mtu3/mtu3_plat.c | 3 ++-
6 files changed, 60 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig
index 928c2cd6fc00..bf98fd36341d 100644
--- a/drivers/usb/mtu3/Kconfig
+++ b/drivers/usb/mtu3/Kconfig
@@ -44,6 +44,7 @@ config USB_MTU3_DUAL_ROLE
bool "Dual Role mode"
depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3))
depends on (EXTCON=y || EXTCON=USB_MTU3)
+ select USB_ROLE_SWITCH
help
This is the default mode of working of MTU3 controller where
both host and gadget features are enabled.
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index 76ecf12fdf62..6087be236a35 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -199,6 +199,9 @@ struct mtu3_gpd_ring {
* @id_nb : notifier for iddig(idpin) detection
* @id_work : work of iddig detection notifier
* @id_event : event of iddig detecion notifier
+* @role_sw : use USB Role Switch to support dual-role switch, can't use
+* extcon at the same time, and extcon is deprecated.
+* @role_sw_used : true when the USB Role Switch is used.
* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
* @manual_drd_enabled: it's true when supports dual-role device by debugfs
* to switch host/device modes depending on user input.
@@ -212,6 +215,8 @@ struct otg_switch_mtk {
struct notifier_block id_nb;
struct work_struct id_work;
unsigned long id_event;
+ struct usb_role_switch *role_sw;
+ bool role_sw_used;
bool is_u3_drd;
bool manual_drd_enabled;
};
diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c
index 62c57ddc554e..c96e5dab0a48 100644
--- a/drivers/usb/mtu3/mtu3_debugfs.c
+++ b/drivers/usb/mtu3/mtu3_debugfs.c
@@ -453,9 +453,9 @@ static ssize_t ssusb_mode_write(struct file *file, const char __user *ubuf,
return -EFAULT;
if (!strncmp(buf, "host", 4) && !ssusb->is_host) {
- ssusb_mode_manual_switch(ssusb, 1);
+ ssusb_mode_switch(ssusb, 1);
} else if (!strncmp(buf, "device", 6) && ssusb->is_host) {
- ssusb_mode_manual_switch(ssusb, 0);
+ ssusb_mode_switch(ssusb, 0);
} else {
dev_err(ssusb->dev, "wrong or duplicated setting\n");
return -EINVAL;
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 5fcb71af875a..08e18448e8b8 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -7,6 +7,8 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
+#include <linux/usb/role.h>
+
#include "mtu3.h"
#include "mtu3_dr.h"
#include "mtu3_debug.h"
@@ -280,7 +282,7 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
* This is useful in special cases, such as uses TYPE-A receptacle but also
* wants to support dual-role mode.
*/
-void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
+void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
{
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
@@ -318,6 +320,47 @@ void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
}
+static int ssusb_role_sw_set(struct device *dev, enum usb_role role)
+{
+ struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+ bool to_host = false;
+
+ if (role == USB_ROLE_HOST)
+ to_host = true;
+
+ if (to_host ^ ssusb->is_host)
+ ssusb_mode_switch(ssusb, to_host);
+
+ return 0;
+}
+
+static enum usb_role ssusb_role_sw_get(struct device *dev)
+{
+ struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+ enum usb_role role;
+
+ role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+
+ return role;
+}
+
+static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx)
+{
+ struct usb_role_switch_desc role_sx_desc = { 0 };
+ struct ssusb_mtk *ssusb =
+ container_of(otg_sx, struct ssusb_mtk, otg_switch);
+
+ if (!otg_sx->role_sw_used)
+ return 0;
+
+ role_sx_desc.set = ssusb_role_sw_set;
+ role_sx_desc.get = ssusb_role_sw_get;
+ role_sx_desc.fwnode = dev_fwnode(ssusb->dev);
+ otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc);
+
+ return PTR_ERR_OR_ZERO(otg_sx->role_sw);
+}
+
int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
{
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
@@ -328,6 +371,8 @@ int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
if (otg_sx->manual_drd_enabled)
ssusb_dr_debugfs_init(ssusb);
+ else if (otg_sx->role_sw_used)
+ ret = ssusb_role_sw_register(otg_sx);
else
ret = ssusb_extcon_register(otg_sx);
@@ -340,4 +385,5 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
cancel_work_sync(&otg_sx->id_work);
cancel_work_sync(&otg_sx->vbus_work);
+ usb_role_switch_unregister(otg_sx->role_sw);
}
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index ba6fe357ce29..5e58c4dbd54a 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -71,7 +71,7 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
#if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
-void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host);
+void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host);
int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
enum mtu3_dr_force_mode mode);
@@ -86,8 +86,8 @@ static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
{}
-static inline void
-ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) {}
+static inline void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
+{}
static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
{
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index fd0f6c5dfbc1..9c256ea3cdf5 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -299,8 +299,9 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd");
otg_sx->manual_drd_enabled =
of_property_read_bool(node, "enable-manual-drd");
+ otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch");
- if (of_property_read_bool(node, "extcon")) {
+ if (!otg_sx->role_sw_used && of_property_read_bool(node, "extcon")) {
otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
if (IS_ERR(otg_sx->edev)) {
dev_err(ssusb->dev, "couldn't get extcon device\n");
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH next v11 10/11] usb: common: add USB GPIO based connection detection driver
From: Chunfeng Yun @ 2019-08-29 9:22 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman, Biju Das
Cc: Mark Rutland, devicetree, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, Linus Walleij, linux-usb, linux-kernel,
Matthias Brugger, Andy Shevchenko, linux-mediatek, Min Guo,
Chunfeng Yun, Nagarjuna Kristam, Adam Thomson, linux-arm-kernel,
Li Jun
In-Reply-To: <1567070558-29417-1-git-send-email-chunfeng.yun@mediatek.com>
Due to the requirement of usb-connector.txt binding, the old way
using extcon to support USB Dual-Role switch is now deprecated
when use Type-B connector.
This patch introduces a USB GPIO based connection detection driver,
used to support Type-B connector which typically uses an input GPIO
to detect USB ID pin, and try to replace the function provided
by the extcon-usb-gpio driver
Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Tested-by: Nagarjuna Kristam <nkristam@nvidia.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
v11 changes:
1. use new compatible "gpio-usb-b-connector"
v10 no changes
v9 changes:
1. add reviewed-by Linus
v8 changes:
1. rename the driver name and usb new compatible suggested by Heikki
2. move the driver into usb/common from usb/roles suggested by Heikki
v7 changes:
1. remove macro DEV_PMS_OPS suggested by Andy
2. add tested-by Nagarjuna
v6 changes:
1. get usb-role-swtich by usb_role_switch_get()
v5 changes:
1. put usb_role_switch when error happens suggested by Biju
2. don't treat bype-B connector as a virtual device suggested by Rob
v4 changes:
1. remove linux/gpio.h suggested by Linus
2. put node when error happens
v3 changes:
1. treat bype-B connector as a virtual device;
2. change file name again
v2 changes:
1. file name is changed
2. use new compatible
---
drivers/usb/common/Kconfig | 13 ++
drivers/usb/common/Makefile | 1 +
drivers/usb/common/usb-conn-gpio.c | 284 +++++++++++++++++++++++++++++
3 files changed, 298 insertions(+)
create mode 100644 drivers/usb/common/usb-conn-gpio.c
diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig
index 848545b099cf..d611477aae41 100644
--- a/drivers/usb/common/Kconfig
+++ b/drivers/usb/common/Kconfig
@@ -36,3 +36,16 @@ config USB_ULPI_BUS
To compile this driver as a module, choose M here: the module will
be called ulpi.
+config USB_CONN_GPIO
+ tristate "USB GPIO Based Connection Detection Driver"
+ depends on GPIOLIB
+ select USB_ROLE_SWITCH
+ help
+ The driver supports USB role switch between host and device via GPIO
+ based USB cable detection, used typically if an input GPIO is used
+ to detect USB ID pin, and another input GPIO may be also used to detect
+ Vbus pin at the same time, it also can be used to enable/disable
+ device if an input GPIO is only used to detect Vbus pin.
+
+ To compile the driver as a module, choose M here: the module will
+ be called usb-conn-gpio.ko
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index 0a7c45e85481..8227ffc2cf0b 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_USB_COMMON) += usb-common.o
usb-common-y += common.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
+obj-$(CONFIG_USB_CONN_GPIO) += usb-conn-gpio.o
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c
new file mode 100644
index 000000000000..87338f9eb5be
--- /dev/null
+++ b/drivers/usb/common/usb-conn-gpio.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB GPIO Based Connection Detection Driver
+ *
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * Some code borrowed from drivers/extcon/extcon-usb-gpio.c
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/role.h>
+
+#define USB_GPIO_DEB_MS 20 /* ms */
+#define USB_GPIO_DEB_US ((USB_GPIO_DEB_MS) * 1000) /* us */
+
+#define USB_CONN_IRQF \
+ (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT)
+
+struct usb_conn_info {
+ struct device *dev;
+ struct usb_role_switch *role_sw;
+ enum usb_role last_role;
+ struct regulator *vbus;
+ struct delayed_work dw_det;
+ unsigned long debounce_jiffies;
+
+ struct gpio_desc *id_gpiod;
+ struct gpio_desc *vbus_gpiod;
+ int id_irq;
+ int vbus_irq;
+};
+
+/**
+ * "DEVICE" = VBUS and "HOST" = !ID, so we have:
+ * Both "DEVICE" and "HOST" can't be set as active at the same time
+ * so if "HOST" is active (i.e. ID is 0) we keep "DEVICE" inactive
+ * even if VBUS is on.
+ *
+ * Role | ID | VBUS
+ * ------------------------------------
+ * [1] DEVICE | H | H
+ * [2] NONE | H | L
+ * [3] HOST | L | H
+ * [4] HOST | L | L
+ *
+ * In case we have only one of these signals:
+ * - VBUS only - we want to distinguish between [1] and [2], so ID is always 1
+ * - ID only - we want to distinguish between [1] and [4], so VBUS = ID
+ */
+static void usb_conn_detect_cable(struct work_struct *work)
+{
+ struct usb_conn_info *info;
+ enum usb_role role;
+ int id, vbus, ret;
+
+ info = container_of(to_delayed_work(work),
+ struct usb_conn_info, dw_det);
+
+ /* check ID and VBUS */
+ id = info->id_gpiod ?
+ gpiod_get_value_cansleep(info->id_gpiod) : 1;
+ vbus = info->vbus_gpiod ?
+ gpiod_get_value_cansleep(info->vbus_gpiod) : id;
+
+ if (!id)
+ role = USB_ROLE_HOST;
+ else if (vbus)
+ role = USB_ROLE_DEVICE;
+ else
+ role = USB_ROLE_NONE;
+
+ dev_dbg(info->dev, "role %d/%d, gpios: id %d, vbus %d\n",
+ info->last_role, role, id, vbus);
+
+ if (info->last_role == role) {
+ dev_warn(info->dev, "repeated role: %d\n", role);
+ return;
+ }
+
+ if (info->last_role == USB_ROLE_HOST)
+ regulator_disable(info->vbus);
+
+ ret = usb_role_switch_set_role(info->role_sw, role);
+ if (ret)
+ dev_err(info->dev, "failed to set role: %d\n", ret);
+
+ if (role == USB_ROLE_HOST) {
+ ret = regulator_enable(info->vbus);
+ if (ret)
+ dev_err(info->dev, "enable vbus regulator failed\n");
+ }
+
+ info->last_role = role;
+
+ dev_dbg(info->dev, "vbus regulator is %s\n",
+ regulator_is_enabled(info->vbus) ? "enabled" : "disabled");
+}
+
+static void usb_conn_queue_dwork(struct usb_conn_info *info,
+ unsigned long delay)
+{
+ queue_delayed_work(system_power_efficient_wq, &info->dw_det, delay);
+}
+
+static irqreturn_t usb_conn_isr(int irq, void *dev_id)
+{
+ struct usb_conn_info *info = dev_id;
+
+ usb_conn_queue_dwork(info, info->debounce_jiffies);
+
+ return IRQ_HANDLED;
+}
+
+static int usb_conn_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_conn_info *info;
+ int ret = 0;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = dev;
+ info->id_gpiod = devm_gpiod_get_optional(dev, "id", GPIOD_IN);
+ if (IS_ERR(info->id_gpiod))
+ return PTR_ERR(info->id_gpiod);
+
+ info->vbus_gpiod = devm_gpiod_get_optional(dev, "vbus", GPIOD_IN);
+ if (IS_ERR(info->vbus_gpiod))
+ return PTR_ERR(info->vbus_gpiod);
+
+ if (!info->id_gpiod && !info->vbus_gpiod) {
+ dev_err(dev, "failed to get gpios\n");
+ return -ENODEV;
+ }
+
+ if (info->id_gpiod)
+ ret = gpiod_set_debounce(info->id_gpiod, USB_GPIO_DEB_US);
+ if (!ret && info->vbus_gpiod)
+ ret = gpiod_set_debounce(info->vbus_gpiod, USB_GPIO_DEB_US);
+ if (ret < 0)
+ info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEB_MS);
+
+ INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
+
+ info->vbus = devm_regulator_get(dev, "vbus");
+ if (IS_ERR(info->vbus)) {
+ dev_err(dev, "failed to get vbus\n");
+ return PTR_ERR(info->vbus);
+ }
+
+ info->role_sw = usb_role_switch_get(dev);
+ if (IS_ERR(info->role_sw)) {
+ if (PTR_ERR(info->role_sw) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get role switch\n");
+
+ return PTR_ERR(info->role_sw);
+ }
+
+ if (info->id_gpiod) {
+ info->id_irq = gpiod_to_irq(info->id_gpiod);
+ if (info->id_irq < 0) {
+ dev_err(dev, "failed to get ID IRQ\n");
+ ret = info->id_irq;
+ goto put_role_sw;
+ }
+
+ ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
+ usb_conn_isr, USB_CONN_IRQF,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request ID IRQ\n");
+ goto put_role_sw;
+ }
+ }
+
+ if (info->vbus_gpiod) {
+ info->vbus_irq = gpiod_to_irq(info->vbus_gpiod);
+ if (info->vbus_irq < 0) {
+ dev_err(dev, "failed to get VBUS IRQ\n");
+ ret = info->vbus_irq;
+ goto put_role_sw;
+ }
+
+ ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
+ usb_conn_isr, USB_CONN_IRQF,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request VBUS IRQ\n");
+ goto put_role_sw;
+ }
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ /* Perform initial detection */
+ usb_conn_queue_dwork(info, 0);
+
+ return 0;
+
+put_role_sw:
+ usb_role_switch_put(info->role_sw);
+ return ret;
+}
+
+static int usb_conn_remove(struct platform_device *pdev)
+{
+ struct usb_conn_info *info = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&info->dw_det);
+
+ if (info->last_role == USB_ROLE_HOST)
+ regulator_disable(info->vbus);
+
+ usb_role_switch_put(info->role_sw);
+
+ return 0;
+}
+
+static int __maybe_unused usb_conn_suspend(struct device *dev)
+{
+ struct usb_conn_info *info = dev_get_drvdata(dev);
+
+ if (info->id_gpiod)
+ disable_irq(info->id_irq);
+ if (info->vbus_gpiod)
+ disable_irq(info->vbus_irq);
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return 0;
+}
+
+static int __maybe_unused usb_conn_resume(struct device *dev)
+{
+ struct usb_conn_info *info = dev_get_drvdata(dev);
+
+ pinctrl_pm_select_default_state(dev);
+
+ if (info->id_gpiod)
+ enable_irq(info->id_irq);
+ if (info->vbus_gpiod)
+ enable_irq(info->vbus_irq);
+
+ usb_conn_queue_dwork(info, 0);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(usb_conn_pm_ops,
+ usb_conn_suspend, usb_conn_resume);
+
+static const struct of_device_id usb_conn_dt_match[] = {
+ { .compatible = "gpio-usb-b-connector", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, usb_conn_dt_match);
+
+static struct platform_driver usb_conn_driver = {
+ .probe = usb_conn_probe,
+ .remove = usb_conn_remove,
+ .driver = {
+ .name = "usb-conn-gpio",
+ .pm = &usb_conn_pm_ops,
+ .of_match_table = usb_conn_dt_match,
+ },
+};
+
+module_platform_driver(usb_conn_driver);
+
+MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
+MODULE_DESCRIPTION("USB GPIO based connection detection driver");
+MODULE_LICENSE("GPL v2");
--
2.23.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [EXT] [PATCH v3 2/2] drm/bridge: Add NWL MIPI DSI host controller support
From: Robert Chiras @ 2019-08-29 9:40 UTC (permalink / raw)
To: dl-linux-imx, narmstrong@baylibre.com, robh+dt@kernel.org,
linux-kernel@vger.kernel.org, lee.jones@linaro.org,
devicetree@vger.kernel.org, agx@sigxcpu.org, festevam@gmail.com,
jernej.skrabec@siol.net, daniel@ffwll.ch, mark.rutland@arm.com,
a.hajda@samsung.com, dri-devel@lists.freedesktop.org,
jonas@kwiboo.se, shawnguo@kernel.org,
linux-arm-kernel@lists.infradead.org, sam@ravnborg.org,
airlied@linux.ie, Laurent.pinchart@ideasonboard.com,
kernel@pengutronix.de, arnd@arndb.de, s.hauer@pengutronix.de
In-Reply-To: <ae518de22f7f0033c607e10ed8ef5330c7b2dff4.1566470526.git.agx@sigxcpu.org>
Hi Guido,
One more thing for you to add in v4, see inline.
On Jo, 2019-08-22 at 12:44 +0200, Guido Günther wrote:
> This adds initial support for the NWL MIPI DSI Host controller found
> on
> i.MX8 SoCs.
>
> It adds support for the i.MX8MQ but the same IP can be found on
> e.g. the i.MX8QXP.
>
> It has been tested on the Librem 5 devkit using mxsfb.
>
> Signed-off-by: Guido Günther <agx@sigxcpu.org>
> Co-developed-by: Robert Chiras <robert.chiras@nxp.com>
> ---
> drivers/gpu/drm/bridge/Kconfig | 2 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/nwl-dsi/Kconfig | 16 +
> drivers/gpu/drm/bridge/nwl-dsi/Makefile | 4 +
> drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c | 501 ++++++++++++++++
> drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h | 65 +++
> drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c | 700
> +++++++++++++++++++++++
> drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h | 112 ++++
> 8 files changed, 1401 insertions(+)
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Kconfig
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Makefile
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
> create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig
> b/drivers/gpu/drm/bridge/Kconfig
> index 1cc9f502c1f2..7980b5c2156f 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -154,6 +154,8 @@ source "drivers/gpu/drm/bridge/analogix/Kconfig"
>
> source "drivers/gpu/drm/bridge/adv7511/Kconfig"
>
> +source "drivers/gpu/drm/bridge/nwl-dsi/Kconfig"
> +
> source "drivers/gpu/drm/bridge/synopsys/Kconfig"
>
> endmenu
> diff --git a/drivers/gpu/drm/bridge/Makefile
> b/drivers/gpu/drm/bridge/Makefile
> index 4934fcf5a6f8..d9f6c0f77592 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -16,4 +16,5 @@ obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
> obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
> obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
> obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
> +obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi/
> obj-y += synopsys/
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
> b/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
> new file mode 100644
> index 000000000000..3b157a9f2229
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
> @@ -0,0 +1,16 @@
> +config DRM_NWL_MIPI_DSI
> + tristate "Support for Northwest Logic MIPI DSI Host
> controller"
> + depends on DRM
> + depends on COMMON_CLK
> + depends on OF && HAS_IOMEM
> + select DRM_KMS_HELPER
> + select DRM_MIPI_DSI
> + select DRM_PANEL_BRIDGE
> + select GENERIC_PHY_MIPI_DPHY
> + select MFD_SYSCON
> + select MULTIPLEXER
> + select REGMAP_MMIO
> + help
> + This enables the Northwest Logic MIPI DSI Host controller
> as
> + for example found on NXP's i.MX8 Processors.
> +
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/Makefile
> b/drivers/gpu/drm/bridge/nwl-dsi/Makefile
> new file mode 100644
> index 000000000000..804baf2f1916
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +nwl-mipi-dsi-y := nwl-drv.o nwl-dsi.o
> +obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-mipi-dsi.o
> +header-test-y += nwl-drv.h nwl-dsi.h
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
> b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
> new file mode 100644
> index 000000000000..e457438738c0
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
> @@ -0,0 +1,501 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * i.MX8 NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/irq.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mux/consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/reset.h>
> +#include <linux/regmap.h>
> +#include <linux/sys_soc.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include "nwl-drv.h"
> +#include "nwl-dsi.h"
> +
> +#define DRV_NAME "nwl-dsi"
> +
> +/* Possible platform specific clocks */
> +#define NWL_DSI_CLK_CORE "core"
> +
> +static const struct regmap_config nwl_dsi_regmap_config = {
> + .reg_bits = 16,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = NWL_DSI_IRQ_MASK2,
> + .name = DRV_NAME,
> +};
> +
> +struct nwl_dsi_platform_data {
> + int (*poweron)(struct nwl_dsi *dsi);
> + int (*poweroff)(struct nwl_dsi *dsi);
> + int (*select_input)(struct nwl_dsi *dsi);
> + int (*deselect_input)(struct nwl_dsi *dsi);
> + struct nwl_dsi_plat_clk_config
> clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
> +};
> +
> +static inline struct nwl_dsi *bridge_to_dsi(struct drm_bridge
> *bridge)
> +{
> + return container_of(bridge, struct nwl_dsi, bridge);
> +}
> +
> +static int nwl_dsi_set_platform_clocks(struct nwl_dsi *dsi, bool
> enable)
> +{
> + struct device *dev = dsi->dev;
> + const char *id;
> + struct clk *clk;
> + size_t i;
> + unsigned long rate;
> + int ret, result = 0;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "%s platform clocks\n",
> + enable ? "enabling" : "disabling");
> + for (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
> + if (!dsi->clk_config[i].present)
> + continue;
> + id = dsi->clk_config[i].id;
> + clk = dsi->clk_config[i].clk;
> +
> + if (enable) {
> + ret = clk_prepare_enable(clk);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev,
> + "Failed to enable %s
> clk: %d\n",
> + id, ret);
> + result = result ?: ret;
> + }
> + rate = clk_get_rate(clk);
> + DRM_DEV_DEBUG_DRIVER(dev, "Enabled %s clk
> @%lu Hz\n",
> + id, rate);
> + } else {
> + clk_disable_unprepare(clk);
> + DRM_DEV_DEBUG_DRIVER(dev, "Disabled %s
> clk\n", id);
> + }
> + }
> +
> + return result;
> +}
> +
> +static int nwl_dsi_plat_enable(struct nwl_dsi *dsi)
> +{
> + struct device *dev = dsi->dev;
> + int ret;
> +
> + if (dsi->pdata->select_input)
> + dsi->pdata->select_input(dsi);
> +
> + ret = nwl_dsi_set_platform_clocks(dsi, true);
> + if (ret < 0)
> + return ret;
> +
> + ret = dsi->pdata->poweron(dsi);
> + if (ret < 0)
> + DRM_DEV_ERROR(dev, "Failed to power on DSI: %d\n",
> ret);
> + return ret;
> +}
> +
> +static void nwl_dsi_plat_disable(struct nwl_dsi *dsi)
> +{
> + dsi->pdata->poweroff(dsi);
> + nwl_dsi_set_platform_clocks(dsi, false);
> + if (dsi->pdata->deselect_input)
> + dsi->pdata->deselect_input(dsi);
> +}
> +
> +static void nwl_dsi_bridge_disable(struct drm_bridge *bridge)
> +{
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +
> + nwl_dsi_disable(dsi);
> + nwl_dsi_plat_disable(dsi);
> + pm_runtime_put(dsi->dev);
> +}
> +
> +static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
> + const struct drm_display_mode
> *mode,
> + union phy_configure_opts
> *phy_opts)
> +{
> + unsigned long rate;
> + int ret;
> +
> + if (dsi->lanes < 1 || dsi->lanes > 4)
> + return -EINVAL;
> +
> + /*
> + * So far the DPHY spec minimal timings work for both mixel
> + * dphy and nwl dsi host
> + */
> + ret = phy_mipi_dphy_get_default_config(
> + mode->crtc_clock * 1000,
> + mipi_dsi_pixel_format_to_bpp(dsi->format), dsi-
> >lanes,
> + &phy_opts->mipi_dphy);
> + if (ret < 0)
> + return ret;
> +
> + rate = clk_get_rate(dsi->tx_esc_clk);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "LP clk is @%lu Hz\n", rate);
> + phy_opts->mipi_dphy.lp_clk_rate = rate;
> +
> + return 0;
> +}
> +
> +static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
> + const struct drm_display_mode
> *mode,
> + struct drm_display_mode
> *adjusted_mode)
> +{
> + /* At least LCDIF + NWL needs active high sync */
> + adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC |
> DRM_MODE_FLAG_PVSYNC);
> + adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC |
> DRM_MODE_FLAG_NVSYNC);
> +
> + return true;
> +}
> +
> +static enum drm_mode_status
> +nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
> + const struct drm_display_mode *mode)
> +{
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> + int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> + if (mode->clock * bpp > 15000000)
> + return MODE_CLOCK_HIGH;
> +
> + if (mode->clock * bpp < 80000)
> + return MODE_CLOCK_LOW;
> +
> + return MODE_OK;
> +}
> +
> +static void
> +nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
> + const struct drm_display_mode *mode,
> + const struct drm_display_mode *adjusted_mode)
> +{
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> + struct device *dev = dsi->dev;
> + union phy_configure_opts new_cfg;
> + unsigned long phy_ref_rate;
> + int ret;
> +
> + ret = nwl_dsi_get_dphy_params(dsi, adjusted_mode, &new_cfg);
> + if (ret < 0)
> + return;
> +
> + /*
> + * If hs clock is unchanged, we're all good - all parameters
> are
> + * derived from it atm.
> + */
> + if (new_cfg.mipi_dphy.hs_clk_rate == dsi-
> >phy_cfg.mipi_dphy.hs_clk_rate)
> + return;
> +
> + phy_ref_rate = clk_get_rate(dsi->phy_ref_clk);
> + DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n",
> phy_ref_rate);
> + /* Save the new desired phy config */
> + memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg));
> +
> + memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode));
> + drm_mode_debug_printmodeline(adjusted_mode);
> +}
> +
> +static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge)
> +{
> + struct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +
> + pm_runtime_get_sync(dsi->dev);
> + nwl_dsi_plat_enable(dsi);
> + nwl_dsi_enable(dsi);
> +}
> +
> +static int nwl_dsi_bridge_attach(struct drm_bridge *bridge)
> +{
> + struct nwl_dsi *dsi = bridge->driver_private;
> +
> + return drm_bridge_attach(bridge->encoder, dsi->panel_bridge,
> bridge);
Have you tested this as a module? I tested this as built-in and it
worked, but today I ran a test with drivers as modules and it fails
here, since the panel is not yet probed (hence, dsi->panel_bridge is
NULL). Here is my dmesg:
[ 11.141177] mxsfb 30320000.lcdif: Cannot connect bridge: -22
[ 11.161062] mxsfb: probe of 30320000.lcdif failed with error -22
...
[ 12.416964] nwl-dsi 30a00000.mipi_dsi: [drm:nwl_dsi_host_attach
[nwl_mipi_dsi]] lanes=4, format=0x0 flags=0x15
You can see that the panel attaches later on, but there is no retry
scenario here.
So, I think you should handle this case here, otherwise the driver will
fail as a module.
> +}
> +
> +static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
> + .pre_enable = nwl_dsi_bridge_pre_enable,
> + .disable = nwl_dsi_bridge_disable,
> + .mode_fixup = nwl_dsi_bridge_mode_fixup,
> + .mode_set = nwl_dsi_bridge_mode_set,
> + .mode_valid = nwl_dsi_bridge_mode_valid,
> + .attach = nwl_dsi_bridge_attach,
> +};
> +
> +static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
> +{
> + struct platform_device *pdev = to_platform_device(dsi->dev);
> + struct clk *clk;
> + const char *clk_id;
> + void __iomem *base;
> + int i, ret;
> +
> + dsi->phy = devm_phy_get(dsi->dev, "dphy");
> + if (IS_ERR(dsi->phy)) {
> + ret = PTR_ERR(dsi->phy);
> + if (ret != -EPROBE_DEFER)
> + DRM_DEV_ERROR(dsi->dev, "Could not get PHY:
> %d\n", ret);
> + return ret;
> + }
> +
> + /* Platform dependent clocks */
> + memcpy(dsi->clk_config, dsi->pdata->clk_config,
> + sizeof(dsi->pdata->clk_config));
> +
> + for (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
> + if (!dsi->clk_config[i].present)
> + continue;
> +
> + clk_id = dsi->clk_config[i].id;
> + clk = devm_clk_get(dsi->dev, clk_id);
> + if (IS_ERR(clk)) {
> + ret = PTR_ERR(clk);
> + DRM_DEV_ERROR(dsi->dev, "Failed to get %s
> clock: %d\n",
> + clk_id, ret);
> + return ret;
> + }
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Setup clk %s (rate:
> %lu)\n",
> + clk_id, clk_get_rate(clk));
> + dsi->clk_config[i].clk = clk;
> + }
> +
> + /* DSI clocks */
> + clk = devm_clk_get(dsi->dev, "phy_ref");
> + if (IS_ERR(clk)) {
> + ret = PTR_ERR(clk);
> + DRM_DEV_ERROR(dsi->dev, "Failed to get phy_ref clock:
> %d\n",
> + ret);
> + return ret;
> + }
> + dsi->phy_ref_clk = clk;
> +
> + clk = devm_clk_get(dsi->dev, "rx_esc");
> + if (IS_ERR(clk)) {
> + ret = PTR_ERR(clk);
> + DRM_DEV_ERROR(dsi->dev, "Failed to get rx_esc clock:
> %d\n",
> + ret);
> + return ret;
> + }
> + dsi->rx_esc_clk = clk;
> +
> + clk = devm_clk_get(dsi->dev, "tx_esc");
> + if (IS_ERR(clk)) {
> + ret = PTR_ERR(clk);
> + DRM_DEV_ERROR(dsi->dev, "Failed to get tx_esc clock:
> %d\n",
> + ret);
> + return ret;
> + }
> + dsi->tx_esc_clk = clk;
> +
> + dsi->mux = devm_mux_control_get(dsi->dev, NULL);
> + if (IS_ERR(dsi->mux)) {
> + ret = PTR_ERR(dsi->mux);
> + if (ret != -EPROBE_DEFER)
> + DRM_DEV_ERROR(dsi->dev, "Failed to get mux:
> %d\n", ret);
> + return ret;
> + }
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + dsi->regmap =
> + devm_regmap_init_mmio(dsi->dev, base,
> &nwl_dsi_regmap_config);
> + if (IS_ERR(dsi->regmap)) {
> + ret = PTR_ERR(dsi->regmap);
> + DRM_DEV_ERROR(dsi->dev, "Failed to create NWL DSI
> regmap: %d\n",
> + ret);
> + return ret;
> + }
> +
> + dsi->irq = platform_get_irq(pdev, 0);
> + if (dsi->irq < 0) {
> + DRM_DEV_ERROR(dsi->dev, "Failed to get device IRQ:
> %d\n",
> + dsi->irq);
> + return dsi->irq;
> + }
> +
> + dsi->rstc = devm_reset_control_array_get(dsi->dev, false,
> true);
> + if (IS_ERR(dsi->rstc)) {
> + DRM_DEV_ERROR(dsi->dev, "Failed to get resets:
> %ld\n",
> + PTR_ERR(dsi->rstc));
> + return PTR_ERR(dsi->rstc);
> + }
> +
> + return 0;
> +}
> +
> +static int imx8mq_dsi_select_input(struct nwl_dsi *dsi)
> +{
> + struct device_node *remote;
> + u32 use_dcss = 1;
> + int ret;
> +
> + remote = of_graph_get_remote_node(dsi->dev->of_node, 0, 0);
> + if (strcmp(remote->name, "lcdif") == 0)
> + use_dcss = 0;
> +
> + DRM_DEV_INFO(dsi->dev, "Using %s as input source\n",
> + (use_dcss) ? "DCSS" : "LCDIF");
> +
> + ret = mux_control_try_select(dsi->mux, use_dcss);
> + if (ret < 0)
> + DRM_DEV_ERROR(dsi->dev, "Failed to select input:
> %d\n", ret);
> +
> + of_node_put(remote);
> + return ret;
> +}
> +
> +
> +static int imx8mq_dsi_deselect_input(struct nwl_dsi *dsi)
> +{
> + int ret;
> +
> + ret = mux_control_deselect(dsi->mux);
> + if (ret < 0)
> + DRM_DEV_ERROR(dsi->dev, "Failed to deselect input:
> %d\n", ret);
> +
> + return ret;
> +}
> +
> +
> +static int imx8mq_dsi_poweron(struct nwl_dsi *dsi)
> +{
> + int ret = 0;
> +
> + /* otherwise the display stays blank */
> + usleep_range(200, 300);
> +
> + if (dsi->rstc)
> + ret = reset_control_deassert(dsi->rstc);
> +
> + return ret;
> +}
> +
> +static int imx8mq_dsi_poweroff(struct nwl_dsi *dsi)
> +{
> + int ret = 0;
> +
> + if (dsi->quirks & SRC_RESET_QUIRK)
> + return 0;
> +
> + if (dsi->rstc)
> + ret = reset_control_assert(dsi->rstc);
> + return ret;
> +}
> +
> +static const struct drm_bridge_timings nwl_dsi_timings = {
> + .input_bus_flags = DRM_BUS_FLAG_DE_LOW,
> +};
> +
> +static const struct nwl_dsi_platform_data imx8mq_dev = {
> + .poweron = &imx8mq_dsi_poweron,
> + .poweroff = &imx8mq_dsi_poweroff,
> + .select_input = &imx8mq_dsi_select_input,
> + .deselect_input = &imx8mq_dsi_deselect_input,
> + .clk_config = {
> + { .id = NWL_DSI_CLK_CORE, .present = true },
> + },
> +};
> +
> +static const struct of_device_id nwl_dsi_dt_ids[] = {
> + { .compatible = "fsl,imx8mq-nwl-dsi", .data = &imx8mq_dev, },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, nwl_dsi_dt_ids);
> +
> +static const struct soc_device_attribute nwl_dsi_quirks_match[] = {
> + { .soc_id = "i.MX8MQ", .revision = "2.0",
> + .data = (void *)(E11418_HS_MODE_QUIRK | SRC_RESET_QUIRK) },
> + { /* sentinel. */ },
> +};
> +
> +static int nwl_dsi_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + const struct of_device_id *of_id =
> of_match_device(nwl_dsi_dt_ids, dev);
> + const struct nwl_dsi_platform_data *pdata = of_id->data;
> + const struct soc_device_attribute *attr;
> + struct nwl_dsi *dsi;
> + int ret;
> +
> + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> + if (!dsi)
> + return -ENOMEM;
> +
> + dsi->dev = dev;
> + dsi->pdata = pdata;
> +
> + ret = nwl_dsi_parse_dt(dsi);
> + if (ret)
> + return ret;
> +
> + ret = devm_request_irq(dev, dsi->irq, nwl_dsi_irq_handler, 0,
> + dev_name(dev), dsi);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to request IRQ %d: %d\n",
> dsi->irq,
> + ret);
> + return ret;
> + }
> +
> + dsi->dsi_host.ops = &nwl_dsi_host_ops;
> + dsi->dsi_host.dev = dev;
> + ret = mipi_dsi_host_register(&dsi->dsi_host);
> + if (ret) {
> + DRM_DEV_ERROR(dev, "Failed to register MIPI host:
> %d\n", ret);
> + return ret;
> + }
> +
> + attr = soc_device_match(nwl_dsi_quirks_match);
> + if (attr)
> + dsi->quirks = (uintptr_t)attr->data;
> +
> + dsi->bridge.driver_private = dsi;
> + dsi->bridge.funcs = &nwl_dsi_bridge_funcs;
> + dsi->bridge.of_node = dev->of_node;
> + dsi->bridge.timings = &nwl_dsi_timings;
> +
> + drm_bridge_add(&dsi->bridge);
> +
> + dev_set_drvdata(dev, dsi);
> + pm_runtime_enable(dev);
> + return 0;
> +}
> +
> +static int nwl_dsi_remove(struct platform_device *pdev)
> +{
> + struct nwl_dsi *dsi = platform_get_drvdata(pdev);
> +
> + mipi_dsi_host_unregister(&dsi->dsi_host);
> + pm_runtime_disable(&pdev->dev);
> + return 0;
> +}
> +
> +static struct platform_driver nwl_dsi_driver = {
> + .probe = nwl_dsi_probe,
> + .remove = nwl_dsi_remove,
> + .driver = {
> + .of_match_table = nwl_dsi_dt_ids,
> + .name = DRV_NAME,
> + },
> +};
> +
> +module_platform_driver(nwl_dsi_driver);
> +
> +MODULE_AUTHOR("NXP Semiconductor");
> +MODULE_AUTHOR("Purism SPC");
> +MODULE_DESCRIPTION("Northwest Logic MIPI-DSI driver");
> +MODULE_LICENSE("GPL"); /* GPLv2 or later */
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
> b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
> new file mode 100644
> index 000000000000..1e72a9221401
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +
> +#ifndef __NWL_DRV_H__
> +#define __NWL_DRV_H__
> +
> +#include <linux/mux/consumer.h>
> +#include <linux/phy/phy.h>
> +
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_mipi_dsi.h>
> +
> +struct nwl_dsi_platform_data;
> +
> +/* i.MX8 NWL quirks */
> +/* i.MX8MQ errata E11418 */
> +#define E11418_HS_MODE_QUIRK BIT(0)
> +/* Skip DSI bits in SRC on disable to avoid blank display on enable
> */
> +#define SRC_RESET_QUIRK BIT(1)
> +
> +#define NWL_DSI_MAX_PLATFORM_CLOCKS 1
> +struct nwl_dsi_plat_clk_config {
> + const char *id;
> + struct clk *clk;
> + bool present;
> +};
> +
> +struct nwl_dsi {
> + struct drm_bridge bridge;
> + struct mipi_dsi_host dsi_host;
> + struct drm_bridge *panel_bridge;
> + struct device *dev;
> + struct phy *phy;
> + union phy_configure_opts phy_cfg;
> + unsigned int quirks;
> +
> + struct regmap *regmap;
> + int irq;
> + struct reset_control *rstc;
> + struct mux_control *mux;
> +
> + /* DSI clocks */
> + struct clk *phy_ref_clk;
> + struct clk *rx_esc_clk;
> + struct clk *tx_esc_clk;
> + /* Platform dependent clocks */
> + struct nwl_dsi_plat_clk_config
> clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
> +
> + /* dsi lanes */
> + u32 lanes;
> + enum mipi_dsi_pixel_format format;
> + struct drm_display_mode mode;
> + unsigned long dsi_mode_flags;
> +
> + struct nwl_dsi_transfer *xfer;
> +
> + const struct nwl_dsi_platform_data *pdata;
> +};
> +
> +#endif /* __NWL_DRV_H__ */
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
> b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
> new file mode 100644
> index 000000000000..fd030af55bb4
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
> @@ -0,0 +1,700 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/irq.h>
> +#include <linux/regmap.h>
> +#include <linux/time64.h>
> +
> +#include <video/mipi_display.h>
> +#include <video/videomode.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +
> +#include "nwl-drv.h"
> +#include "nwl-dsi.h"
> +
> +#define NWL_DSI_MIPI_FIFO_TIMEOUT msecs_to_jiffies(500)
> +
> +/*
> + * PKT_CONTROL format:
> + * [15: 0] - word count
> + * [17:16] - virtual channel
> + * [23:18] - data type
> + * [24] - LP or HS select (0 - LP, 1 - HS)
> + * [25] - perform BTA after packet is sent
> + * [26] - perform BTA only, no packet tx
> + */
> +#define NWL_DSI_WC(x) FIELD_PREP(GENMASK(15, 0), (x))
> +#define NWL_DSI_TX_VC(x) FIELD_PREP(GENMASK(17, 16), (x))
> +#define NWL_DSI_TX_DT(x) FIELD_PREP(GENMASK(23, 18), (x))
> +#define NWL_DSI_HS_SEL(x) FIELD_PREP(GENMASK(24, 24), (x))
> +#define NWL_DSI_BTA_TX(x) FIELD_PREP(GENMASK(25, 25), (x))
> +#define NWL_DSI_BTA_NO_TX(x) FIELD_PREP(GENMASK(26, 26), (x))
> +
> +/*
> + * RX_PKT_HEADER format:
> + * [15: 0] - word count
> + * [21:16] - data type
> + * [23:22] - virtual channel
> + */
> +#define NWL_DSI_RX_DT(x) FIELD_GET(GENMASK(21, 16), (x))
> +#define NWL_DSI_RX_VC(x) FIELD_GET(GENMASK(23, 22), (x))
> +
> +/* DSI Video mode */
> +#define NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES 0
> +#define NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS BIT(0)
> +#define NWL_DSI_VM_BURST_MODE BIT(1)
> +
> +/* * DPI color coding */
> +#define NWL_DSI_DPI_16_BIT_565_PACKED 0
> +#define NWL_DSI_DPI_16_BIT_565_ALIGNED 1
> +#define NWL_DSI_DPI_16_BIT_565_SHIFTED 2
> +#define NWL_DSI_DPI_18_BIT_PACKED 3
> +#define NWL_DSI_DPI_18_BIT_ALIGNED 4
> +#define NWL_DSI_DPI_24_BIT 5
> +
> +/* * DPI Pixel format */
> +#define NWL_DSI_PIXEL_FORMAT_16 0
> +#define NWL_DSI_PIXEL_FORMAT_18 BIT(0)
> +#define NWL_DSI_PIXEL_FORMAT_18L BIT(1)
> +#define NWL_DSI_PIXEL_FORMAT_24 (BIT(0) | BIT(1))
> +
> +enum transfer_direction {
> + DSI_PACKET_SEND,
> + DSI_PACKET_RECEIVE,
> +};
> +
> +struct nwl_dsi_transfer {
> + const struct mipi_dsi_msg *msg;
> + struct mipi_dsi_packet packet;
> + struct completion completed;
> +
> + int status; /* status of transmission */
> + enum transfer_direction direction;
> + bool need_bta;
> + u8 cmd;
> + u16 rx_word_count;
> + size_t tx_len; /* in bytes */
> + size_t rx_len; /* in bytes */
> +};
> +
> +static int nwl_dsi_write(struct nwl_dsi *dsi, unsigned int reg, u32
> val)
> +{
> + int ret;
> +
> + ret = regmap_write(dsi->regmap, reg, val);
> + if (ret < 0)
> + DRM_DEV_ERROR(dsi->dev,
> + "Failed to write NWL DSI reg 0x%x:
> %d\n", reg,
> + ret);
> + return ret;
> +}
> +
> +static u32 nwl_dsi_read(struct nwl_dsi *dsi, u32 reg)
> +{
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(dsi->regmap, reg, &val);
> + if (ret < 0)
> + DRM_DEV_ERROR(dsi->dev, "Failed to read NWL DSI reg
> 0x%x: %d\n",
> + reg, ret);
> +
> + return val;
> +}
> +
> +static u32 nwl_dsi_get_dpi_pixel_format(enum mipi_dsi_pixel_format
> format)
> +{
> + switch (format) {
> + case MIPI_DSI_FMT_RGB565:
> + return NWL_DSI_PIXEL_FORMAT_16;
> + case MIPI_DSI_FMT_RGB666:
> + return NWL_DSI_PIXEL_FORMAT_18L;
> + case MIPI_DSI_FMT_RGB666_PACKED:
> + return NWL_DSI_PIXEL_FORMAT_18;
> + case MIPI_DSI_FMT_RGB888:
> + return NWL_DSI_PIXEL_FORMAT_24;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +#define PSEC_PER_SEC 1000000000000LL
> +/*
> + * ps2bc - Picoseconds to byte clock cycles
> + */
> +static u32 ps2bc(struct nwl_dsi *dsi, unsigned long long ps)
> +{
> + int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> + return DIV_ROUND_UP(ps * dsi->mode.clock * 1000 * bpp,
> + dsi->lanes * 8 * PSEC_PER_SEC);
> +}
> +
> +/*
> + * ui2bc - UI time periods to byte clock cycles
> + */
> +static u32 ui2bc(struct nwl_dsi *dsi, unsigned long long ui)
> +{
> + int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> + return DIV_ROUND_UP(ui * dsi->lanes, dsi->mode.clock * 1000 *
> bpp);
> +}
> +
> +/*
> + * us2bc - micro seconds to lp clock cycles
> + */
> +static u32 us2lp(u32 lp_clk_rate, unsigned long us)
> +{
> + return DIV_ROUND_UP(us * lp_clk_rate, USEC_PER_SEC);
> +}
> +
> +static int nwl_dsi_config_host(struct nwl_dsi *dsi)
> +{
> + u32 cycles;
> + struct phy_configure_opts_mipi_dphy *cfg = &dsi-
> >phy_cfg.mipi_dphy;
> +
> + if (dsi->lanes < 1 || dsi->lanes > 4)
> + return -EINVAL;
> +
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "DSI Lanes %d\n", dsi->lanes);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_NUM_LANES, dsi->lanes - 1);
> +
> + if (dsi->dsi_mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> + nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK,
> 0x01);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP,
> 0x01);
> + } else {
> + nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK,
> 0x00);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP,
> 0x00);
> + }
> +
> + /* values in byte clock cycles */
> + cycles = ui2bc(dsi, cfg->clk_pre);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_pre: 0x%x\n", cycles);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_T_PRE, cycles);
> + cycles = ps2bc(dsi, cfg->lpx + cfg->clk_prepare + cfg-
> >clk_zero);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap (pre): 0x%x\n",
> cycles);
> + cycles += ui2bc(dsi, cfg->clk_pre);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap: 0x%x\n", cycles);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_T_POST, cycles);
> + cycles = ps2bc(dsi, cfg->hs_exit);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap: 0x%x\n", cycles);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_TX_GAP, cycles);
> +
> + nwl_dsi_write(dsi, NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP, 0x01);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_HTX_TO_COUNT, 0x00);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_LRX_H_TO_COUNT, 0x00);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_BTA_H_TO_COUNT, 0x00);
> + /* In LP clock cycles */
> + cycles = us2lp(cfg->lp_clk_rate, cfg->wakeup);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_twakeup: 0x%x\n",
> cycles);
> + nwl_dsi_write(dsi, NWL_DSI_CFG_TWAKEUP, cycles);
> +
> + return 0;
> +}
> +
> +static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
> +{
> + u32 color_format, mode;
> + bool burst_mode;
> + int hfront_porch, hback_porch, vfront_porch, vback_porch;
> + int hsync_len, vsync_len;
> +
> + hfront_porch = dsi->mode.hsync_start - dsi->mode.hdisplay;
> + hsync_len = dsi->mode.hsync_end - dsi->mode.hsync_start;
> + hback_porch = dsi->mode.htotal - dsi->mode.hsync_end;
> +
> + vfront_porch = dsi->mode.vsync_start - dsi->mode.vdisplay;
> + vsync_len = dsi->mode.vsync_end - dsi->mode.vsync_start;
> + vback_porch = dsi->mode.vtotal - dsi->mode.vsync_end;
> +
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "hfront_porch = %d\n",
> hfront_porch);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "hback_porch = %d\n",
> hback_porch);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "hsync_len = %d\n",
> hsync_len);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "hdisplay = %d\n", dsi-
> >mode.hdisplay);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "vfront_porch = %d\n",
> vfront_porch);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "vback_porch = %d\n",
> vback_porch);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "vsync_len = %d\n",
> vsync_len);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "vactive = %d\n", dsi-
> >mode.vdisplay);
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "clock = %d kHz\n", dsi-
> >mode.clock);
> +
> + color_format = nwl_dsi_get_dpi_pixel_format(dsi->format);
> + if (color_format < 0) {
> + DRM_DEV_ERROR(dsi->dev, "Invalid color format
> 0x%x\n",
> + dsi->format);
> + return color_format;
> + }
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "pixel fmt = %d\n", dsi-
> >format);
> +
> + nwl_dsi_write(dsi, NWL_DSI_INTERFACE_COLOR_CODING,
> NWL_DSI_DPI_24_BIT);
> + nwl_dsi_write(dsi, NWL_DSI_PIXEL_FORMAT, color_format);
> + /*
> + * Adjusting input polarity based on the video mode results
> in
> + * a black screen so always pick active low:
> + */
> + nwl_dsi_write(dsi, NWL_DSI_VSYNC_POLARITY,
> + NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW);
> + nwl_dsi_write(dsi, NWL_DSI_HSYNC_POLARITY,
> + NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW);
> +
> + burst_mode = (dsi->dsi_mode_flags &
> MIPI_DSI_MODE_VIDEO_BURST) &&
> + !(dsi->dsi_mode_flags &
> MIPI_DSI_MODE_VIDEO_SYNC_PULSE);
> +
> + if (burst_mode) {
> + nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE,
> NWL_DSI_VM_BURST_MODE);
> + nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL,
> 256);
> + } else {
> + mode = ((dsi->dsi_mode_flags &
> MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ?
> + NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSE
> S :
> + NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_E
> VENTS);
> + nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, mode);
> + nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL,
> + dsi->mode.hdisplay);
> + }
> +
> + nwl_dsi_write(dsi, NWL_DSI_HFP, hfront_porch);
> + nwl_dsi_write(dsi, NWL_DSI_HBP, hback_porch);
> + nwl_dsi_write(dsi, NWL_DSI_HSA, hsync_len);
> +
> + nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
> + nwl_dsi_write(dsi, NWL_DSI_BLLP_MODE, 0x1);
> + nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
> + nwl_dsi_write(dsi, NWL_DSI_USE_NULL_PKT_BLLP, 0x0);
> + nwl_dsi_write(dsi, NWL_DSI_VC, 0x0);
> +
> + nwl_dsi_write(dsi, NWL_DSI_PIXEL_PAYLOAD_SIZE, dsi-
> >mode.hdisplay);
> + nwl_dsi_write(dsi, NWL_DSI_VACTIVE, dsi->mode.vdisplay - 1);
> + nwl_dsi_write(dsi, NWL_DSI_VBP, vback_porch);
> + nwl_dsi_write(dsi, NWL_DSI_VFP, vfront_porch);
> +
> + return 0;
> +}
> +
> +static void nwl_dsi_init_interrupts(struct nwl_dsi *dsi)
> +{
> + u32 irq_enable;
> +
> + nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, 0xffffffff);
> + nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7);
> +
> + irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK |
> + NWL_DSI_RX_PKT_HDR_RCVD_MASK |
> + NWL_DSI_TX_FIFO_OVFLW_MASK |
> + NWL_DSI_HS_TX_TIMEOUT_MASK);
> +
> + nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, irq_enable);
> +}
> +
> +static int nwl_dsi_host_attach(struct mipi_dsi_host *dsi_host,
> + struct mipi_dsi_device *device)
> +{
> + struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi,
> dsi_host);
> + struct device *dev = dsi->dev;
> + struct drm_bridge *bridge;
> + struct drm_panel *panel;
> + int ret;
> +
> + DRM_DEV_INFO(dev, "lanes=%u, format=0x%x flags=0x%lx\n",
> device->lanes,
> + device->format, device->mode_flags);
> +
> + if (device->lanes < 1 || device->lanes > 4)
> + return -EINVAL;
> +
> + dsi->lanes = device->lanes;
> + dsi->format = device->format;
> + dsi->dsi_mode_flags = device->mode_flags;
> +
> + ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0,
> &panel,
> + &bridge);
> + if (ret)
> + return ret;
> +
> + if (panel) {
> + bridge = drm_panel_bridge_add(panel,
> DRM_MODE_CONNECTOR_DSI);
> + if (IS_ERR(bridge))
> + return PTR_ERR(bridge);
> + }
> +
> + dsi->panel_bridge = bridge;
> + drm_bridge_add(&dsi->bridge);
> +
> + return 0;
> +}
> +
> +static int nwl_dsi_host_detach(struct mipi_dsi_host *dsi_host,
> + struct mipi_dsi_device *device)
> +{
> + struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi,
> dsi_host);
> +
> + drm_of_panel_bridge_remove(dsi->dev->of_node, 1, 0);
> + drm_bridge_remove(&dsi->bridge);
> +
> + return 0;
> +}
> +
> +static bool nwl_dsi_read_packet(struct nwl_dsi *dsi, u32 status)
> +{
> + struct device *dev = dsi->dev;
> + struct nwl_dsi_transfer *xfer = dsi->xfer;
> + u8 *payload = xfer->msg->rx_buf;
> + u32 val;
> + u16 word_count;
> + u8 channel;
> + u8 data_type;
> +
> + xfer->status = 0;
> +
> + if (xfer->rx_word_count == 0) {
> + if (!(status & NWL_DSI_RX_PKT_HDR_RCVD))
> + return false;
> + /* Get the RX header and parse it */
> + val = nwl_dsi_read(dsi, NWL_DSI_RX_PKT_HEADER);
> + word_count = NWL_DSI_WC(val);
> + channel = NWL_DSI_RX_VC(val);
> + data_type = NWL_DSI_RX_DT(val);
> +
> + if (channel != xfer->msg->channel) {
> + DRM_DEV_ERROR(dev,
> + "[%02X] Channel mismatch (%u !=
> %u)\n",
> + xfer->cmd, channel, xfer->msg-
> >channel);
> + return true;
> + }
> +
> + switch (data_type) {
> + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> + /* Fall through */
> + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> + if (xfer->msg->rx_len > 1) {
> + /* read second byte */
> + payload[1] = word_count >> 8;
> + ++xfer->rx_len;
> + }
> + /* Fall through */
> + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> + /* Fall through */
> + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> + if (xfer->msg->rx_len > 0) {
> + /* read first byte */
> + payload[0] = word_count & 0xff;
> + ++xfer->rx_len;
> + }
> + xfer->status = xfer->rx_len;
> + return true;
> + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> + word_count &= 0xff;
> + DRM_DEV_ERROR(dev, "[%02X] DSI error report:
> 0x%02x\n",
> + xfer->cmd, word_count);
> + xfer->status = -EPROTO;
> + return true;
> + }
> +
> + if (word_count > xfer->msg->rx_len) {
> + DRM_DEV_ERROR(
> + dev,
> + "[%02X] Receive buffer too small: %lu
> (< %u)\n",
> + xfer->cmd, xfer->msg->rx_len,
> word_count);
> + return true;
> + }
> +
> + xfer->rx_word_count = word_count;
> + } else {
> + /* Set word_count from previous header read */
> + word_count = xfer->rx_word_count;
> + }
> +
> + /* If RX payload is not yet received, wait for it */
> + if (!(status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD))
> + return false;
> +
> + /* Read the RX payload */
> + while (word_count >= 4) {
> + val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
> + payload[0] = (val >> 0) & 0xff;
> + payload[1] = (val >> 8) & 0xff;
> + payload[2] = (val >> 16) & 0xff;
> + payload[3] = (val >> 24) & 0xff;
> + payload += 4;
> + xfer->rx_len += 4;
> + word_count -= 4;
> + }
> +
> + if (word_count > 0) {
> + val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
> + switch (word_count) {
> + case 3:
> + payload[2] = (val >> 16) & 0xff;
> + ++xfer->rx_len;
> + /* Fall through */
> + case 2:
> + payload[1] = (val >> 8) & 0xff;
> + ++xfer->rx_len;
> + /* Fall through */
> + case 1:
> + payload[0] = (val >> 0) & 0xff;
> + ++xfer->rx_len;
> + break;
> + }
> + }
> +
> + xfer->status = xfer->rx_len;
> +
> + return true;
> +}
> +
> +static void nwl_dsi_finish_transmission(struct nwl_dsi *dsi, u32
> status)
> +{
> + struct nwl_dsi_transfer *xfer = dsi->xfer;
> + bool end_packet = false;
> +
> + if (!xfer)
> + return;
> +
> + if (status & NWL_DSI_TX_FIFO_OVFLW) {
> + DRM_DEV_ERROR_RATELIMITED(dsi->dev, "tx fifo
> overflow\n");
> + return;
> + }
> +
> + if (status & NWL_DSI_HS_TX_TIMEOUT) {
> + DRM_DEV_ERROR_RATELIMITED(dsi->dev, "HS tx
> timeout\n");
> + return;
> + }
> +
> + if (xfer->direction == DSI_PACKET_SEND &&
> + status & NWL_DSI_TX_PKT_DONE) {
> + xfer->status = xfer->tx_len;
> + end_packet = true;
> + } else if (status & NWL_DSI_DPHY_DIRECTION &&
> + ((status & (NWL_DSI_RX_PKT_HDR_RCVD |
> + NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)))) {
> + end_packet = nwl_dsi_read_packet(dsi, status);
> + }
> +
> + if (end_packet)
> + complete(&xfer->completed);
> +}
> +
> +static void nwl_dsi_begin_transmission(struct nwl_dsi *dsi)
> +{
> + struct nwl_dsi_transfer *xfer = dsi->xfer;
> + struct mipi_dsi_packet *pkt = &xfer->packet;
> + const u8 *payload;
> + size_t length;
> + u16 word_count;
> + u8 hs_mode;
> + u32 val;
> + u32 hs_workaround = 0;
> +
> + /* Send the payload, if any */
> + length = pkt->payload_length;
> + payload = pkt->payload;
> +
> + while (length >= 4) {
> + val = *(u32 *)payload;
> + hs_workaround |= !(val & 0xFFFF00);
> + nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
> + payload += 4;
> + length -= 4;
> + }
> + /* Send the rest of the payload */
> + val = 0;
> + switch (length) {
> + case 3:
> + val |= payload[2] << 16;
> + /* Fall through */
> + case 2:
> + val |= payload[1] << 8;
> + hs_workaround |= !(val & 0xFFFF00);
> + /* Fall through */
> + case 1:
> + val |= payload[0];
> + nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
> + break;
> + }
> + xfer->tx_len = pkt->payload_length;
> +
> + /*
> + * Send the header
> + * header[0] = Virtual Channel + Data Type
> + * header[1] = Word Count LSB (LP) or first param (SP)
> + * header[2] = Word Count MSB (LP) or second param (SP)
> + */
> + word_count = pkt->header[1] | (pkt->header[2] << 8);
> + if (hs_workaround && (dsi->quirks & E11418_HS_MODE_QUIRK)) {
> + DRM_DEV_DEBUG_DRIVER(dsi->dev,
> + "Using hs mode workaround for
> cmd 0x%x\n",
> + xfer->cmd);
> + hs_mode = 1;
> + } else {
> + hs_mode = (xfer->msg->flags & MIPI_DSI_MSG_USE_LPM) ?
> 0 : 1;
> + }
> + val = NWL_DSI_WC(word_count) | NWL_DSI_TX_VC(xfer->msg-
> >channel) |
> + NWL_DSI_TX_DT(xfer->msg->type) |
> NWL_DSI_HS_SEL(hs_mode) |
> + NWL_DSI_BTA_TX(xfer->need_bta);
> + nwl_dsi_write(dsi, NWL_DSI_PKT_CONTROL, val);
> +
> + /* Send packet command */
> + nwl_dsi_write(dsi, NWL_DSI_SEND_PACKET, 0x1);
> +}
> +
> +static ssize_t nwl_dsi_host_transfer(struct mipi_dsi_host *dsi_host,
> + const struct mipi_dsi_msg *msg)
> +{
> + struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi,
> dsi_host);
> + struct nwl_dsi_transfer xfer;
> + ssize_t ret = 0;
> +
> + /* Create packet to be sent */
> + dsi->xfer = &xfer;
> + ret = mipi_dsi_create_packet(&xfer.packet, msg);
> + if (ret < 0) {
> + dsi->xfer = NULL;
> + return ret;
> + }
> +
> + if ((msg->type & MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM ||
> + msg->type & MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM ||
> + msg->type & MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM ||
> + msg->type & MIPI_DSI_DCS_READ) &&
> + msg->rx_len > 0 && msg->rx_buf != NULL)
> + xfer.direction = DSI_PACKET_RECEIVE;
> + else
> + xfer.direction = DSI_PACKET_SEND;
> +
> + xfer.need_bta = (xfer.direction == DSI_PACKET_RECEIVE);
> + xfer.need_bta |= (msg->flags & MIPI_DSI_MSG_REQ_ACK) ? 1 : 0;
> + xfer.msg = msg;
> + xfer.status = -ETIMEDOUT;
> + xfer.rx_word_count = 0;
> + xfer.rx_len = 0;
> + xfer.cmd = 0x00;
> + if (msg->tx_len > 0)
> + xfer.cmd = ((u8 *)(msg->tx_buf))[0];
> + init_completion(&xfer.completed);
> +
> + ret = clk_prepare_enable(dsi->rx_esc_clk);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dsi->dev, "Failed to enable rx_esc clk:
> %zd\n",
> + ret);
> + return ret;
> + }
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled rx_esc clk @%lu
> Hz\n",
> + clk_get_rate(dsi->rx_esc_clk));
> +
> + /* Initiate the DSI packet transmision */
> + nwl_dsi_begin_transmission(dsi);
> +
> + if (!wait_for_completion_timeout(&xfer.completed,
> + NWL_DSI_MIPI_FIFO_TIMEOUT))
> {
> + DRM_DEV_ERROR(dsi_host->dev, "[%02X] DSI transfer
> timed out\n",
> + xfer.cmd);
> + ret = -ETIMEDOUT;
> + } else {
> + ret = xfer.status;
> + }
> +
> + clk_disable_unprepare(dsi->rx_esc_clk);
> +
> + return ret;
> +}
> +
> +const struct mipi_dsi_host_ops nwl_dsi_host_ops = {
> + .attach = nwl_dsi_host_attach,
> + .detach = nwl_dsi_host_detach,
> + .transfer = nwl_dsi_host_transfer,
> +};
> +
> +irqreturn_t nwl_dsi_irq_handler(int irq, void *data)
> +{
> + u32 irq_status;
> + struct nwl_dsi *dsi = data;
> +
> + irq_status = nwl_dsi_read(dsi, NWL_DSI_IRQ_STATUS);
> +
> + if (irq_status & NWL_DSI_TX_PKT_DONE ||
> + irq_status & NWL_DSI_RX_PKT_HDR_RCVD ||
> + irq_status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)
> + nwl_dsi_finish_transmission(dsi, irq_status);
> +
> + return IRQ_HANDLED;
> +}
> +
> +int nwl_dsi_enable(struct nwl_dsi *dsi)
> +{
> + struct device *dev = dsi->dev;
> + union phy_configure_opts *phy_cfg = &dsi->phy_cfg;
> + int ret;
> +
> + if (!dsi->lanes) {
> + DRM_DEV_ERROR(dev, "Need DSI lanes: %d\n", dsi-
> >lanes);
> + return -EINVAL;
> + }
> +
> + ret = phy_init(dsi->phy);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to init DSI phy: %d\n",
> ret);
> + return ret;
> + }
> +
> + ret = phy_configure(dsi->phy, phy_cfg);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to configure DSI phy:
> %d\n", ret);
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(dsi->tx_esc_clk);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dsi->dev, "Failed to enable tx_esc clk:
> %d\n",
> + ret);
> + return ret;
> + }
> + DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled tx_esc clk @%lu
> Hz\n",
> + clk_get_rate(dsi->tx_esc_clk));
> +
> + ret = nwl_dsi_config_host(dsi);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to set up DSI: %d", ret);
> + return ret;
> + }
> +
> + ret = nwl_dsi_config_dpi(dsi);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to set up DPI: %d", ret);
> + return ret;
> + }
> +
> + ret = phy_power_on(dsi->phy);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Failed to power on DPHY (%d)\n",
> ret);
> + return ret;
> + }
> +
> + nwl_dsi_init_interrupts(dsi);
> +
> + return 0;
> +}
> +
> +int nwl_dsi_disable(struct nwl_dsi *dsi)
> +{
> + struct device *dev = dsi->dev;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "Disabling clocks and phy\n");
> +
> + phy_power_off(dsi->phy);
> + phy_exit(dsi->phy);
> +
> + /* Disabling the clock before the phy breaks enabling dsi
> again */
> + clk_disable_unprepare(dsi->tx_esc_clk);
> +
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
> b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
> new file mode 100644
> index 000000000000..579b366de652
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
> @@ -0,0 +1,112 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +#ifndef __NWL_DSI_H__
> +#define __NWL_DSI_H__
> +
> +#include <linux/irqreturn.h>
> +
> +#include <drm/drm_mipi_dsi.h>
> +
> +#include "nwl-drv.h"
> +
> +/* DSI HOST registers */
> +#define NWL_DSI_CFG_NUM_LANES 0x0
> +#define NWL_DSI_CFG_NONCONTINUOUS_CLK 0x4
> +#define NWL_DSI_CFG_T_PRE 0x8
> +#define NWL_DSI_CFG_T_POST 0xc
> +#define NWL_DSI_CFG_TX_GAP 0x10
> +#define NWL_DSI_CFG_AUTOINSERT_EOTP 0x14
> +#define NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP 0x18
> +#define NWL_DSI_CFG_HTX_TO_COUNT 0x1c
> +#define NWL_DSI_CFG_LRX_H_TO_COUNT 0x20
> +#define NWL_DSI_CFG_BTA_H_TO_COUNT 0x24
> +#define NWL_DSI_CFG_TWAKEUP 0x28
> +#define NWL_DSI_CFG_STATUS_OUT 0x2c
> +#define NWL_DSI_RX_ERROR_STATUS 0x30
> +
> +/* DSI DPI registers */
> +#define NWL_DSI_PIXEL_PAYLOAD_SIZE 0x200
> +#define NWL_DSI_PIXEL_FIFO_SEND_LEVEL 0x204
> +#define NWL_DSI_INTERFACE_COLOR_CODING 0x208
> +#define NWL_DSI_PIXEL_FORMAT 0x20c
> +#define NWL_DSI_VSYNC_POLARITY 0x210
> +#define NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW 0
> +#define NWL_DSI_VSYNC_POLARITY_ACTIVE_HIGH BIT(1)
> +
> +#define NWL_DSI_HSYNC_POLARITY 0x214
> +#define NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW 0
> +#define NWL_DSI_HSYNC_POLARITY_ACTIVE_HIGH BIT(1)
> +
> +#define NWL_DSI_VIDEO_MODE 0x218
> +#define NWL_DSI_HFP 0x21c
> +#define NWL_DSI_HBP 0x220
> +#define NWL_DSI_HSA 0x224
> +#define NWL_DSI_ENABLE_MULT_PKTS 0x228
> +#define NWL_DSI_VBP 0x22c
> +#define NWL_DSI_VFP 0x230
> +#define NWL_DSI_BLLP_MODE 0x234
> +#define NWL_DSI_USE_NULL_PKT_BLLP 0x238
> +#define NWL_DSI_VACTIVE 0x23c
> +#define NWL_DSI_VC 0x240
> +
> +/* DSI APB PKT control */
> +#define NWL_DSI_TX_PAYLOAD 0x280
> +#define NWL_DSI_PKT_CONTROL 0x284
> +#define NWL_DSI_SEND_PACKET 0x288
> +#define NWL_DSI_PKT_STATUS 0x28c
> +#define NWL_DSI_PKT_FIFO_WR_LEVEL 0x290
> +#define NWL_DSI_PKT_FIFO_RD_LEVEL 0x294
> +#define NWL_DSI_RX_PAYLOAD 0x298
> +#define NWL_DSI_RX_PKT_HEADER 0x29c
> +
> +/* DSI IRQ handling */
> +#define NWL_DSI_IRQ_STATUS 0x2a0
> +#define NWL_DSI_SM_NOT_IDLE BIT(0)
> +#define NWL_DSI_TX_PKT_DONE BIT(1)
> +#define NWL_DSI_DPHY_DIRECTION BIT(2)
> +#define NWL_DSI_TX_FIFO_OVFLW BIT(3)
> +#define NWL_DSI_TX_FIFO_UDFLW BIT(4)
> +#define NWL_DSI_RX_FIFO_OVFLW BIT(5)
> +#define NWL_DSI_RX_FIFO_UDFLW BIT(6)
> +#define NWL_DSI_RX_PKT_HDR_RCVD BIT(7)
> +#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD BIT(8)
> +#define NWL_DSI_BTA_TIMEOUT BIT(29)
> +#define NWL_DSI_LP_RX_TIMEOUT BIT(30)
> +#define NWL_DSI_HS_TX_TIMEOUT BIT(31)
> +
> +#define NWL_DSI_IRQ_STATUS2 0x2a4
> +#define NWL_DSI_SINGLE_BIT_ECC_ERR BIT(0)
> +#define NWL_DSI_MULTI_BIT_ECC_ERR BIT(1)
> +#define NWL_DSI_CRC_ERR BIT(2)
> +
> +#define NWL_DSI_IRQ_MASK 0x2a8
> +#define NWL_DSI_SM_NOT_IDLE_MASK BIT(0)
> +#define NWL_DSI_TX_PKT_DONE_MASK BIT(1)
> +#define NWL_DSI_DPHY_DIRECTION_MASK BIT(2)
> +#define NWL_DSI_TX_FIFO_OVFLW_MASK BIT(3)
> +#define NWL_DSI_TX_FIFO_UDFLW_MASK BIT(4)
> +#define NWL_DSI_RX_FIFO_OVFLW_MASK BIT(5)
> +#define NWL_DSI_RX_FIFO_UDFLW_MASK BIT(6)
> +#define NWL_DSI_RX_PKT_HDR_RCVD_MASK BIT(7)
> +#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD_MASK BIT(8)
> +#define NWL_DSI_BTA_TIMEOUT_MASK BIT(29)
> +#define NWL_DSI_LP_RX_TIMEOUT_MASK BIT(30)
> +#define NWL_DSI_HS_TX_TIMEOUT_MASK BIT(31)
> +
> +#define NWL_DSI_IRQ_MASK2 0x2ac
> +#define NWL_DSI_SINGLE_BIT_ECC_ERR_MASK BIT(0)
> +#define NWL_DSI_MULTI_BIT_ECC_ERR_MASK BIT(1)
> +#define NWL_DSI_CRC_ERR_MASK BIT(2)
> +
> +extern const struct mipi_dsi_host_ops nwl_dsi_host_ops;
> +
> +irqreturn_t nwl_dsi_irq_handler(int irq, void *data);
> +int nwl_dsi_enable(struct nwl_dsi *dsi);
> +int nwl_dsi_disable(struct nwl_dsi *dsi);
> +
> +#endif /* __NWL_DSI_H__ */
> --
> 2.20.1
>
>
Best regards,
Robert
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] arm64: use x22 to save boot exception level
From: Mark Rutland @ 2019-08-29 9:47 UTC (permalink / raw)
To: Andrew F. Davis
Cc: Nishanth Menon, Matthew Leach, Catalin Marinas, linux-kernel,
Tero Kristo, Will Deacon, linux-arm-kernel
In-Reply-To: <20190828173318.12428-1-afd@ti.com>
Hi Andrew,
On Wed, Aug 28, 2019 at 01:33:18PM -0400, Andrew F. Davis wrote:
> The exception level in which the kernel was entered needs to be saved for
> later. We do this by writing the exception level to memory. As this data
> is written with the MMU/cache off it will bypass any cache, after this we
> invalidate the address so that later reads from cacheable mappings do not
> read a stale cache line. The side effect of this invalidate is any
> existing cache line for this address in the same coherency domain will be
> cleaned and written into memory, possibly overwriting the data we just
> wrote. As this memory is treated as cacheable by already running cores it
> on not architecturally safe to perform any non-caching accesses to this
> memory anyway.
Are you seeing an issue in practice here, or is this something that
you've found by inspection?
If this is an issue in practice, can you tell me more about the system,
i.e.
- Which CPU models do you see this with?
- Do you see this with the boot CPU, secondaries, or both?
- Do you have a system-level cache? If so, which model?
- Do you see this on bare-metal?
- Do you see this under a hypervisor? If so, which hypervisor?
We place __boot_cpu_mode in the .mmuoff.data.write section, which is
only written with the MMU off (i.e. with non-cacheable accesses), such
that the cached copy should always be clean and shouldn't be written
back. Your description sounds like you're seeing a write-back, which is
surprising and may indicate a bug elsewhere.
Depending on what exactly you're seeing, this could also be an issue for
__early_cpu_boot_status and the early page table creation, so I'd like
to understand that better.
Thanks,
Mark.
> Lets avoid these issues altogether by moving the writing of the boot
> exception level to after MMU/caching has been enabled. Saving the boot
> state in unused register x22 until we can safely and coherently write out
> this data.
>
> As the data is not written with the MMU off anymore we move the variable
> definition out of this section and into a regular C code data section.
>
> Signed-off-by: Andrew F. Davis <afd@ti.com>
> ---
> arch/arm64/kernel/head.S | 31 +++++++++++--------------------
> arch/arm64/kernel/smp.c | 10 ++++++++++
> 2 files changed, 21 insertions(+), 20 deletions(-)
>
> diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
> index 2cdacd1c141b..4c71493742c5 100644
> --- a/arch/arm64/kernel/head.S
> +++ b/arch/arm64/kernel/head.S
> @@ -99,6 +99,7 @@ pe_header:
> *
> * Register Scope Purpose
> * x21 stext() .. start_kernel() FDT pointer passed at boot in x0
> + * x22 stext() .. start_kernel() exception level core was booted
> * x23 stext() .. start_kernel() physical misalignment/KASLR offset
> * x28 __create_page_tables() callee preserved temp register
> * x19/x20 __primary_switch() callee preserved temp registers
> @@ -108,7 +109,6 @@ ENTRY(stext)
> bl el2_setup // Drop to EL1, w0=cpu_boot_mode
> adrp x23, __PHYS_OFFSET
> and x23, x23, MIN_KIMG_ALIGN - 1 // KASLR offset, defaults to 0
> - bl set_cpu_boot_mode_flag
> bl __create_page_tables
> /*
> * The following calls CPU setup code, see arch/arm64/mm/proc.S for
> @@ -428,6 +428,8 @@ __primary_switched:
> sub x4, x4, x0 // the kernel virtual and
> str_l x4, kimage_voffset, x5 // physical mappings
>
> + bl set_cpu_boot_mode_flag
> +
> // Clear BSS
> adr_l x0, __bss_start
> mov x1, xzr
> @@ -470,7 +472,7 @@ EXPORT_SYMBOL(kimage_vaddr)
> * If we're fortunate enough to boot at EL2, ensure that the world is
> * sane before dropping to EL1.
> *
> - * Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in w0 if
> + * Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in w22 if
> * booted in EL1 or EL2 respectively.
> */
> ENTRY(el2_setup)
> @@ -480,7 +482,7 @@ ENTRY(el2_setup)
> b.eq 1f
> mov_q x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1)
> msr sctlr_el1, x0
> - mov w0, #BOOT_CPU_MODE_EL1 // This cpu booted in EL1
> + mov w22, #BOOT_CPU_MODE_EL1 // This cpu booted in EL1
> isb
> ret
>
> @@ -593,7 +595,7 @@ set_hcr:
>
> cbz x2, install_el2_stub
>
> - mov w0, #BOOT_CPU_MODE_EL2 // This CPU booted in EL2
> + mov w22, #BOOT_CPU_MODE_EL2 // This CPU booted in EL2
> isb
> ret
>
> @@ -632,7 +634,7 @@ install_el2_stub:
> PSR_MODE_EL1h)
> msr spsr_el2, x0
> msr elr_el2, lr
> - mov w0, #BOOT_CPU_MODE_EL2 // This CPU booted in EL2
> + mov w22, #BOOT_CPU_MODE_EL2 // This CPU booted in EL2
> eret
> ENDPROC(el2_setup)
>
> @@ -642,12 +644,10 @@ ENDPROC(el2_setup)
> */
> set_cpu_boot_mode_flag:
> adr_l x1, __boot_cpu_mode
> - cmp w0, #BOOT_CPU_MODE_EL2
> + cmp w22, #BOOT_CPU_MODE_EL2
> b.ne 1f
> - add x1, x1, #4
> -1: str w0, [x1] // This CPU has booted in EL1
> - dmb sy
> - dc ivac, x1 // Invalidate potentially stale cache line
> + add x1, x1, #4 // This CPU has booted in EL2
> +1: str w22, [x1]
> ret
> ENDPROC(set_cpu_boot_mode_flag)
>
> @@ -658,16 +658,7 @@ ENDPROC(set_cpu_boot_mode_flag)
> * sufficient alignment that the CWG doesn't overlap another section.
> */
> .pushsection ".mmuoff.data.write", "aw"
> -/*
> - * We need to find out the CPU boot mode long after boot, so we need to
> - * store it in a writable variable.
> - *
> - * This is not in .bss, because we set it sufficiently early that the boot-time
> - * zeroing of .bss would clobber it.
> - */
> -ENTRY(__boot_cpu_mode)
> - .long BOOT_CPU_MODE_EL2
> - .long BOOT_CPU_MODE_EL1
> +
> /*
> * The booting CPU updates the failed status @__early_cpu_boot_status,
> * with MMU turned off.
> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> index 018a33e01b0e..66bdcaf61a46 100644
> --- a/arch/arm64/kernel/smp.c
> +++ b/arch/arm64/kernel/smp.c
> @@ -65,6 +65,16 @@ struct secondary_data secondary_data;
> /* Number of CPUs which aren't online, but looping in kernel text. */
> int cpus_stuck_in_kernel;
>
> +/*
> + * We need to find out the CPU boot mode long after boot, so we need to
> + * store it in a writable variable in early boot. Any core started in
> + * EL1 will write that to the first location, EL2 to the second. After
> + * all cores are started this allows us to check that all cores started
> + * in the same mode.
> + */
> +u32 __boot_cpu_mode[2] = { BOOT_CPU_MODE_EL2, BOOT_CPU_MODE_EL1 };
> +EXPORT_SYMBOL(__boot_cpu_mode);
> +
> enum ipi_msg_type {
> IPI_RESCHEDULE,
> IPI_CALL_FUNC,
> --
> 2.17.1
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [arm:for-next 13/25] include/linux/error-injection.h:7:10: fatal error: asm/error-injection.h: No such file or directory
From: Leo Yan @ 2019-08-29 9:57 UTC (permalink / raw)
To: Will Deacon, rmk+kernel; +Cc: kbuild test robot, linux-arm-kernel, kbuild-all
In-Reply-To: <20190829083159.a3ywiivey3u4kxwf@willie-the-truck>
Hi Will,
On Thu, Aug 29, 2019 at 09:32:00AM +0100, Will Deacon wrote:
> [ Move RMK to To: ]
>
> On Thu, Aug 29, 2019 at 02:43:10PM +0800, Leo Yan wrote:
> > On Thu, Aug 29, 2019 at 08:49:16AM +0800, kbuild test robot wrote:
> > > tree: git://git.armlinux.org.uk/~rmk/linux-arm.git for-next
> > > head: d0d54dc04e37be14a9e51d9a2e431f302948e99d
> > > commit: 566c290c6498b2fdc04a54556c4e8747f0298c7b [13/25] ARM: 8899/1: arm/arm64: Add support for function error injection
> > > config: arm-allmodconfig (attached as .config)
> > > compiler: arm-linux-gnueabi-gcc (GCC) 7.4.0
> > > reproduce:
> > > wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> > > chmod +x ~/bin/make.cross
> > > git checkout 566c290c6498b2fdc04a54556c4e8747f0298c7b
> > > # save the attached .config to linux build tree
> > > GCC_VERSION=7.4.0 make.cross ARCH=arm
> > >
> > > If you fix the issue, kindly add following tag
> > > Reported-by: kbuild test robot <lkp@intel.com>
> > >
> > > All errors (new ones prefixed by >>):
> > >
> > > In file included from include/linux/module.h:22:0,
> > > from drivers/pps/pps.c:11:
> > > >> include/linux/error-injection.h:7:10: fatal error: asm/error-injection.h: No such file or directory
> > > #include <asm/error-injection.h>
> > > ^~~~~~~~~~~~~~~~~~~~~~~
> > > compilation terminated.
> >
> > This building error is caused by there have a dependent patch:
> > error-injection: Consolidate override function definition
> > https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/commit/?h=for-next/error-injection&id=45880f7b7b19e043ce0aaa4cb7d05369425c82fa
>
> Ah, I guess you put the ARM patch into the patch system without reference to
> the core parts?
Yes, this is exactly what I did. Sorry I only put the ARM patch and
didn't give out the whole context.
> > This patch has been picked up by Will in one of arm64's next branch:
> > https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/log/?h=for-next/error-injection
> >
> > I don't know what's the best practice for the dependency between
> > two branches, if need me to follow up anything, please let me know.
>
> The for-next/error-injection branch here:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/log/?h=for-next/error-injection
>
> is stable, so I suppose either:
>
> * That could be pulled into the ARM tree, or
> * The ARM part could wait until the core stuff has landed in mainline, or
> * I could take the ARM patch via the arm64 tree if Russell is ok with it
Thanks for these options. I'd like to know what's Russell's decision,
then I will look closely to know what I should follow up or not.
Thanks,
Leo Yan
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 01/11] dt-bindings: firmware: imx-scu: new binding to parse clocks from device tree
From: Oliver Graute @ 2019-08-29 10:01 UTC (permalink / raw)
To: Dong Aisheng
Cc: devicetree@vger.kernel.org, sboyd@kernel.org,
mturquette@baylibre.com, Rob Herring, linux-imx@nxp.com,
kernel@pengutronix.de, fabio.estevam@nxp.com, shawnguo@kernel.org,
linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org
In-Reply-To: <1563289265-10977-2-git-send-email-aisheng.dong@nxp.com>
On 16/07/19, Dong Aisheng wrote:
> There's a few limitations on the original one cell clock binding
> (#clock-cells = <1>) that we have to define some SW clock IDs for device
> tree to reference. This may cause troubles if we want to use common
> clock IDs for multi platforms support when the clock of those platforms
> are mostly the same.
> e.g. Current clock IDs name are defined with SS prefix.
>
> However the device may reside in different SS across CPUs, that means the
> SS prefix may not valid anymore for a new SoC. Furthermore, the device
> availability of those clocks may also vary a bit.
>
> For such situation, we want to eliminate the using of SW Clock IDs and
> change to use a more close to HW one instead.
> For SCU clocks usage, only two params required: Resource id + Clock Type.
> Both parameters are platform independent. So we could use two cells binding
> to pass those parameters,
>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Stephen Boyd <sboyd@kernel.org>
> Cc: Shawn Guo <shawnguo@kernel.org>
> Cc: Sascha Hauer <kernel@pengutronix.de>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
This Patch series (v3) is running fine for some weeks on my iMX8QM Board.
So feel free to use my Tag:
Tested-by: Oliver Graute <oliver.graute@kococonnector.com>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v9 2/3] fdt: add support for rng-seed
From: Hsin-Yi Wang @ 2019-08-29 10:03 UTC (permalink / raw)
To: Kees Cook
Cc: Kate Stewart, Peter Zijlstra, Catalin Marinas, Mukesh Ojha,
Grzegorz Halat, H . Peter Anvin, Guenter Roeck, Will Deacon,
Marek Szyprowski, Rob Herring, Daniel Thompson, Anders Roxell,
Yury Norov, Marc Zyngier, Russell King, Aaro Koskinen,
Ingo Molnar, Viresh Kumar, Waiman Long, Paul E . McKenney, Wei Li,
Alexey Dobriyan, Julien Thierry, Len Brown, Arnd Bergmann,
Rik van Riel, Stephen Boyd, Shaokun Zhang, Mike Rapoport,
Borislav Petkov, Josh Poimboeuf, Thomas Gleixner,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
Theodore Y . Ts'o, Greg Kroah-Hartman, Marcelo Tosatti, lkml,
Armijn Hemel, Jiri Kosina, Mathieu Desnoyers, Andrew Morton,
Tim Chen, David S . Miller
In-Reply-To: <201908241203.92CC0BE8@keescook>
On Thu, Aug 29, 2019 at 1:36 AM Kees Cook <keescook@chromium.org> wrote:
>
> Can this please be a boot param (with the default controlled by the
> CONFIG)? See how CONFIG_RANDOM_TRUST_CPU is wired up...
>
> -Kees
>
Currently rng-seed read and added in setup_arch() -->
setup_machine_fdt().. -> early_init_dt_scan_chosen(), which is earlier
than parse_early_param() that initializes early_param.
If we want to set it as a boot param, add_bootloader_randomness() can
only be called after parse_early_param(). The seed can't be directly
added to pool after it's read in. We need to store into global
variable and load it later.
If this seems okay then I'll add a patch for this. Thanks
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -1096,13 +1096,15 @@ static const char *config_cmdline = CONFIG_CMDLINE;
+const void* rng_seed;
+int rng_seed_len;
+
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
int depth,
void *data)
{
int l = 0;
const char *p = NULL;
char *cmdline = data;
- const void *rng_seed;
pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
@@ -1137,10 +1139,8 @@ int __init early_init_dt_scan_chosen(unsigned
long node, const char *uname,
pr_debug("Command line is: %s\n", (char*)data);
- rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l);
- if (rng_seed && l > 0) {
- add_bootloader_randomness(rng_seed, l); //
Originally it's added to entropy pool here
-
+ rng_seed = of_get_flat_dt_prop(node, "rng-seed", &rng_seed_len);
+ if (rng_seed && rng_seed_len > 0) {
/* try to clear seed so it won't be found. */
diff --git a/include/linux/random.h b/include/linux/random.h
index 831a002a1882..946840bba7c1 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -31,6 +31,15 @@ static inline void add_latent_entropy(void)
static inline void add_latent_entropy(void) {}
#endif
+extern const void* rng_seed;
+extern int rng_seed_len;
+
+static inline void add_bootloader_entropy(void)
+{
+ if (rng_seed && rng_seed_len > 0)
+ add_bootloader_randomness(rng_seed, rng_seed_len);
+}
+
extern void add_input_randomness(unsigned int type, unsigned int code,
unsigned int value) __latent_entropy;
extern void add_interrupt_randomness(int irq, int irq_flags) __latent_entropy;
diff --git a/init/main.c b/init/main.c
index 71847af32e4e..f74a8c7b34af 100644
--- a/init/main.c
+++ b/init/main.c
@@ -645,6 +645,7 @@ asmlinkage __visible void __init start_kernel(void)
* - adding command line entropy
*/
rand_initialize();
+ add_bootloader_entropy();
add_latent_entropy();
add_device_randomness(command_line, strlen(command_line));
boot_init_stack_canary();
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v3 03/11] kselftest: arm64: mangle_pstate_invalid_daif_bits
From: Cristian Marussi @ 2019-08-29 10:19 UTC (permalink / raw)
To: Dave Martin; +Cc: andreyknvl, shuah, linux-arm-kernel, linux-kselftest
In-Reply-To: <20190813162434.GA10425@arm.com>
On 13/08/2019 17:24, Dave Martin wrote:
> On Fri, Aug 02, 2019 at 06:02:52PM +0100, Cristian Marussi wrote:
>> Added a simple mangle testcase which messes with the ucontext_t
>
> Strange past tense? How about "Add"?
>
>> from within the sig_handler, trying to set PSTATE DAIF bits to an
>
> "signal handler"?
>
Ok I'll fix the commit message and use imperative mood.
Cheers
Cristian
>> invalid value (masking everything). Expects SIGSEGV on test PASS.
>>
>> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
>> ---
>> .../arm64/signal/testcases/.gitignore | 1 +
>> .../mangle_pstate_invalid_daif_bits.c | 28 +++++++++++++++++++
>> 2 files changed, 29 insertions(+)
>> create mode 100644 tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c
>>
>> diff --git a/tools/testing/selftests/arm64/signal/testcases/.gitignore b/tools/testing/selftests/arm64/signal/testcases/.gitignore
>> index 8651272e3cfc..8a0a29f0cc2a 100644
>> --- a/tools/testing/selftests/arm64/signal/testcases/.gitignore
>> +++ b/tools/testing/selftests/arm64/signal/testcases/.gitignore
>> @@ -1 +1,2 @@
>> mangle_pstate_invalid_compat_toggle
>> +mangle_pstate_invalid_daif_bits
>> diff --git a/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c
>> new file mode 100644
>> index 000000000000..af899d4bb655
>> --- /dev/null
>> +++ b/tools/testing/selftests/arm64/signal/testcases/mangle_pstate_invalid_daif_bits.c
>> @@ -0,0 +1,28 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* Copyright (C) 2019 ARM Limited */
>> +
>> +#include "test_signals_utils.h"
>> +#include "testcases.h"
>> +
>> +static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si,
>> + ucontext_t *uc)
>> +{
>> + ASSERT_GOOD_CONTEXT(uc);
>> +
>> + /*
>> + * This config should trigger a SIGSEGV by Kernel when it checks
>> + * the sigframe consistency in valid_user_regs() routine.
>> + */
>> + uc->uc_mcontext.pstate |= PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT;
>> +
>> + return 1;
>> +}
>
> Hmmm, there was a lot of common framework code, but it seems like a good
> investment if adding a new test is as simple as this :)
>
> [...]
>
> Cheers
> ---Dave
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 01/15] arm64: dts: imx8qxp: add fallback compatible string for scu pd
From: Oliver Graute @ 2019-08-29 10:20 UTC (permalink / raw)
To: Dong Aisheng
Cc: Mark Rutland, devicetree, dongas86, catalin.marinas, will.deacon,
robh+dt, linux-imx, kernel, fabio.estevam, shawnguo,
linux-arm-kernel
In-Reply-To: <1563290089-11085-2-git-send-email-aisheng.dong@nxp.com>
On 16/07/19, Dong Aisheng wrote:
> According to binding doc, add the fallback compatible string for
> scu pd.
This Patch series (v2) is running fine for some weeks on my iMX8QM Board.
So feel free to use my Tag:
Tested-by: Oliver Graute <oliver.graute@kococonnector.com>
Will there be an updated version of this Patch series which apply on
latest linux next?
Best Regards,
Oliver
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 4/7] usb: mtk-xhci: support ip-sleep wakeup for MT8183
From: Mathias Nyman @ 2019-08-29 10:35 UTC (permalink / raw)
To: Chunfeng Yun, Greg Kroah-Hartman, Rob Herring
Cc: Mark Rutland, devicetree, Mathias Nyman, linux-usb, linux-kernel,
linux-mediatek, Matthias Brugger, linux-arm-kernel
In-Reply-To: <1566977671-22191-5-git-send-email-chunfeng.yun@mediatek.com>
On 28.8.2019 10.34, Chunfeng Yun wrote:
> Support USB wakeup by ip-sleep mode for MT8183, it's similar to
> MT8173
>
> Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Acked-by: Mathias Nyman <mathias.nyman@linux.intel.com>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 1/1] sched/rt: avoid contend with CFS task
From: Valentin Schneider @ 2019-08-29 10:38 UTC (permalink / raw)
To: Jing-Ting Wu, Peter Zijlstra, Matthias Brugger
Cc: linux-arm-kernel, linux-mediatek, linux-kernel, wsd_upstream,
Qais Yousef
In-Reply-To: <1567048502-6064-1-git-send-email-jing-ting.wu@mediatek.com>
On 29/08/2019 04:15, Jing-Ting Wu wrote:
> At original linux design, RT & CFS scheduler are independent.
> Current RT task placement policy will select the first cpu in
> lowest_mask, even if the first CPU is running a CFS task.
> This may put RT task to a running cpu and let CFS task runnable.
>
> So we select idle cpu in lowest_mask first to avoid preempting
> CFS task.
>
Regarding the RT & CFS thing, that's working as intended. RT is a whole
class above CFS, it shouldn't have to worry about CFS.
On the other side of things, CFS does worry about RT. We have the concept
of RT-pressure in the CFS scheduler, where RT tasks will reduce a CPU's
capacity (see fair.c::scale_rt_capacity()).
CPU capacity is looked at on CFS wakeup (see wake_cap() and
find_idlest_cpu()), and the periodic load balancer tries to spread load
over capacity, so it'll tend to put less things on CPUs that are also
running RT tasks.
If RT were to start avoiding rqs with CFS tasks, we'd end up with a nasty
situation were both are avoiding each other. It's even more striking when
you see that RT pressure is done with a rq-wide RT util_avg, which
*doesn't* get migrated when a RT task migrates. So if you decide to move
a RT task to an idle CPU "B" because CPU "A" had runnable CFS tasks, the
CFS scheduler will keep seeing CPU "B" as not significantly RT-pressured
while that util_avg signal ramps up, whereas it would correctly see CPU
"A" as RT-pressured if the RT task previously ran there.
So overall I think this is the wrong approach.
> Signed-off-by: Jing-Ting Wu <jing-ting.wu@mediatek.com>
> ---
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] arm64: tegra: Hook up edp interrupt on Tegra210 SOCTHERM
From: Thierry Reding @ 2019-08-29 10:56 UTC (permalink / raw)
To: Thierry Reding; +Cc: linux-tegra, linux-arm-kernel, Jon Hunter
From: Thierry Reding <treding@nvidia.com>
For some reason this was never hooked up. Do it now so that over-current
interrupts can be logged.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index 2db208f20191..a20cd368a772 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1458,7 +1458,9 @@
reg = <0x0 0x700e2000 0x0 0x600 /* SOC_THERM reg_base */
0x0 0x60006000 0x0 0x400>; /* CAR reg_base */
reg-names = "soctherm-reg", "car-reg";
- interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "thermal", "edp";
clocks = <&tegra_car TEGRA210_CLK_TSENSOR>,
<&tegra_car TEGRA210_CLK_SOC_THERM>;
clock-names = "tsensor", "soctherm";
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH 8/8] media: cedrus: Add support for V4L2_DEC_CMD_FLUSH
From: Hans Verkuil @ 2019-08-29 11:11 UTC (permalink / raw)
To: Jernej Skrabec, mchehab, paul.kocialkowski, mripard
Cc: devel, pawel, acourbot, jonas, gregkh, wens, tfiga, kyungmin.park,
linux-arm-kernel, linux-media, ezequiel, linux-kernel,
m.szyprowski
In-Reply-To: <20190822194500.2071-9-jernej.skrabec@siol.net>
On 8/22/19 9:45 PM, Jernej Skrabec wrote:
> This command is useful for explicitly flushing last decoded frame.
>
> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> ---
> .../staging/media/sunxi/cedrus/cedrus_video.c | 34 +++++++++++++++++++
> 1 file changed, 34 insertions(+)
>
> diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
> index 5153b2bba21e..9eae69d5741c 100644
> --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
> +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
> @@ -331,6 +331,37 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv,
> return 0;
> }
>
> +static int cedrus_try_decoder_cmd(struct file *file, void *fh,
> + struct v4l2_decoder_cmd *dc)
> +{
> + if (dc->cmd != V4L2_DEC_CMD_FLUSH)
> + return -EINVAL;
You need to add this line here as well:
dc->flags = 0;
As per the decoder_cmd spec.
> +
> + return 0;
> +}
> +
> +static int cedrus_decoder_cmd(struct file *file, void *fh,
> + struct v4l2_decoder_cmd *dc)
> +{
> + struct cedrus_ctx *ctx = cedrus_file2ctx(file);
You don't need this...
> + struct vb2_v4l2_buffer *out_vb, *cap_vb;
> + int ret;
> +
> + ret = cedrus_try_decoder_cmd(file, fh, dc);
> + if (ret < 0)
> + return ret;
> +
> + out_vb = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx);
... since you can use fh->m2m_ctx directly.
> + cap_vb = v4l2_m2m_last_dst_buf(ctx->fh.m2m_ctx);
> +
> + if (out_vb)
> + out_vb->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
> + else if (cap_vb && cap_vb->is_held)
> + v4l2_m2m_buf_done(cap_vb, VB2_BUF_STATE_DONE);
> +
> + return 0;
> +}
> +
Both these functions should be moved to v4l2-mem2mem.c and renamed to
v4l2_m2m_ioctl_stateless_(try_)decoder_cmd.
As far as I can see they are completely generic and valid for any
stateless decoder. Which is very nice :-)
> const struct v4l2_ioctl_ops cedrus_ioctl_ops = {
> .vidioc_querycap = cedrus_querycap,
>
> @@ -355,6 +386,9 @@ const struct v4l2_ioctl_ops cedrus_ioctl_ops = {
> .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
>
> + .vidioc_try_decoder_cmd = cedrus_try_decoder_cmd,
> + .vidioc_decoder_cmd = cedrus_decoder_cmd,
> +
> .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> };
>
Regards,
Hans
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 0/5] iommu: Implement iommu_put_resv_regions_simple()
From: Thierry Reding @ 2019-08-29 11:17 UTC (permalink / raw)
To: Joerg Roedel
Cc: Jean-Philippe Brucker, Will Deacon, linux-kernel, virtualization,
iommu, Robin Murphy, David Woodhouse, linux-arm-kernel
From: Thierry Reding <treding@nvidia.com>
Most IOMMU drivers only need to free the memory allocated for each
reserved region. Instead of open-coding the loop to do this in each
driver, extract the code into a common function that can be used by
all these drivers.
Thierry
Thierry Reding (5):
iommu: Implement iommu_put_resv_regions_simple()
iommu: arm: Use iommu_put_resv_regions_simple()
iommu: amd: Use iommu_put_resv_regions_simple()
iommu: intel: Use iommu_put_resv_regions_simple()
iommu: virt: Use iommu_put_resv_regions_simple()
drivers/iommu/amd_iommu.c | 11 +----------
drivers/iommu/arm-smmu-v3.c | 11 +----------
drivers/iommu/arm-smmu.c | 11 +----------
drivers/iommu/intel-iommu.c | 11 +----------
drivers/iommu/iommu.c | 19 +++++++++++++++++++
drivers/iommu/virtio-iommu.c | 14 +++-----------
include/linux/iommu.h | 2 ++
7 files changed, 28 insertions(+), 51 deletions(-)
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 1/5] iommu: Implement iommu_put_resv_regions_simple()
From: Thierry Reding @ 2019-08-29 11:17 UTC (permalink / raw)
To: Joerg Roedel
Cc: Jean-Philippe Brucker, Will Deacon, linux-kernel, virtualization,
iommu, Robin Murphy, David Woodhouse, linux-arm-kernel
In-Reply-To: <20190829111752.17513-1-thierry.reding@gmail.com>
From: Thierry Reding <treding@nvidia.com>
Implement a generic function for removing reserved regions. This can be
used by drivers that don't do anything fancy with these regions other
than allocating memory for them.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
drivers/iommu/iommu.c | 19 +++++++++++++++++++
include/linux/iommu.h | 2 ++
2 files changed, 21 insertions(+)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 0f585b614657..73a2a6b13507 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -2170,6 +2170,25 @@ void iommu_put_resv_regions(struct device *dev, struct list_head *list)
ops->put_resv_regions(dev, list);
}
+/**
+ * iommu_put_resv_regions_simple - Reserved region driver helper
+ * @dev: device for which to free reserved regions
+ * @list: reserved region list for device
+ *
+ * IOMMU drivers can use this to implement their .put_resv_regions() callback
+ * for simple reservations. Memory allocated for each reserved region will be
+ * freed. If an IOMMU driver allocates additional resources per region, it is
+ * going to have to implement a custom callback.
+ */
+void iommu_put_resv_regions_simple(struct device *dev, struct list_head *list)
+{
+ struct iommu_resv_region *entry, *next;
+
+ list_for_each_entry_safe(entry, next, list, list)
+ kfree(entry);
+}
+EXPORT_SYMBOL(iommu_put_resv_regions_simple);
+
struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start,
size_t length, int prot,
enum iommu_resv_type type)
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 29bac5345563..d9c91e37ac2e 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -434,6 +434,8 @@ extern void iommu_set_fault_handler(struct iommu_domain *domain,
extern void iommu_get_resv_regions(struct device *dev, struct list_head *list);
extern void iommu_put_resv_regions(struct device *dev, struct list_head *list);
+extern void iommu_put_resv_regions_simple(struct device *dev,
+ struct list_head *list);
extern int iommu_request_dm_for_dev(struct device *dev);
extern int iommu_request_dma_domain_for_dev(struct device *dev);
extern void iommu_set_default_passthrough(bool cmd_line);
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 2/5] iommu: arm: Use iommu_put_resv_regions_simple()
From: Thierry Reding @ 2019-08-29 11:17 UTC (permalink / raw)
To: Joerg Roedel
Cc: Jean-Philippe Brucker, Will Deacon, linux-kernel, virtualization,
iommu, Robin Murphy, David Woodhouse, linux-arm-kernel
In-Reply-To: <20190829111752.17513-1-thierry.reding@gmail.com>
From: Thierry Reding <treding@nvidia.com>
Use the new standard function instead of open-coding it.
Cc: Will Deacon <will@kernel.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
drivers/iommu/arm-smmu-v3.c | 11 +----------
drivers/iommu/arm-smmu.c | 11 +----------
2 files changed, 2 insertions(+), 20 deletions(-)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 0ad6d34d1e96..b3b7ca2c057a 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2263,15 +2263,6 @@ static void arm_smmu_get_resv_regions(struct device *dev,
iommu_dma_get_resv_regions(dev, head);
}
-static void arm_smmu_put_resv_regions(struct device *dev,
- struct list_head *head)
-{
- struct iommu_resv_region *entry, *next;
-
- list_for_each_entry_safe(entry, next, head, list)
- kfree(entry);
-}
-
static struct iommu_ops arm_smmu_ops = {
.capable = arm_smmu_capable,
.domain_alloc = arm_smmu_domain_alloc,
@@ -2289,7 +2280,7 @@ static struct iommu_ops arm_smmu_ops = {
.domain_set_attr = arm_smmu_domain_set_attr,
.of_xlate = arm_smmu_of_xlate,
.get_resv_regions = arm_smmu_get_resv_regions,
- .put_resv_regions = arm_smmu_put_resv_regions,
+ .put_resv_regions = iommu_put_resv_regions_simple,
.pgsize_bitmap = -1UL, /* Restricted during device attach */
};
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index d6fe997e9466..e547b4322bcc 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1724,15 +1724,6 @@ static void arm_smmu_get_resv_regions(struct device *dev,
iommu_dma_get_resv_regions(dev, head);
}
-static void arm_smmu_put_resv_regions(struct device *dev,
- struct list_head *head)
-{
- struct iommu_resv_region *entry, *next;
-
- list_for_each_entry_safe(entry, next, head, list)
- kfree(entry);
-}
-
static struct iommu_ops arm_smmu_ops = {
.capable = arm_smmu_capable,
.domain_alloc = arm_smmu_domain_alloc,
@@ -1750,7 +1741,7 @@ static struct iommu_ops arm_smmu_ops = {
.domain_set_attr = arm_smmu_domain_set_attr,
.of_xlate = arm_smmu_of_xlate,
.get_resv_regions = arm_smmu_get_resv_regions,
- .put_resv_regions = arm_smmu_put_resv_regions,
+ .put_resv_regions = iommu_put_resv_regions_simple,
.pgsize_bitmap = -1UL, /* Restricted during device attach */
};
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 3/5] iommu: amd: Use iommu_put_resv_regions_simple()
From: Thierry Reding @ 2019-08-29 11:17 UTC (permalink / raw)
To: Joerg Roedel
Cc: Jean-Philippe Brucker, Will Deacon, linux-kernel, virtualization,
iommu, Robin Murphy, David Woodhouse, linux-arm-kernel
In-Reply-To: <20190829111752.17513-1-thierry.reding@gmail.com>
From: Thierry Reding <treding@nvidia.com>
Use the new standard function instead of open-coding it.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
drivers/iommu/amd_iommu.c | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 04a9f8443344..7d8896d5fab9 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3160,15 +3160,6 @@ static void amd_iommu_get_resv_regions(struct device *dev,
list_add_tail(®ion->list, head);
}
-static void amd_iommu_put_resv_regions(struct device *dev,
- struct list_head *head)
-{
- struct iommu_resv_region *entry, *next;
-
- list_for_each_entry_safe(entry, next, head, list)
- kfree(entry);
-}
-
static void amd_iommu_apply_resv_region(struct device *dev,
struct iommu_domain *domain,
struct iommu_resv_region *region)
@@ -3216,7 +3207,7 @@ const struct iommu_ops amd_iommu_ops = {
.remove_device = amd_iommu_remove_device,
.device_group = amd_iommu_device_group,
.get_resv_regions = amd_iommu_get_resv_regions,
- .put_resv_regions = amd_iommu_put_resv_regions,
+ .put_resv_regions = iommu_put_resv_regions_simple,
.apply_resv_region = amd_iommu_apply_resv_region,
.is_attach_deferred = amd_iommu_is_attach_deferred,
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox