diff options
Diffstat (limited to 'drivers/usb/phy')
24 files changed, 943 insertions, 331 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 08e2f39027e..7d1451d5bbe 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -6,6 +6,15 @@ menu "USB Physical Layer drivers" config USB_PHY def_bool n +config USB_OTG_FSM + tristate "USB 2.0 OTG FSM implementation" + depends on USB + select USB_OTG + select USB_PHY + help + Implements OTG Final State Machine as specified in On-The-Go + and Embedded Host Supplement to the USB Revision 2.0 Specification. + # # USB Transceiver Drivers # @@ -20,7 +29,7 @@ config AB8500_USB config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" - depends on USB_EHCI_FSL && USB_FSL_USB2 && PM_RUNTIME + depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM_RUNTIME select USB_OTG select USB_PHY help @@ -29,6 +38,7 @@ config FSL_USB2_OTG config ISP1301_OMAP tristate "Philips ISP1301 with OMAP OTG" depends on I2C && ARCH_OMAP_OTG + depends on USB select USB_PHY help If you say yes here you get support for the Philips ISP1301 @@ -38,7 +48,16 @@ config ISP1301_OMAP Instruments OMAP processors. This driver can also be built as a module. If so, the module - will be called isp1301_omap. + will be called phy-isp1301-omap. + +config KEYSTONE_USB_PHY + tristate "Keystone USB PHY Driver" + depends on ARCH_KEYSTONE || COMPILE_TEST + select NOP_USB_XCEIV + help + Enable this to support Keystone USB phy. This driver provides + interface to interact with USB 2.0 and USB 3.0 PHY that is part + of the Keystone SOC. config MV_U3D_PHY bool "Marvell USB 3.0 PHY controller Driver" @@ -134,6 +153,31 @@ config USB_GPIO_VBUS optionally control of a D+ pullup GPIO as well as a VBUS current limit regulator. +config OMAP_OTG + tristate "OMAP USB OTG controller driver" + depends on ARCH_OMAP_OTG && EXTCON + help + Enable this to support some transceivers on OMAP1 platforms. OTG + controller is needed to switch between host and peripheral modes. + + This driver can also be built as a module. If so, the module + will be called phy-omap-otg. + +config TAHVO_USB + tristate "Tahvo USB transceiver driver" + depends on MFD_RETU && EXTCON + select USB_PHY + help + Enable this to support USB transceiver on Tahvo. This is used + at least on Nokia 770. + +config TAHVO_USB_HOST_BY_DEFAULT + depends on TAHVO_USB + boolean "Device in USB host mode by default" + help + Say Y here, if you want the device to enter USB host mode + by default on bootup. + config USB_ISP1301 tristate "NXP ISP1301 USB transceiver support" depends on USB || USB_GADGET @@ -145,7 +189,7 @@ config USB_ISP1301 and OTG drivers (to be selected separately). To compile this driver as a module, choose M here: the - module will be called isp1301. + module will be called phy-isp1301. config USB_MSM_OTG tristate "OTG support for Qualcomm on-chip USB controller" diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 022c1da7fb7..be58adae349 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -3,18 +3,20 @@ # obj-$(CONFIG_USB_PHY) += phy.o obj-$(CONFIG_OF) += of.o +obj-$(CONFIG_USB_OTG_FSM) += phy-fsm-usb.o # transceiver drivers, keep the list sorted obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o -phy-fsl-usb2-objs := phy-fsl-usb.o phy-fsm-usb.o -obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb2.o +obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb.o obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o +obj-$(CONFIG_TAHVO_USB) += phy-tahvo.o obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o +obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o @@ -30,3 +32,4 @@ obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o +obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 087402350b6..11ab2c45e46 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1415,8 +1415,6 @@ static int ab8500_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ab); - ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier); - /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 634f49acd20..d75196ad5f2 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -3,11 +3,7 @@ #include <linux/err.h> #include <linux/of.h> #include <linux/io.h> - -struct phy_control { - void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on); - void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on); -}; +#include "am35x-phy-control.h" struct am335x_control_usb { struct device *dev; diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 6370e50649d..12fc3468a01 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -52,8 +52,7 @@ static int am335x_phy_probe(struct platform_device *pdev) return am_phy->id; } - ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, - USB_PHY_TYPE_USB2, 0, false); + ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, NULL); if (ret) return ret; @@ -64,10 +63,21 @@ static int am335x_phy_probe(struct platform_device *pdev) am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; platform_set_drvdata(pdev, am_phy); + device_init_wakeup(dev, true); + + /* + * If we leave PHY wakeup enabled then AM33XX wakes up + * immediately from DS0. To avoid this we mark dev->power.can_wakeup + * to false. The same is checked in suspend routine to decide + * on whether to enable PHY wakeup or not. + * PHY wakeup works fine in standby mode, there by allowing us to + * handle remote wakeup, wakeup on disconnect and connect. + */ + + device_set_wakeup_enable(dev, false); + phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false); return 0; - - return ret; } static int am335x_phy_remove(struct platform_device *pdev) @@ -78,38 +88,48 @@ static int am335x_phy_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_RUNTIME - -static int am335x_phy_runtime_suspend(struct device *dev) +#ifdef CONFIG_PM_SLEEP +static int am335x_phy_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct am335x_phy *am_phy = platform_get_drvdata(pdev); + /* + * Enable phy wakeup only if dev->power.can_wakeup is true. + * Make sure to enable wakeup to support remote wakeup in + * standby mode ( same is not supported in OFF(DS0) mode). + * Enable it by doing + * echo enabled > /sys/bus/platform/devices/<usb-phy-id>/power/wakeup + */ + if (device_may_wakeup(dev)) phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true); + phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false); + return 0; } -static int am335x_phy_runtime_resume(struct device *dev) +static int am335x_phy_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct am335x_phy *am_phy = platform_get_drvdata(pdev); phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true); + if (device_may_wakeup(dev)) phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false); + return 0; } static const struct dev_pm_ops am335x_pm_ops = { - SET_RUNTIME_PM_OPS(am335x_phy_runtime_suspend, - am335x_phy_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume) }; -#define DEV_PM_OPS (&am335x_pm_ops) +#define DEV_PM_OPS (&am335x_pm_ops) #else -#define DEV_PM_OPS NULL +#define DEV_PM_OPS NULL #endif static const struct of_device_id am335x_phy_ids[] = { diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index 7f3c73b967c..2b0f968d932 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -27,7 +27,6 @@ #include <linux/slab.h> #include <linux/proc_fs.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/timer.h> @@ -848,7 +847,7 @@ static int fsl_otg_conf(struct platform_device *pdev) pr_info("Couldn't init OTG timers\n"); goto err; } - spin_lock_init(&fsl_otg_tc->fsm.lock); + mutex_init(&fsl_otg_tc->fsm.lock); /* Set OTG state machine operations */ fsl_otg_tc->fsm.ops = &fsl_otg_ops; @@ -1017,10 +1016,9 @@ static int show_fsl_usb2_otg_state(struct device *dev, struct otg_fsm *fsm = &fsl_otg_dev->fsm; char *next = buf; unsigned size = PAGE_SIZE; - unsigned long flags; int t; - spin_lock_irqsave(&fsm->lock, flags); + mutex_lock(&fsm->lock); /* basic driver infomation */ t = scnprintf(next, size, @@ -1088,7 +1086,7 @@ static int show_fsl_usb2_otg_state(struct device *dev, size -= t; next += t; - spin_unlock_irqrestore(&fsm->lock, flags); + mutex_unlock(&fsm->lock); return PAGE_SIZE - size; } diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index 7365170a2f2..5986c96354d 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -15,7 +15,7 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "phy-fsm-usb.h" +#include <linux/usb/otg-fsm.h> #include <linux/usb/otg.h> #include <linux/ioctl.h> diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 329c2d2f859..7aa314ef4a8 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -23,13 +23,12 @@ #include <linux/kernel.h> #include <linux/types.h> -#include <linux/spinlock.h> +#include <linux/mutex.h> #include <linux/delay.h> #include <linux/usb.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> - -#include "phy-fsm-usb.h" +#include <linux/usb/otg-fsm.h> /* Change USB protocol when there is a protocol change */ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) @@ -65,7 +64,7 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) static int state_changed; /* Called when leaving a state. Do state clean up jobs here */ -void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) +static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) { switch (old_state) { case OTG_STATE_B_IDLE: @@ -122,7 +121,7 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) } /* Called when entering a state */ -int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) +static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) { state_changed = 1; if (fsm->otg->phy->state == new_state) @@ -245,9 +244,8 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) int otg_statemachine(struct otg_fsm *fsm) { enum usb_otg_state state; - unsigned long flags; - spin_lock_irqsave(&fsm->lock, flags); + mutex_lock(&fsm->lock); state = fsm->otg->phy->state; state_changed = 0; @@ -359,7 +357,7 @@ int otg_statemachine(struct otg_fsm *fsm) default: break; } - spin_unlock_irqrestore(&fsm->lock, flags); + mutex_unlock(&fsm->lock); VDBG("quit statemachine, changed = %d\n", state_changed); return state_changed; diff --git a/drivers/usb/phy/phy-fsm-usb.h b/drivers/usb/phy/phy-fsm-usb.h deleted file mode 100644 index 7441b46a27f..00000000000 --- a/drivers/usb/phy/phy-fsm-usb.h +++ /dev/null @@ -1,236 +0,0 @@ -/* 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. - */ - -#undef VERBOSE - -#ifdef VERBOSE -#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \ - __func__, ## args) -#else -#define VDBG(stuff...) do {} while (0) -#endif - -#ifdef VERBOSE -#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) -#else -#define MPC_LOC do {} while (0) -#endif - -#define PROTO_UNDEF (0) -#define PROTO_HOST (1) -#define PROTO_GADGET (2) - -enum otg_fsm_timer { - /* Standard OTG timers */ - A_WAIT_VRISE, - A_WAIT_VFALL, - A_WAIT_BCON, - A_AIDL_BDIS, - B_ASE0_BRST, - A_BIDL_ADIS, - - /* Auxiliary timers */ - B_SE0_SRP, - B_SRP_FAIL, - A_WAIT_ENUM, - - NUM_OTG_FSM_TIMERS, -}; - -/* OTG state machine according to the OTG spec */ -struct otg_fsm { - /* Input */ - int id; - int adp_change; - int power_up; - int test_device; - int a_bus_drop; - int a_bus_req; - int a_srp_det; - int a_vbus_vld; - int b_conn; - int a_bus_resume; - int a_bus_suspend; - int a_conn; - int b_bus_req; - int b_se0_srp; - int b_ssend_srp; - int b_sess_vld; - /* Auxilary inputs */ - int a_sess_vld; - int b_bus_resume; - int b_bus_suspend; - - /* Output */ - int data_pulse; - int drv_vbus; - int loc_conn; - int loc_sof; - int adp_prb; - int adp_sns; - - /* Internal variables */ - int a_set_b_hnp_en; - int b_srp_done; - int b_hnp_enable; - int a_clr_err; - - /* Informative variables */ - int a_bus_drop_inf; - int a_bus_req_inf; - int a_clr_err_inf; - int b_bus_req_inf; - /* Auxilary informative variables */ - int a_suspend_req_inf; - - /* Timeout indicator for timers */ - int a_wait_vrise_tmout; - int a_wait_vfall_tmout; - int a_wait_bcon_tmout; - int a_aidl_bdis_tmout; - int b_ase0_brst_tmout; - int a_bidl_adis_tmout; - - struct otg_fsm_ops *ops; - struct usb_otg *otg; - - /* Current usb protocol used: 0:undefine; 1:host; 2:client */ - int protocol; - spinlock_t lock; -}; - -struct otg_fsm_ops { - void (*chrg_vbus)(struct otg_fsm *fsm, int on); - void (*drv_vbus)(struct otg_fsm *fsm, int on); - void (*loc_conn)(struct otg_fsm *fsm, int on); - void (*loc_sof)(struct otg_fsm *fsm, int on); - void (*start_pulse)(struct otg_fsm *fsm); - void (*start_adp_prb)(struct otg_fsm *fsm); - void (*start_adp_sns)(struct otg_fsm *fsm); - void (*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); - void (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); - int (*start_host)(struct otg_fsm *fsm, int on); - int (*start_gadget)(struct otg_fsm *fsm, int on); -}; - - -static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->chrg_vbus) - return -EOPNOTSUPP; - fsm->ops->chrg_vbus(fsm, on); - return 0; -} - -static inline int otg_drv_vbus(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->drv_vbus) - return -EOPNOTSUPP; - if (fsm->drv_vbus != on) { - fsm->drv_vbus = on; - fsm->ops->drv_vbus(fsm, on); - } - return 0; -} - -static inline int otg_loc_conn(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->loc_conn) - return -EOPNOTSUPP; - if (fsm->loc_conn != on) { - fsm->loc_conn = on; - fsm->ops->loc_conn(fsm, on); - } - return 0; -} - -static inline int otg_loc_sof(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->loc_sof) - return -EOPNOTSUPP; - if (fsm->loc_sof != on) { - fsm->loc_sof = on; - fsm->ops->loc_sof(fsm, on); - } - return 0; -} - -static inline int otg_start_pulse(struct otg_fsm *fsm) -{ - if (!fsm->ops->start_pulse) - return -EOPNOTSUPP; - if (!fsm->data_pulse) { - fsm->data_pulse = 1; - fsm->ops->start_pulse(fsm); - } - return 0; -} - -static inline int otg_start_adp_prb(struct otg_fsm *fsm) -{ - if (!fsm->ops->start_adp_prb) - return -EOPNOTSUPP; - if (!fsm->adp_prb) { - fsm->adp_sns = 0; - fsm->adp_prb = 1; - fsm->ops->start_adp_prb(fsm); - } - return 0; -} - -static inline int otg_start_adp_sns(struct otg_fsm *fsm) -{ - if (!fsm->ops->start_adp_sns) - return -EOPNOTSUPP; - if (!fsm->adp_sns) { - fsm->adp_sns = 1; - fsm->ops->start_adp_sns(fsm); - } - return 0; -} - -static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) -{ - if (!fsm->ops->add_timer) - return -EOPNOTSUPP; - fsm->ops->add_timer(fsm, timer); - return 0; -} - -static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) -{ - if (!fsm->ops->del_timer) - return -EOPNOTSUPP; - fsm->ops->del_timer(fsm, timer); - return 0; -} - -static inline int otg_start_host(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->start_host) - return -EOPNOTSUPP; - return fsm->ops->start_host(fsm, on); -} - -static inline int otg_start_gadget(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->start_gadget) - return -EOPNOTSUPP; - return fsm->ops->start_gadget(fsm, on); -} - -int otg_statemachine(struct otg_fsm *fsm); diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index fce3a9e9bb5..bb394980532 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -48,8 +48,9 @@ void usb_nop_xceiv_register(void) if (pd) return; pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0); - if (!pd) { + if (IS_ERR(pd)) { pr_err("Unable to register generic usb transceiver\n"); + pd = NULL; return; } } @@ -150,10 +151,40 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) } int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, - enum usb_phy_type type, u32 clk_rate, bool needs_vcc) + struct usb_phy_gen_xceiv_platform_data *pdata) { + enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; + u32 clk_rate = 0; + bool needs_vcc = false; + + nop->reset_active_low = true; /* default behaviour */ + + if (dev->of_node) { + struct device_node *node = dev->of_node; + enum of_gpio_flags flags = 0; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + needs_vcc = of_property_read_bool(node, "vcc-supply"); + nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios", + 0, &flags); + if (nop->gpio_reset == -EPROBE_DEFER) + return -EPROBE_DEFER; + + nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; + + } else if (pdata) { + type = pdata->type; + clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + nop->gpio_reset = pdata->gpio_reset; + } else { + nop->gpio_reset = -1; + } + nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg), GFP_KERNEL); if (!nop->phy.otg) @@ -210,7 +241,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); return 0; } EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); @@ -218,43 +248,14 @@ EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usb_phy_gen_xceiv_platform_data *pdata = - dev_get_platdata(&pdev->dev); struct usb_phy_gen_xceiv *nop; - enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; - u32 clk_rate = 0; - bool needs_vcc = false; nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); if (!nop) return -ENOMEM; - nop->reset_active_low = true; /* default behaviour */ - - if (dev->of_node) { - struct device_node *node = dev->of_node; - enum of_gpio_flags flags; - - if (of_property_read_u32(node, "clock-frequency", &clk_rate)) - clk_rate = 0; - - needs_vcc = of_property_read_bool(node, "vcc-supply"); - nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios", - 0, &flags); - if (nop->gpio_reset == -EPROBE_DEFER) - return -EPROBE_DEFER; - - nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; - - } else if (pdata) { - type = pdata->type; - clk_rate = pdata->clk_rate; - needs_vcc = pdata->needs_vcc; - nop->gpio_reset = pdata->gpio_reset; - } - - err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc); + err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev)); if (err) return err; @@ -271,8 +272,6 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nop); return 0; - - return err; } static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h index d2a220d8173..38a81f307b8 100644 --- a/drivers/usb/phy/phy-generic.h +++ b/drivers/usb/phy/phy-generic.h @@ -1,6 +1,8 @@ #ifndef _PHY_GENERIC_H_ #define _PHY_GENERIC_H_ +#include <linux/usb/usb_phy_gen_xceiv.h> + struct usb_phy_gen_xceiv { struct usb_phy phy; struct device *dev; @@ -14,6 +16,6 @@ int usb_gen_phy_init(struct usb_phy *phy); void usb_gen_phy_shutdown(struct usb_phy *phy); int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, - enum usb_phy_type type, u32 clk_rate, bool needs_vcc); + struct usb_phy_gen_xceiv_platform_data *pdata); #endif diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 02799a5efcd..69462e09d01 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -314,8 +314,6 @@ static int gpio_vbus_probe(struct platform_device *pdev) goto err_irq; } - ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier); - INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index d3a5160e4cc..6e146d723b3 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -1277,7 +1277,7 @@ isp1301_set_host(struct usb_otg *otg, struct usb_bus *host) { struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - if (!otg || isp != the_transceiver) + if (isp != the_transceiver) return -ENODEV; if (!host) { @@ -1333,7 +1333,7 @@ isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) { struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - if (!otg || isp != the_transceiver) + if (isp != the_transceiver) return -ENODEV; if (!gadget) { @@ -1414,8 +1414,7 @@ isp1301_start_srp(struct usb_otg *otg) struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); u32 otg_ctrl; - if (!otg || isp != the_transceiver - || isp->phy.state != OTG_STATE_B_IDLE) + if (isp != the_transceiver || isp->phy.state != OTG_STATE_B_IDLE) return -ENODEV; otg_ctrl = omap_readl(OTG_CTRL); @@ -1442,7 +1441,7 @@ isp1301_start_hnp(struct usb_otg *otg) struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); u32 l; - if (!otg || isp != the_transceiver) + if (isp != the_transceiver) return -ENODEV; if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable)) return -ENOTCONN; diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c new file mode 100644 index 00000000000..d762003896c --- /dev/null +++ b/drivers/usb/phy/phy-keystone.c @@ -0,0 +1,136 @@ +/* + * 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> +#include <linux/platform_device.h> +#include <linux/usb/usb_phy_gen_xceiv.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "phy-generic.h" + +/* USB PHY control register offsets */ +#define USB_PHY_CTL_UTMI 0x0000 +#define USB_PHY_CTL_PIPE 0x0004 +#define USB_PHY_CTL_PARAM_1 0x0008 +#define USB_PHY_CTL_PARAM_2 0x000c +#define USB_PHY_CTL_CLOCK 0x0010 +#define USB_PHY_CTL_PLL 0x0014 + +#define PHY_REF_SSP_EN BIT(29) + +struct keystone_usbphy { + struct usb_phy_gen_xceiv usb_phy_gen; + void __iomem *phy_ctrl; +}; + +static inline u32 keystone_usbphy_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void keystone_usbphy_writel(void __iomem *base, + u32 offset, u32 value) +{ + writel(value, base + offset); +} + +static int keystone_usbphy_init(struct usb_phy *phy) +{ + struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev); + u32 val; + + val = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK); + keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK, + val | PHY_REF_SSP_EN); + return 0; +} + +static void keystone_usbphy_shutdown(struct usb_phy *phy) +{ + struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev); + u32 val; + + val = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK); + keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK, + val &= ~PHY_REF_SSP_EN); +} + +static int keystone_usbphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct keystone_usbphy *k_phy; + struct resource *res; + int ret; + + k_phy = devm_kzalloc(dev, sizeof(*k_phy), GFP_KERNEL); + if (!k_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + k_phy->phy_ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(k_phy->phy_ctrl)) + return PTR_ERR(k_phy->phy_ctrl); + + ret = usb_phy_gen_create_phy(dev, &k_phy->usb_phy_gen, NULL); + if (ret) + return ret; + + k_phy->usb_phy_gen.phy.init = keystone_usbphy_init; + k_phy->usb_phy_gen.phy.shutdown = keystone_usbphy_shutdown; + + platform_set_drvdata(pdev, k_phy); + + ret = usb_add_phy_dev(&k_phy->usb_phy_gen.phy); + if (ret) + return ret; + + return 0; +} + +static int keystone_usbphy_remove(struct platform_device *pdev) +{ + struct keystone_usbphy *k_phy = platform_get_drvdata(pdev); + + usb_remove_phy(&k_phy->usb_phy_gen.phy); + + return 0; +} + +static const struct of_device_id keystone_usbphy_ids[] = { + { .compatible = "ti,keystone-usbphy" }, + { } +}; +MODULE_DEVICE_TABLE(of, keystone_usbphy_ids); + +static struct platform_driver keystone_usbphy_driver = { + .probe = keystone_usbphy_probe, + .remove = keystone_usbphy_remove, + .driver = { + .name = "keystone-usbphy", + .owner = THIS_MODULE, + .of_match_table = keystone_usbphy_ids, + }, +}; + +module_platform_driver(keystone_usbphy_driver); + +MODULE_ALIAS("platform:keystone-usbphy"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("Keystone USB phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index e9d4cd960ec..37752832b77 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -669,6 +669,7 @@ static void msm_otg_start_host(struct usb_phy *phy, int on) 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"); diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index 98f6ac6a78e..7d80c54f0ac 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/device.h> @@ -213,10 +212,12 @@ static void mv_otg_start_host(struct mv_otg *mvotg, int on) hcd = bus_to_hcd(otg->host); - if (on) + if (on) { usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); - else + device_wakeup_enable(hcd->self.controller); + } else { usb_remove_hcd(hcd); + } #endif /* CONFIG_USB */ } diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index fdd33b44dbd..b42897b6474 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -63,9 +63,13 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) static int mxs_phy_init(struct usb_phy *phy) { + int ret; struct mxs_phy *mxs_phy = to_mxs_phy(phy); - clk_prepare_enable(mxs_phy->clk); + ret = clk_prepare_enable(mxs_phy->clk); + if (ret) + return ret; + return mxs_phy_hw_init(mxs_phy); } @@ -81,6 +85,7 @@ static void mxs_phy_shutdown(struct usb_phy *phy) static int mxs_phy_suspend(struct usb_phy *x, int suspend) { + int ret; struct mxs_phy *mxs_phy = to_mxs_phy(x); if (suspend) { @@ -89,7 +94,9 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) x->io_priv + HW_USBPHY_CTRL_SET); clk_disable_unprepare(mxs_phy->clk); } else { - clk_prepare_enable(mxs_phy->clk); + ret = clk_prepare_enable(mxs_phy->clk); + if (ret) + return ret; writel(BM_USBPHY_CTRL_CLKGATE, x->io_priv + HW_USBPHY_CTRL_CLR); writel(0, x->io_priv + HW_USBPHY_PWD); @@ -160,11 +167,9 @@ 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; - ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier); - mxs_phy->clk = clk; - platform_set_drvdata(pdev, &mxs_phy->phy); + platform_set_drvdata(pdev, mxs_phy); ret = usb_add_phy_dev(&mxs_phy->phy); if (ret) diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c index 09c5ace1edd..e7253182e47 100644 --- a/drivers/usb/phy/phy-omap-control.c +++ b/drivers/usb/phy/phy-omap-control.c @@ -84,6 +84,20 @@ void omap_control_usb_phy_power(struct device *dev, int on) else val |= OMAP_CTRL_USB2_PHY_PD; break; + + case OMAP_CTRL_TYPE_AM437USB2: + if (on) { + val &= ~(AM437X_CTRL_USB2_PHY_PD | + AM437X_CTRL_USB2_OTG_PD); + val |= (AM437X_CTRL_USB2_OTGVDET_EN | + AM437X_CTRL_USB2_OTGSESSEND_EN); + } else { + val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | + AM437X_CTRL_USB2_OTGSESSEND_EN); + val |= (AM437X_CTRL_USB2_PHY_PD | + AM437X_CTRL_USB2_OTG_PD); + } + break; default: dev_err(dev, "%s: type %d not recognized\n", __func__, control_usb->type); @@ -197,6 +211,7 @@ static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS; static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2; static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; +static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; static const struct of_device_id omap_control_usb_id_table[] = { { @@ -215,6 +230,10 @@ static const struct of_device_id omap_control_usb_id_table[] = { .compatible = "ti,control-phy-dra7usb2", .data = &dra7usb2_data, }, + { + .compatible = "ti,control-phy-am437usb2", + .data = &am437usb2_data, + }, {}, }; MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c new file mode 100644 index 00000000000..11598cdb318 --- /dev/null +++ b/drivers/usb/phy/phy-omap-otg.c @@ -0,0 +1,169 @@ +/* + * OMAP OTG controller driver + * + * Based on code from tahvo-usb.c and isp1301_omap.c drivers. + * + * 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> +#include <linux/err.h> +#include <linux/extcon.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/platform_data/usb-omap1.h> + +struct otg_device { + void __iomem *base; + bool id; + bool vbus; + struct extcon_specific_cable_nb vbus_dev; + struct extcon_specific_cable_nb id_dev; + struct notifier_block vbus_nb; + struct notifier_block id_nb; +}; + +#define OMAP_OTG_CTRL 0x0c +#define OMAP_OTG_ASESSVLD (1 << 20) +#define OMAP_OTG_BSESSEND (1 << 19) +#define OMAP_OTG_BSESSVLD (1 << 18) +#define OMAP_OTG_VBUSVLD (1 << 17) +#define OMAP_OTG_ID (1 << 16) +#define OMAP_OTG_XCEIV_OUTPUTS \ + (OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \ + OMAP_OTG_VBUSVLD | OMAP_OTG_ID) + +static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs) +{ + u32 l; + + l = readl(otg_dev->base + OMAP_OTG_CTRL); + l &= ~OMAP_OTG_XCEIV_OUTPUTS; + l |= outputs; + writel(l, otg_dev->base + OMAP_OTG_CTRL); +} + +static void omap_otg_set_mode(struct otg_device *otg_dev) +{ + if (!otg_dev->id && otg_dev->vbus) + /* Set B-session valid. */ + omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD); + else if (otg_dev->vbus) + /* Set A-session valid. */ + omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD); + else if (!otg_dev->id) + /* Set B-session end to indicate no VBUS. */ + omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND); +} + +static int omap_otg_id_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb); + + otg_dev->id = event; + omap_otg_set_mode(otg_dev); + + return NOTIFY_DONE; +} + +static int omap_otg_vbus_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct otg_device *otg_dev = container_of(nb, struct otg_device, + vbus_nb); + + otg_dev->vbus = event; + omap_otg_set_mode(otg_dev); + + return NOTIFY_DONE; +} + +static int omap_otg_probe(struct platform_device *pdev) +{ + const struct omap_usb_config *config = pdev->dev.platform_data; + struct otg_device *otg_dev; + struct extcon_dev *extcon; + int ret; + u32 rev; + + if (!config || !config->extcon) + return -ENODEV; + + extcon = extcon_get_extcon_dev(config->extcon); + if (!extcon) + return -EPROBE_DEFER; + + otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL); + if (!otg_dev) + return -ENOMEM; + + otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]); + if (IS_ERR(otg_dev->base)) + return PTR_ERR(otg_dev->base); + + otg_dev->id_nb.notifier_call = omap_otg_id_notifier; + otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier; + + ret = extcon_register_interest(&otg_dev->id_dev, config->extcon, + "USB-HOST", &otg_dev->id_nb); + if (ret) + return ret; + + ret = extcon_register_interest(&otg_dev->vbus_dev, config->extcon, + "USB", &otg_dev->vbus_nb); + if (ret) { + extcon_unregister_interest(&otg_dev->id_dev); + return ret; + } + + otg_dev->id = extcon_get_cable_state(extcon, "USB-HOST"); + otg_dev->vbus = extcon_get_cable_state(extcon, "USB"); + omap_otg_set_mode(otg_dev); + + rev = readl(otg_dev->base); + + dev_info(&pdev->dev, + "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n", + (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id, + otg_dev->vbus); + + return 0; +} + +static int omap_otg_remove(struct platform_device *pdev) +{ + struct otg_device *otg_dev = platform_get_drvdata(pdev); + + extcon_unregister_interest(&otg_dev->id_dev); + extcon_unregister_interest(&otg_dev->vbus_dev); + + return 0; +} + +static struct platform_driver omap_otg_driver = { + .probe = omap_otg_probe, + .remove = omap_otg_remove, + .driver = { + .owner = THIS_MODULE, + .name = "omap_otg", + }, +}; +module_platform_driver(omap_otg_driver); + +MODULE_DESCRIPTION("OMAP USB OTG controller driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c index a99a6953f11..551e0a6c0e2 100644 --- a/drivers/usb/phy/phy-rcar-gen2-usb.c +++ b/drivers/usb/phy/phy-rcar-gen2-usb.c @@ -107,10 +107,10 @@ static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv) clk_prepare_enable(priv->clk); /* Set USB channels in the USBHS UGCTRL2 register */ - val = ioread32(priv->base); + val = ioread32(priv->base + USBHS_UGCTRL2_REG); val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS); val |= priv->ugctrl2; - iowrite32(val, priv->base); + iowrite32(val, priv->base + USBHS_UGCTRL2_REG); } /* Shutdown USB channels */ @@ -213,7 +213,7 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) priv->phy.shutdown = rcar_gen2_usb_phy_shutdown; priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend; - retval = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2); + retval = usb_add_phy_dev(&priv->phy); if (retval < 0) { dev_err(dev, "Failed to add USB phy\n"); return retval; diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c new file mode 100644 index 00000000000..cc61ee44b91 --- /dev/null +++ b/drivers/usb/phy/phy-tahvo.c @@ -0,0 +1,457 @@ +/* + * Tahvo USB transceiver driver + * + * Copyright (C) 2005-2006 Nokia Corporation + * + * Parts copied from isp1301_omap.c. + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2004 David Brownell + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/usb/otg.h> +#include <linux/mfd/retu.h> +#include <linux/usb/gadget.h> +#include <linux/platform_device.h> + +#define DRIVER_NAME "tahvo-usb" + +#define TAHVO_REG_IDSR 0x02 +#define TAHVO_REG_USBR 0x06 + +#define USBR_SLAVE_CONTROL (1 << 8) +#define USBR_VPPVIO_SW (1 << 7) +#define USBR_SPEED (1 << 6) +#define USBR_REGOUT (1 << 5) +#define USBR_MASTER_SW2 (1 << 4) +#define USBR_MASTER_SW1 (1 << 3) +#define USBR_SLAVE_SW (1 << 2) +#define USBR_NSUSPEND (1 << 1) +#define USBR_SEMODE (1 << 0) + +#define TAHVO_MODE_HOST 0 +#define TAHVO_MODE_PERIPHERAL 1 + +struct tahvo_usb { + struct platform_device *pt_dev; + struct usb_phy phy; + int vbus_state; + struct mutex serialize; + struct clk *ick; + int irq; + int tahvo_mode; + struct extcon_dev extcon; +}; + +static const char *tahvo_cable[] = { + "USB-HOST", + "USB", + NULL, +}; + +static ssize_t vbus_state_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tahvo_usb *tu = dev_get_drvdata(device); + return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off"); +} +static DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL); + +static void check_vbus_state(struct tahvo_usb *tu) +{ + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + int reg, prev_state; + + reg = retu_read(rdev, TAHVO_REG_IDSR); + if (reg & TAHVO_STAT_VBUS) { + switch (tu->phy.state) { + case OTG_STATE_B_IDLE: + /* Enable the gadget driver */ + if (tu->phy.otg->gadget) + usb_gadget_vbus_connect(tu->phy.otg->gadget); + tu->phy.state = OTG_STATE_B_PERIPHERAL; + break; + case OTG_STATE_A_IDLE: + /* + * Session is now valid assuming the USB hub is driving + * Vbus. + */ + tu->phy.state = OTG_STATE_A_HOST; + break; + default: + break; + } + dev_info(&tu->pt_dev->dev, "USB cable connected\n"); + } else { + switch (tu->phy.state) { + case OTG_STATE_B_PERIPHERAL: + if (tu->phy.otg->gadget) + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); + tu->phy.state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_A_HOST: + tu->phy.state = OTG_STATE_A_IDLE; + break; + default: + break; + } + dev_info(&tu->pt_dev->dev, "USB cable disconnected\n"); + } + + prev_state = tu->vbus_state; + tu->vbus_state = reg & TAHVO_STAT_VBUS; + if (prev_state != tu->vbus_state) { + extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); + sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state"); + } +} + +static void tahvo_usb_become_host(struct tahvo_usb *tu) +{ + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + + extcon_set_cable_state(&tu->extcon, "USB-HOST", true); + + /* Power up the transceiver in USB host mode */ + retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND | + USBR_MASTER_SW2 | USBR_MASTER_SW1); + tu->phy.state = OTG_STATE_A_IDLE; + + check_vbus_state(tu); +} + +static void tahvo_usb_stop_host(struct tahvo_usb *tu) +{ + tu->phy.state = OTG_STATE_A_IDLE; +} + +static void tahvo_usb_become_peripheral(struct tahvo_usb *tu) +{ + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + + extcon_set_cable_state(&tu->extcon, "USB-HOST", false); + + /* Power up transceiver and set it in USB peripheral mode */ + retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | + USBR_NSUSPEND | USBR_SLAVE_SW); + tu->phy.state = OTG_STATE_B_IDLE; + + check_vbus_state(tu); +} + +static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu) +{ + if (tu->phy.otg->gadget) + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); + tu->phy.state = OTG_STATE_B_IDLE; +} + +static void tahvo_usb_power_off(struct tahvo_usb *tu) +{ + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + + /* Disable gadget controller if any */ + if (tu->phy.otg->gadget) + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); + + /* Power off transceiver */ + retu_write(rdev, TAHVO_REG_USBR, 0); + tu->phy.state = OTG_STATE_UNDEFINED; +} + +static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend) +{ + struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy); + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + u16 w; + + dev_dbg(&tu->pt_dev->dev, "%s\n", __func__); + + w = retu_read(rdev, TAHVO_REG_USBR); + if (suspend) + w &= ~USBR_NSUSPEND; + else + w |= USBR_NSUSPEND; + retu_write(rdev, TAHVO_REG_USBR, w); + + return 0; +} + +static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); + + dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host); + + mutex_lock(&tu->serialize); + + if (host == NULL) { + if (tu->tahvo_mode == TAHVO_MODE_HOST) + tahvo_usb_power_off(tu); + otg->host = NULL; + mutex_unlock(&tu->serialize); + return 0; + } + + if (tu->tahvo_mode == TAHVO_MODE_HOST) { + otg->host = NULL; + tahvo_usb_become_host(tu); + } + + otg->host = host; + + mutex_unlock(&tu->serialize); + + return 0; +} + +static int tahvo_usb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); + + dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget); + + mutex_lock(&tu->serialize); + + if (!gadget) { + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) + tahvo_usb_power_off(tu); + tu->phy.otg->gadget = NULL; + mutex_unlock(&tu->serialize); + return 0; + } + + tu->phy.otg->gadget = gadget; + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) + tahvo_usb_become_peripheral(tu); + + mutex_unlock(&tu->serialize); + + return 0; +} + +static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu) +{ + struct tahvo_usb *tu = _tu; + + mutex_lock(&tu->serialize); + check_vbus_state(tu); + mutex_unlock(&tu->serialize); + + return IRQ_HANDLED; +} + +static ssize_t otg_mode_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tahvo_usb *tu = dev_get_drvdata(device); + + switch (tu->tahvo_mode) { + case TAHVO_MODE_HOST: + return sprintf(buf, "host\n"); + case TAHVO_MODE_PERIPHERAL: + return sprintf(buf, "peripheral\n"); + } + + return -EINVAL; +} + +static ssize_t otg_mode_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tahvo_usb *tu = dev_get_drvdata(device); + int r; + + mutex_lock(&tu->serialize); + if (count >= 4 && strncmp(buf, "host", 4) == 0) { + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) + tahvo_usb_stop_peripheral(tu); + tu->tahvo_mode = TAHVO_MODE_HOST; + if (tu->phy.otg->host) { + dev_info(device, "HOST mode: host controller present\n"); + tahvo_usb_become_host(tu); + } else { + dev_info(device, "HOST mode: no host controller, powering off\n"); + tahvo_usb_power_off(tu); + } + r = strlen(buf); + } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) { + if (tu->tahvo_mode == TAHVO_MODE_HOST) + tahvo_usb_stop_host(tu); + tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; + if (tu->phy.otg->gadget) { + dev_info(device, "PERIPHERAL mode: gadget driver present\n"); + tahvo_usb_become_peripheral(tu); + } else { + dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n"); + tahvo_usb_power_off(tu); + } + r = strlen(buf); + } else { + r = -EINVAL; + } + mutex_unlock(&tu->serialize); + + return r; +} +static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); + +static struct attribute *tahvo_attributes[] = { + &dev_attr_vbus.attr, + &dev_attr_otg_mode.attr, + NULL +}; + +static struct attribute_group tahvo_attr_group = { + .attrs = tahvo_attributes, +}; + +static int tahvo_usb_probe(struct platform_device *pdev) +{ + struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); + struct tahvo_usb *tu; + int ret; + + tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL); + if (!tu) + return -ENOMEM; + + tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg), + GFP_KERNEL); + if (!tu->phy.otg) + return -ENOMEM; + + tu->pt_dev = pdev; + + /* Default mode */ +#ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT + tu->tahvo_mode = TAHVO_MODE_HOST; +#else + tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; +#endif + + mutex_init(&tu->serialize); + + tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick"); + if (!IS_ERR(tu->ick)) + clk_enable(tu->ick); + + /* + * Set initial state, so that we generate kevents only on state changes. + */ + tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS; + + tu->extcon.name = DRIVER_NAME; + tu->extcon.supported_cable = tahvo_cable; + tu->extcon.dev.parent = &pdev->dev; + + ret = extcon_dev_register(&tu->extcon); + if (ret) { + dev_err(&pdev->dev, "could not register extcon device: %d\n", + ret); + goto err_disable_clk; + } + + /* Set the initial cable state. */ + extcon_set_cable_state(&tu->extcon, "USB-HOST", + tu->tahvo_mode == TAHVO_MODE_HOST); + extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); + + /* Create OTG interface */ + tahvo_usb_power_off(tu); + tu->phy.dev = &pdev->dev; + tu->phy.state = OTG_STATE_UNDEFINED; + tu->phy.label = DRIVER_NAME; + tu->phy.set_suspend = tahvo_usb_set_suspend; + + tu->phy.otg->phy = &tu->phy; + tu->phy.otg->set_host = tahvo_usb_set_host; + tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral; + + ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2); + if (ret < 0) { + dev_err(&pdev->dev, "cannot register USB transceiver: %d\n", + ret); + goto err_extcon_unreg; + } + + dev_set_drvdata(&pdev->dev, tu); + + tu->irq = platform_get_irq(pdev, 0); + ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0, + "tahvo-vbus", tu); + if (ret) { + dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n", + ret); + goto err_remove_phy; + } + + /* Attributes */ + ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group); + if (ret) { + dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(tu->irq, tu); +err_remove_phy: + usb_remove_phy(&tu->phy); +err_extcon_unreg: + extcon_dev_unregister(&tu->extcon); +err_disable_clk: + if (!IS_ERR(tu->ick)) + clk_disable(tu->ick); + + return ret; +} + +static int tahvo_usb_remove(struct platform_device *pdev) +{ + struct tahvo_usb *tu = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group); + free_irq(tu->irq, tu); + usb_remove_phy(&tu->phy); + extcon_dev_unregister(&tu->extcon); + if (!IS_ERR(tu->ick)) + clk_disable(tu->ick); + + return 0; +} + +static struct platform_driver tahvo_usb_driver = { + .probe = tahvo_usb_probe, + .remove = tahvo_usb_remove, + .driver = { + .name = "tahvo-usb", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(tahvo_usb_driver); + +MODULE_DESCRIPTION("Tahvo USB transceiver driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 82232acf1ab..bbe4f8e6e8d 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -876,7 +876,7 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!tegra_phy->regs) { + if (!tegra_phy->pad_regs) { dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n"); return -ENOMEM; } diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 30e8a61552d..214172b68d5 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -127,7 +127,8 @@ static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) { - u8 data, ret = 0; + u8 data; + int ret; ret = twl_i2c_read_u8(module, &data, address); if (ret >= 0) @@ -327,7 +328,7 @@ static int twl6030_usb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct twl4030_usb_data *pdata = dev_get_platdata(dev); - twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL); + twl = devm_kzalloc(dev, sizeof(*twl), GFP_KERNEL); if (!twl) return -ENOMEM; diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 1b74523e1fe..e6f61e4361d 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -329,6 +329,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) return -EINVAL; } + ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier); + spin_lock_irqsave(&phy_lock, flags); list_for_each_entry(phy, &phy_list, head) { @@ -367,6 +369,8 @@ int usb_add_phy_dev(struct usb_phy *x) return -EINVAL; } + ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier); + spin_lock_irqsave(&phy_lock, flags); list_for_each_entry(phy_bind, &phy_bind_list, list) if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev)))) |