From 16c403614bcdb1227c217da54e5d6683adaddac8 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 3 Jan 2017 23:25:31 +0800 Subject: phy: sun4i-usb: add support for V3s USB PHY Allwinner V3s come with a USB PHY controller slightly different to other SoCs, with only one PHY. Add support for it. Signed-off-by: Icenowy Zheng Acked-by: Maxime Ripard Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt index 287150db6db4..e42334258185 100644 --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt @@ -10,6 +10,7 @@ Required properties: * allwinner,sun8i-a23-usb-phy * allwinner,sun8i-a33-usb-phy * allwinner,sun8i-h3-usb-phy + * allwinner,sun8i-v3s-usb-phy * allwinner,sun50i-a64-usb-phy - reg : a list of offset + length pairs - reg-names : -- cgit v1.2.3 From 63376c10f0f6eaaa20765ef00dd2b9a398576fd4 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 18 Jan 2017 14:08:26 +0800 Subject: dt-bindings: mt8173-xhci: add reference clock add a reference clock for compatibility Signed-off-by: Chunfeng Yun Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/mt8173-xhci.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt index 2a930bd52b94..ab8bb27e854c 100644 --- a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt +++ b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt @@ -23,6 +23,7 @@ Required properties: entry in clock-names - clock-names : must contain "sys_ck": for clock of xHCI MAC + "ref_ck": for reference clock of xHCI MAC "wakeup_deb_p0": for USB wakeup debounce clock of port0 "wakeup_deb_p1": for USB wakeup debounce clock of port1 @@ -47,10 +48,10 @@ usb30: usb@11270000 { reg-names = "mac", "ippc"; interrupts = ; power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>; - clocks = <&topckgen CLK_TOP_USB30_SEL>, + clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>, <&pericfg CLK_PERI_USB0>, <&pericfg CLK_PERI_USB1>; - clock-names = "sys_ck", + clock-names = "sys_ck", "ref_ck", "wakeup_deb_p0", "wakeup_deb_p1"; phys = <&phy_port0 PHY_TYPE_USB3>, @@ -82,6 +83,7 @@ Required properties: entry in clock-names - clock-names : must be "sys_ck": for clock of xHCI MAC + "ref_ck": for reference clock of xHCI MAC Optional properties: - vbus-supply : reference to the VBUS regulator; @@ -94,8 +96,8 @@ usb30: usb@11270000 { reg-names = "mac"; interrupts = ; power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>; - clocks = <&topckgen CLK_TOP_USB30_SEL>; - clock-names = "sys_ck"; + clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>; + clock-names = "sys_ck", "ref_ck"; vusb33-supply = <&mt6397_vusb_reg>; usb3-lpm-capable; }; -- cgit v1.2.3 From afa197e3cb240fdd6194dd4c8545cdd45c82770e Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 18 Jan 2017 14:08:27 +0800 Subject: dt-bindings: mt8173-mtu3: add reference clock add a reference clock for compatibility Signed-off-by: Chunfeng Yun Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/mt8173-mtu3.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt b/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt index e049d199bf0d..8c976cdd5776 100644 --- a/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt +++ b/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt @@ -10,7 +10,7 @@ Required properties: - vusb33-supply : regulator of USB avdd3.3v - clocks : a list of phandle + clock-specifier pairs, one for each entry in clock-names - - clock-names : must contain "sys_ck" for clock of controller; + - clock-names : must contain "sys_ck" and "ref_ck" for clock of controller; "wakeup_deb_p0" and "wakeup_deb_p1" are optional, they are depends on "mediatek,enable-wakeup" - phys : a list of phandle + phy specifier pairs @@ -56,10 +56,10 @@ ssusb: usb@11271000 { phys = <&phy_port0 PHY_TYPE_USB3>, <&phy_port1 PHY_TYPE_USB2>; power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>; - clocks = <&topckgen CLK_TOP_USB30_SEL>, + clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>, <&pericfg CLK_PERI_USB0>, <&pericfg CLK_PERI_USB1>; - clock-names = "sys_ck", + clock-names = "sys_ck", "ref_ck", "wakeup_deb_p0", "wakeup_deb_p1"; vusb33-supply = <&mt6397_vusb_reg>; @@ -79,8 +79,8 @@ ssusb: usb@11271000 { reg-names = "mac"; interrupts = ; power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>; - clocks = <&topckgen CLK_TOP_USB30_SEL>; - clock-names = "sys_ck"; + clocks = <&topckgen CLK_TOP_USB30_SEL>, <&clk26m>; + clock-names = "sys_ck", "ref_ck"; vusb33-supply = <&mt6397_vusb_reg>; status = "disabled"; }; -- cgit v1.2.3 From ef6a7bcfb01c9c8df172ad06fb547216ca788711 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 28 Dec 2016 14:56:49 -0800 Subject: usb: ulpi: Support device discovery via DT The qcom HSIC ULPI phy doesn't have any bits set in the vendor or product ID registers. This makes it impossible to make a ULPI driver match against the ID registers. Add support to discover the ULPI phys via DT help alleviate this problem. In the DT case, we'll look for a ULPI bus node underneath the device registering the ULPI viewport (or the parent of that device to support chipidea's device layout) and then match up the phy node underneath that with the ULPI device that's created. The side benefit of this is that we can use standard properties in the phy node like clks, regulators, gpios, etc. because we don't have firmware like ACPI to turn these things on for us. And we can use the DT phy binding to point our phy consumer to the phy provider. The ULPI bus code supports native enumeration by reading the vendor ID and product ID registers at device creation time, but we can't be certain that those register reads will succeed if the phy is not powered up. To avoid any problems with reading the ID registers before the phy is powered we fallback to DT matching when the ID reads fail. If the ULPI spec had some generic power sequencing for these registers we could put that into the ULPI bus layer and power up the device before reading the ID registers. Unfortunately this doesn't exist and the power sequence is usually device specific. By having the device matched up with DT we can avoid this problem. Cc: Greg Kroah-Hartman Acked-by: Heikki Krogerus Cc: Acked-by: Rob Herring Signed-off-by: Stephen Boyd Signed-off-by: Peter Chen --- Documentation/devicetree/bindings/usb/ulpi.txt | 20 +++++++ drivers/usb/common/ulpi.c | 79 ++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/usb/ulpi.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/ulpi.txt b/Documentation/devicetree/bindings/usb/ulpi.txt new file mode 100644 index 000000000000..ca179dc4bd50 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ulpi.txt @@ -0,0 +1,20 @@ +ULPI bus binding +---------------- + +Phys that are behind a ULPI connection can be described with the following +binding. The host controller shall have a "ulpi" named node as a child, and +that node shall have one enabled node underneath it representing the ulpi +device on the bus. + +EXAMPLE +------- + +usb { + compatible = "vendor,usb-controller"; + + ulpi { + phy { + compatible = "vendor,phy"; + }; + }; +}; diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 8b317702d761..c9480d77810c 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include /* -------------------------------------------------------------------------- */ @@ -39,6 +42,10 @@ static int ulpi_match(struct device *dev, struct device_driver *driver) struct ulpi *ulpi = to_ulpi_dev(dev); const struct ulpi_device_id *id; + /* Some ULPI devices don't have a vendor id so rely on OF match */ + if (ulpi->id.vendor == 0) + return of_driver_match_device(dev, driver); + for (id = drv->id_table; id->vendor; id++) if (id->vendor == ulpi->id.vendor && id->product == ulpi->id.product) @@ -50,6 +57,11 @@ static int ulpi_match(struct device *dev, struct device_driver *driver) static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) { struct ulpi *ulpi = to_ulpi_dev(dev); + int ret; + + ret = of_device_uevent_modalias(dev, env); + if (ret != -ENODEV) + return ret; if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product)) @@ -60,6 +72,11 @@ static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) static int ulpi_probe(struct device *dev) { struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + int ret; + + ret = of_clk_set_defaults(dev->of_node, false); + if (ret < 0) + return ret; return drv->probe(to_ulpi_dev(dev)); } @@ -87,8 +104,13 @@ static struct bus_type ulpi_bus = { static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { + int len; struct ulpi *ulpi = to_ulpi_dev(dev); + len = of_device_get_modalias(dev, buf, PAGE_SIZE - 1); + if (len != -ENODEV) + return len; + return sprintf(buf, "ulpi:v%04xp%04x\n", ulpi->id.vendor, ulpi->id.product); } @@ -153,23 +175,45 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_driver); /* -------------------------------------------------------------------------- */ -static int ulpi_register(struct device *dev, struct ulpi *ulpi) +static int ulpi_of_register(struct ulpi *ulpi) { - int ret; + struct device_node *np = NULL, *child; + struct device *parent; + + /* Find a ulpi bus underneath the parent or the grandparent */ + parent = ulpi->dev.parent; + if (parent->of_node) + np = of_find_node_by_name(parent->of_node, "ulpi"); + else if (parent->parent && parent->parent->of_node) + np = of_find_node_by_name(parent->parent->of_node, "ulpi"); + if (!np) + return 0; + + child = of_get_next_available_child(np, NULL); + of_node_put(np); + if (!child) + return -EINVAL; - ulpi->dev.parent = dev; /* needed early for ops */ + ulpi->dev.of_node = child; + + return 0; +} + +static int ulpi_read_id(struct ulpi *ulpi) +{ + int ret; /* Test the interface */ ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); if (ret < 0) - return ret; + goto err; ret = ulpi_read(ulpi, ULPI_SCRATCH); if (ret < 0) return ret; if (ret != 0xaa) - return -ENODEV; + goto err; ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW); ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8; @@ -177,13 +221,35 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; + /* Some ULPI devices don't have a vendor id so rely on OF match */ + if (ulpi->id.vendor == 0) + goto err; + + request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product); + return 0; +err: + of_device_request_module(&ulpi->dev); + return 0; +} + +static int ulpi_register(struct device *dev, struct ulpi *ulpi) +{ + int ret; + + ulpi->dev.parent = dev; /* needed early for ops */ ulpi->dev.bus = &ulpi_bus; ulpi->dev.type = &ulpi_dev_type; dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); - request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product); + ret = ulpi_of_register(ulpi); + if (ret) + return ret; + + ret = ulpi_read_id(ulpi); + if (ret) + return ret; ret = device_register(&ulpi->dev); if (ret) @@ -234,6 +300,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface); */ void ulpi_unregister_interface(struct ulpi *ulpi) { + of_node_put(ulpi->dev.of_node); device_unregister(&ulpi->dev); } EXPORT_SYMBOL_GPL(ulpi_unregister_interface); -- cgit v1.2.3 From 433def4da8a557aeb91ec8158c7724231639c4f9 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Wed, 11 Jan 2017 15:59:42 +0100 Subject: Documentation: dt: dwc3: add reference to the usb-xhci properties dwc3 internally creates a usb-xhci device which means that all properties documented in usb-xhci.txt are supported as well. Acked-by: Rob Herring Signed-off-by: Martin Blumenstingl Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc3.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index e3e6983288e3..f658f394c2d3 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -56,6 +56,10 @@ Optional properties: - tx-fifo-resize: determines if the FIFO *has* to be reallocated. + - in addition all properties from usb-xhci.txt from the current directory are + supported as well + + This is usually a subnode to DWC3 glue to which it is connected. dwc3@4a030000 { -- cgit v1.2.3 From 07423fd8e0e06f252d9126611b985bacde6878be Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Sun, 15 Jan 2017 13:14:28 +0100 Subject: Documentation: usb: fix wrong documentation paths Fixes wrong spelled "pinctrl-bindings.txt" and "qcom-dwc3-usb-phy.txt" file names as also wrong specified "mt8173-mtu3.txt" file name. Signed-off-by: Yegor Yefremov Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc3-st.txt | 4 ++-- Documentation/devicetree/bindings/usb/ehci-st.txt | 2 +- Documentation/devicetree/bindings/usb/mt8173-mtu3.txt | 2 +- Documentation/devicetree/bindings/usb/mt8173-xhci.txt | 4 ++-- Documentation/devicetree/bindings/usb/qcom,dwc3.txt | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/dwc3-st.txt b/Documentation/devicetree/bindings/usb/dwc3-st.txt index 01c71b1258f4..50dee3b44665 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-st.txt +++ b/Documentation/devicetree/bindings/usb/dwc3-st.txt @@ -20,10 +20,10 @@ See: Documentation/devicetree/bindings/reset/reset.txt with 'reg' property - pinctl-names : A pinctrl state named "default" must be defined -See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt +See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt - pinctrl-0 : Pin control group -See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt +See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt - ranges : allows valid 1:1 translation between child's address space and parent's address space diff --git a/Documentation/devicetree/bindings/usb/ehci-st.txt b/Documentation/devicetree/bindings/usb/ehci-st.txt index fb45fa5770bb..410d922cfdd7 100644 --- a/Documentation/devicetree/bindings/usb/ehci-st.txt +++ b/Documentation/devicetree/bindings/usb/ehci-st.txt @@ -7,7 +7,7 @@ Required properties: - interrupts : one EHCI interrupt should be described here - pinctrl-names : a pinctrl state named "default" must be defined - pinctrl-0 : phandle referencing pin configuration of the USB controller -See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt +See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt - clocks : phandle list of usb clocks - clock-names : should be "ic" for interconnect clock and "clk48" See: Documentation/devicetree/bindings/clock/clock-bindings.txt diff --git a/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt b/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt index e049d199bf0d..718386a63b5c 100644 --- a/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt +++ b/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt @@ -30,7 +30,7 @@ Optional properties: "id_float" and "id_ground" are optinal which depends on "mediatek,enable-manual-drd" - pinctrl-0 : pin control group - See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt + See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt - maximum-speed : valid arguments are "super-speed", "high-speed" and "full-speed"; refer to usb/generic.txt diff --git a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt index 2a930bd52b94..7ea39710621e 100644 --- a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt +++ b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt @@ -37,7 +37,7 @@ Optional properties: - usb3-lpm-capable : supports USB3.0 LPM - pinctrl-names : a pinctrl state named "default" must be defined - pinctrl-0 : pin control group - See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt + See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt Example: usb30: usb@11270000 { @@ -67,7 +67,7 @@ usb30: usb@11270000 { In the case, xhci is added as subnode to mtu3. An example and the DT binding details of mtu3 can be found in: -Documentation/devicetree/bindings/usb/mtu3.txt +Documentation/devicetree/bindings/usb/mt8173-mtu3.txt Required properties: - compatible : should contain "mediatek,mt8173-xhci" diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt index 39acb084bce9..73cc0963e823 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt @@ -18,7 +18,7 @@ A child node must exist to represent the core DWC3 IP block. The name of the node is not important. The content of the node is defined in dwc3.txt. Phy documentation is provided in the following places: -Documentation/devicetree/bindings/phy/qcom,dwc3-usb-phy.txt +Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt Example device nodes: -- cgit v1.2.3 From e92b9d449d0490800160bfeb5ee1175a02979f47 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 4 Jan 2017 10:19:23 +0800 Subject: usb: gadget: uac2: add req_number as parameter There are only two requests for uac2, it may not be enough at high loading system which usb interrupt handler can't be serviced on time, then the data will be lost since it is isoc transfer for audio. In this patch, we introduce a parameter for the number for usb request, and the user can override it if current number for request is not enough for his/her use case. Besides, update this parameter for legacy audio gadget and documentation. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 2 ++ drivers/usb/gadget/function/f_uac2.c | 39 +++++++++++++++++++++++++++--------- drivers/usb/gadget/function/u_uac2.h | 2 ++ drivers/usb/gadget/legacy/audio.c | 1 + 4 files changed, 34 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 581960574889..fb0cc4df1765 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -632,6 +632,8 @@ The uac2 function provides these attributes in its function directory: p_chmask - playback channel mask p_srate - playback sampling rate p_ssize - playback sample size (bytes) + req_number - the number of pre-allocated request for both capture + and playback The attributes have sane default values. diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 3f4e4785418f..f6a0d3a1311b 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -22,9 +22,6 @@ #include "u_uac2.h" -/* Keep everyone on toes */ -#define USB_XFERS 2 - /* * The driver implements a simple UAC_2 topology. * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture @@ -78,7 +75,7 @@ struct uac2_rtd_params { size_t period_size; unsigned max_psize; - struct uac2_req ureq[USB_XFERS]; + struct uac2_req *ureq; spinlock_t lock; }; @@ -269,6 +266,8 @@ static int uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct audio_dev *agdev = uac2_to_agdev(uac2); + struct f_uac2_opts *uac2_opts = agdev_to_uac2_opts(agdev); struct uac2_rtd_params *prm; unsigned long flags; int err = 0; @@ -300,7 +299,7 @@ uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd) /* Clear buffer after Play stops */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) - memset(prm->rbuf, 0, prm->max_psize * USB_XFERS); + memset(prm->rbuf, 0, prm->max_psize * uac2_opts->req_number); return err; } @@ -943,6 +942,8 @@ static inline void free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) { struct snd_uac2_chip *uac2 = prm->uac2; + struct audio_dev *agdev = uac2_to_agdev(uac2); + struct f_uac2_opts *uac2_opts = agdev_to_uac2_opts(agdev); int i; if (!prm->ep_enabled) @@ -950,7 +951,7 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) prm->ep_enabled = false; - for (i = 0; i < USB_XFERS; i++) { + for (i = 0; i < uac2_opts->req_number; i++) { if (prm->ureq[i].req) { usb_ep_dequeue(ep, prm->ureq[i].req); usb_ep_free_request(ep, prm->ureq[i].req); @@ -1095,7 +1096,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) prm = &agdev->uac2.c_prm; prm->max_psize = hs_epout_desc.wMaxPacketSize; - prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); + prm->ureq = kcalloc(uac2_opts->req_number, sizeof(struct uac2_req), + GFP_KERNEL); + if (!prm->ureq) { + ret = -ENOMEM; + goto err_free_descs; + } + prm->rbuf = kcalloc(uac2_opts->req_number, prm->max_psize, GFP_KERNEL); if (!prm->rbuf) { prm->max_psize = 0; ret = -ENOMEM; @@ -1104,7 +1111,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) prm = &agdev->uac2.p_prm; prm->max_psize = hs_epin_desc.wMaxPacketSize; - prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); + prm->ureq = kcalloc(uac2_opts->req_number, sizeof(struct uac2_req), + GFP_KERNEL); + if (!prm->ureq) { + ret = -ENOMEM; + goto err_free_descs; + } + prm->rbuf = kcalloc(uac2_opts->req_number, prm->max_psize, GFP_KERNEL); if (!prm->rbuf) { prm->max_psize = 0; ret = -ENOMEM; @@ -1117,6 +1130,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) return 0; err_no_memory: + kfree(agdev->uac2.p_prm.ureq); + kfree(agdev->uac2.c_prm.ureq); kfree(agdev->uac2.p_prm.rbuf); kfree(agdev->uac2.c_prm.rbuf); err_free_descs: @@ -1129,6 +1144,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) { struct usb_composite_dev *cdev = fn->config->cdev; struct audio_dev *agdev = func_to_agdev(fn); + struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev); struct snd_uac2_chip *uac2 = &agdev->uac2; struct usb_gadget *gadget = cdev->gadget; struct device *dev = &uac2->pdev.dev; @@ -1159,7 +1175,6 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) agdev->as_out_alt = alt; req_len = prm->max_psize; } else if (intf == agdev->as_in_intf) { - struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev); unsigned int factor, rate; struct usb_endpoint_descriptor *ep_desc; @@ -1205,7 +1220,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) prm->ep_enabled = true; usb_ep_enable(ep); - for (i = 0; i < USB_XFERS; i++) { + for (i = 0; i < opts->req_number; i++) { if (!prm->ureq[i].req) { req = usb_ep_alloc_request(ep, GFP_ATOMIC); if (req == NULL) @@ -1489,6 +1504,7 @@ UAC2_ATTRIBUTE(p_ssize); UAC2_ATTRIBUTE(c_chmask); UAC2_ATTRIBUTE(c_srate); UAC2_ATTRIBUTE(c_ssize); +UAC2_ATTRIBUTE(req_number); static struct configfs_attribute *f_uac2_attrs[] = { &f_uac2_opts_attr_p_chmask, @@ -1497,6 +1513,7 @@ static struct configfs_attribute *f_uac2_attrs[] = { &f_uac2_opts_attr_c_chmask, &f_uac2_opts_attr_c_srate, &f_uac2_opts_attr_c_ssize, + &f_uac2_opts_attr_req_number, NULL, }; @@ -1534,6 +1551,7 @@ static struct usb_function_instance *afunc_alloc_inst(void) opts->c_chmask = UAC2_DEF_CCHMASK; opts->c_srate = UAC2_DEF_CSRATE; opts->c_ssize = UAC2_DEF_CSSIZE; + opts->req_number = UAC2_DEF_REQ_NUM; return &opts->func_inst; } @@ -1562,6 +1580,7 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) prm = &agdev->uac2.c_prm; kfree(prm->rbuf); + kfree(prm->ureq); usb_free_all_descriptors(f); } diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index 78dd37279bd4..19eeb83538a5 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -24,6 +24,7 @@ #define UAC2_DEF_CCHMASK 0x3 #define UAC2_DEF_CSRATE 64000 #define UAC2_DEF_CSSIZE 2 +#define UAC2_DEF_REQ_NUM 2 struct f_uac2_opts { struct usb_function_instance func_inst; @@ -33,6 +34,7 @@ struct f_uac2_opts { int c_chmask; int c_srate; int c_ssize; + int req_number; bool bound; struct mutex lock; diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index 5d7b3c6a422b..8a39f42a4d56 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -229,6 +229,7 @@ static int audio_bind(struct usb_composite_dev *cdev) uac2_opts->c_chmask = c_chmask; uac2_opts->c_srate = c_srate; uac2_opts->c_ssize = c_ssize; + uac2_opts->req_number = UAC2_DEF_REQ_NUM; #else uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst); uac1_opts->fn_play = fn_play; -- cgit v1.2.3 From 21939f003ad09355d9c975735750bb22aa37d8de Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 Jan 2017 14:19:59 +0200 Subject: usb: host: xhci-plat: enable BROKEN_PED quirk if platform requested In case 'quirk-broken-port-ped' property is passed in via device property, we should enable the corresponding BROKEN_PED quirk flag for XHCI core. [rogerq@ti.com] Updated code from platform data to device property and added DT binding. Signed-off-by: Felipe Balbi Signed-off-by: Roger Quadros Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-xhci.txt | 1 + drivers/usb/host/xhci-plat.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt index 0b7d8576001c..2d80b60eeabe 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -27,6 +27,7 @@ Required properties: Optional properties: - clocks: reference to a clock - usb3-lpm-capable: determines if platform is USB3 LPM capable + - quirk-broken-port-ped: set if the controller has broken port disable mechanism Example: usb@f0931000 { diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index c0cd98e804a3..6d33b42ffcf5 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -232,6 +232,9 @@ static int xhci_plat_probe(struct platform_device *pdev) if (device_property_read_bool(&pdev->dev, "usb3-lpm-capable")) xhci->quirks |= XHCI_LPM_SUPPORT; + if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped")) + xhci->quirks |= XHCI_BROKEN_PORT_PED; + hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); if (IS_ERR(hcd->usb_phy)) { ret = PTR_ERR(hcd->usb_phy); -- cgit v1.2.3 From 605b8652f7f005452c89db1e6cef085479da2f16 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 25 Jan 2017 14:32:44 -0800 Subject: phy: Add support for Qualcomm's USB HSIC phy The HSIC USB controller on qcom SoCs has an integrated all digital phy controlled via the ULPI viewport. Cc: Kishon Vijay Abraham I Acked-by: Rob Herring Cc: Signed-off-by: Stephen Boyd Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/qcom,usb-hsic-phy.txt | 65 +++++++++ drivers/phy/Kconfig | 7 + drivers/phy/Makefile | 1 + drivers/phy/phy-qcom-usb-hsic.c | 160 +++++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt create mode 100644 drivers/phy/phy-qcom-usb-hsic.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt b/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt new file mode 100644 index 000000000000..3c7cb2be4b12 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,usb-hsic-phy.txt @@ -0,0 +1,65 @@ +Qualcomm's USB HSIC PHY + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: Should contain "qcom,usb-hsic-phy" and more specifically one of the + following: + + "qcom,usb-hsic-phy-mdm9615" + "qcom,usb-hsic-phy-msm8974" + +- #phy-cells: + Usage: required + Value type: + Definition: Should contain 0 + +- clocks: + Usage: required + Value type: + Definition: Should contain clock specifier for phy, calibration and + a calibration sleep clock + +- clock-names: + Usage: required + Value type: + Definition: Should contain "phy, "cal" and "cal_sleep" + +- pinctrl-names: + Usage: required + Value type: + Definition: Should contain "init" and "default" in that order + +- pinctrl-0: + Usage: required + Value type: + Definition: List of pinctrl settings to apply to keep HSIC pins in a glitch + free state + +- pinctrl-1: + Usage: required + Value type: + Definition: List of pinctrl settings to apply to mux out the HSIC pins + +EXAMPLE + +usb-controller { + ulpi { + phy { + compatible = "qcom,usb-hsic-phy-msm8974", + "qcom,usb-hsic-phy"; + #phy-cells = <0>; + pinctrl-names = "init", "default"; + pinctrl-0 = <&hsic_sleep>; + pinctrl-1 = <&hsic_default>; + clocks = <&gcc GCC_USB_HSIC_CLK>, + <&gcc GCC_USB_HSIC_IO_CAL_CLK>, + <&gcc GCC_USB_HSIC_IO_CAL_SLEEP_CLK>; + clock-names = "phy", "cal", "cal_sleep"; + assigned-clocks = <&gcc GCC_USB_HSIC_IO_CAL_CLK>; + assigned-clock-rates = <960000>; + }; + }; +}; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e8eb7f225a88..a430a64981d5 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -437,6 +437,13 @@ config PHY_QCOM_UFS help Support for UFS PHY on QCOM chipsets. +config PHY_QCOM_USB_HSIC + tristate "Qualcomm USB HSIC ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. + config PHY_TUSB1210 tristate "TI TUSB1210 ULPI PHY module" depends on USB_ULPI_BUS diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 65eb2f436a41..c43c9df5d301 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o +obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o diff --git a/drivers/phy/phy-qcom-usb-hsic.c b/drivers/phy/phy-qcom-usb-hsic.c new file mode 100644 index 000000000000..47690f9945b9 --- /dev/null +++ b/drivers/phy/phy-qcom-usb-hsic.c @@ -0,0 +1,160 @@ +/** + * Copyright (C) 2016 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "ulpi_phy.h" + +#define ULPI_HSIC_CFG 0x30 +#define ULPI_HSIC_IO_CAL 0x33 + +struct qcom_usb_hsic_phy { + struct ulpi *ulpi; + struct phy *phy; + struct pinctrl *pctl; + struct clk *phy_clk; + struct clk *cal_clk; + struct clk *cal_sleep_clk; +}; + +static int qcom_usb_hsic_phy_power_on(struct phy *phy) +{ + struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); + struct ulpi *ulpi = uphy->ulpi; + struct pinctrl_state *pins_default; + int ret; + + ret = clk_prepare_enable(uphy->phy_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(uphy->cal_clk); + if (ret) + goto err_cal; + + ret = clk_prepare_enable(uphy->cal_sleep_clk); + if (ret) + goto err_sleep; + + /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ + ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff); + if (ret) + goto err_ulpi; + + /* Enable periodic IO calibration in HSIC_CFG register */ + ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8); + if (ret) + goto err_ulpi; + + /* Configure pins for HSIC functionality */ + pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT); + if (IS_ERR(pins_default)) + return PTR_ERR(pins_default); + + ret = pinctrl_select_state(uphy->pctl, pins_default); + if (ret) + goto err_ulpi; + + /* Enable HSIC mode in HSIC_CFG register */ + ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01); + if (ret) + goto err_ulpi; + + /* Disable auto-resume */ + ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL), + ULPI_IFC_CTRL_AUTORESUME); + if (ret) + goto err_ulpi; + + return ret; +err_ulpi: + clk_disable_unprepare(uphy->cal_sleep_clk); +err_sleep: + clk_disable_unprepare(uphy->cal_clk); +err_cal: + clk_disable_unprepare(uphy->phy_clk); + return ret; +} + +static int qcom_usb_hsic_phy_power_off(struct phy *phy) +{ + struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); + + clk_disable_unprepare(uphy->cal_sleep_clk); + clk_disable_unprepare(uphy->cal_clk); + clk_disable_unprepare(uphy->phy_clk); + + return 0; +} + +static const struct phy_ops qcom_usb_hsic_phy_ops = { + .power_on = qcom_usb_hsic_phy_power_on, + .power_off = qcom_usb_hsic_phy_power_off, + .owner = THIS_MODULE, +}; + +static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi) +{ + struct qcom_usb_hsic_phy *uphy; + struct phy_provider *p; + struct clk *clk; + + uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); + if (!uphy) + return -ENOMEM; + ulpi_set_drvdata(ulpi, uphy); + + uphy->ulpi = ulpi; + uphy->pctl = devm_pinctrl_get(&ulpi->dev); + if (IS_ERR(uphy->pctl)) + return PTR_ERR(uphy->pctl); + + uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, + &qcom_usb_hsic_phy_ops); + if (IS_ERR(uphy->phy)) + return PTR_ERR(uphy->phy); + phy_set_drvdata(uphy->phy, uphy); + + p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(p); +} + +static const struct of_device_id qcom_usb_hsic_phy_match[] = { + { .compatible = "qcom,usb-hsic-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); + +static struct ulpi_driver qcom_usb_hsic_phy_driver = { + .probe = qcom_usb_hsic_phy_probe, + .driver = { + .name = "qcom_usb_hsic_phy", + .of_match_table = qcom_usb_hsic_phy_match, + }, +}; +module_ulpi_driver(qcom_usb_hsic_phy_driver); + +MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From e2427b09ba929c2b9d02556b74a85161a7364792 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 25 Jan 2017 14:32:45 -0800 Subject: phy: Add support for Qualcomm's USB HS phy The high-speed phy on qcom SoCs is controlled via the ULPI viewport. Cc: Kishon Vijay Abraham I Cc: Acked-by: Rob Herring Signed-off-by: Stephen Boyd Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/qcom,usb-hs-phy.txt | 84 ++++++ drivers/phy/Kconfig | 8 + drivers/phy/Makefile | 1 + drivers/phy/phy-qcom-usb-hs.c | 296 +++++++++++++++++++++ 4 files changed, 389 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt create mode 100644 drivers/phy/phy-qcom-usb-hs.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt new file mode 100644 index 000000000000..b3b75c1e6285 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,usb-hs-phy.txt @@ -0,0 +1,84 @@ +Qualcomm's USB HS PHY + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: Should contain "qcom,usb-hs-phy" and more specifically one of the + following: + + "qcom,usb-hs-phy-apq8064" + "qcom,usb-hs-phy-msm8916" + "qcom,usb-hs-phy-msm8974" + +- #phy-cells: + Usage: required + Value type: + Definition: Should contain 0 + +- clocks: + Usage: required + Value type: + Definition: Should contain clock specifier for the reference and sleep + clocks + +- clock-names: + Usage: required + Value type: + Definition: Should contain "ref" and "sleep" for the reference and sleep + clocks respectively + +- resets: + Usage: required + Value type: + Definition: Should contain the phy and POR resets + +- reset-names: + Usage: required + Value type: + Definition: Should contain "phy" and "por" for the phy and POR resets + respectively + +- v3p3-supply: + Usage: required + Value type: + Definition: Should contain a reference to the 3.3V supply + +- v1p8-supply: + Usage: required + Value type: + Definition: Should contain a reference to the 1.8V supply + +- extcon: + Usage: optional + Value type: + Definition: Should contain the vbus extcon + +- qcom,init-seq: + Usage: optional + Value type: + Definition: Should contain a sequence of ULPI address and value pairs to + program into the ULPI_EXT_VENDOR_SPECIFIC area. This is related + to Device Mode Eye Diagram test. The addresses are offsets + from the ULPI_EXT_VENDOR_SPECIFIC address, for example, + <0x1 0x53> would mean "write the value 0x53 to address 0x81". + +EXAMPLE + +otg: usb-controller { + ulpi { + phy { + compatible = "qcom,usb-hs-phy-msm8974", "qcom,usb-hs-phy"; + #phy-cells = <0>; + clocks = <&xo_board>, <&gcc GCC_USB2A_PHY_SLEEP_CLK>; + clock-names = "ref", "sleep"; + resets = <&gcc GCC_USB2A_PHY_BCR>, <&otg 0>; + reset-names = "phy", "por"; + v3p3-supply = <&pm8941_l24>; + v1p8-supply = <&pm8941_l6>; + extcon = <&smbb>; + qcom,init-seq = /bits/ 8 <0x1 0x63>; + }; + }; +}; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index a430a64981d5..61a22e985831 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -437,6 +437,14 @@ config PHY_QCOM_UFS help Support for UFS PHY on QCOM chipsets. +config PHY_QCOM_USB_HS + tristate "Qualcomm USB HS PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for the USB high-speed ULPI compliant phy on Qualcomm + chipsets. + config PHY_QCOM_USB_HSIC tristate "Qualcomm USB HSIC ULPI PHY module" depends on USB_ULPI_BUS diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c43c9df5d301..0e4259473d28 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o +obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o diff --git a/drivers/phy/phy-qcom-usb-hs.c b/drivers/phy/phy-qcom-usb-hs.c new file mode 100644 index 000000000000..94dfbfd739c3 --- /dev/null +++ b/drivers/phy/phy-qcom-usb-hs.c @@ -0,0 +1,296 @@ +/** + * Copyright (C) 2016 Linaro Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ulpi_phy.h" + +#define ULPI_PWR_CLK_MNG_REG 0x88 +# define ULPI_PWR_OTG_COMP_DISABLE BIT(0) + +#define ULPI_MISC_A 0x96 +# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) +# define ULPI_MISC_A_VBUSVLDEXT BIT(0) + + +struct ulpi_seq { + u8 addr; + u8 val; +}; + +struct qcom_usb_hs_phy { + struct ulpi *ulpi; + struct phy *phy; + struct clk *ref_clk; + struct clk *sleep_clk; + struct regulator *v1p8; + struct regulator *v3p3; + struct reset_control *reset; + struct ulpi_seq *init_seq; + struct extcon_dev *vbus_edev; + struct notifier_block vbus_notify; +}; + +static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + u8 addr; + int ret; + + if (!uphy->vbus_edev) { + u8 val = 0; + + switch (mode) { + case PHY_MODE_USB_OTG: + case PHY_MODE_USB_HOST: + val |= ULPI_INT_IDGRD; + case PHY_MODE_USB_DEVICE: + val |= ULPI_INT_SESS_VALID; + default: + break; + } + + ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val); + if (ret) + return ret; + ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val); + } else { + switch (mode) { + case PHY_MODE_USB_OTG: + case PHY_MODE_USB_DEVICE: + addr = ULPI_SET(ULPI_MISC_A); + break; + case PHY_MODE_USB_HOST: + addr = ULPI_CLR(ULPI_MISC_A); + break; + default: + return -EINVAL; + } + + ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG), + ULPI_PWR_OTG_COMP_DISABLE); + if (ret) + return ret; + ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL); + } + + return ret; +} + +static int +qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct qcom_usb_hs_phy *uphy; + u8 addr; + + uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify); + + if (event) + addr = ULPI_SET(ULPI_MISC_A); + else + addr = ULPI_CLR(ULPI_MISC_A); + + return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT); +} + +static int qcom_usb_hs_phy_power_on(struct phy *phy) +{ + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + struct ulpi *ulpi = uphy->ulpi; + const struct ulpi_seq *seq; + int ret, state; + + ret = clk_prepare_enable(uphy->ref_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(uphy->sleep_clk); + if (ret) + goto err_sleep; + + ret = regulator_set_load(uphy->v1p8, 50000); + if (ret < 0) + goto err_1p8; + + ret = regulator_enable(uphy->v1p8); + if (ret) + goto err_1p8; + + ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000, + 3300000); + if (ret) + goto err_3p3; + + ret = regulator_set_load(uphy->v3p3, 50000); + if (ret < 0) + goto err_3p3; + + ret = regulator_enable(uphy->v3p3); + if (ret) + goto err_3p3; + + for (seq = uphy->init_seq; seq->addr; seq++) { + ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr, + seq->val); + if (ret) + goto err_ulpi; + } + + if (uphy->reset) { + ret = reset_control_reset(uphy->reset); + if (ret) + goto err_ulpi; + } + + if (uphy->vbus_edev) { + state = extcon_get_cable_state_(uphy->vbus_edev, EXTCON_USB); + /* setup initial state */ + qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state, + uphy->vbus_edev); + ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB, + &uphy->vbus_notify); + if (ret) + goto err_ulpi; + } + + return 0; +err_ulpi: + regulator_disable(uphy->v3p3); +err_3p3: + regulator_disable(uphy->v1p8); +err_1p8: + clk_disable_unprepare(uphy->sleep_clk); +err_sleep: + clk_disable_unprepare(uphy->ref_clk); + return ret; +} + +static int qcom_usb_hs_phy_power_off(struct phy *phy) +{ + int ret; + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + + if (uphy->vbus_edev) { + ret = extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB, + &uphy->vbus_notify); + if (ret) + return ret; + } + + regulator_disable(uphy->v3p3); + regulator_disable(uphy->v1p8); + clk_disable_unprepare(uphy->sleep_clk); + clk_disable_unprepare(uphy->ref_clk); + + return 0; +} + +static const struct phy_ops qcom_usb_hs_phy_ops = { + .power_on = qcom_usb_hs_phy_power_on, + .power_off = qcom_usb_hs_phy_power_off, + .set_mode = qcom_usb_hs_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int qcom_usb_hs_phy_probe(struct ulpi *ulpi) +{ + struct qcom_usb_hs_phy *uphy; + struct phy_provider *p; + struct clk *clk; + struct regulator *reg; + struct reset_control *reset; + int size; + int ret; + + uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); + if (!uphy) + return -ENOMEM; + ulpi_set_drvdata(ulpi, uphy); + uphy->ulpi = ulpi; + + size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq"); + if (size < 0) + size = 0; + uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1, + sizeof(*uphy->init_seq), GFP_KERNEL); + if (!uphy->init_seq) + return -ENOMEM; + ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq", + (u8 *)uphy->init_seq, size); + if (ret && size) + return ret; + /* NUL terminate */ + uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0; + + uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por"); + if (IS_ERR(reset)) { + if (PTR_ERR(reset) == -EPROBE_DEFER) + return PTR_ERR(reset); + uphy->reset = NULL; + } + + uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, + &qcom_usb_hs_phy_ops); + if (IS_ERR(uphy->phy)) + return PTR_ERR(uphy->phy); + + uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0); + if (IS_ERR(uphy->vbus_edev)) { + if (PTR_ERR(uphy->vbus_edev) != -ENODEV) + return PTR_ERR(uphy->vbus_edev); + uphy->vbus_edev = NULL; + } + + uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier; + phy_set_drvdata(uphy->phy, uphy); + + p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(p); +} + +static const struct of_device_id qcom_usb_hs_phy_match[] = { + { .compatible = "qcom,usb-hs-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match); + +static struct ulpi_driver qcom_usb_hs_phy_driver = { + .probe = qcom_usb_hs_phy_probe, + .driver = { + .name = "qcom_usb_hs_phy", + .of_match_table = qcom_usb_hs_phy_match, + }, +}; +module_ulpi_driver(qcom_usb_hs_phy_driver); + +MODULE_DESCRIPTION("Qualcomm USB HS phy"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 96c163f108ef7c0102ff71a543f5d5f2ad5c60a7 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 22 Jan 2017 13:17:48 -0800 Subject: phy: qcom-ufs: Remove -always-on property The fact that a regulator is always-on is a property of the regulator, not a specific consumer. Implementing this in the driver leads to a system behaviour that is dependent on if the Qualcomm UFS PHY was ever (partially) probed. If the specific regulator should be always on in a particular device, mark it so by specifying "regulator-always-on" in the regulator node. Reviewed-by: Vivek Gautam Reviewed-by: Subhash Jadavani Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/ufs/ufs-qcom.txt | 1 - drivers/phy/phy-qcom-ufs-i.h | 1 - drivers/phy/phy-qcom-ufs.c | 5 +---- 3 files changed, 1 insertion(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/ufs/ufs-qcom.txt b/Documentation/devicetree/bindings/ufs/ufs-qcom.txt index b6b5130e5f65..1f69ee1a61ea 100644 --- a/Documentation/devicetree/bindings/ufs/ufs-qcom.txt +++ b/Documentation/devicetree/bindings/ufs/ufs-qcom.txt @@ -29,7 +29,6 @@ Optional properties: - vdda-pll-max-microamp : specifies max. load that can be drawn from pll supply - vddp-ref-clk-supply : phandle to UFS device ref_clk pad power supply - vddp-ref-clk-max-microamp : specifies max. load that can be drawn from this supply -- vddp-ref-clk-always-on : specifies if this supply needs to be kept always on Example: diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h index d505d98cf5f8..13b02b7de30b 100644 --- a/drivers/phy/phy-qcom-ufs-i.h +++ b/drivers/phy/phy-qcom-ufs-i.h @@ -77,7 +77,6 @@ struct ufs_qcom_phy_vreg { int min_uV; int max_uV; bool enabled; - bool is_always_on; }; struct ufs_qcom_phy { diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c index bbd317158084..c145fa6e824c 100644 --- a/drivers/phy/phy-qcom-ufs.c +++ b/drivers/phy/phy-qcom-ufs.c @@ -242,9 +242,6 @@ static int ufs_qcom_phy_init_vreg(struct device *dev, } err = 0; } - snprintf(prop_name, MAX_PROP_NAME, "%s-always-on", name); - vreg->is_always_on = of_property_read_bool(dev->of_node, - prop_name); } if (!strcmp(name, "vdda-pll")) { @@ -402,7 +399,7 @@ static int ufs_qcom_phy_disable_vreg(struct device *dev, { int ret = 0; - if (!vreg || !vreg->enabled || vreg->is_always_on) + if (!vreg || !vreg->enabled) goto out; ret = regulator_disable(vreg->reg); -- cgit v1.2.3 From c8ca631f9480cb4c6df78dd3d2eeeb5ac99ba4f3 Mon Sep 17 00:00:00 2001 From: Yendapally Reddy Dhananjaya Reddy Date: Tue, 17 Jan 2017 11:14:27 -0500 Subject: dt-bindings: phy: Add documentation for NSP USB3 PHY Add documentation for USB3 PHY available in Northstar plus SoC Signed-off-by: Yendapally Reddy Dhananjaya Reddy Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/brcm,nsp-usb3-phy.txt | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/brcm,nsp-usb3-phy.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/brcm,nsp-usb3-phy.txt b/Documentation/devicetree/bindings/phy/brcm,nsp-usb3-phy.txt new file mode 100644 index 000000000000..e68ae5dec9c9 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,nsp-usb3-phy.txt @@ -0,0 +1,39 @@ +Broadcom USB3 phy binding for northstar plus SoC +The USB3 phy is internal to the SoC and is accessed using mdio interface. + +Required mdio bus properties: +- reg: Should be 0x0 for SoC internal USB3 phy +- #address-cells: must be 1 +- #size-cells: must be 0 + +Required USB3 PHY properties: +- compatible: should be "brcm,nsp-usb3-phy" +- reg: USB3 Phy address on SoC internal MDIO bus and it should be 0x10. +- usb3-ctrl-syscon: handler of syscon node defining physical address + of usb3 control register. +- #phy-cells: must be 0 + +Required usb3 control properties: +- compatible: should be "brcm,nsp-usb3-ctrl" +- reg: offset and length of the control registers + +Example: + + mdio@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + + usb3_phy: usb-phy@10 { + compatible = "brcm,nsp-usb3-phy"; + reg = <0x10>; + usb3-ctrl-syscon = <&usb3_ctrl>; + #phy-cells = <0>; + status = "disabled"; + }; + }; + + usb3_ctrl: syscon@104408 { + compatible = "brcm,nsp-usb3-ctrl", "syscon"; + reg = <0x104408 0x3fc>; + }; -- cgit v1.2.3 From c1fce66ecd271dee5379f419a69b8ff5dae49ba1 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Wed, 1 Feb 2017 21:30:27 -0600 Subject: usb: musb: sunxi: add support for the variant in H3/V3s SoC Allwinner H3/V3s features a variant of MUSB controller, which lacks one endpoint. Add support for it. Signed-off-by: Icenowy Zheng Acked-by: Maxime Ripard [b-liu@ti.com: added usb: to commit subject prefix] Signed-off-by: Bin Liu Signed-off-by: Greg Kroah-Hartman --- .../bindings/usb/allwinner,sun4i-a10-musb.txt | 4 +-- drivers/usb/musb/sunxi.c | 35 ++++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt index 862cd7c79805..d9b42da016f3 100644 --- a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt +++ b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt @@ -2,8 +2,8 @@ Allwinner sun4i A10 musb DRC/OTG controller ------------------------------------------- Required properties: - - compatible : "allwinner,sun4i-a10-musb", "allwinner,sun6i-a31-musb" - or "allwinner,sun8i-a33-musb" + - compatible : "allwinner,sun4i-a10-musb", "allwinner,sun6i-a31-musb", + "allwinner,sun8i-a33-musb" or "allwinner,sun8i-h3-musb" - reg : mmio address range of the musb controller - clocks : clock specifier for the musb controller ahb gate clock - reset : reset specifier for the ahb reset (A31 and newer only) diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index 05cab67d4106..c9a09b5bb6e5 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -639,6 +639,20 @@ static struct musb_fifo_cfg sunxi_musb_mode_cfg[] = { MUSB_EP_FIFO_SINGLE(5, FIFO_RX, 512), }; +/* H3/V3s OTG supports only 4 endpoints */ +#define SUNXI_MUSB_MAX_EP_NUM_H3 5 + +static struct musb_fifo_cfg sunxi_musb_mode_cfg_h3[] = { + MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(2, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(3, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(3, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(4, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(4, FIFO_RX, 512), +}; + static const struct musb_hdrc_config sunxi_musb_hdrc_config = { .fifo_cfg = sunxi_musb_mode_cfg, .fifo_cfg_size = ARRAY_SIZE(sunxi_musb_mode_cfg), @@ -650,6 +664,18 @@ static const struct musb_hdrc_config sunxi_musb_hdrc_config = { .dma = 0, }; +static struct musb_hdrc_config sunxi_musb_hdrc_config_h3 = { + .fifo_cfg = sunxi_musb_mode_cfg_h3, + .fifo_cfg_size = ARRAY_SIZE(sunxi_musb_mode_cfg_h3), + .multipoint = true, + .dyn_fifo = true, + .soft_con = true, + .num_eps = SUNXI_MUSB_MAX_EP_NUM_H3, + .ram_bits = SUNXI_MUSB_RAM_BITS, + .dma = 0, +}; + + static int sunxi_musb_probe(struct platform_device *pdev) { struct musb_hdrc_platform_data pdata; @@ -692,7 +718,10 @@ static int sunxi_musb_probe(struct platform_device *pdev) return -EINVAL; } pdata.platform_ops = &sunxi_musb_ops; - pdata.config = &sunxi_musb_hdrc_config; + if (!of_device_is_compatible(np, "allwinner,sun8i-h3-musb")) + pdata.config = &sunxi_musb_hdrc_config; + else + pdata.config = &sunxi_musb_hdrc_config_h3; glue->dev = &pdev->dev; INIT_WORK(&glue->work, sunxi_musb_work); @@ -704,7 +733,8 @@ static int sunxi_musb_probe(struct platform_device *pdev) if (of_device_is_compatible(np, "allwinner,sun6i-a31-musb")) set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags); - if (of_device_is_compatible(np, "allwinner,sun8i-a33-musb")) { + if (of_device_is_compatible(np, "allwinner,sun8i-a33-musb") || + of_device_is_compatible(np, "allwinner,sun8i-h3-musb")) { set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags); set_bit(SUNXI_MUSB_FL_NO_CONFIGDATA, &glue->flags); } @@ -798,6 +828,7 @@ static const struct of_device_id sunxi_musb_match[] = { { .compatible = "allwinner,sun4i-a10-musb", }, { .compatible = "allwinner,sun6i-a31-musb", }, { .compatible = "allwinner,sun8i-a33-musb", }, + { .compatible = "allwinner,sun8i-h3-musb", }, {} }; MODULE_DEVICE_TABLE(of, sunxi_musb_match); -- cgit v1.2.3 From 3ec72a2a1e5d79f64bbe7b89e1064f851d2620e9 Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Fri, 10 Feb 2017 09:19:27 +0100 Subject: usb: misc: add USB251xB/xBi Hi-Speed Hub Controller Driver This patch adds a driver for configuration of the Microchip USB251xB/xBi USB 2.0 hub controller series with USB 2.0 upstream connectivity, SMBus configuration interface and two to four USB 2.0 downstream ports. Furthermore add myself as a maintainer for this driver. The datasheet can be found at the manufacturers website, see [1]. All device-tree exposed configuration features have been tested on a i.MX6 platform with a USB2512B hub. [1] http://ww1.microchip.com/downloads/en/DeviceDoc/00001692C.pdf Signed-off-by: Richard Leitner Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb251xb.txt | 83 +++ MAINTAINERS | 8 + drivers/usb/misc/Kconfig | 9 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/usb251xb.c | 605 +++++++++++++++++++++ 5 files changed, 706 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/usb251xb.txt create mode 100644 drivers/usb/misc/usb251xb.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/usb251xb.txt b/Documentation/devicetree/bindings/usb/usb251xb.txt new file mode 100644 index 000000000000..0c065f77658f --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb251xb.txt @@ -0,0 +1,83 @@ +Microchip USB 2.0 Hi-Speed Hub Controller + +The device node for the configuration of a Microchip USB251xB/xBi USB 2.0 +Hi-Speed Controller. + +Required properties : + - compatible : Should be "microchip,usb251xb" or one of the specific types: + "microchip,usb2512b", "microchip,usb2512bi", "microchip,usb2513b", + "microchip,usb2513bi", "microchip,usb2514b", "microchip,usb2514bi" + - hub-reset-gpios : Should specify the gpio for hub reset + +Optional properties : + - reg : I2C address on the selected bus (default is <0x2C>) + - skip-config : Skip Hub configuration, but only send the USB-Attach command + - vendor-id : USB Vendor ID of the hub (16 bit, default is 0x0424) + - product-id : USB Product ID of the hub (16 bit, default depends on type) + - device-id : USB Device ID of the hub (16 bit, default is 0x0bb3) + - language-id : USB Language ID (16 bit, default is 0x0000) + - manufacturer : USB Manufacturer string (max 31 characters long) + - product : USB Product string (max 31 characters long) + - serial : USB Serial string (max 31 characters long) + - {bus,self}-powered : selects between self- and bus-powered operation (default + is self-powered) + - disable-hi-speed : disable USB Hi-Speed support + - {multi,single}-tt : selects between multi- and single-transaction-translator + (default is multi-tt) + - disable-eop : disable End of Packet generation in full-speed mode + - {ganged,individual}-sensing : select over-current sense type in self-powered + mode (default is individual) + - {ganged,individual}-port-switching : select port power switching mode + (default is individual) + - dynamic-power-switching : enable auto-switching from self- to bus-powered + operation if the local power source is removed or unavailable + - oc-delay-{100us,4ms,8ms,16ms} : set over current timer delay (default is 8ms) + - compound-device : indicated the hub is part of a compound device + - port-mapping-mode : enable port mapping mode + - string-support : enable string descriptor support (required for manufacturer, + product and serial string configuration) + - non-removable-ports : Should specify the ports which have a non-removable + device connected. + - sp-disabled-ports : Specifies the ports which will be self-power disabled + - bp-disabled-ports : Specifies the ports which will be bus-power disabled + - max-sp-power : Specifies the maximum current the hub consumes from an + upstream port when operating as self-powered hub including the power + consumption of a permanently attached peripheral if the hub is + configured as a compound device. The value is given in mA in a 0 - 500 + range (default is 2). + - max-bp-power : Specifies the maximum current the hub consumes from an + upstream port when operating as bus-powered hub including the power + consumption of a permanently attached peripheral if the hub is + configured as a compound device. The value is given in mA in a 0 - 500 + range (default is 100). + - max-sp-current : Specifies the maximum current the hub consumes from an + upstream port when operating as self-powered hub EXCLUDING the power + consumption of a permanently attached peripheral if the hub is + configured as a compound device. The value is given in mA in a 0 - 500 + range (default is 2). + - max-bp-current : Specifies the maximum current the hub consumes from an + upstream port when operating as bus-powered hub EXCLUDING the power + consumption of a permanently attached peripheral if the hub is + configured as a compound device. The value is given in mA in a 0 - 500 + range (default is 100). + - power-on-time : Specifies the time it takes from the time the host initiates + the power-on sequence to a port until the port has adequate power. The + value is given in ms in a 0 - 510 range (default is 100ms). + +Examples: + usb2512b@2c { + compatible = "microchip,usb2512b"; + hub-reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; + }; + + usb2514b@2c { + compatible = "microchip,usb2514b"; + reg = <0x2c>; + reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; + vendor-id = /bits/ 16 <0x0000>; + product-id = /bits/ 16 <0x0000>; + string-support; + manufacturer = "Foo"; + product = "Foo-Bar"; + serial = "1234567890A"; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 187b9615e31a..cd705bfe0baf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8201,6 +8201,14 @@ F: drivers/media/platform/atmel/atmel-isc.c F: drivers/media/platform/atmel/atmel-isc-regs.h F: devicetree/bindings/media/atmel-isc.txt +MICROCHIP USB251XB DRIVER +M: Richard Leitner +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/usb/misc/usb251xb.c +F: include/linux/platform_data/usb251xb.h +F: Documentation/devicetree/bindings/usb/usb251xb.txt + MICROSOFT SURFACE PRO 3 BUTTON DRIVER M: Chen Yu L: platform-driver-x86@vger.kernel.org diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 47b357760afc..1d1d70d62a19 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -233,6 +233,15 @@ config USB_EZUSB_FX2 Say Y here if you need EZUSB device support. (Cypress FX/FX2/FX2LP microcontrollers) +config USB_HUB_USB251XB + tristate "USB251XB Hub Controller Configuration Driver" + depends on I2C + help + This option enables support for configuration via SMBus of the + Microchip USB251xB/xBi USB 2.0 Hub Controller series. + Configuration parameters may be set in devicetree or platform data. + Say Y or M here if you need to configure such a device via SMBus. + config USB_HSIC_USB3503 tristate "USB3503 HSIC to USB20 Driver" depends on I2C diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 3d1992750da4..f6ac6c99a6e6 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o obj-$(CONFIG_USB_YUREX) += yurex.o +obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c new file mode 100644 index 000000000000..4e18600dc9b4 --- /dev/null +++ b/drivers/usb/misc/usb251xb.c @@ -0,0 +1,605 @@ +/* + * Driver for Microchip USB251xB USB 2.0 Hi-Speed Hub Controller + * Configuration via SMBus. + * + * Copyright (c) 2017 SKIDATA AG + * + * This work is based on the USB3503 driver by Dongjin Kim and + * a not-accepted patch by Fabien Lahoudere, see: + * https://patchwork.kernel.org/patch/9257715/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Internal Register Set Addresses & Default Values acc. to DS00001692C */ +#define USB251XB_ADDR_VENDOR_ID_LSB 0x00 +#define USB251XB_ADDR_VENDOR_ID_MSB 0x01 +#define USB251XB_DEF_VENDOR_ID 0x0424 + +#define USB251XB_ADDR_PRODUCT_ID_LSB 0x02 +#define USB251XB_ADDR_PRODUCT_ID_MSB 0x03 +#define USB251XB_DEF_PRODUCT_ID_12 0x2512 /* USB2512B/12Bi */ +#define USB251XB_DEF_PRODUCT_ID_13 0x2513 /* USB2513B/13Bi */ +#define USB251XB_DEF_PRODUCT_ID_14 0x2514 /* USB2514B/14Bi */ + +#define USB251XB_ADDR_DEVICE_ID_LSB 0x04 +#define USB251XB_ADDR_DEVICE_ID_MSB 0x05 +#define USB251XB_DEF_DEVICE_ID 0x0BB3 + +#define USB251XB_ADDR_CONFIG_DATA_1 0x06 +#define USB251XB_DEF_CONFIG_DATA_1 0x9B +#define USB251XB_ADDR_CONFIG_DATA_2 0x07 +#define USB251XB_DEF_CONFIG_DATA_2 0x20 +#define USB251XB_ADDR_CONFIG_DATA_3 0x08 +#define USB251XB_DEF_CONFIG_DATA_3 0x02 + +#define USB251XB_ADDR_NON_REMOVABLE_DEVICES 0x09 +#define USB251XB_DEF_NON_REMOVABLE_DEVICES 0x00 + +#define USB251XB_ADDR_PORT_DISABLE_SELF 0x0A +#define USB251XB_DEF_PORT_DISABLE_SELF 0x00 +#define USB251XB_ADDR_PORT_DISABLE_BUS 0x0B +#define USB251XB_DEF_PORT_DISABLE_BUS 0x00 + +#define USB251XB_ADDR_MAX_POWER_SELF 0x0C +#define USB251XB_DEF_MAX_POWER_SELF 0x01 +#define USB251XB_ADDR_MAX_POWER_BUS 0x0D +#define USB251XB_DEF_MAX_POWER_BUS 0x32 + +#define USB251XB_ADDR_MAX_CURRENT_SELF 0x0E +#define USB251XB_DEF_MAX_CURRENT_SELF 0x01 +#define USB251XB_ADDR_MAX_CURRENT_BUS 0x0F +#define USB251XB_DEF_MAX_CURRENT_BUS 0x32 + +#define USB251XB_ADDR_POWER_ON_TIME 0x10 +#define USB251XB_DEF_POWER_ON_TIME 0x32 + +#define USB251XB_ADDR_LANGUAGE_ID_HIGH 0x11 +#define USB251XB_ADDR_LANGUAGE_ID_LOW 0x12 +#define USB251XB_DEF_LANGUAGE_ID 0x0000 + +#define USB251XB_STRING_BUFSIZE 62 +#define USB251XB_ADDR_MANUFACTURER_STRING_LEN 0x13 +#define USB251XB_ADDR_MANUFACTURER_STRING 0x16 +#define USB251XB_DEF_MANUFACTURER_STRING "Microchip" + +#define USB251XB_ADDR_PRODUCT_STRING_LEN 0x14 +#define USB251XB_ADDR_PRODUCT_STRING 0x54 +#define USB251XB_DEF_PRODUCT_STRING "USB251xB/xBi" + +#define USB251XB_ADDR_SERIAL_STRING_LEN 0x15 +#define USB251XB_ADDR_SERIAL_STRING 0x92 +#define USB251XB_DEF_SERIAL_STRING "" + +#define USB251XB_ADDR_BATTERY_CHARGING_ENABLE 0xD0 +#define USB251XB_DEF_BATTERY_CHARGING_ENABLE 0x00 + +#define USB251XB_ADDR_BOOST_UP 0xF6 +#define USB251XB_DEF_BOOST_UP 0x00 +#define USB251XB_ADDR_BOOST_X 0xF8 +#define USB251XB_DEF_BOOST_X 0x00 + +#define USB251XB_ADDR_PORT_SWAP 0xFA +#define USB251XB_DEF_PORT_SWAP 0x00 + +#define USB251XB_ADDR_PORT_MAP_12 0xFB +#define USB251XB_DEF_PORT_MAP_12 0x00 +#define USB251XB_ADDR_PORT_MAP_34 0xFC +#define USB251XB_DEF_PORT_MAP_34 0x00 /* USB2513B/i & USB2514B/i only */ + +#define USB251XB_ADDR_STATUS_COMMAND 0xFF +#define USB251XB_STATUS_COMMAND_SMBUS_DOWN 0x04 +#define USB251XB_STATUS_COMMAND_RESET 0x02 +#define USB251XB_STATUS_COMMAND_ATTACH 0x01 + +#define USB251XB_I2C_REG_SZ 0x100 +#define USB251XB_I2C_WRITE_SZ 0x10 + +#define DRIVER_NAME "usb251xb" +#define DRIVER_DESC "Microchip USB 2.0 Hi-Speed Hub Controller" +#define DRIVER_VERSION "1.0" + +struct usb251xb { + struct device *dev; + struct i2c_client *i2c; + u8 skip_config; + int gpio_reset; + u16 vendor_id; + u16 product_id; + u16 device_id; + u8 conf_data1; + u8 conf_data2; + u8 conf_data3; + u8 non_rem_dev; + u8 port_disable_sp; + u8 port_disable_bp; + u8 max_power_sp; + u8 max_power_bp; + u8 max_current_sp; + u8 max_current_bp; + u8 power_on_time; + u16 lang_id; + u8 manufacturer_len; + u8 product_len; + u8 serial_len; + char manufacturer[USB251XB_STRING_BUFSIZE]; + char product[USB251XB_STRING_BUFSIZE]; + char serial[USB251XB_STRING_BUFSIZE]; + u8 bat_charge_en; + u8 boost_up; + u8 boost_x; + u8 port_swap; + u8 port_map12; + u8 port_map34; + u8 status; +}; + +struct usb251xb_data { + u16 product_id; + char product_str[USB251XB_STRING_BUFSIZE / 2]; /* ASCII string */ +}; + +static const struct usb251xb_data usb2512b_data = { + .product_id = 0x2512, + .product_str = "USB2512B", +}; + +static const struct usb251xb_data usb2512bi_data = { + .product_id = 0x2512, + .product_str = "USB2512Bi", +}; + +static const struct usb251xb_data usb2513b_data = { + .product_id = 0x2513, + .product_str = "USB2513B", +}; + +static const struct usb251xb_data usb2513bi_data = { + .product_id = 0x2513, + .product_str = "USB2513Bi", +}; + +static const struct usb251xb_data usb2514b_data = { + .product_id = 0x2514, + .product_str = "USB2514B", +}; + +static const struct usb251xb_data usb2514bi_data = { + .product_id = 0x2514, + .product_str = "USB2514Bi", +}; + +static void usb251xb_reset(struct usb251xb *hub, int state) +{ + if (!gpio_is_valid(hub->gpio_reset)) + return; + + gpio_set_value_cansleep(hub->gpio_reset, state); + + /* wait for hub recovery/stabilization */ + if (state) + usleep_range(500, 750); /* >=500us at power on */ + else + usleep_range(1, 10); /* >=1us at power down */ +} + +static int usb251xb_connect(struct usb251xb *hub) +{ + struct device *dev = hub->dev; + int err, i; + char i2c_wb[USB251XB_I2C_REG_SZ]; + + memset(i2c_wb, 0, USB251XB_I2C_REG_SZ); + + if (hub->skip_config) { + dev_info(dev, "Skip hub configuration, only attach.\n"); + i2c_wb[0] = 0x01; + i2c_wb[1] = USB251XB_STATUS_COMMAND_ATTACH; + + usb251xb_reset(hub, 1); + + err = i2c_smbus_write_i2c_block_data(hub->i2c, + USB251XB_ADDR_STATUS_COMMAND, 2, i2c_wb); + if (err) { + dev_err(dev, "attaching hub failed: %d\n", err); + return err; + } + return 0; + } + + i2c_wb[USB251XB_ADDR_VENDOR_ID_MSB] = (hub->vendor_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_VENDOR_ID_LSB] = hub->vendor_id & 0xFF; + i2c_wb[USB251XB_ADDR_PRODUCT_ID_MSB] = (hub->product_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_PRODUCT_ID_LSB] = hub->product_id & 0xFF; + i2c_wb[USB251XB_ADDR_DEVICE_ID_MSB] = (hub->device_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_DEVICE_ID_LSB] = hub->device_id & 0xFF; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_1] = hub->conf_data1; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_2] = hub->conf_data2; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_3] = hub->conf_data3; + i2c_wb[USB251XB_ADDR_NON_REMOVABLE_DEVICES] = hub->non_rem_dev; + i2c_wb[USB251XB_ADDR_PORT_DISABLE_SELF] = hub->port_disable_sp; + i2c_wb[USB251XB_ADDR_PORT_DISABLE_BUS] = hub->port_disable_bp; + i2c_wb[USB251XB_ADDR_MAX_POWER_SELF] = hub->max_power_sp; + i2c_wb[USB251XB_ADDR_MAX_POWER_BUS] = hub->max_power_bp; + i2c_wb[USB251XB_ADDR_MAX_CURRENT_SELF] = hub->max_current_sp; + i2c_wb[USB251XB_ADDR_MAX_CURRENT_BUS] = hub->max_current_bp; + i2c_wb[USB251XB_ADDR_POWER_ON_TIME] = hub->power_on_time; + i2c_wb[USB251XB_ADDR_LANGUAGE_ID_HIGH] = (hub->lang_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_LANGUAGE_ID_LOW] = hub->lang_id & 0xFF; + i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING_LEN] = hub->manufacturer_len; + i2c_wb[USB251XB_ADDR_PRODUCT_STRING_LEN] = hub->product_len; + i2c_wb[USB251XB_ADDR_SERIAL_STRING_LEN] = hub->serial_len; + memcpy(&i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING], hub->manufacturer, + USB251XB_STRING_BUFSIZE); + memcpy(&i2c_wb[USB251XB_ADDR_SERIAL_STRING], hub->serial, + USB251XB_STRING_BUFSIZE); + memcpy(&i2c_wb[USB251XB_ADDR_PRODUCT_STRING], hub->product, + USB251XB_STRING_BUFSIZE); + i2c_wb[USB251XB_ADDR_BATTERY_CHARGING_ENABLE] = hub->bat_charge_en; + i2c_wb[USB251XB_ADDR_BOOST_UP] = hub->boost_up; + i2c_wb[USB251XB_ADDR_BOOST_X] = hub->boost_x; + i2c_wb[USB251XB_ADDR_PORT_SWAP] = hub->port_swap; + i2c_wb[USB251XB_ADDR_PORT_MAP_12] = hub->port_map12; + i2c_wb[USB251XB_ADDR_PORT_MAP_34] = hub->port_map34; + i2c_wb[USB251XB_ADDR_STATUS_COMMAND] = USB251XB_STATUS_COMMAND_ATTACH; + + usb251xb_reset(hub, 1); + + /* write registers */ + for (i = 0; i < (USB251XB_I2C_REG_SZ / USB251XB_I2C_WRITE_SZ); i++) { + int offset = i * USB251XB_I2C_WRITE_SZ; + char wbuf[USB251XB_I2C_WRITE_SZ + 1]; + + /* The first data byte transferred tells the hub how many data + * bytes will follow (byte count). + */ + wbuf[0] = USB251XB_I2C_WRITE_SZ; + memcpy(&wbuf[1], &i2c_wb[offset], USB251XB_I2C_WRITE_SZ); + + dev_dbg(dev, "writing %d byte block %d to 0x%02X\n", + USB251XB_I2C_WRITE_SZ, i, offset); + + err = i2c_smbus_write_i2c_block_data(hub->i2c, offset, + USB251XB_I2C_WRITE_SZ + 1, + wbuf); + if (err) + goto out_err; + } + + dev_info(dev, "Hub configuration was successful.\n"); + return 0; + +out_err: + dev_err(dev, "configuring block %d failed: %d\n", i, err); + return err; +} + +#ifdef CONFIG_OF +static int usb251xb_get_ofdata(struct usb251xb *hub, + struct usb251xb_data *data) +{ + struct device *dev = hub->dev; + struct device_node *np = dev->of_node; + int len, err, i; + u32 *property_u32 = NULL; + const u32 *cproperty_u32; + const char *cproperty_char; + char str[USB251XB_STRING_BUFSIZE / 2]; + + if (!np) { + dev_err(dev, "failed to get ofdata\n"); + return -ENODEV; + } + + if (of_get_property(np, "skip-config", NULL)) + hub->skip_config = 1; + else + hub->skip_config = 0; + + hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0); + if (hub->gpio_reset == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(hub->gpio_reset)) { + err = devm_gpio_request_one(dev, hub->gpio_reset, + GPIOF_OUT_INIT_LOW, + "usb251xb reset"); + if (err) { + dev_err(dev, + "unable to request GPIO %d as reset pin (%d)\n", + hub->gpio_reset, err); + return err; + } + } + + if (of_property_read_u16_array(np, "vendor-id", &hub->vendor_id, 1)) + hub->vendor_id = USB251XB_DEF_VENDOR_ID; + + if (of_property_read_u16_array(np, "product-id", + &hub->product_id, 1)) + hub->product_id = data->product_id; + + if (of_property_read_u16_array(np, "device-id", &hub->device_id, 1)) + hub->device_id = USB251XB_DEF_DEVICE_ID; + + hub->conf_data1 = USB251XB_DEF_CONFIG_DATA_1; + if (of_get_property(np, "self-powered", NULL)) { + hub->conf_data1 |= BIT(7); + + /* Configure Over-Current sens when self-powered */ + hub->conf_data1 &= ~BIT(2); + if (of_get_property(np, "ganged-sensing", NULL)) + hub->conf_data1 &= ~BIT(1); + else if (of_get_property(np, "individual-sensing", NULL)) + hub->conf_data1 |= BIT(1); + } else if (of_get_property(np, "bus-powered", NULL)) { + hub->conf_data1 &= ~BIT(7); + + /* Disable Over-Current sense when bus-powered */ + hub->conf_data1 |= BIT(2); + } + + if (of_get_property(np, "disable-hi-speed", NULL)) + hub->conf_data1 |= BIT(5); + + if (of_get_property(np, "multi-tt", NULL)) + hub->conf_data1 |= BIT(4); + else if (of_get_property(np, "single-tt", NULL)) + hub->conf_data1 &= ~BIT(4); + + if (of_get_property(np, "disable-eop", NULL)) + hub->conf_data1 |= BIT(3); + + if (of_get_property(np, "individual-port-switching", NULL)) + hub->conf_data1 |= BIT(0); + else if (of_get_property(np, "ganged-port-switching", NULL)) + hub->conf_data1 &= ~BIT(0); + + hub->conf_data2 = USB251XB_DEF_CONFIG_DATA_2; + if (of_get_property(np, "dynamic-power-switching", NULL)) + hub->conf_data2 |= BIT(7); + + if (of_get_property(np, "oc-delay-100us", NULL)) { + hub->conf_data2 &= ~BIT(5); + hub->conf_data2 &= ~BIT(4); + } else if (of_get_property(np, "oc-delay-4ms", NULL)) { + hub->conf_data2 &= ~BIT(5); + hub->conf_data2 |= BIT(4); + } else if (of_get_property(np, "oc-delay-8ms", NULL)) { + hub->conf_data2 |= BIT(5); + hub->conf_data2 &= ~BIT(4); + } else if (of_get_property(np, "oc-delay-16ms", NULL)) { + hub->conf_data2 |= BIT(5); + hub->conf_data2 |= BIT(4); + } + + if (of_get_property(np, "compound-device", NULL)) + hub->conf_data2 |= BIT(3); + + hub->conf_data3 = USB251XB_DEF_CONFIG_DATA_3; + if (of_get_property(np, "port-mapping-mode", NULL)) + hub->conf_data3 |= BIT(3); + + if (of_get_property(np, "string-support", NULL)) + hub->conf_data3 |= BIT(0); + + hub->non_rem_dev = USB251XB_DEF_NON_REMOVABLE_DEVICES; + cproperty_u32 = of_get_property(np, "non-removable-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= 4)) + hub->non_rem_dev |= BIT(port); + } + } + + hub->port_disable_sp = USB251XB_DEF_PORT_DISABLE_SELF; + cproperty_u32 = of_get_property(np, "sp-disabled-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= 4)) + hub->port_disable_sp |= BIT(port); + } + } + + hub->port_disable_bp = USB251XB_DEF_PORT_DISABLE_BUS; + cproperty_u32 = of_get_property(np, "bp-disabled-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= 4)) + hub->port_disable_bp |= BIT(port); + } + } + + hub->max_power_sp = USB251XB_DEF_MAX_POWER_SELF; + if (!of_property_read_u32(np, "max-sp-power", property_u32)) + hub->max_power_sp = min_t(u8, be32_to_cpu(*property_u32) / 2, + 250); + + hub->max_power_bp = USB251XB_DEF_MAX_POWER_BUS; + if (!of_property_read_u32(np, "max-bp-power", property_u32)) + hub->max_power_bp = min_t(u8, be32_to_cpu(*property_u32) / 2, + 250); + + hub->max_current_sp = USB251XB_DEF_MAX_CURRENT_SELF; + if (!of_property_read_u32(np, "max-sp-current", property_u32)) + hub->max_current_sp = min_t(u8, be32_to_cpu(*property_u32) / 2, + 250); + + hub->max_current_bp = USB251XB_DEF_MAX_CURRENT_BUS; + if (!of_property_read_u32(np, "max-bp-current", property_u32)) + hub->max_current_bp = min_t(u8, be32_to_cpu(*property_u32) / 2, + 250); + + hub->power_on_time = USB251XB_DEF_POWER_ON_TIME; + if (!of_property_read_u32(np, "power-on-time", property_u32)) + hub->power_on_time = min_t(u8, be32_to_cpu(*property_u32) / 2, + 255); + + if (of_property_read_u16_array(np, "language-id", &hub->lang_id, 1)) + hub->lang_id = USB251XB_DEF_LANGUAGE_ID; + + cproperty_char = of_get_property(np, "manufacturer", NULL); + strlcpy(str, cproperty_char ? : USB251XB_DEF_MANUFACTURER_STRING, + sizeof(str)); + hub->manufacturer_len = strlen(str) & 0xFF; + memset(hub->manufacturer, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->manufacturer, + USB251XB_STRING_BUFSIZE); + + cproperty_char = of_get_property(np, "product", NULL); + strlcpy(str, cproperty_char ? : data->product_str, sizeof(str)); + hub->product_len = strlen(str) & 0xFF; + memset(hub->product, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->product, + USB251XB_STRING_BUFSIZE); + + cproperty_char = of_get_property(np, "serial", NULL); + strlcpy(str, cproperty_char ? : USB251XB_DEF_SERIAL_STRING, + sizeof(str)); + hub->serial_len = strlen(str) & 0xFF; + memset(hub->serial, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->serial, + USB251XB_STRING_BUFSIZE); + + /* The following parameters are currently not exposed to devicetree, but + * may be as soon as needed. + */ + hub->bat_charge_en = USB251XB_DEF_BATTERY_CHARGING_ENABLE; + hub->boost_up = USB251XB_DEF_BOOST_UP; + hub->boost_x = USB251XB_DEF_BOOST_X; + hub->port_swap = USB251XB_DEF_PORT_SWAP; + hub->port_map12 = USB251XB_DEF_PORT_MAP_12; + hub->port_map34 = USB251XB_DEF_PORT_MAP_34; + + return 0; +} + +static const struct of_device_id usb251xb_of_match[] = { + { + .compatible = "microchip,usb2512b", + .data = &usb2512b_data, + }, { + .compatible = "microchip,usb2512bi", + .data = &usb2512bi_data, + }, { + .compatible = "microchip,usb2513b", + .data = &usb2513b_data, + }, { + .compatible = "microchip,usb2513bi", + .data = &usb2513bi_data, + }, { + .compatible = "microchip,usb2514b", + .data = &usb2514b_data, + }, { + .compatible = "microchip,usb2514bi", + .data = &usb2514bi_data, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, usb251xb_of_match); +#else /* CONFIG_OF */ +static int usb251xb_get_ofdata(struct usb251xb *hub, + struct usb251xb_data *data) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int usb251xb_probe(struct usb251xb *hub) +{ + struct device *dev = hub->dev; + struct device_node *np = dev->of_node; + const struct of_device_id *of_id = of_match_device(usb251xb_of_match, + dev); + int err; + + if (np) { + err = usb251xb_get_ofdata(hub, + (struct usb251xb_data *)of_id->data); + if (err) { + dev_err(dev, "failed to get ofdata: %d\n", err); + return err; + } + } + + err = usb251xb_connect(hub); + if (err) { + dev_err(dev, "Failed to connect hub (%d)\n", err); + return err; + } + + dev_info(dev, "Hub probed successfully\n"); + + return 0; +} + +static int usb251xb_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct usb251xb *hub; + + hub = devm_kzalloc(&i2c->dev, sizeof(struct usb251xb), GFP_KERNEL); + if (!hub) + return -ENOMEM; + + i2c_set_clientdata(i2c, hub); + hub->dev = &i2c->dev; + hub->i2c = i2c; + + return usb251xb_probe(hub); +} + +static const struct i2c_device_id usb251xb_id[] = { + { "usb2512b", 0 }, + { "usb2512bi", 0 }, + { "usb2513b", 0 }, + { "usb2513bi", 0 }, + { "usb2514b", 0 }, + { "usb2514bi", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, usb251xb_id); + +static struct i2c_driver usb251xb_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(usb251xb_of_match), + }, + .probe = usb251xb_i2c_probe, + .id_table = usb251xb_id, +}; + +module_i2c_driver(usb251xb_i2c_driver); + +MODULE_AUTHOR("Richard Leitner "); +MODULE_DESCRIPTION("USB251xB/xBi USB 2.0 Hub Controller Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3