diff options
Diffstat (limited to 'drivers/usb/phy')
27 files changed, 215 insertions, 2685 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index aff702c0eb9f..0f8ab981d572 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -137,35 +137,6 @@ config USB_ISP1301 To compile this driver as a module, choose M here: the module will be called phy-isp1301. -config USB_MSM_OTG - tristate "Qualcomm on-chip USB OTG controller support" - depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) - depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' - depends on RESET_CONTROLLER - select USB_PHY - help - Enable this to support the USB OTG transceiver on Qualcomm chips. It - handles PHY initialization, clock management, and workarounds - required after resetting the hardware and power management. - This driver is required even for peripheral only or host only - mode configurations. - This driver is not supported on boards like trout which - has an external PHY. - -config USB_QCOM_8X16_PHY - tristate "Qualcomm APQ8016/MSM8916 on-chip USB PHY controller support" - depends on ARCH_QCOM || COMPILE_TEST - depends on RESET_CONTROLLER - select USB_PHY - select USB_ULPI_VIEWPORT - help - Enable this to support the USB transceiver on Qualcomm 8x16 chipsets. - It handles PHY initialization, clock management, power management, - and workarounds required after resetting the hardware. - - To compile this driver as a module, choose M here: the - module will be called phy-qcom-8x16-usb. - config USB_MV_OTG tristate "Marvell USB OTG support" depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index e7c9ca8cafb0..25e579fb92b8 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Makefile for physical layer USB drivers # @@ -18,8 +19,6 @@ obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o -obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o -obj-$(CONFIG_USB_QCOM_8X16_PHY) += phy-qcom-8x16-usb.o obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o diff --git a/drivers/usb/phy/of.c b/drivers/usb/phy/of.c index 66ffa82457a8..1ab134f45d67 100644 --- a/drivers/usb/phy/of.c +++ b/drivers/usb/phy/of.c @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB of helper code - * - * 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. */ #include <linux/kernel.h> diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 61bf2285d5b1..87295313a10c 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB transceiver driver for AB8500 family chips * @@ -5,21 +6,6 @@ * Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> * Avinash Kumar <avinash.kumar@stericsson.com> * Thirupathi Chippakurthy <thirupathi.chippakurthy@stericsson.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #include <linux/module.h> diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 5f5f19813fde..a3cb25cb74f8 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include <linux/module.h> #include <linux/platform_device.h> #include <linux/err.h> diff --git a/drivers/usb/phy/phy-am335x-control.h b/drivers/usb/phy/phy-am335x-control.h index e86b3165d69d..cd4acfc6e360 100644 --- a/drivers/usb/phy/phy-am335x-control.h +++ b/drivers/usb/phy/phy-am335x-control.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _AM335x_PHY_CONTROL_H_ #define _AM335x_PHY_CONTROL_H_ diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 7e5aece769da..b36fa8b953d0 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include <linux/module.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index cf8f40ae6e01..900875f326d7 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2007,2008 Freescale semiconductor, Inc. * @@ -5,20 +6,6 @@ * Jerry Huang <Chang-Ming.Huang@freescale.com> * * Initialization based on code from Shlomi Gridish. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index 23149954a09c..43d410f6641b 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -1,19 +1,5 @@ -/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. */ #include <linux/usb/otg-fsm.h> #include <linux/usb/otg.h> diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 89d6e7a5fdb7..74ba88297991 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * NOP USB transceiver for all USB transceiver which are either built-in * into USB IP or which are mostly autonomous. @@ -5,20 +6,6 @@ * Copyright (C) 2009 Texas Instruments Inc * Author: Ajay Kumar Gupta <ajay.gupta@ti.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * * Current status: * This provides a "nop" transceiver for PHYs which are * autonomous such as isp1504, isp1707, etc. @@ -224,7 +211,7 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, int err = 0; u32 clk_rate = 0; - bool needs_vcc = false; + bool needs_vcc = false, needs_clk = false; if (dev->of_node) { struct device_node *node = dev->of_node; @@ -233,6 +220,7 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, clk_rate = 0; needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_clk = of_property_read_bool(node, "clocks"); nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); err = PTR_ERR_OR_ZERO(nop->gpiod_reset); @@ -275,6 +263,8 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, if (IS_ERR(nop->clk)) { dev_dbg(dev, "Can't get phy clock: %ld\n", PTR_ERR(nop->clk)); + if (needs_clk) + return PTR_ERR(nop->clk); } if (!IS_ERR(nop->clk) && clk_rate) { diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h index 0d0eadd54ed9..97289627561d 100644 --- a/drivers/usb/phy/phy-generic.h +++ b/drivers/usb/phy/phy-generic.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _PHY_GENERIC_H_ #define _PHY_GENERIC_H_ diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index f66120db8a41..553e2573c74f 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices * * Copyright (c) 2008 Philipp Zabel <philipp.zabel@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/kernel.h> diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index c6052c814bcc..7041ba030052 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -1,22 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller * * Copyright (C) 2004 Texas Instruments * Copyright (C) 2004 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/kernel.h> @@ -1183,9 +1170,11 @@ static irqreturn_t isp1301_irq(int irq, void *isp) return IRQ_HANDLED; } -static void isp1301_timer(unsigned long _isp) +static void isp1301_timer(struct timer_list *t) { - isp1301_defer_work((void *)_isp, WORK_TIMER); + struct isp1301 *isp = from_timer(isp, t, timer); + + isp1301_defer_work(isp, WORK_TIMER); } /*-------------------------------------------------------------------------*/ @@ -1222,7 +1211,6 @@ static int isp1301_remove(struct i2c_client *i2c) if (machine_is_omap_h2()) gpio_free(2); - isp->timer.data = 0; set_bit(WORK_STOP, &isp->todo); del_timer_sync(&isp->timer); flush_work(&isp->work); @@ -1507,9 +1495,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } INIT_WORK(&isp->work, isp1301_work); - init_timer(&isp->timer); - isp->timer.function = isp1301_timer; - isp->timer.data = (unsigned long) isp; + timer_setup(&isp->timer, isp1301_timer, 0); i2c_set_clientdata(i2c, isp); isp->client = i2c; diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index f333024660b4..93b7d6a30aad 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * NXP ISP1301 USB transceiver driver * * Copyright (C) 2012 Roland Stigge * * Author: Roland Stigge <stigge@antcom.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/module.h> diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index 01d4e4cdbc79..19871266312d 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * phy-keystone - USB PHY, talking to dwc3 controller in Keystone. * * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * 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. * * Author: WingMan Kwok <w-kwok2@ti.com> - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/module.h> diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c deleted file mode 100644 index 3d0dd2f97415..000000000000 --- a/drivers/usb/phy/phy-msm-usb.c +++ /dev/null @@ -1,2085 +0,0 @@ -/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ - -#include <linux/module.h> -#include <linux/device.h> -#include <linux/extcon.h> -#include <linux/gpio/consumer.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/err.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/uaccess.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/pm_runtime.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/reboot.h> -#include <linux/reset.h> -#include <linux/types.h> -#include <linux/usb/otg.h> - -#include <linux/usb.h> -#include <linux/usb/otg.h> -#include <linux/usb/of.h> -#include <linux/usb/ulpi.h> -#include <linux/usb/gadget.h> -#include <linux/usb/hcd.h> -#include <linux/usb/msm_hsusb_hw.h> -#include <linux/regulator/consumer.h> - -/** - * OTG control - * - * OTG_NO_CONTROL Id/VBUS notifications not required. Useful in host - * only configuration. - * OTG_PHY_CONTROL Id/VBUS notifications comes form USB PHY. - * OTG_PMIC_CONTROL Id/VBUS notifications comes from PMIC hardware. - * OTG_USER_CONTROL Id/VBUS notifcations comes from User via sysfs. - * - */ -enum otg_control_type { - OTG_NO_CONTROL = 0, - OTG_PHY_CONTROL, - OTG_PMIC_CONTROL, - OTG_USER_CONTROL, -}; - -/** - * PHY used in - * - * INVALID_PHY Unsupported PHY - * CI_45NM_INTEGRATED_PHY Chipidea 45nm integrated PHY - * SNPS_28NM_INTEGRATED_PHY Synopsis 28nm integrated PHY - * - */ -enum msm_usb_phy_type { - INVALID_PHY = 0, - CI_45NM_INTEGRATED_PHY, - SNPS_28NM_INTEGRATED_PHY, -}; - -#define IDEV_CHG_MAX 1500 -#define IUNIT 100 - -/** - * Different states involved in USB charger detection. - * - * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection - * process is not yet started. - * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. - * USB_CHG_STATE_DCD_DONE Data pin contact is detected. - * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects - * between SDP and DCP/CDP). - * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects - * between DCP and CDP). - * USB_CHG_STATE_DETECTED USB charger type is determined. - * - */ -enum usb_chg_state { - USB_CHG_STATE_UNDEFINED = 0, - USB_CHG_STATE_WAIT_FOR_DCD, - USB_CHG_STATE_DCD_DONE, - USB_CHG_STATE_PRIMARY_DONE, - USB_CHG_STATE_SECONDARY_DONE, - USB_CHG_STATE_DETECTED, -}; - -/** - * USB charger types - * - * USB_INVALID_CHARGER Invalid USB charger. - * USB_SDP_CHARGER Standard downstream port. Refers to a downstream port - * on USB2.0 compliant host/hub. - * USB_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger). - * USB_CDP_CHARGER Charging downstream port. Enumeration can happen and - * IDEV_CHG_MAX can be drawn irrespective of USB state. - * - */ -enum usb_chg_type { - USB_INVALID_CHARGER = 0, - USB_SDP_CHARGER, - USB_DCP_CHARGER, - USB_CDP_CHARGER, -}; - -/** - * struct msm_otg_platform_data - platform device data - * for msm_otg driver. - * @phy_init_seq: PHY configuration sequence values. Value of -1 is reserved as - * "do not overwrite default vaule at this address". - * @phy_init_sz: PHY configuration sequence size. - * @vbus_power: VBUS power on/off routine. - * @power_budget: VBUS power budget in mA (0 will be treated as 500mA). - * @mode: Supported mode (OTG/peripheral/host). - * @otg_control: OTG switch controlled by user/Id pin - */ -struct msm_otg_platform_data { - int *phy_init_seq; - int phy_init_sz; - void (*vbus_power)(bool on); - unsigned power_budget; - enum usb_dr_mode mode; - enum otg_control_type otg_control; - enum msm_usb_phy_type phy_type; - void (*setup_gpio)(enum usb_otg_state state); -}; - -/** - * struct msm_otg: OTG driver data. Shared by HCD and DCD. - * @otg: USB OTG Transceiver structure. - * @pdata: otg device platform data. - * @irq: IRQ number assigned for HSUSB controller. - * @clk: clock struct of usb_hs_clk. - * @pclk: clock struct of usb_hs_pclk. - * @core_clk: clock struct of usb_hs_core_clk. - * @regs: ioremapped register base address. - * @inputs: OTG state machine inputs(Id, SessValid etc). - * @sm_work: OTG state machine work. - * @in_lpm: indicates low power mode (LPM) state. - * @async_int: Async interrupt arrived. - * @cur_power: The amount of mA available from downstream port. - * @chg_work: Charger detection work. - * @chg_state: The state of charger detection process. - * @chg_type: The type of charger attached. - * @dcd_retires: The retry count used to track Data contact - * detection process. - * @manual_pullup: true if VBUS is not routed to USB controller/phy - * and controller driver therefore enables pull-up explicitly before - * starting controller using usbcmd run/stop bit. - * @vbus: VBUS signal state trakining, using extcon framework - * @id: ID signal state trakining, using extcon framework - * @switch_gpio: Descriptor for GPIO used to control external Dual - * SPDT USB Switch. - * @reboot: Used to inform the driver to route USB D+/D- line to Device - * connector - */ -struct msm_otg { - struct usb_phy phy; - struct msm_otg_platform_data *pdata; - int irq; - struct clk *clk; - struct clk *pclk; - struct clk *core_clk; - void __iomem *regs; -#define ID 0 -#define B_SESS_VLD 1 - unsigned long inputs; - struct work_struct sm_work; - atomic_t in_lpm; - int async_int; - unsigned cur_power; - int phy_number; - struct delayed_work chg_work; - enum usb_chg_state chg_state; - enum usb_chg_type chg_type; - u8 dcd_retries; - struct regulator *v3p3; - struct regulator *v1p8; - struct regulator *vddcx; - struct regulator_bulk_data supplies[3]; - - struct reset_control *phy_rst; - struct reset_control *link_rst; - int vdd_levels[3]; - - bool manual_pullup; - - struct gpio_desc *switch_gpio; - struct notifier_block reboot; -}; - -#define MSM_USB_BASE (motg->regs) -#define DRIVER_NAME "msm_otg" - -#define ULPI_IO_TIMEOUT_USEC (10 * 1000) -#define LINK_RESET_TIMEOUT_USEC (250 * 1000) - -#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ -#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ -#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ -#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */ - -#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */ -#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */ -#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */ -#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */ - -#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ -#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ -#define USB_PHY_SUSP_DIG_VOL 500000 /* uV */ - -enum vdd_levels { - VDD_LEVEL_NONE = 0, - VDD_LEVEL_MIN, - VDD_LEVEL_MAX, -}; - -static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) -{ - int ret = 0; - - if (init) { - ret = regulator_set_voltage(motg->vddcx, - motg->vdd_levels[VDD_LEVEL_MIN], - motg->vdd_levels[VDD_LEVEL_MAX]); - if (ret) { - dev_err(motg->phy.dev, "Cannot set vddcx voltage\n"); - return ret; - } - - ret = regulator_enable(motg->vddcx); - if (ret) - dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n"); - } else { - ret = regulator_set_voltage(motg->vddcx, 0, - motg->vdd_levels[VDD_LEVEL_MAX]); - if (ret) - dev_err(motg->phy.dev, "Cannot set vddcx voltage\n"); - ret = regulator_disable(motg->vddcx); - if (ret) - dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n"); - } - - return ret; -} - -static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) -{ - int rc = 0; - - if (init) { - rc = regulator_set_voltage(motg->v3p3, USB_PHY_3P3_VOL_MIN, - USB_PHY_3P3_VOL_MAX); - if (rc) { - dev_err(motg->phy.dev, "Cannot set v3p3 voltage\n"); - goto exit; - } - rc = regulator_enable(motg->v3p3); - if (rc) { - dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n"); - goto exit; - } - rc = regulator_set_voltage(motg->v1p8, USB_PHY_1P8_VOL_MIN, - USB_PHY_1P8_VOL_MAX); - if (rc) { - dev_err(motg->phy.dev, "Cannot set v1p8 voltage\n"); - goto disable_3p3; - } - rc = regulator_enable(motg->v1p8); - if (rc) { - dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n"); - goto disable_3p3; - } - - return 0; - } - - regulator_disable(motg->v1p8); -disable_3p3: - regulator_disable(motg->v3p3); -exit: - return rc; -} - -static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, int on) -{ - int ret = 0; - - if (on) { - ret = regulator_set_load(motg->v1p8, USB_PHY_1P8_HPM_LOAD); - if (ret < 0) { - pr_err("Could not set HPM for v1p8\n"); - return ret; - } - ret = regulator_set_load(motg->v3p3, USB_PHY_3P3_HPM_LOAD); - if (ret < 0) { - pr_err("Could not set HPM for v3p3\n"); - regulator_set_load(motg->v1p8, USB_PHY_1P8_LPM_LOAD); - return ret; - } - } else { - ret = regulator_set_load(motg->v1p8, USB_PHY_1P8_LPM_LOAD); - if (ret < 0) - pr_err("Could not set LPM for v1p8\n"); - ret = regulator_set_load(motg->v3p3, USB_PHY_3P3_LPM_LOAD); - if (ret < 0) - pr_err("Could not set LPM for v3p3\n"); - } - - pr_debug("reg (%s)\n", on ? "HPM" : "LPM"); - return ret < 0 ? ret : 0; -} - -static int ulpi_read(struct usb_phy *phy, u32 reg) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int cnt = 0; - - /* initiate read operation */ - writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), - USB_ULPI_VIEWPORT); - - /* wait for completion */ - while (cnt < ULPI_IO_TIMEOUT_USEC) { - if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) - break; - udelay(1); - cnt++; - } - - if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(phy->dev, "ulpi_read: timeout %08x\n", - readl(USB_ULPI_VIEWPORT)); - return -ETIMEDOUT; - } - return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); -} - -static int ulpi_write(struct usb_phy *phy, u32 val, u32 reg) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int cnt = 0; - - /* initiate write operation */ - writel(ULPI_RUN | ULPI_WRITE | - ULPI_ADDR(reg) | ULPI_DATA(val), - USB_ULPI_VIEWPORT); - - /* wait for completion */ - while (cnt < ULPI_IO_TIMEOUT_USEC) { - if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) - break; - udelay(1); - cnt++; - } - - if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(phy->dev, "ulpi_write: timeout\n"); - return -ETIMEDOUT; - } - return 0; -} - -static struct usb_phy_io_ops msm_otg_io_ops = { - .read = ulpi_read, - .write = ulpi_write, -}; - -static void ulpi_init(struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata = motg->pdata; - int *seq = pdata->phy_init_seq, idx; - u32 addr = ULPI_EXT_VENDOR_SPECIFIC; - - for (idx = 0; idx < pdata->phy_init_sz; idx++) { - if (seq[idx] == -1) - continue; - - dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n", - seq[idx], addr + idx); - ulpi_write(&motg->phy, seq[idx], addr + idx); - } -} - -static int msm_phy_notify_disconnect(struct usb_phy *phy, - enum usb_device_speed speed) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int val; - - if (motg->manual_pullup) { - val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; - usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A)); - } - - /* - * Put the transceiver in non-driving mode. Otherwise host - * may not detect soft-disconnection. - */ - val = ulpi_read(phy, ULPI_FUNC_CTRL); - val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - ulpi_write(phy, val, ULPI_FUNC_CTRL); - - return 0; -} - -static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) -{ - int ret; - - if (assert) - ret = reset_control_assert(motg->link_rst); - else - ret = reset_control_deassert(motg->link_rst); - - if (ret) - dev_err(motg->phy.dev, "usb link clk reset %s failed\n", - assert ? "assert" : "deassert"); - - return ret; -} - -static int msm_otg_phy_clk_reset(struct msm_otg *motg) -{ - int ret = 0; - - if (motg->phy_rst) - ret = reset_control_reset(motg->phy_rst); - - if (ret) - dev_err(motg->phy.dev, "usb phy clk reset failed\n"); - - return ret; -} - -static int msm_link_reset(struct msm_otg *motg) -{ - u32 val; - int ret; - - ret = msm_otg_link_clk_reset(motg, 1); - if (ret) - return ret; - - /* wait for 1ms delay as suggested in HPG. */ - usleep_range(1000, 1200); - - ret = msm_otg_link_clk_reset(motg, 0); - if (ret) - return ret; - - if (motg->phy_number) - writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); - - /* put transceiver in serial mode as part of reset */ - val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; - writel(val | PORTSC_PTS_SERIAL, USB_PORTSC); - - return 0; -} - -static int msm_otg_reset(struct usb_phy *phy) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int cnt = 0; - - writel(USBCMD_RESET, USB_USBCMD); - while (cnt < LINK_RESET_TIMEOUT_USEC) { - if (!(readl(USB_USBCMD) & USBCMD_RESET)) - break; - udelay(1); - cnt++; - } - if (cnt >= LINK_RESET_TIMEOUT_USEC) - return -ETIMEDOUT; - - /* select ULPI phy and clear other status/control bits in PORTSC */ - writel(PORTSC_PTS_ULPI, USB_PORTSC); - - writel(0x0, USB_AHBBURST); - writel(0x08, USB_AHBMODE); - - if (motg->phy_number) - writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); - return 0; -} - -static void msm_phy_reset(struct msm_otg *motg) -{ - void __iomem *addr; - - if (motg->pdata->phy_type != SNPS_28NM_INTEGRATED_PHY) { - msm_otg_phy_clk_reset(motg); - return; - } - - addr = USB_PHY_CTRL; - if (motg->phy_number) - addr = USB_PHY_CTRL2; - - /* Assert USB PHY_POR */ - writel(readl(addr) | PHY_POR_ASSERT, addr); - - /* - * wait for minimum 10 microseconds as suggested in HPG. - * Use a slightly larger value since the exact value didn't - * work 100% of the time. - */ - udelay(12); - - /* Deassert USB PHY_POR */ - writel(readl(addr) & ~PHY_POR_ASSERT, addr); -} - -static int msm_usb_reset(struct usb_phy *phy) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - int ret; - - if (!IS_ERR(motg->core_clk)) - clk_prepare_enable(motg->core_clk); - - ret = msm_link_reset(motg); - if (ret) { - dev_err(phy->dev, "phy_reset failed\n"); - return ret; - } - - ret = msm_otg_reset(&motg->phy); - if (ret) { - dev_err(phy->dev, "link reset failed\n"); - return ret; - } - - msleep(100); - - /* Reset USB PHY after performing USB Link RESET */ - msm_phy_reset(motg); - - if (!IS_ERR(motg->core_clk)) - clk_disable_unprepare(motg->core_clk); - - return 0; -} - -static int msm_phy_init(struct usb_phy *phy) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - u32 val, ulpi_val = 0; - - /* Program USB PHY Override registers. */ - ulpi_init(motg); - - /* - * It is recommended in HPG to reset USB PHY after programming - * USB PHY Override registers. - */ - msm_phy_reset(motg); - - if (pdata->otg_control == OTG_PHY_CONTROL) { - val = readl(USB_OTGSC); - if (pdata->mode == USB_DR_MODE_OTG) { - ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; - val |= OTGSC_IDIE | OTGSC_BSVIE; - } else if (pdata->mode == USB_DR_MODE_PERIPHERAL) { - ulpi_val = ULPI_INT_SESS_VALID; - val |= OTGSC_BSVIE; - } - writel(val, USB_OTGSC); - ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE); - ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); - } - - if (motg->manual_pullup) { - val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; - ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A)); - - val = readl(USB_GENCONFIG_2); - val |= GENCONFIG_2_SESS_VLD_CTRL_EN; - writel(val, USB_GENCONFIG_2); - - val = readl(USB_USBCMD); - val |= USBCMD_SESS_VLD_CTRL; - writel(val, USB_USBCMD); - - val = ulpi_read(phy, ULPI_FUNC_CTRL); - val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; - ulpi_write(phy, val, ULPI_FUNC_CTRL); - } - - if (motg->phy_number) - writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); - - return 0; -} - -#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) -#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) - -#ifdef CONFIG_PM - -static int msm_hsusb_config_vddcx(struct msm_otg *motg, int high) -{ - int max_vol = motg->vdd_levels[VDD_LEVEL_MAX]; - int min_vol; - int ret; - - if (high) - min_vol = motg->vdd_levels[VDD_LEVEL_MIN]; - else - min_vol = motg->vdd_levels[VDD_LEVEL_NONE]; - - ret = regulator_set_voltage(motg->vddcx, min_vol, max_vol); - if (ret) { - pr_err("Cannot set vddcx voltage\n"); - return ret; - } - - pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); - - return ret; -} - -static int msm_otg_suspend(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - struct usb_bus *bus = phy->otg->host; - struct msm_otg_platform_data *pdata = motg->pdata; - void __iomem *addr; - int cnt = 0; - - if (atomic_read(&motg->in_lpm)) - return 0; - - disable_irq(motg->irq); - /* - * Chipidea 45-nm PHY suspend sequence: - * - * Interrupt Latch Register auto-clear feature is not present - * in all PHY versions. Latch register is clear on read type. - * Clear latch register to avoid spurious wakeup from - * low power mode (LPM). - * - * PHY comparators are disabled when PHY enters into low power - * mode (LPM). Keep PHY comparators ON in LPM only when we expect - * VBUS/Id notifications from USB PHY. Otherwise turn off USB - * PHY comparators. This save significant amount of power. - * - * PLL is not turned off when PHY enters into low power mode (LPM). - * Disable PLL for maximum power savings. - */ - - if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { - ulpi_read(phy, 0x14); - if (pdata->otg_control == OTG_PHY_CONTROL) - ulpi_write(phy, 0x01, 0x30); - ulpi_write(phy, 0x08, 0x09); - } - - /* - * PHY may take some time or even fail to enter into low power - * mode (LPM). Hence poll for 500 msec and reset the PHY and link - * in failure case. - */ - writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { - if (readl(USB_PORTSC) & PORTSC_PHCD) - break; - udelay(1); - cnt++; - } - - if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { - dev_err(phy->dev, "Unable to suspend PHY\n"); - msm_otg_reset(phy); - enable_irq(motg->irq); - return -ETIMEDOUT; - } - - /* - * PHY has capability to generate interrupt asynchronously in low - * power mode (LPM). This interrupt is level triggered. So USB IRQ - * line must be disabled till async interrupt enable bit is cleared - * in USBCMD register. Assert STP (ULPI interface STOP signal) to - * block data communication from PHY. - */ - writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); - - addr = USB_PHY_CTRL; - if (motg->phy_number) - addr = USB_PHY_CTRL2; - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) - writel(readl(addr) | PHY_RETEN, addr); - - clk_disable_unprepare(motg->pclk); - clk_disable_unprepare(motg->clk); - if (!IS_ERR(motg->core_clk)) - clk_disable_unprepare(motg->core_clk); - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) { - msm_hsusb_ldo_set_mode(motg, 0); - msm_hsusb_config_vddcx(motg, 0); - } - - if (device_may_wakeup(phy->dev)) - enable_irq_wake(motg->irq); - if (bus) - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); - - atomic_set(&motg->in_lpm, 1); - enable_irq(motg->irq); - - dev_info(phy->dev, "USB in low power mode\n"); - - return 0; -} - -static int msm_otg_resume(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - struct usb_bus *bus = phy->otg->host; - void __iomem *addr; - int cnt = 0; - unsigned temp; - - if (!atomic_read(&motg->in_lpm)) - return 0; - - clk_prepare_enable(motg->pclk); - clk_prepare_enable(motg->clk); - if (!IS_ERR(motg->core_clk)) - clk_prepare_enable(motg->core_clk); - - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) { - - addr = USB_PHY_CTRL; - if (motg->phy_number) - addr = USB_PHY_CTRL2; - - msm_hsusb_ldo_set_mode(motg, 1); - msm_hsusb_config_vddcx(motg, 1); - writel(readl(addr) & ~PHY_RETEN, addr); - } - - temp = readl(USB_USBCMD); - temp &= ~ASYNC_INTR_CTRL; - temp &= ~ULPI_STP_CTRL; - writel(temp, USB_USBCMD); - - /* - * PHY comes out of low power mode (LPM) in case of wakeup - * from asynchronous interrupt. - */ - if (!(readl(USB_PORTSC) & PORTSC_PHCD)) - goto skip_phy_resume; - - writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_RESUME_TIMEOUT_USEC) { - if (!(readl(USB_PORTSC) & PORTSC_PHCD)) - break; - udelay(1); - cnt++; - } - - if (cnt >= PHY_RESUME_TIMEOUT_USEC) { - /* - * This is a fatal error. Reset the link and - * PHY. USB state can not be restored. Re-insertion - * of USB cable is the only way to get USB working. - */ - dev_err(phy->dev, "Unable to resume USB. Re-plugin the cable\n"); - msm_otg_reset(phy); - } - -skip_phy_resume: - if (device_may_wakeup(phy->dev)) - disable_irq_wake(motg->irq); - if (bus) - set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); - - atomic_set(&motg->in_lpm, 0); - - if (motg->async_int) { - motg->async_int = 0; - pm_runtime_put(phy->dev); - enable_irq(motg->irq); - } - - dev_info(phy->dev, "USB exited from low power mode\n"); - - return 0; -} -#endif - -static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) -{ - if (motg->cur_power == mA) - return; - - /* TODO: Notify PMIC about available current */ - dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA); - motg->cur_power = mA; -} - -static void msm_otg_start_host(struct usb_phy *phy, int on) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - struct usb_hcd *hcd; - - if (!phy->otg->host) - return; - - hcd = bus_to_hcd(phy->otg->host); - - if (on) { - dev_dbg(phy->dev, "host on\n"); - - if (pdata->vbus_power) - pdata->vbus_power(1); - /* - * Some boards have a switch cotrolled by gpio - * to enable/disable internal HUB. Enable internal - * HUB before kicking the host. - */ - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_A_HOST); -#ifdef CONFIG_USB - usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); - device_wakeup_enable(hcd->self.controller); -#endif - } else { - dev_dbg(phy->dev, "host off\n"); - -#ifdef CONFIG_USB - usb_remove_hcd(hcd); -#endif - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_UNDEFINED); - if (pdata->vbus_power) - pdata->vbus_power(0); - } -} - -static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy); - struct usb_hcd *hcd; - - /* - * Fail host registration if this board can support - * only peripheral configuration. - */ - if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL) { - dev_info(otg->usb_phy->dev, "Host mode is not supported\n"); - return -ENODEV; - } - - if (!host) { - if (otg->state == OTG_STATE_A_HOST) { - pm_runtime_get_sync(otg->usb_phy->dev); - msm_otg_start_host(otg->usb_phy, 0); - otg->host = NULL; - otg->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); - } else { - otg->host = NULL; - } - - return 0; - } - - hcd = bus_to_hcd(host); - hcd->power_budget = motg->pdata->power_budget; - - otg->host = host; - dev_dbg(otg->usb_phy->dev, "host driver registered w/ tranceiver\n"); - - pm_runtime_get_sync(otg->usb_phy->dev); - schedule_work(&motg->sm_work); - - return 0; -} - -static void msm_otg_start_peripheral(struct usb_phy *phy, int on) -{ - struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; - - if (!phy->otg->gadget) - return; - - if (on) { - dev_dbg(phy->dev, "gadget on\n"); - /* - * Some boards have a switch cotrolled by gpio - * to enable/disable internal HUB. Disable internal - * HUB before kicking the gadget. - */ - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); - usb_gadget_vbus_connect(phy->otg->gadget); - } else { - dev_dbg(phy->dev, "gadget off\n"); - usb_gadget_vbus_disconnect(phy->otg->gadget); - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_UNDEFINED); - } - -} - -static int msm_otg_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy); - - /* - * Fail peripheral registration if this board can support - * only host configuration. - */ - if (motg->pdata->mode == USB_DR_MODE_HOST) { - dev_info(otg->usb_phy->dev, "Peripheral mode is not supported\n"); - return -ENODEV; - } - - if (!gadget) { - if (otg->state == OTG_STATE_B_PERIPHERAL) { - pm_runtime_get_sync(otg->usb_phy->dev); - msm_otg_start_peripheral(otg->usb_phy, 0); - otg->gadget = NULL; - otg->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); - } else { - otg->gadget = NULL; - } - - return 0; - } - otg->gadget = gadget; - dev_dbg(otg->usb_phy->dev, - "peripheral driver registered w/ tranceiver\n"); - - pm_runtime_get_sync(otg->usb_phy->dev); - schedule_work(&motg->sm_work); - - return 0; -} - -static bool msm_chg_check_secondary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - ret = chg_det & (1 << 4); - break; - case SNPS_28NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x87); - ret = chg_det & 1; - break; - default: - break; - } - return ret; -} - -static void msm_chg_enable_secondary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn off charger block */ - chg_det |= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - /* control chg block via ULPI */ - chg_det &= ~(1 << 3); - ulpi_write(phy, chg_det, 0x34); - /* put it in host mode for enabling D- source */ - chg_det &= ~(1 << 2); - ulpi_write(phy, chg_det, 0x34); - /* Turn on chg detect block */ - chg_det &= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - /* enable chg detection */ - chg_det &= ~(1 << 0); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* - * Configure DM as current source, DP as current sink - * and enable battery charging comparators. - */ - ulpi_write(phy, 0x8, 0x85); - ulpi_write(phy, 0x2, 0x85); - ulpi_write(phy, 0x1, 0x85); - break; - default: - break; - } -} - -static bool msm_chg_check_primary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - ret = chg_det & (1 << 4); - break; - case SNPS_28NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x87); - ret = chg_det & 1; - break; - default: - break; - } - return ret; -} - -static void msm_chg_enable_primary_det(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* enable chg detection */ - chg_det &= ~(1 << 0); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* - * Configure DP as current source, DM as current sink - * and enable battery charging comparators. - */ - ulpi_write(phy, 0x2, 0x85); - ulpi_write(phy, 0x1, 0x85); - break; - default: - break; - } -} - -static bool msm_chg_check_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 line_state; - bool ret = false; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - line_state = ulpi_read(phy, 0x15); - ret = !(line_state & 1); - break; - case SNPS_28NM_INTEGRATED_PHY: - line_state = ulpi_read(phy, 0x87); - ret = line_state & 2; - break; - default: - break; - } - return ret; -} - -static void msm_chg_disable_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - chg_det &= ~(1 << 5); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - ulpi_write(phy, 0x10, 0x86); - break; - default: - break; - } -} - -static void msm_chg_enable_dcd(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn on D+ current source */ - chg_det |= (1 << 5); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Data contact detection enable */ - ulpi_write(phy, 0x10, 0x85); - break; - default: - break; - } -} - -static void msm_chg_block_on(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 func_ctrl, chg_det; - - /* put the controller in non-driving mode */ - func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); - func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* control chg block via ULPI */ - chg_det &= ~(1 << 3); - ulpi_write(phy, chg_det, 0x34); - /* Turn on chg detect block */ - chg_det &= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - udelay(20); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Clear charger detecting control bits */ - ulpi_write(phy, 0x3F, 0x86); - /* Clear alt interrupt latch and enable bits */ - ulpi_write(phy, 0x1F, 0x92); - ulpi_write(phy, 0x1F, 0x95); - udelay(100); - break; - default: - break; - } -} - -static void msm_chg_block_off(struct msm_otg *motg) -{ - struct usb_phy *phy = &motg->phy; - u32 func_ctrl, chg_det; - - switch (motg->pdata->phy_type) { - case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(phy, 0x34); - /* Turn off charger block */ - chg_det |= ~(1 << 1); - ulpi_write(phy, chg_det, 0x34); - break; - case SNPS_28NM_INTEGRATED_PHY: - /* Clear charger detecting control bits */ - ulpi_write(phy, 0x3F, 0x86); - /* Clear alt interrupt latch and enable bits */ - ulpi_write(phy, 0x1F, 0x92); - ulpi_write(phy, 0x1F, 0x95); - break; - default: - break; - } - - /* put the controller in normal mode */ - func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); - func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; - ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); -} - -#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */ -#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */ -#define MSM_CHG_PRIMARY_DET_TIME (40 * HZ/1000) /* TVDPSRC_ON */ -#define MSM_CHG_SECONDARY_DET_TIME (40 * HZ/1000) /* TVDMSRC_ON */ -static void msm_chg_detect_work(struct work_struct *w) -{ - struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); - struct usb_phy *phy = &motg->phy; - bool is_dcd, tmout, vout; - unsigned long delay; - - dev_dbg(phy->dev, "chg detection work\n"); - switch (motg->chg_state) { - case USB_CHG_STATE_UNDEFINED: - pm_runtime_get_sync(phy->dev); - msm_chg_block_on(motg); - msm_chg_enable_dcd(motg); - motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; - motg->dcd_retries = 0; - delay = MSM_CHG_DCD_POLL_TIME; - break; - case USB_CHG_STATE_WAIT_FOR_DCD: - is_dcd = msm_chg_check_dcd(motg); - tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES; - if (is_dcd || tmout) { - msm_chg_disable_dcd(motg); - msm_chg_enable_primary_det(motg); - delay = MSM_CHG_PRIMARY_DET_TIME; - motg->chg_state = USB_CHG_STATE_DCD_DONE; - } else { - delay = MSM_CHG_DCD_POLL_TIME; - } - break; - case USB_CHG_STATE_DCD_DONE: - vout = msm_chg_check_primary_det(motg); - if (vout) { - msm_chg_enable_secondary_det(motg); - delay = MSM_CHG_SECONDARY_DET_TIME; - motg->chg_state = USB_CHG_STATE_PRIMARY_DONE; - } else { - motg->chg_type = USB_SDP_CHARGER; - motg->chg_state = USB_CHG_STATE_DETECTED; - delay = 0; - } - break; - case USB_CHG_STATE_PRIMARY_DONE: - vout = msm_chg_check_secondary_det(motg); - if (vout) - motg->chg_type = USB_DCP_CHARGER; - else - motg->chg_type = USB_CDP_CHARGER; - motg->chg_state = USB_CHG_STATE_SECONDARY_DONE; - /* fall through */ - case USB_CHG_STATE_SECONDARY_DONE: - motg->chg_state = USB_CHG_STATE_DETECTED; - case USB_CHG_STATE_DETECTED: - msm_chg_block_off(motg); - dev_dbg(phy->dev, "charger = %d\n", motg->chg_type); - schedule_work(&motg->sm_work); - return; - default: - return; - } - - schedule_delayed_work(&motg->chg_work, delay); -} - -/* - * We support OTG, Peripheral only and Host only configurations. In case - * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen - * via Id pin status or user request (debugfs). Id/BSV interrupts are not - * enabled when switch is controlled by user and default mode is supplied - * by board file, which can be changed by userspace later. - */ -static void msm_otg_init_sm(struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata = motg->pdata; - u32 otgsc = readl(USB_OTGSC); - - switch (pdata->mode) { - case USB_DR_MODE_OTG: - if (pdata->otg_control == OTG_PHY_CONTROL) { - if (otgsc & OTGSC_ID) - set_bit(ID, &motg->inputs); - else - clear_bit(ID, &motg->inputs); - - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - } else if (pdata->otg_control == OTG_USER_CONTROL) { - set_bit(ID, &motg->inputs); - clear_bit(B_SESS_VLD, &motg->inputs); - } - break; - case USB_DR_MODE_HOST: - clear_bit(ID, &motg->inputs); - break; - case USB_DR_MODE_PERIPHERAL: - set_bit(ID, &motg->inputs); - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - break; - default: - break; - } -} - -static void msm_otg_sm_work(struct work_struct *w) -{ - struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); - struct usb_otg *otg = motg->phy.otg; - - switch (otg->state) { - case OTG_STATE_UNDEFINED: - dev_dbg(otg->usb_phy->dev, "OTG_STATE_UNDEFINED state\n"); - msm_otg_reset(otg->usb_phy); - msm_otg_init_sm(motg); - otg->state = OTG_STATE_B_IDLE; - /* FALL THROUGH */ - case OTG_STATE_B_IDLE: - dev_dbg(otg->usb_phy->dev, "OTG_STATE_B_IDLE state\n"); - if (!test_bit(ID, &motg->inputs) && otg->host) { - /* disable BSV bit */ - writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); - msm_otg_start_host(otg->usb_phy, 1); - otg->state = OTG_STATE_A_HOST; - } else if (test_bit(B_SESS_VLD, &motg->inputs)) { - switch (motg->chg_state) { - case USB_CHG_STATE_UNDEFINED: - msm_chg_detect_work(&motg->chg_work.work); - break; - case USB_CHG_STATE_DETECTED: - switch (motg->chg_type) { - case USB_DCP_CHARGER: - msm_otg_notify_charger(motg, - IDEV_CHG_MAX); - break; - case USB_CDP_CHARGER: - msm_otg_notify_charger(motg, - IDEV_CHG_MAX); - msm_otg_start_peripheral(otg->usb_phy, - 1); - otg->state - = OTG_STATE_B_PERIPHERAL; - break; - case USB_SDP_CHARGER: - msm_otg_notify_charger(motg, IUNIT); - msm_otg_start_peripheral(otg->usb_phy, - 1); - otg->state - = OTG_STATE_B_PERIPHERAL; - break; - default: - break; - } - break; - default: - break; - } - } else { - /* - * If charger detection work is pending, decrement - * the pm usage counter to balance with the one that - * is incremented in charger detection work. - */ - if (cancel_delayed_work_sync(&motg->chg_work)) { - pm_runtime_put_sync(otg->usb_phy->dev); - msm_otg_reset(otg->usb_phy); - } - msm_otg_notify_charger(motg, 0); - motg->chg_state = USB_CHG_STATE_UNDEFINED; - motg->chg_type = USB_INVALID_CHARGER; - } - - if (otg->state == OTG_STATE_B_IDLE) - pm_runtime_put_sync(otg->usb_phy->dev); - break; - case OTG_STATE_B_PERIPHERAL: - dev_dbg(otg->usb_phy->dev, "OTG_STATE_B_PERIPHERAL state\n"); - if (!test_bit(B_SESS_VLD, &motg->inputs) || - !test_bit(ID, &motg->inputs)) { - msm_otg_notify_charger(motg, 0); - msm_otg_start_peripheral(otg->usb_phy, 0); - motg->chg_state = USB_CHG_STATE_UNDEFINED; - motg->chg_type = USB_INVALID_CHARGER; - otg->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg->usb_phy); - schedule_work(w); - } - break; - case OTG_STATE_A_HOST: - dev_dbg(otg->usb_phy->dev, "OTG_STATE_A_HOST state\n"); - if (test_bit(ID, &motg->inputs)) { - msm_otg_start_host(otg->usb_phy, 0); - otg->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg->usb_phy); - schedule_work(w); - } - break; - default: - break; - } -} - -static irqreturn_t msm_otg_irq(int irq, void *data) -{ - struct msm_otg *motg = data; - struct usb_phy *phy = &motg->phy; - u32 otgsc = 0; - - if (atomic_read(&motg->in_lpm)) { - disable_irq_nosync(irq); - motg->async_int = 1; - pm_runtime_get(phy->dev); - return IRQ_HANDLED; - } - - otgsc = readl(USB_OTGSC); - if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) - return IRQ_NONE; - - if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { - if (otgsc & OTGSC_ID) - set_bit(ID, &motg->inputs); - else - clear_bit(ID, &motg->inputs); - dev_dbg(phy->dev, "ID set/clear\n"); - pm_runtime_get_noresume(phy->dev); - } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - dev_dbg(phy->dev, "BSV set/clear\n"); - pm_runtime_get_noresume(phy->dev); - } - - writel(otgsc, USB_OTGSC); - schedule_work(&motg->sm_work); - return IRQ_HANDLED; -} - -static int msm_otg_mode_show(struct seq_file *s, void *unused) -{ - struct msm_otg *motg = s->private; - struct usb_otg *otg = motg->phy.otg; - - switch (otg->state) { - case OTG_STATE_A_HOST: - seq_puts(s, "host\n"); - break; - case OTG_STATE_B_PERIPHERAL: - seq_puts(s, "peripheral\n"); - break; - default: - seq_puts(s, "none\n"); - break; - } - - return 0; -} - -static int msm_otg_mode_open(struct inode *inode, struct file *file) -{ - return single_open(file, msm_otg_mode_show, inode->i_private); -} - -static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct seq_file *s = file->private_data; - struct msm_otg *motg = s->private; - char buf[16]; - struct usb_otg *otg = motg->phy.otg; - int status = count; - enum usb_dr_mode req_mode; - - memset(buf, 0x00, sizeof(buf)); - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { - status = -EFAULT; - goto out; - } - - if (!strncmp(buf, "host", 4)) { - req_mode = USB_DR_MODE_HOST; - } else if (!strncmp(buf, "peripheral", 10)) { - req_mode = USB_DR_MODE_PERIPHERAL; - } else if (!strncmp(buf, "none", 4)) { - req_mode = USB_DR_MODE_UNKNOWN; - } else { - status = -EINVAL; - goto out; - } - - switch (req_mode) { - case USB_DR_MODE_UNKNOWN: - switch (otg->state) { - case OTG_STATE_A_HOST: - case OTG_STATE_B_PERIPHERAL: - set_bit(ID, &motg->inputs); - clear_bit(B_SESS_VLD, &motg->inputs); - break; - default: - goto out; - } - break; - case USB_DR_MODE_PERIPHERAL: - switch (otg->state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_A_HOST: - set_bit(ID, &motg->inputs); - set_bit(B_SESS_VLD, &motg->inputs); - break; - default: - goto out; - } - break; - case USB_DR_MODE_HOST: - switch (otg->state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_B_PERIPHERAL: - clear_bit(ID, &motg->inputs); - break; - default: - goto out; - } - break; - default: - goto out; - } - - pm_runtime_get_sync(otg->usb_phy->dev); - schedule_work(&motg->sm_work); -out: - return status; -} - -static const struct file_operations msm_otg_mode_fops = { - .open = msm_otg_mode_open, - .read = seq_read, - .write = msm_otg_mode_write, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *msm_otg_dbg_root; -static struct dentry *msm_otg_dbg_mode; - -static int msm_otg_debugfs_init(struct msm_otg *motg) -{ - msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); - - if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) - return -ENODEV; - - msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, - msm_otg_dbg_root, motg, &msm_otg_mode_fops); - if (!msm_otg_dbg_mode) { - debugfs_remove(msm_otg_dbg_root); - msm_otg_dbg_root = NULL; - return -ENODEV; - } - - return 0; -} - -static void msm_otg_debugfs_cleanup(void) -{ - debugfs_remove(msm_otg_dbg_mode); - debugfs_remove(msm_otg_dbg_root); -} - -static const struct of_device_id msm_otg_dt_match[] = { - { - .compatible = "qcom,usb-otg-ci", - .data = (void *) CI_45NM_INTEGRATED_PHY - }, - { - .compatible = "qcom,usb-otg-snps", - .data = (void *) SNPS_28NM_INTEGRATED_PHY - }, - { } -}; -MODULE_DEVICE_TABLE(of, msm_otg_dt_match); - -static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct usb_phy *usb_phy = container_of(nb, struct usb_phy, vbus_nb); - struct msm_otg *motg = container_of(usb_phy, struct msm_otg, phy); - - if (event) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - - if (test_bit(B_SESS_VLD, &motg->inputs)) { - /* Switch D+/D- lines to Device connector */ - gpiod_set_value_cansleep(motg->switch_gpio, 0); - } else { - /* Switch D+/D- lines to Hub */ - gpiod_set_value_cansleep(motg->switch_gpio, 1); - } - - schedule_work(&motg->sm_work); - - return NOTIFY_DONE; -} - -static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct usb_phy *usb_phy = container_of(nb, struct usb_phy, id_nb); - struct msm_otg *motg = container_of(usb_phy, struct msm_otg, phy); - - if (event) - clear_bit(ID, &motg->inputs); - else - set_bit(ID, &motg->inputs); - - schedule_work(&motg->sm_work); - - return NOTIFY_DONE; -} - -static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata; - struct device_node *node = pdev->dev.of_node; - struct property *prop; - int len, ret, words; - u32 val, tmp[3]; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - motg->pdata = pdata; - - pdata->phy_type = (enum msm_usb_phy_type)of_device_get_match_data(&pdev->dev); - if (!pdata->phy_type) - return 1; - - motg->link_rst = devm_reset_control_get(&pdev->dev, "link"); - if (IS_ERR(motg->link_rst)) - return PTR_ERR(motg->link_rst); - - motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy"); - if (IS_ERR(motg->phy_rst)) - motg->phy_rst = NULL; - - pdata->mode = usb_get_dr_mode(&pdev->dev); - if (pdata->mode == USB_DR_MODE_UNKNOWN) - pdata->mode = USB_DR_MODE_OTG; - - pdata->otg_control = OTG_PHY_CONTROL; - if (!of_property_read_u32(node, "qcom,otg-control", &val)) - if (val == OTG_PMIC_CONTROL) - pdata->otg_control = val; - - if (!of_property_read_u32(node, "qcom,phy-num", &val) && val < 2) - motg->phy_number = val; - - motg->vdd_levels[VDD_LEVEL_NONE] = USB_PHY_SUSP_DIG_VOL; - motg->vdd_levels[VDD_LEVEL_MIN] = USB_PHY_VDD_DIG_VOL_MIN; - motg->vdd_levels[VDD_LEVEL_MAX] = USB_PHY_VDD_DIG_VOL_MAX; - - if (of_get_property(node, "qcom,vdd-levels", &len) && - len == sizeof(tmp)) { - of_property_read_u32_array(node, "qcom,vdd-levels", - tmp, len / sizeof(*tmp)); - motg->vdd_levels[VDD_LEVEL_NONE] = tmp[VDD_LEVEL_NONE]; - motg->vdd_levels[VDD_LEVEL_MIN] = tmp[VDD_LEVEL_MIN]; - motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; - } - - motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); - - motg->switch_gpio = devm_gpiod_get_optional(&pdev->dev, "switch", - GPIOD_OUT_LOW); - if (IS_ERR(motg->switch_gpio)) - return PTR_ERR(motg->switch_gpio); - - prop = of_find_property(node, "qcom,phy-init-sequence", &len); - if (!prop || !len) - return 0; - - words = len / sizeof(u32); - - if (words >= ULPI_EXT_VENDOR_SPECIFIC) { - dev_warn(&pdev->dev, "Too big PHY init sequence %d\n", words); - return 0; - } - - pdata->phy_init_seq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); - if (!pdata->phy_init_seq) - return 0; - - ret = of_property_read_u32_array(node, "qcom,phy-init-sequence", - pdata->phy_init_seq, words); - if (!ret) - pdata->phy_init_sz = words; - - return 0; -} - -static int msm_otg_reboot_notify(struct notifier_block *this, - unsigned long code, void *unused) -{ - struct msm_otg *motg = container_of(this, struct msm_otg, reboot); - - /* - * Ensure that D+/D- lines are routed to uB connector, so - * we could load bootloader/kernel at next reboot - */ - gpiod_set_value_cansleep(motg->switch_gpio, 0); - return NOTIFY_DONE; -} - -static int msm_otg_probe(struct platform_device *pdev) -{ - int ret = 0; - struct device_node *np = pdev->dev.of_node; - struct msm_otg_platform_data *pdata; - struct resource *res; - struct msm_otg *motg; - struct usb_phy *phy; - void __iomem *phy_select; - - motg = devm_kzalloc(&pdev->dev, sizeof(struct msm_otg), GFP_KERNEL); - if (!motg) - return -ENOMEM; - - motg->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), - GFP_KERNEL); - if (!motg->phy.otg) - return -ENOMEM; - - phy = &motg->phy; - phy->dev = &pdev->dev; - - motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk"); - if (IS_ERR(motg->clk)) { - dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); - return PTR_ERR(motg->clk); - } - - /* - * If USB Core is running its protocol engine based on CORE CLK, - * CORE CLK must be running at >55Mhz for correct HSUSB - * operation and USB core cannot tolerate frequency changes on - * CORE CLK. - */ - motg->pclk = devm_clk_get(&pdev->dev, np ? "iface" : "usb_hs_pclk"); - if (IS_ERR(motg->pclk)) { - dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); - return PTR_ERR(motg->pclk); - } - - /* - * USB core clock is not present on all MSM chips. This - * clock is introduced to remove the dependency on AXI - * bus frequency. - */ - motg->core_clk = devm_clk_get(&pdev->dev, - np ? "alt_core" : "usb_hs_core_clk"); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - motg->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!motg->regs) - return -ENOMEM; - - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - if (!np) - return -ENXIO; - ret = msm_otg_read_dt(pdev, motg); - if (ret) - return ret; - } - - /* - * NOTE: The PHYs can be multiplexed between the chipidea controller - * and the dwc3 controller, using a single bit. It is important that - * the dwc3 driver does not set this bit in an incompatible way. - */ - if (motg->phy_number) { - phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4); - if (!phy_select) - return -ENOMEM; - - /* Enable second PHY with the OTG port */ - writel(0x1, phy_select); - } - - dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); - - motg->irq = platform_get_irq(pdev, 0); - if (motg->irq < 0) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); - ret = motg->irq; - return motg->irq; - } - - motg->supplies[0].supply = "vddcx"; - motg->supplies[1].supply = "v3p3"; - motg->supplies[2].supply = "v1p8"; - - ret = devm_regulator_bulk_get(motg->phy.dev, ARRAY_SIZE(motg->supplies), - motg->supplies); - if (ret) - return ret; - - motg->vddcx = motg->supplies[0].consumer; - motg->v3p3 = motg->supplies[1].consumer; - motg->v1p8 = motg->supplies[2].consumer; - - clk_set_rate(motg->clk, 60000000); - - clk_prepare_enable(motg->clk); - clk_prepare_enable(motg->pclk); - - if (!IS_ERR(motg->core_clk)) - clk_prepare_enable(motg->core_clk); - - ret = msm_hsusb_init_vddcx(motg, 1); - if (ret) { - dev_err(&pdev->dev, "hsusb vddcx configuration failed\n"); - goto disable_clks; - } - - ret = msm_hsusb_ldo_init(motg, 1); - if (ret) { - dev_err(&pdev->dev, "hsusb vreg configuration failed\n"); - goto disable_vddcx; - } - ret = msm_hsusb_ldo_set_mode(motg, 1); - if (ret) { - dev_err(&pdev->dev, "hsusb vreg enable failed\n"); - goto disable_ldo; - } - - writel(0, USB_USBINTR); - writel(0, USB_OTGSC); - - INIT_WORK(&motg->sm_work, msm_otg_sm_work); - INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work); - ret = devm_request_irq(&pdev->dev, motg->irq, msm_otg_irq, IRQF_SHARED, - "msm_otg", motg); - if (ret) { - dev_err(&pdev->dev, "request irq failed\n"); - goto disable_ldo; - } - - phy->init = msm_phy_init; - phy->notify_disconnect = msm_phy_notify_disconnect; - phy->type = USB_PHY_TYPE_USB2; - phy->vbus_nb.notifier_call = msm_otg_vbus_notifier; - phy->id_nb.notifier_call = msm_otg_id_notifier; - - phy->io_ops = &msm_otg_io_ops; - - phy->otg->usb_phy = &motg->phy; - phy->otg->set_host = msm_otg_set_host; - phy->otg->set_peripheral = msm_otg_set_peripheral; - - msm_usb_reset(phy); - - ret = usb_add_phy_dev(&motg->phy); - if (ret) { - dev_err(&pdev->dev, "usb_add_phy failed\n"); - goto disable_ldo; - } - - ret = extcon_get_state(phy->edev, EXTCON_USB); - if (ret) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - - ret = extcon_get_state(phy->id_edev, EXTCON_USB_HOST); - if (ret) - clear_bit(ID, &motg->inputs); - else - set_bit(ID, &motg->inputs); - - platform_set_drvdata(pdev, motg); - device_init_wakeup(&pdev->dev, 1); - - if (motg->pdata->mode == USB_DR_MODE_OTG && - motg->pdata->otg_control == OTG_USER_CONTROL) { - ret = msm_otg_debugfs_init(motg); - if (ret) - dev_dbg(&pdev->dev, "Can not create mode change file\n"); - } - - if (test_bit(B_SESS_VLD, &motg->inputs)) { - /* Switch D+/D- lines to Device connector */ - gpiod_set_value_cansleep(motg->switch_gpio, 0); - } else { - /* Switch D+/D- lines to Hub */ - gpiod_set_value_cansleep(motg->switch_gpio, 1); - } - - motg->reboot.notifier_call = msm_otg_reboot_notify; - register_reboot_notifier(&motg->reboot); - - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - return 0; - -disable_ldo: - msm_hsusb_ldo_init(motg, 0); -disable_vddcx: - msm_hsusb_init_vddcx(motg, 0); -disable_clks: - clk_disable_unprepare(motg->pclk); - clk_disable_unprepare(motg->clk); - if (!IS_ERR(motg->core_clk)) - clk_disable_unprepare(motg->core_clk); - - return ret; -} - -static int msm_otg_remove(struct platform_device *pdev) -{ - struct msm_otg *motg = platform_get_drvdata(pdev); - struct usb_phy *phy = &motg->phy; - int cnt = 0; - - if (phy->otg->host || phy->otg->gadget) - return -EBUSY; - - unregister_reboot_notifier(&motg->reboot); - - /* - * Ensure that D+/D- lines are routed to uB connector, so - * we could load bootloader/kernel at next reboot - */ - gpiod_set_value_cansleep(motg->switch_gpio, 0); - - msm_otg_debugfs_cleanup(); - cancel_delayed_work_sync(&motg->chg_work); - cancel_work_sync(&motg->sm_work); - - pm_runtime_resume(&pdev->dev); - - device_init_wakeup(&pdev->dev, 0); - pm_runtime_disable(&pdev->dev); - - usb_remove_phy(phy); - disable_irq(motg->irq); - - /* - * Put PHY in low power mode. - */ - ulpi_read(phy, 0x14); - ulpi_write(phy, 0x08, 0x09); - - writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { - if (readl(USB_PORTSC) & PORTSC_PHCD) - break; - udelay(1); - cnt++; - } - if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) - dev_err(phy->dev, "Unable to suspend PHY\n"); - - clk_disable_unprepare(motg->pclk); - clk_disable_unprepare(motg->clk); - if (!IS_ERR(motg->core_clk)) - clk_disable_unprepare(motg->core_clk); - msm_hsusb_ldo_init(motg, 0); - - pm_runtime_set_suspended(&pdev->dev); - - return 0; -} - -#ifdef CONFIG_PM -static int msm_otg_runtime_idle(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - struct usb_otg *otg = motg->phy.otg; - - dev_dbg(dev, "OTG runtime idle\n"); - - /* - * It is observed some times that a spurious interrupt - * comes when PHY is put into LPM immediately after PHY reset. - * This 1 sec delay also prevents entering into LPM immediately - * after asynchronous interrupt. - */ - if (otg->state != OTG_STATE_UNDEFINED) - pm_schedule_suspend(dev, 1000); - - return -EAGAIN; -} - -static int msm_otg_runtime_suspend(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG runtime suspend\n"); - return msm_otg_suspend(motg); -} - -static int msm_otg_runtime_resume(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG runtime resume\n"); - return msm_otg_resume(motg); -} -#endif - -#ifdef CONFIG_PM_SLEEP -static int msm_otg_pm_suspend(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG PM suspend\n"); - return msm_otg_suspend(motg); -} - -static int msm_otg_pm_resume(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - int ret; - - dev_dbg(dev, "OTG PM resume\n"); - - ret = msm_otg_resume(motg); - if (ret) - return ret; - - /* - * Runtime PM Documentation recommends bringing the - * device to full powered state upon resume. - */ - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - return 0; -} -#endif - -static const struct dev_pm_ops msm_otg_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) - SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, - msm_otg_runtime_idle) -}; - -static struct platform_driver msm_otg_driver = { - .probe = msm_otg_probe, - .remove = msm_otg_remove, - .driver = { - .name = DRIVER_NAME, - .pm = &msm_otg_dev_pm_ops, - .of_match_table = msm_otg_dt_match, - }, -}; - -module_platform_driver(msm_otg_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MSM USB transceiver driver"); diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index 0e315694adc9..554b72282276 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. * Author: Chao Xie <chao.xie@marvell.com> * Neil Zhang <zhangwm@marvell.com> - * - * 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. */ #include <linux/module.h> @@ -87,9 +83,10 @@ static void mv_otg_run_state_machine(struct mv_otg *mvotg, queue_delayed_work(mvotg->qwork, &mvotg->work, delay); } -static void mv_otg_timer_await_bcon(unsigned long data) +static void mv_otg_timer_await_bcon(struct timer_list *t) { - struct mv_otg *mvotg = (struct mv_otg *) data; + struct mv_otg *mvotg = from_timer(mvotg, t, + otg_ctrl.timer[A_WAIT_BCON_TIMER]); mvotg->otg_ctrl.a_wait_bcon_timeout = 1; @@ -117,8 +114,7 @@ static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id) } static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id, - unsigned long interval, - void (*callback) (unsigned long)) + unsigned long interval) { struct timer_list *timer; @@ -131,9 +127,6 @@ static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id, return -EBUSY; } - init_timer(timer); - timer->data = (unsigned long) mvotg; - timer->function = callback; timer->expires = jiffies + interval; add_timer(timer); @@ -459,8 +452,7 @@ run: if (old_state != OTG_STATE_A_HOST) mv_otg_start_host(mvotg, 1); mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER, - T_A_WAIT_BCON, - mv_otg_timer_await_bcon); + T_A_WAIT_BCON); /* * Now, we directly enter A_HOST. So set b_conn = 1 * here. In fact, it need host driver to notify us. @@ -722,7 +714,8 @@ static int mv_otg_probe(struct platform_device *pdev) otg->set_vbus = mv_otg_set_vbus; for (i = 0; i < OTG_TIMER_NUM; i++) - init_timer(&mvotg->otg_ctrl.timer[i]); + timer_setup(&mvotg->otg_ctrl.timer[i], + mv_otg_timer_await_bcon, 0); r = platform_get_resource_byname(mvotg->pdev, IORESOURCE_MEM, "phyregs"); diff --git a/drivers/usb/phy/phy-mv-usb.h b/drivers/usb/phy/phy-mv-usb.h index 551da6eb0ba8..96701a1229ad 100644 --- a/drivers/usb/phy/phy-mv-usb.h +++ b/drivers/usb/phy/phy-mv-usb.h @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #ifndef __MV_USB_OTG_CONTROLLER__ diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 0e2f1a36d315..da031c45395a 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -1,14 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2012-2014 Freescale Semiconductor, Inc. * Copyright (C) 2012 Marek Vasut <marex@denx.de> * on behalf of DENX Software Engineering GmbH - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html */ #include <linux/module.h> @@ -67,11 +61,26 @@ #define ANADIG_ANA_MISC0_SET 0x154 #define ANADIG_ANA_MISC0_CLR 0x158 +#define ANADIG_USB1_CHRG_DETECT_SET 0x1b4 +#define ANADIG_USB1_CHRG_DETECT_CLR 0x1b8 +#define ANADIG_USB1_CHRG_DETECT_EN_B BIT(20) +#define ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B BIT(19) +#define ANADIG_USB1_CHRG_DETECT_CHK_CONTACT BIT(18) + #define ANADIG_USB1_VBUS_DET_STAT 0x1c0 +#define ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) + +#define ANADIG_USB1_CHRG_DET_STAT 0x1d0 +#define ANADIG_USB1_CHRG_DET_STAT_DM_STATE BIT(2) +#define ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED BIT(1) +#define ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT BIT(0) + #define ANADIG_USB2_VBUS_DET_STAT 0x220 #define ANADIG_USB1_LOOPBACK_SET 0x1e4 #define ANADIG_USB1_LOOPBACK_CLR 0x1e8 +#define ANADIG_USB1_LOOPBACK_UTMI_TESTSTART BIT(0) + #define ANADIG_USB2_LOOPBACK_SET 0x244 #define ANADIG_USB2_LOOPBACK_CLR 0x248 @@ -479,6 +488,144 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, return 0; } +#define MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT 100 +static int mxs_charger_data_contact_detect(struct mxs_phy *x) +{ + struct regmap *regmap = x->regmap_anatop; + int i, stable_contact_count = 0; + u32 val; + + /* Check if vbus is valid */ + regmap_read(regmap, ANADIG_USB1_VBUS_DET_STAT, &val); + if (!(val & ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)) { + dev_err(x->phy.dev, "vbus is not valid\n"); + return -EINVAL; + } + + /* Enable charger detector */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_CLR, + ANADIG_USB1_CHRG_DETECT_EN_B); + /* + * - Do not check whether a charger is connected to the USB port + * - Check whether the USB plug has been in contact with each other + */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, + ANADIG_USB1_CHRG_DETECT_CHK_CONTACT | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + + /* Check if plug is connected */ + for (i = 0; i < MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT; i++) { + regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); + if (val & ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT) { + stable_contact_count++; + if (stable_contact_count > 5) + /* Data pin makes contact */ + break; + else + usleep_range(5000, 10000); + } else { + stable_contact_count = 0; + usleep_range(5000, 6000); + } + } + + if (i == MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT) { + dev_err(x->phy.dev, + "Data pin can't make good contact.\n"); + /* Disable charger detector */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, + ANADIG_USB1_CHRG_DETECT_EN_B | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + return -ENXIO; + } + + return 0; +} + +static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x) +{ + struct regmap *regmap = x->regmap_anatop; + enum usb_charger_type chgr_type = UNKNOWN_TYPE; + u32 val; + + /* + * - Do check whether a charger is connected to the USB port + * - Do not Check whether the USB plug has been in contact with + * each other + */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_CLR, + ANADIG_USB1_CHRG_DETECT_CHK_CONTACT | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + + msleep(100); + + /* Check if it is a charger */ + regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); + if (!(val & ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED)) { + chgr_type = SDP_TYPE; + dev_dbg(x->phy.dev, "It is a stardard downstream port\n"); + } + + /* Disable charger detector */ + regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, + ANADIG_USB1_CHRG_DETECT_EN_B | + ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); + + return chgr_type; +} + +/* + * It must be called after DP is pulled up, which is used to + * differentiate DCP and CDP. + */ +enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x) +{ + struct regmap *regmap = x->regmap_anatop; + int val; + + msleep(80); + + regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); + if (val & ANADIG_USB1_CHRG_DET_STAT_DM_STATE) { + dev_dbg(x->phy.dev, "It is a dedicate charging port\n"); + return DCP_TYPE; + } else { + dev_dbg(x->phy.dev, "It is a charging downstream port\n"); + return CDP_TYPE; + } +} + +static enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(phy); + struct regmap *regmap = mxs_phy->regmap_anatop; + void __iomem *base = phy->io_priv; + enum usb_charger_type chgr_type = UNKNOWN_TYPE; + + if (mxs_charger_data_contact_detect(mxs_phy)) + return chgr_type; + + chgr_type = mxs_charger_primary_detection(mxs_phy); + + if (chgr_type != SDP_TYPE) { + /* Pull up DP via test */ + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_CLR); + regmap_write(regmap, ANADIG_USB1_LOOPBACK_SET, + ANADIG_USB1_LOOPBACK_UTMI_TESTSTART); + + chgr_type = mxs_charger_secondary_detection(mxs_phy); + + /* Stop the test */ + regmap_write(regmap, ANADIG_USB1_LOOPBACK_CLR, + ANADIG_USB1_LOOPBACK_UTMI_TESTSTART); + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_SET); + } + + return chgr_type; +} + static int mxs_phy_probe(struct platform_device *pdev) { struct resource *res; @@ -567,6 +714,7 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; mxs_phy->phy.type = USB_PHY_TYPE_USB2; mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup; + mxs_phy->phy.charger_detect = mxs_phy_charger_detect; mxs_phy->clk = clk; mxs_phy->data = of_id->data; diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c index 800d1d90753d..ee0863c6553e 100644 --- a/drivers/usb/phy/phy-omap-otg.c +++ b/drivers/usb/phy/phy-omap-otg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OMAP OTG controller driver * @@ -6,15 +7,6 @@ * Copyright (C) 2005-2006 Nokia Corporation * Copyright (C) 2004 Texas Instruments * Copyright (C) 2004 David Brownell - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file "COPYING" in the main directory of this - * archive for more details. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/io.h> diff --git a/drivers/usb/phy/phy-qcom-8x16-usb.c b/drivers/usb/phy/phy-qcom-8x16-usb.c deleted file mode 100644 index 679afeaaa9a8..000000000000 --- a/drivers/usb/phy/phy-qcom-8x16-usb.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2015, Linaro Limited - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/extcon.h> -#include <linux/gpio/consumer.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/reboot.h> -#include <linux/regulator/consumer.h> -#include <linux/reset.h> -#include <linux/slab.h> -#include <linux/usb.h> -#include <linux/usb/ulpi.h> - -#define HSPHY_AHBBURST 0x0090 -#define HSPHY_AHBMODE 0x0098 -#define HSPHY_GENCONFIG 0x009c -#define HSPHY_GENCONFIG_2 0x00a0 - -#define HSPHY_USBCMD 0x0140 -#define HSPHY_ULPI_VIEWPORT 0x0170 -#define HSPHY_CTRL 0x0240 - -#define HSPHY_TXFIFO_IDLE_FORCE_DIS BIT(4) -#define HSPHY_SESS_VLD_CTRL_EN BIT(7) -#define HSPHY_POR_ASSERT BIT(0) -#define HSPHY_RETEN BIT(1) - -#define HSPHY_SESS_VLD_CTRL BIT(25) - -#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) - -#define HSPHY_3P3_MIN 3050000 /* uV */ -#define HSPHY_3P3_MAX 3300000 /* uV */ - -#define HSPHY_1P8_MIN 1800000 /* uV */ -#define HSPHY_1P8_MAX 1800000 /* uV */ - -#define HSPHY_VDD_MIN 5 -#define HSPHY_VDD_MAX 7 - -struct phy_8x16 { - struct usb_phy phy; - void __iomem *regs; - struct clk *core_clk; - struct clk *iface_clk; - struct regulator_bulk_data regulator[3]; - - struct reset_control *phy_reset; - - struct gpio_desc *switch_gpio; - struct notifier_block reboot_notify; -}; - -static int phy_8x16_notify_connect(struct usb_phy *phy, - enum usb_device_speed speed) -{ - struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy); - u32 val; - - val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; - usb_phy_io_write(&qphy->phy, val, ULPI_SET(ULPI_MISC_A)); - - val = readl(qphy->regs + HSPHY_USBCMD); - val |= HSPHY_SESS_VLD_CTRL; - writel(val, qphy->regs + HSPHY_USBCMD); - - return 0; -} - -static int phy_8x16_notify_disconnect(struct usb_phy *phy, - enum usb_device_speed speed) -{ - struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy); - u32 val; - - val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; - usb_phy_io_write(&qphy->phy, val, ULPI_CLR(ULPI_MISC_A)); - - val = readl(qphy->regs + HSPHY_USBCMD); - val &= ~HSPHY_SESS_VLD_CTRL; - writel(val, qphy->regs + HSPHY_USBCMD); - - return 0; -} - -static int phy_8x16_vbus_on(struct phy_8x16 *qphy) -{ - phy_8x16_notify_connect(&qphy->phy, USB_SPEED_UNKNOWN); - - /* Switch D+/D- lines to Device connector */ - gpiod_set_value_cansleep(qphy->switch_gpio, 0); - - return 0; -} - -static int phy_8x16_vbus_off(struct phy_8x16 *qphy) -{ - phy_8x16_notify_disconnect(&qphy->phy, USB_SPEED_UNKNOWN); - - /* Switch D+/D- lines to USB HUB */ - gpiod_set_value_cansleep(qphy->switch_gpio, 1); - - return 0; -} - -static int phy_8x16_vbus_notify(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct usb_phy *usb_phy = container_of(nb, struct usb_phy, vbus_nb); - struct phy_8x16 *qphy = container_of(usb_phy, struct phy_8x16, phy); - - if (event) - phy_8x16_vbus_on(qphy); - else - phy_8x16_vbus_off(qphy); - - return NOTIFY_DONE; -} - -static int phy_8x16_init(struct usb_phy *phy) -{ - struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy); - u32 val, init[] = {0x44, 0x6B, 0x24, 0x13}; - u32 addr = ULPI_EXT_VENDOR_SPECIFIC; - int idx, state; - - for (idx = 0; idx < ARRAY_SIZE(init); idx++) - usb_phy_io_write(phy, init[idx], addr + idx); - - reset_control_reset(qphy->phy_reset); - - /* Assert USB HSPHY_POR */ - val = readl(qphy->regs + HSPHY_CTRL); - val |= HSPHY_POR_ASSERT; - writel(val, qphy->regs + HSPHY_CTRL); - - /* - * wait for minimum 10 microseconds as suggested in HPG. - * Use a slightly larger value since the exact value didn't - * work 100% of the time. - */ - usleep_range(12, 15); - - /* Deassert USB HSPHY_POR */ - val = readl(qphy->regs + HSPHY_CTRL); - val &= ~HSPHY_POR_ASSERT; - writel(val, qphy->regs + HSPHY_CTRL); - - usleep_range(10, 15); - - writel(0x00, qphy->regs + HSPHY_AHBBURST); - writel(0x08, qphy->regs + HSPHY_AHBMODE); - - /* workaround for rx buffer collision issue */ - val = readl(qphy->regs + HSPHY_GENCONFIG); - val &= ~HSPHY_TXFIFO_IDLE_FORCE_DIS; - writel(val, qphy->regs + HSPHY_GENCONFIG); - - val = readl(qphy->regs + HSPHY_GENCONFIG_2); - val |= HSPHY_SESS_VLD_CTRL_EN; - writel(val, qphy->regs + HSPHY_GENCONFIG_2); - - val = ULPI_PWR_OTG_COMP_DISABLE; - usb_phy_io_write(phy, val, ULPI_SET(ULPI_PWR_CLK_MNG_REG)); - - state = extcon_get_state(qphy->phy.edev, EXTCON_USB); - if (state) - phy_8x16_vbus_on(qphy); - else - phy_8x16_vbus_off(qphy); - - val = usb_phy_io_read(&qphy->phy, ULPI_FUNC_CTRL); - val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; - usb_phy_io_write(&qphy->phy, val, ULPI_FUNC_CTRL); - - return 0; -} - -static void phy_8x16_shutdown(struct usb_phy *phy) -{ - u32 val; - - /* Put the controller in non-driving mode */ - val = usb_phy_io_read(phy, ULPI_FUNC_CTRL); - val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; - val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - usb_phy_io_write(phy, val, ULPI_FUNC_CTRL); -} - -static int phy_8x16_read_devicetree(struct phy_8x16 *qphy) -{ - struct device *dev = qphy->phy.dev; - int ret; - - qphy->core_clk = devm_clk_get(dev, "core"); - if (IS_ERR(qphy->core_clk)) - return PTR_ERR(qphy->core_clk); - - qphy->iface_clk = devm_clk_get(dev, "iface"); - if (IS_ERR(qphy->iface_clk)) - return PTR_ERR(qphy->iface_clk); - - qphy->regulator[0].supply = "v3p3"; - qphy->regulator[1].supply = "v1p8"; - qphy->regulator[2].supply = "vddcx"; - - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(qphy->regulator), - qphy->regulator); - if (ret) - return ret; - - qphy->phy_reset = devm_reset_control_get(dev, "phy"); - if (IS_ERR(qphy->phy_reset)) - return PTR_ERR(qphy->phy_reset); - - qphy->switch_gpio = devm_gpiod_get_optional(dev, "switch", - GPIOD_OUT_LOW); - return PTR_ERR_OR_ZERO(qphy->switch_gpio); -} - -static int phy_8x16_reboot_notify(struct notifier_block *this, - unsigned long code, void *unused) -{ - struct phy_8x16 *qphy; - - qphy = container_of(this, struct phy_8x16, reboot_notify); - - /* - * Ensure that D+/D- lines are routed to uB connector, so - * we could load bootloader/kernel at next reboot_notify - */ - gpiod_set_value_cansleep(qphy->switch_gpio, 0); - return NOTIFY_DONE; -} - -static int phy_8x16_probe(struct platform_device *pdev) -{ - struct phy_8x16 *qphy; - struct resource *res; - struct usb_phy *phy; - int ret; - - qphy = devm_kzalloc(&pdev->dev, sizeof(*qphy), GFP_KERNEL); - if (!qphy) - return -ENOMEM; - - platform_set_drvdata(pdev, qphy); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - qphy->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(qphy->regs)) - return PTR_ERR(qphy->regs); - - phy = &qphy->phy; - phy->dev = &pdev->dev; - phy->label = dev_name(&pdev->dev); - phy->init = phy_8x16_init; - phy->shutdown = phy_8x16_shutdown; - phy->notify_connect = phy_8x16_notify_connect; - phy->notify_disconnect = phy_8x16_notify_disconnect; - phy->io_priv = qphy->regs + HSPHY_ULPI_VIEWPORT; - phy->io_ops = &ulpi_viewport_access_ops; - phy->type = USB_PHY_TYPE_USB2; - phy->vbus_nb.notifier_call = phy_8x16_vbus_notify; - phy->id_nb.notifier_call = NULL; - - ret = phy_8x16_read_devicetree(qphy); - if (ret < 0) - return ret; - - ret = clk_set_rate(qphy->core_clk, INT_MAX); - if (ret < 0) - dev_dbg(phy->dev, "Can't boost core clock\n"); - - ret = clk_prepare_enable(qphy->core_clk); - if (ret < 0) - return ret; - - ret = clk_prepare_enable(qphy->iface_clk); - if (ret < 0) - goto off_core; - - ret = regulator_bulk_enable(ARRAY_SIZE(qphy->regulator), - qphy->regulator); - if (WARN_ON(ret)) - goto off_clks; - - ret = usb_add_phy_dev(&qphy->phy); - if (ret) - goto off_power; - - qphy->reboot_notify.notifier_call = phy_8x16_reboot_notify; - register_reboot_notifier(&qphy->reboot_notify); - - return 0; - -off_power: - regulator_bulk_disable(ARRAY_SIZE(qphy->regulator), qphy->regulator); -off_clks: - clk_disable_unprepare(qphy->iface_clk); -off_core: - clk_disable_unprepare(qphy->core_clk); - return ret; -} - -static int phy_8x16_remove(struct platform_device *pdev) -{ - struct phy_8x16 *qphy = platform_get_drvdata(pdev); - - unregister_reboot_notifier(&qphy->reboot_notify); - - /* - * Ensure that D+/D- lines are routed to uB connector, so - * we could load bootloader/kernel at next reboot_notify - */ - gpiod_set_value_cansleep(qphy->switch_gpio, 0); - - usb_remove_phy(&qphy->phy); - - clk_disable_unprepare(qphy->iface_clk); - clk_disable_unprepare(qphy->core_clk); - regulator_bulk_disable(ARRAY_SIZE(qphy->regulator), qphy->regulator); - return 0; -} - -static const struct of_device_id phy_8x16_dt_match[] = { - { .compatible = "qcom,usb-8x16-phy" }, - { } -}; -MODULE_DEVICE_TABLE(of, phy_8x16_dt_match); - -static struct platform_driver phy_8x16_driver = { - .probe = phy_8x16_probe, - .remove = phy_8x16_remove, - .driver = { - .name = "phy-qcom-8x16-usb", - .of_match_table = phy_8x16_dt_match, - }, -}; -module_platform_driver(phy_8x16_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Qualcomm APQ8016/MSM8916 chipsets USB transceiver driver"); diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 8babd318c0ed..b3ce42edb373 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Tahvo USB transceiver driver * @@ -9,21 +10,12 @@ * * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs. * Modified for Retu/Tahvo MFD by Aaro Koskinen. - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file "COPYING" in the main directory of this - * archive for more details. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/io.h> #include <linux/clk.h> #include <linux/usb.h> -#include <linux/extcon.h> +#include <linux/extcon-provider.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/usb/otg.h> @@ -368,7 +360,8 @@ static int tahvo_usb_probe(struct platform_device *pdev) tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable); if (IS_ERR(tu->extcon)) { dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); - return -ENOMEM; + ret = PTR_ERR(tu->extcon); + goto err_disable_clk; } ret = devm_extcon_dev_register(&pdev->dev, tu->extcon); diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 5fe4a5704bde..f668bfb708d3 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2010 Google, Inc. * Copyright (C) 2013 NVIDIA Corporation @@ -6,16 +7,6 @@ * Erik Gilling <konkers@google.com> * Benoit Goby <benoit@android.com> * Venu Byravarasu <vbyravarasu@nvidia.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/resource.h> @@ -329,6 +320,14 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) unsigned long val; void __iomem *base = phy->regs; + /* + * The USB driver may have already initiated the phy clock + * disable so wait to see if the clock turns off and if not + * then proceed with gating the clock. + */ + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) == 0) + return; + if (phy->is_legacy_phy) { val = readl(base + USB_SUSP_CTRL); val |= USB_SUSP_SET; @@ -351,6 +350,15 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) unsigned long val; void __iomem *base = phy->regs; + /* + * The USB driver may have already initiated the phy clock + * enable so wait to see if the clock turns on and if not + * then proceed with ungating the clock. + */ + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, + USB_PHY_CLK_VALID) == 0) + return; + if (phy->is_legacy_phy) { val = readl(base + USB_SUSP_CTRL); val |= USB_SUSP_CLR; diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index b5dc077ed7d3..e78ed52339e6 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver. * * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com - * 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. * * Author: Hema HK <hemahk@ti.com> - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #include <linux/module.h> diff --git a/drivers/usb/phy/phy-ulpi-viewport.c b/drivers/usb/phy/phy-ulpi-viewport.c index 18bb8264b5a0..7a14e0e3b635 100644 --- a/drivers/usb/phy/phy-ulpi-viewport.c +++ b/drivers/usb/phy/phy-ulpi-viewport.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2011 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/export.h> diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c index f48a7a21e3c2..a43c49369a60 100644 --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Generic ULPI USB transceiver support * @@ -7,20 +8,6 @@ * * Sascha Hauer <s.hauer@pengutronix.de> * Freescale Semiconductors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/kernel.h> diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 89f4ac4cd93e..f97cb47577fc 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * phy.c -- USB phy handling * * Copyright (C) 2004-2013 Texas Instruments - * - * 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. */ #include <linux/kernel.h> #include <linux/export.h> |