From 1a7e3948cb9f5bb9241112706267b8fbc7812c7a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 9 Nov 2017 18:07:21 +0100 Subject: USB: add device-tree support for interfaces Add OF device-tree support for USB interfaces. USB "interface nodes" are children of USB "device nodes" and are identified by an interface number and a configuration value: &usb1 { /* host controller */ dev1: device@1 { /* device at port 1 */ compatible = "usb1234,5678"; reg = <1>; #address-cells = <2>; #size-cells = <0>; interface@0,2 { /* interface 0 of configuration 2 */ compatible = "usbif1234,5678.config2.0"; reg = <0 2>; }; }; }; The configuration component is not included in the textual representation of an interface-node unit address for configuration 1: &dev1 { interface@0 { /* interface 0 of configuration 1 */ compatible = "usbif1234,5678.config1.0"; reg = <0 1>; }; }; When a USB device of class 0 or 9 (hub) has only a single configuration with a single interface, a special case "combined node" is used instead of a device node with an interface node: &usb1 { device@2 { compatible = "usb1234,abcd"; reg = <2>; }; }; Combined nodes are shared by the two device structures representing the USB device and its interface in the kernel's device model. Note that, as for device nodes, the compatible strings for interface nodes are currently not used. For more details see "Open Firmware Recommended Practice: Universal Serial Bus Version 1" and the binding documentation. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/of.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 6cbe7a5c2b57..0294ccac4f1d 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -12,6 +12,8 @@ #include #include +struct usb_device; + #if IS_ENABLED(CONFIG_OF) enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0); bool of_usb_host_tpl_support(struct device_node *np); @@ -19,6 +21,9 @@ int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps); struct device_node *usb_of_get_child_node(struct device_node *parent, int portnum); +bool usb_of_has_combined_node(struct usb_device *udev); +struct device_node *usb_of_get_interface_node(struct usb_device *udev, + u8 config, u8 ifnum); struct device *usb_of_get_companion_dev(struct device *dev); #else static inline enum usb_dr_mode @@ -40,6 +45,15 @@ static inline struct device_node *usb_of_get_child_node { return NULL; } +static inline bool usb_of_has_combined_node(struct usb_device *udev) +{ + return false; +} +static inline struct device_node * +usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum) +{ + return NULL; +} static inline struct device *usb_of_get_companion_dev(struct device *dev) { return NULL; -- cgit v1.2.3 From 7739376eb1ed68593805e5b4ed359123d0718549 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 9 Nov 2017 18:07:23 +0100 Subject: USB: of: clean up device-node helper Clean up the USB device-node helper that is used to look up a device node given a parent hub device and a port number. Also pass in a struct usb_device as first argument to provide some type checking. Give the helper the more descriptive name usb_of_get_device_node(), which matches the new usb_of_get_interface_node() helper that is used to look up a second type of of child node from a USB device. Note that the terms "device node" and "interface node" are defined and used by the OF Recommended Practice for USB. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/ledtrig-usbport.c | 2 +- drivers/usb/core/of.c | 27 ++++++++++++++------------- drivers/usb/core/usb.c | 3 +-- include/linux/usb/of.h | 7 +++---- 4 files changed, 19 insertions(+), 20 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index f1fde5165068..d775ffea20c3 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -142,7 +142,7 @@ static bool usbport_trig_port_observed(struct usbport_trig_data *usbport_data, * * FIXME: This is really the device node of the connected device */ - port_np = usb_of_get_child_node(usb_dev->dev.of_node, port1); + port_np = usb_of_get_device_node(usb_dev, port1); if (!port_np) return false; diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 074fabc26d6c..fd77442c2d12 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -12,31 +12,32 @@ #include /** - * usb_of_get_child_node - Find the device node match port number - * @parent: the parent device node - * @portnum: the port number which device is connecting + * usb_of_get_device_node() - get a USB device node + * @hub: hub to which device is connected + * @port1: one-based index of port * - * Find the node from device tree according to its port number. + * Look up the node of a USB device given its parent hub device and one-based + * port number. * * Return: A pointer to the node with incremented refcount if found, or * %NULL otherwise. */ -struct device_node *usb_of_get_child_node(struct device_node *parent, - int portnum) +struct device_node *usb_of_get_device_node(struct usb_device *hub, int port1) { struct device_node *node; - u32 port; + u32 reg; - for_each_child_of_node(parent, node) { - if (!of_property_read_u32(node, "reg", &port)) { - if (port == portnum) - return node; - } + for_each_child_of_node(hub->dev.of_node, node) { + if (of_property_read_u32(node, "reg", ®)) + continue; + + if (reg == port1) + return node; } return NULL; } -EXPORT_SYMBOL_GPL(usb_of_get_child_node); +EXPORT_SYMBOL_GPL(usb_of_get_device_node); /** * usb_of_has_combined_node() - determine whether a device has a combined node diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 845286f08ab0..2f5fbc56a9dd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -645,8 +645,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, raw_port = usb_hcd_find_raw_port_number(usb_hcd, port1); } - dev->dev.of_node = usb_of_get_child_node(parent->dev.of_node, - raw_port); + dev->dev.of_node = usb_of_get_device_node(parent, raw_port); /* hub driver sets up TT records */ } diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 0294ccac4f1d..dba55ccb9b53 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -19,8 +19,7 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0); bool of_usb_host_tpl_support(struct device_node *np); int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps); -struct device_node *usb_of_get_child_node(struct device_node *parent, - int portnum); +struct device_node *usb_of_get_device_node(struct usb_device *hub, int port1); bool usb_of_has_combined_node(struct usb_device *udev); struct device_node *usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum); @@ -40,8 +39,8 @@ static inline int of_usb_update_otg_caps(struct device_node *np, { return 0; } -static inline struct device_node *usb_of_get_child_node - (struct device_node *parent, int portnum) +static inline struct device_node * +usb_of_get_device_node(struct usb_device *hub, int port1) { return NULL; } -- cgit v1.2.3 From 5007e1b5db736e76360047a6974c5cf7beb2d40e Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 15 Nov 2017 17:01:55 -0800 Subject: typec: tcpm: Validate source and sink caps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The source and sink caps should follow the following rules. This patch validates whether the src_caps/snk_caps adheres to it. 6.4.1 Capabilities Message A Capabilities message (Source Capabilities message or Sink Capabilities message) shall have at least one Power Data Object for vSafe5V. The Capabilities message shall also contain the sending Port’s information followed by up to 6 additional Power Data Objects. Power Data Objects in a Capabilities message shall be sent in the following order: 1. The vSafe5V Fixed Supply Object shall always be the first object. 2. The remaining Fixed Supply Objects, if present, shall be sent in voltage order; lowest to highest. 3. The Battery Supply Objects, if present shall be sent in Minimum Voltage order; lowest to highest. 4. The Variable Supply (non-battery) Objects, if present, shall be sent in Minimum Voltage order; lowest to highest. Errors in source/sink_caps of the local port will prevent the port registration. Whereas, errors in source caps of partner device would only log them. Signed-off-by: Badhri Jagan Sridharan Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm.c | 130 +++++++++++++++++++++++++++++++++++++++++++---- include/linux/usb/pd.h | 2 + include/linux/usb/tcpm.h | 16 +++--- 3 files changed, 131 insertions(+), 17 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index c166fc77dfb8..8b637a4b474b 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -1247,6 +1247,100 @@ static void vdm_state_machine_work(struct work_struct *work) mutex_unlock(&port->lock); } +enum pdo_err { + PDO_NO_ERR, + PDO_ERR_NO_VSAFE5V, + PDO_ERR_VSAFE5V_NOT_FIRST, + PDO_ERR_PDO_TYPE_NOT_IN_ORDER, + PDO_ERR_FIXED_NOT_SORTED, + PDO_ERR_VARIABLE_BATT_NOT_SORTED, + PDO_ERR_DUPE_PDO, +}; + +static const char * const pdo_err_msg[] = { + [PDO_ERR_NO_VSAFE5V] = + " err: source/sink caps should atleast have vSafe5V", + [PDO_ERR_VSAFE5V_NOT_FIRST] = + " err: vSafe5V Fixed Supply Object Shall always be the first object", + [PDO_ERR_PDO_TYPE_NOT_IN_ORDER] = + " err: PDOs should be in the following order: Fixed; Battery; Variable", + [PDO_ERR_FIXED_NOT_SORTED] = + " err: Fixed supply pdos should be in increasing order of their fixed voltage", + [PDO_ERR_VARIABLE_BATT_NOT_SORTED] = + " err: Variable/Battery supply pdos should be in increasing order of their minimum voltage", + [PDO_ERR_DUPE_PDO] = + " err: Variable/Batt supply pdos cannot have same min/max voltage", +}; + +static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) +{ + unsigned int i; + + /* Should at least contain vSafe5v */ + if (nr_pdo < 1) + return PDO_ERR_NO_VSAFE5V; + + /* The vSafe5V Fixed Supply Object Shall always be the first object */ + if (pdo_type(pdo[0]) != PDO_TYPE_FIXED || + pdo_fixed_voltage(pdo[0]) != VSAFE5V) + return PDO_ERR_VSAFE5V_NOT_FIRST; + + for (i = 1; i < nr_pdo; i++) { + if (pdo_type(pdo[i]) < pdo_type(pdo[i - 1])) { + return PDO_ERR_PDO_TYPE_NOT_IN_ORDER; + } else if (pdo_type(pdo[i]) == pdo_type(pdo[i - 1])) { + enum pd_pdo_type type = pdo_type(pdo[i]); + + switch (type) { + /* + * The remaining Fixed Supply Objects, if + * present, shall be sent in voltage order; + * lowest to highest. + */ + case PDO_TYPE_FIXED: + if (pdo_fixed_voltage(pdo[i]) <= + pdo_fixed_voltage(pdo[i - 1])) + return PDO_ERR_FIXED_NOT_SORTED; + break; + /* + * The Battery Supply Objects and Variable + * supply, if present shall be sent in Minimum + * Voltage order; lowest to highest. + */ + case PDO_TYPE_VAR: + case PDO_TYPE_BATT: + if (pdo_min_voltage(pdo[i]) < + pdo_min_voltage(pdo[i - 1])) + return PDO_ERR_VARIABLE_BATT_NOT_SORTED; + else if ((pdo_min_voltage(pdo[i]) == + pdo_min_voltage(pdo[i - 1])) && + (pdo_max_voltage(pdo[i]) == + pdo_min_voltage(pdo[i - 1]))) + return PDO_ERR_DUPE_PDO; + break; + default: + tcpm_log_force(port, " Unknown pdo type"); + } + } + } + + return PDO_NO_ERR; +} + +static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) +{ + enum pdo_err err_index = tcpm_caps_err(port, pdo, nr_pdo); + + if (err_index != PDO_NO_ERR) { + tcpm_log_force(port, " %s", pdo_err_msg[err_index]); + return -EINVAL; + } + + return 0; +} + /* * PD (data, control) command handling functions */ @@ -1269,6 +1363,9 @@ static void tcpm_pd_data_request(struct tcpm_port *port, tcpm_log_source_caps(port); + tcpm_validate_caps(port, port->source_caps, + port->nr_source_caps); + /* * This message may be received even if VBUS is not * present. This is quite unexpected; see USB PD @@ -3435,9 +3532,12 @@ static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo, return nr_vdo; } -void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo) +int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) { + if (tcpm_validate_caps(port, pdo, nr_pdo)) + return -EINVAL; + mutex_lock(&port->lock); port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, pdo, nr_pdo); switch (port->state) { @@ -3457,16 +3557,20 @@ void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, break; } mutex_unlock(&port->lock); + return 0; } EXPORT_SYMBOL_GPL(tcpm_update_source_capabilities); -void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo, - unsigned int max_snk_mv, - unsigned int max_snk_ma, - unsigned int max_snk_mw, - unsigned int operating_snk_mw) +int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo, + unsigned int max_snk_mv, + unsigned int max_snk_ma, + unsigned int max_snk_mw, + unsigned int operating_snk_mw) { + if (tcpm_validate_caps(port, pdo, nr_pdo)) + return -EINVAL; + mutex_lock(&port->lock); port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo); port->max_snk_mv = max_snk_mv; @@ -3485,6 +3589,7 @@ void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, break; } mutex_unlock(&port->lock); + return 0; } EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities); @@ -3520,7 +3625,15 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) init_completion(&port->tx_complete); init_completion(&port->swap_complete); + tcpm_debugfs_init(port); + if (tcpm_validate_caps(port, tcpc->config->src_pdo, + tcpc->config->nr_src_pdo) || + tcpm_validate_caps(port, tcpc->config->snk_pdo, + tcpc->config->nr_snk_pdo)) { + err = -EINVAL; + goto out_destroy_wq; + } port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo, tcpc->config->nr_src_pdo); port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo, @@ -3575,7 +3688,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) } } - tcpm_debugfs_init(port); mutex_lock(&port->lock); tcpm_init(port); mutex_unlock(&port->lock); diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index e00051ced806..b3d41d7409b3 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -148,6 +148,8 @@ enum pd_pdo_type { (PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \ PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma)) +#define VSAFE5V 5000 /* mv units */ + #define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */ #define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */ #define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 073197f0d2bb..ca1c0b57f03f 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -183,14 +183,14 @@ struct tcpm_port; struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc); void tcpm_unregister_port(struct tcpm_port *port); -void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo); -void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo, - unsigned int max_snk_mv, - unsigned int max_snk_ma, - unsigned int max_snk_mw, - unsigned int operating_snk_mw); +int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo); +int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo, + unsigned int max_snk_mv, + unsigned int max_snk_ma, + unsigned int max_snk_mw, + unsigned int operating_snk_mw); void tcpm_vbus_change(struct tcpm_port *port); void tcpm_cc_change(struct tcpm_port *port); -- cgit v1.2.3 From c7000aa030ee098e59d126325805f5037d155a48 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 14 Nov 2017 12:27:24 +0200 Subject: usb: gadget: add isoch_delay member Whenever a USB host issues a Set Isoch Delay request, we should cache the result so relevant gadget drivers can make use of the value for calculating how many uFrames ahead a transfer should be queued. Signed-off-by: Felipe Balbi --- include/linux/usb/gadget.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 0142f3af0da6..66a5cff7ee14 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -330,6 +330,7 @@ struct usb_gadget_ops { * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device. + * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP * @out_epnum: last used out ep number * @in_epnum: last used in ep number * @mA: last set mA value @@ -394,6 +395,7 @@ struct usb_gadget { enum usb_device_state state; const char *name; struct device dev; + unsigned isoch_delay; unsigned out_epnum; unsigned in_epnum; unsigned mA; -- cgit v1.2.3 From f16323fdbd40a4062fb6c89d563e26d93854caa0 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 13 Dec 2017 15:46:58 +0900 Subject: usb: renesas_usbhs: add a new callback for extcon notifier To set host/peripheral mode by using extcon notifier, this patch adds a new callback as "notifier" in renesas_usbhs_platform_callback. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- include/linux/usb/renesas_usbhs.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/linux/usb') diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 67102f3d59d4..9482735d4ca5 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -17,6 +17,7 @@ */ #ifndef RENESAS_USB_H #define RENESAS_USB_H +#include #include #include @@ -98,6 +99,13 @@ struct renesas_usbhs_platform_callback { * VBUS control is needed for Host */ int (*set_vbus)(struct platform_device *pdev, int enable); + + /* + * option: + * extcon notifier to set host/peripheral mode. + */ + int (*notifier)(struct notifier_block *nb, unsigned long event, + void *data); }; /* -- cgit v1.2.3 From 3df613ec12f71f00dad545a9df75573951e5c59f Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 18 Dec 2017 17:03:03 +0300 Subject: usb: pd: fix the offset for SVID specific commands The SVID specific commands in the Command field of the Structured VDM Header start from 16, not 10. Changing the value used in VDO_CMD_VENDOR() macro from 10 to 0x10. Signed-off-by: Heikki Krogerus Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/pd_vdo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h index d92259f8de0a..2b64d23ace5c 100644 --- a/include/linux/usb/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h @@ -65,7 +65,7 @@ #define CMD_EXIT_MODE 5 #define CMD_ATTENTION 6 -#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f)) +#define VDO_CMD_VENDOR(x) (((0x10 + (x)) & 0x1f)) /* ChromeOS specific commands */ #define VDO_CMD_VERSION VDO_CMD_VENDOR(0) -- cgit v1.2.3 From aec2927b5944df70bca4bdeea6c4e7c3195dc37a Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Mon, 8 Jan 2018 07:30:53 -0500 Subject: usb: renesas_usbhs: Add support for RZ/A1 This patch adds the capability to support RZ/A1 SoCs. Signed-off-by: Chris Brandt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/Makefile | 2 +- drivers/usb/renesas_usbhs/common.c | 13 ++++++++++ drivers/usb/renesas_usbhs/common.h | 6 +++++ drivers/usb/renesas_usbhs/rza.c | 52 ++++++++++++++++++++++++++++++++++++++ drivers/usb/renesas_usbhs/rza.h | 4 +++ include/linux/usb/renesas_usbhs.h | 1 + 6 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/renesas_usbhs/rza.c create mode 100644 drivers/usb/renesas_usbhs/rza.h (limited to 'include/linux/usb') diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index fac147a3ad23..5c5b51bb48ef 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o -renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o +renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o rza.o ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) renesas_usbhs-y += mod_host.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index c5289b3ecf8d..4310df46639d 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -17,6 +17,7 @@ #include "common.h" #include "rcar2.h" #include "rcar3.h" +#include "rza.h" /* * image of renesas_usbhs @@ -488,6 +489,10 @@ static const struct of_device_id usbhs_of_match[] = { .compatible = "renesas,rcar-gen3-usbhs", .data = (void *)USBHS_TYPE_RCAR_GEN3, }, + { + .compatible = "renesas,rza1-usbhs", + .data = (void *)USBHS_TYPE_RZA1, + }, { }, }; MODULE_DEVICE_TABLE(of, usbhs_of_match); @@ -520,6 +525,11 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); } + if (dparam->type == USBHS_TYPE_RZA1) { + dparam->pipe_configs = usbhsc_new_pipe; + dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); + } + return info; } @@ -591,6 +601,9 @@ static int usbhs_probe(struct platform_device *pdev) dev_err(&pdev->dev, "no notifier registered\n"); } break; + case USBHS_TYPE_RZA1: + priv->pfunc = usbhs_rza1_ops; + break; default: if (!info->platform_callback.get_id) { dev_err(&pdev->dev, "no platform callbacks"); diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index c9747f064601..f619afeae2b8 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -98,6 +98,7 @@ struct usbhs_priv; #define D2FIFOCTR 0x00F2 /* for R-Car Gen2 */ #define D3FIFOSEL 0x00F4 /* for R-Car Gen2 */ #define D3FIFOCTR 0x00F6 /* for R-Car Gen2 */ +#define SUSPMODE 0x0102 /* for RZ/A */ /* SYSCFG */ #define SCKE (1 << 10) /* USB Module Clock Enable */ @@ -106,6 +107,8 @@ struct usbhs_priv; #define DRPD (1 << 5) /* D+ Line/D- Line Resistance Control */ #define DPRPU (1 << 4) /* D+ Line Resistance Control */ #define USBE (1 << 0) /* USB Module Operation Enable */ +#define UCKSEL (1 << 2) /* Clock Select for RZ/A1 */ +#define UPLLE (1 << 1) /* USB PLL Enable for RZ/A1 */ /* DVSTCTR */ #define EXTLP (1 << 10) /* Controls the EXTLP pin output state */ @@ -233,6 +236,9 @@ struct usbhs_priv; #define USBSPD_SPEED_FULL 0x2 #define USBSPD_SPEED_HIGH 0x3 +/* SUSPMODE */ +#define SUSPM (1 << 14) /* SuspendM Control */ + /* * struct */ diff --git a/drivers/usb/renesas_usbhs/rza.c b/drivers/usb/renesas_usbhs/rza.c new file mode 100644 index 000000000000..5b287257ec11 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rza.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas USB driver RZ/A initialization and power control + * + * Copyright (C) 2018 Chris Brandt + * Copyright (C) 2018 Renesas Electronics Corporation + */ + +#include +#include +#include +#include "common.h" +#include "rza.h" + +static int usbhs_rza1_hardware_init(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + struct device_node *usb_x1_clk, *extal_clk; + u32 freq_usb = 0, freq_extal = 0; + + /* Input Clock Selection (NOTE: ch0 controls both ch0 and ch1) */ + usb_x1_clk = of_find_node_by_name(NULL, "usb_x1"); + extal_clk = of_find_node_by_name(NULL, "extal"); + of_property_read_u32(usb_x1_clk, "clock-frequency", &freq_usb); + of_property_read_u32(extal_clk, "clock-frequency", &freq_extal); + if (freq_usb == 0) { + if (freq_extal == 12000000) { + /* Select 12MHz XTAL */ + usbhs_bset(priv, SYSCFG, UCKSEL, UCKSEL); + } else { + dev_err(usbhs_priv_to_dev(priv), "A 48MHz USB clock or 12MHz main clock is required.\n"); + return -EIO; + } + } + + /* Enable USB PLL (NOTE: ch0 controls both ch0 and ch1) */ + usbhs_bset(priv, SYSCFG, UPLLE, UPLLE); + udelay(1000); + usbhs_bset(priv, SUSPMODE, SUSPM, SUSPM); + + return 0; +} + +static int usbhs_rza_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +const struct renesas_usbhs_platform_callback usbhs_rza1_ops = { + .hardware_init = usbhs_rza1_hardware_init, + .get_id = usbhs_rza_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rza.h b/drivers/usb/renesas_usbhs/rza.h new file mode 100644 index 000000000000..ca917ca54f6d --- /dev/null +++ b/drivers/usb/renesas_usbhs/rza.h @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "common.h" + +extern const struct renesas_usbhs_platform_callback usbhs_rza1_ops; diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 9482735d4ca5..53924f8e840c 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -195,6 +195,7 @@ struct renesas_usbhs_driver_param { #define USBHS_TYPE_RCAR_GEN2 1 #define USBHS_TYPE_RCAR_GEN3 2 #define USBHS_TYPE_RCAR_GEN3_WITH_PLL 3 +#define USBHS_TYPE_RZA1 4 /* * option: -- cgit v1.2.3