From 511089a5c2c77205bb253988128a131149cfa3df Mon Sep 17 00:00:00 2001 From: Sakethram Bommisetti Date: Thu, 14 Jul 2011 17:30:24 +0530 Subject: USB:Enable GPIO configuration at connect GPIO alternate configuration is set when the USB is connected and reset at USB disconnect. Change-Id: I07d9c2ed5028879ecff309aa9e4ac25deac148f5 Signed-off-by: Sakethram Bommisetti Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27203 Reviewed-by: QATOOLS Reviewed-by: Praveena NADAHALLY Reviewed-by: Philippe LANGLAIS --- arch/arm/mach-ux500/include/mach/usb.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'arch/arm/mach-ux500/include/mach') diff --git a/arch/arm/mach-ux500/include/mach/usb.h b/arch/arm/mach-ux500/include/mach/usb.h index 4c1cc50a595..40e4b621864 100644 --- a/arch/arm/mach-ux500/include/mach/usb.h +++ b/arch/arm/mach-ux500/include/mach/usb.h @@ -22,4 +22,12 @@ struct ux500_musb_board_data { void ux500_add_usb(struct device *parent, resource_size_t base, int irq, int *dma_rx_cfg, int *dma_tx_cfg); + +struct ab8500_usbgpio_platform_data { + int (*get)(struct device *device); + void (*enable)(void); + void (*disable)(void); + void (*put)(void); +}; + #endif -- cgit v1.2.3 From 212f06b70a02881a4f5f87f9905784806c80da06 Mon Sep 17 00:00:00 2001 From: Sakethram Bommisetti Date: Tue, 13 Sep 2011 14:03:27 +0530 Subject: ux500:USB:Generic USB GPIO frame work Making the existing U8500 usb gpio framework to generic. This allows other platforms to use the same structure. ST-Ericsson ID: NA ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: NA Change-Id: I3d5edc139e247b2373d1dd77243421e74783a0ea Signed-off-by: Sakethram Bommisetti Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30790 --- arch/arm/mach-ux500/board-mop500-usb.h | 13 ------------- arch/arm/mach-ux500/board-mop500.c | 2 ++ arch/arm/mach-ux500/board-ux500-usb.h | 13 +++++++++++++ arch/arm/mach-ux500/include/mach/usb.h | 2 +- arch/arm/mach-ux500/usb.c | 4 ++-- drivers/usb/otg/ab8500-usb.c | 2 +- include/linux/mfd/abx500/ab8500.h | 2 +- 7 files changed, 20 insertions(+), 18 deletions(-) delete mode 100644 arch/arm/mach-ux500/board-mop500-usb.h create mode 100644 arch/arm/mach-ux500/board-ux500-usb.h (limited to 'arch/arm/mach-ux500/include/mach') diff --git a/arch/arm/mach-ux500/board-mop500-usb.h b/arch/arm/mach-ux500/board-mop500-usb.h deleted file mode 100644 index 85288463a73..00000000000 --- a/arch/arm/mach-ux500/board-mop500-usb.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2011 - * - * Author: Saketh Ram Bommisetti - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __BOARD_MOP500_USB_H -#define __BOARD_MOP500_USB_H - -extern struct ab8500_usbgpio_platform_data ab8500_usbgpio_plat_data; - -#endif diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 77d03c1fbd0..5c1da28427b 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -53,6 +53,7 @@ #include "devices-db8500.h" #include "board-mop500.h" #include "board-mop500-regulators.h" +#include "board-ux500-usb.h" static struct gpio_led snowball_led_array[] = { { @@ -194,6 +195,7 @@ static struct ab8500_platform_data ab8500_platdata = { .regulator = ab8500_regulators, .num_regulator = ARRAY_SIZE(ab8500_regulators), .gpio = &ab8500_gpio_pdata, + .usb = &abx500_usbgpio_plat_data, }; static struct resource ab8500_resources[] = { diff --git a/arch/arm/mach-ux500/board-ux500-usb.h b/arch/arm/mach-ux500/board-ux500-usb.h new file mode 100644 index 00000000000..6b35a181c0a --- /dev/null +++ b/arch/arm/mach-ux500/board-ux500-usb.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Saketh Ram Bommisetti + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __BOARD_UX500_USB_H +#define __BOARD_UX500_USB_H + +extern struct abx500_usbgpio_platform_data abx500_usbgpio_plat_data; + +#endif diff --git a/arch/arm/mach-ux500/include/mach/usb.h b/arch/arm/mach-ux500/include/mach/usb.h index 40e4b621864..145823a4d37 100644 --- a/arch/arm/mach-ux500/include/mach/usb.h +++ b/arch/arm/mach-ux500/include/mach/usb.h @@ -23,7 +23,7 @@ struct ux500_musb_board_data { void ux500_add_usb(struct device *parent, resource_size_t base, int irq, int *dma_rx_cfg, int *dma_tx_cfg); -struct ab8500_usbgpio_platform_data { +struct abx500_usbgpio_platform_data { int (*get)(struct device *device); void (*enable)(void); void (*disable)(void); diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c index 3851a34f0ca..170b8934200 100644 --- a/arch/arm/mach-ux500/usb.c +++ b/arch/arm/mach-ux500/usb.c @@ -13,7 +13,7 @@ #include #include #include "pins.h" -#include "board-mop500-usb.h" +#include "board-ux500-usb.h" #define MUSB_DMA40_RX_CH { \ .mode = STEDMA40_MODE_LOGICAL, \ @@ -153,7 +153,7 @@ static void put_gpio(void) { ux500_pins_put(usb_gpio_pins); } -struct ab8500_usbgpio_platform_data ab8500_usbgpio_plat_data = { +struct abx500_usbgpio_platform_data abx500_usbgpio_plat_data = { .get = &get_gpio, .enable = &enable_gpio, .disable = &disable_gpio, diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index bc0123101c2..13fbabacbc8 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -120,7 +120,7 @@ struct ab8500_usb { struct regulator *v_ape; struct regulator *v_musb; struct regulator *v_ulpi; - struct ab8500_usbgpio_platform_data *usb_gpio; + struct abx500_usbgpio_platform_data *usb_gpio; struct delayed_work work_usb_workaround; struct kobject *serial_number_kobj; }; diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 8cc2185b0a3..94a3329ccf9 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -274,7 +274,7 @@ struct ab8500_platform_data { int num_regulator; struct regulator_init_data *regulator; struct ab8500_gpio_platform_data *gpio; - struct ab8500_usbgpio_platform_data *usb; + struct abx500_usbgpio_platform_data *usb; }; extern int __devinit ab8500_init(struct ab8500 *ab8500, -- cgit v1.2.3 From 9c31e476c753f7c628a39fcb0e28c3c6a602cf43 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Date: Mon, 19 Sep 2011 15:36:01 +0530 Subject: usb:Migrate the patches to kernel 3.0 Following patches for usb driver are manually merged to kernel 3.0 d11e52a ux500: usb: add usb device mode support on u5500 1fecc55 ux500: usb: enabling adb and ACM functionality in u5500_defconfig 2847bf4 ux500: usb: error handling in musb for u5500 37f22da ux500: usb: U5500 v2 update e1b079d ux500: usb: Fix for enumeration when ON with cable 181701a ux500: usb: Fix for V2 emmc2 boot USB device issue 3a60721 mach-ux500: Handle the LinkStatus register. 086a83e usb: ux500: usb gpio enable/disable in pair ST-Ericsson ID: 352334 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: NA Change-Id: Id918b9a55d3b85faf7c003547748f17086af3ec7 Signed-off-by: Avinash Kumar Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30860 --- arch/arm/mach-ux500/board-u5500.c | 4 + arch/arm/mach-ux500/include/mach/usb.h | 1 + arch/arm/mach-ux500/usb.c | 3 + drivers/usb/musb/Kconfig | 2 +- drivers/usb/otg/Kconfig | 9 + drivers/usb/otg/Makefile | 1 + drivers/usb/otg/ab5500-usb.c | 734 +++++++++++++++++++++++++++++++++ 7 files changed, 753 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/otg/ab5500-usb.c (limited to 'arch/arm/mach-ux500/include/mach') diff --git a/arch/arm/mach-ux500/board-u5500.c b/arch/arm/mach-ux500/board-u5500.c index 0ff4be72a80..f040ab0ba7c 100644 --- a/arch/arm/mach-ux500/board-u5500.c +++ b/arch/arm/mach-ux500/board-u5500.c @@ -23,10 +23,12 @@ #include #include #include +#include #include "pins-db5500.h" #include "devices-db5500.h" #include +#include "board-ux500-usb.h" /* * GPIO @@ -111,6 +113,8 @@ static struct ab5500_platform_data ab5500_plf_data = { .init_settings = NULL, .init_settings_sz = 0, .pm_power_off = false, + .dev_data[AB5500_DEVID_USB] = &abx500_usbgpio_plat_data, + .dev_data_sz[AB5500_DEVID_USB] = sizeof(abx500_usbgpio_plat_data), }; static struct platform_device ab5500_device = { diff --git a/arch/arm/mach-ux500/include/mach/usb.h b/arch/arm/mach-ux500/include/mach/usb.h index 145823a4d37..a9aa06190eb 100644 --- a/arch/arm/mach-ux500/include/mach/usb.h +++ b/arch/arm/mach-ux500/include/mach/usb.h @@ -28,6 +28,7 @@ struct abx500_usbgpio_platform_data { void (*enable)(void); void (*disable)(void); void (*put)(void); + int usb_cs; }; #endif diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c index 170b8934200..37f9427307f 100644 --- a/arch/arm/mach-ux500/usb.c +++ b/arch/arm/mach-ux500/usb.c @@ -35,6 +35,8 @@ .dst_info.psize = STEDMA40_PSIZE_LOG_16, \ } +#define USB_OTG_GPIO_CS 76 + static struct stedma40_chan_cfg musb_dma_rx_ch[UX500_MUSB_DMA_NUM_RX_CHANNELS] = { MUSB_DMA40_RX_CH, @@ -158,6 +160,7 @@ struct abx500_usbgpio_platform_data abx500_usbgpio_plat_data = { .enable = &enable_gpio, .disable = &disable_gpio, .put = &put_gpio, + .usb_cs = USB_OTG_GPIO_CS, }; static inline void ux500_usb_dma_update_rx_ch_config(int *src_dev_type) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 1169c42b56e..ac26a4f8545 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -60,7 +60,7 @@ config USB_MUSB_BLACKFIN config USB_MUSB_UX500 tristate "U8500 and U5500" - depends on (ARCH_U8500 && AB8500_USB) + depends on (ARCH_U8500) || (ARCH_U5500) endchoice diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 5c87db06b59..168f5b0364f 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -108,6 +108,15 @@ config AB8500_USB This transceiver supports high and full speed devices plus, in host mode, low speed. +config AB5500_USB + tristate "AB5500 USB Transceiver Driver" + depends on AB5500_CORE + select USB_OTG_UTILS + help + Enable this to support the USB OTG transceiver in AB5500 chip. + This transceiver supports high and full speed devices plus, + in host mode, low speed. + config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 41aa5098b13..e227d9add96 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_USB_ULPI) += ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o obj-$(CONFIG_AB8500_USB) += ab8500-usb.o +obj-$(CONFIG_AB5500_USB) += ab5500-usb.o fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o obj-$(CONFIG_USB_MV_OTG) += mv_otg.o diff --git a/drivers/usb/otg/ab5500-usb.c b/drivers/usb/otg/ab5500-usb.c new file mode 100644 index 00000000000..a4f4f58f847 --- /dev/null +++ b/drivers/usb/otg/ab5500-usb.c @@ -0,0 +1,734 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Avinash Kumar for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AB5500 USB macros + */ +#define AB5500_USB_HOST_ENABLE 0x1 +#define AB5500_USB_HOST_DISABLE 0x0 +#define AB5500_USB_DEVICE_ENABLE 0x2 +#define AB5500_USB_DEVICE_DISABLE 0x0 +#define AB5500_MAIN_WATCHDOG_ENABLE 0x1 +#define AB5500_MAIN_WATCHDOG_KICK 0x2 +#define AB5500_MAIN_WATCHDOG_DISABLE 0x0 +#define AB5500_USB_ADP_ENABLE 0x1 +#define AB5500_WATCHDOG_DELAY 10 +#define AB5500_WATCHDOG_DELAY_US 100 +#define AB5500_PHY_DELAY_US 100 +#define AB5500_USB_DEVICE_DISABLE 0x0 +#define AB5500_MAIN_WDOG_CTRL_REG 0x01 +#define AB5500_USB_LINE_STAT_REG 0x80 +#define AB5500_USB_PHY_CTRL_REG 0x8A +#define AB5500_MAIN_WATCHDOG_ENABLE 0x1 +#define AB5500_MAIN_WATCHDOG_KICK 0x2 +#define AB5500_MAIN_WATCHDOG_DISABLE 0x0 +#define AB5500_SYS_CTRL2_BLOCK 0x2 + +#define USB_PROBE_DELAY 1000 /* 1 seconds */ + +/* UsbLineStatus register - usb types */ +enum ab8500_usb_link_status { + USB_LINK_NOT_CONFIGURED, + USB_LINK_STD_HOST_NC, + USB_LINK_STD_HOST_C_NS, + USB_LINK_STD_HOST_C_S, + USB_LINK_HOST_CHG_NM, + USB_LINK_HOST_CHG_HS, + USB_LINK_HOST_CHG_HS_CHIRP, + USB_LINK_DEDICATED_CHG, + USB_LINK_ACA_RID_A, + USB_LINK_ACA_RID_B, + USB_LINK_ACA_RID_C_NM, + USB_LINK_ACA_RID_C_HS, + USB_LINK_ACA_RID_C_HS_CHIRP, + USB_LINK_HM_IDGND, + USB_LINK_OTG_HOST_NO_CURRENT, + USB_LINK_NOT_VALID_LINK, + USB_LINK_HM_IDGND_V2 = 18, +}; + +/** + * ab5500_usb_mode - Different states of ab usb_chip + * + * Used for USB cable plug-in state machine + */ +enum ab5500_usb_mode { + USB_IDLE, + USB_DEVICE, + USB_HOST, + USB_DEDICATED_CHG, +}; +struct ab5500_usb { + struct otg_transceiver otg; + struct device *dev; + int irq_num_id_rise; + int irq_num_id_fall; + int irq_num_vbus_rise; + int irq_num_vbus_fall; + int irq_num_link_status; + unsigned vbus_draw; + struct delayed_work dwork; + struct work_struct phy_dis_work; + unsigned long link_status_wait; + int rev; + int usb_cs_gpio; + enum ab5500_usb_mode mode; + struct clk *sysclk; + struct regulator *v_ape; + struct abx500_usbgpio_platform_data *usb_gpio; + struct delayed_work work_usb_workaround; +}; + +static int ab5500_usb_irq_setup(struct platform_device *pdev, + struct ab5500_usb *ab); +static int ab5500_usb_boot_detect(struct ab5500_usb *ab); +static int ab5500_usb_link_status_update(struct ab5500_usb *ab); + +static void ab5500_usb_phy_enable(struct ab5500_usb *ab, bool sel_host); + +static inline struct ab5500_usb *xceiv_to_ab(struct otg_transceiver *x) +{ + return container_of(x, struct ab5500_usb, otg); +} + +/** + * ab5500_usb_wd_workaround() - Kick the watch dog timer + * + * This function used to Kick the watch dog timer + */ +static void ab5500_usb_wd_workaround(struct ab5500_usb *ab) +{ + abx500_set_register_interruptible(ab->dev, + AB5500_SYS_CTRL2_BLOCK, + AB5500_MAIN_WDOG_CTRL_REG, + AB5500_MAIN_WATCHDOG_ENABLE); + + udelay(AB5500_WATCHDOG_DELAY_US); + + abx500_set_register_interruptible(ab->dev, + AB5500_SYS_CTRL2_BLOCK, + AB5500_MAIN_WDOG_CTRL_REG, + (AB5500_MAIN_WATCHDOG_ENABLE + | AB5500_MAIN_WATCHDOG_KICK)); + + udelay(AB5500_WATCHDOG_DELAY_US); + + abx500_set_register_interruptible(ab->dev, + AB5500_SYS_CTRL2_BLOCK, + AB5500_MAIN_WDOG_CTRL_REG, + AB5500_MAIN_WATCHDOG_DISABLE); + + udelay(AB5500_WATCHDOG_DELAY_US); +} + +static void ab5500_usb_phy_enable(struct ab5500_usb *ab, bool sel_host) +{ + u8 bit; + bit = sel_host ? AB5500_USB_HOST_ENABLE : + AB5500_USB_DEVICE_ENABLE; + + ab->usb_gpio->enable(); + clk_enable(ab->sysclk); + regulator_enable(ab->v_ape); + + if (!sel_host) { + schedule_delayed_work_on(0, + &ab->work_usb_workaround, + msecs_to_jiffies(USB_PROBE_DELAY)); + } + + abx500_set_register_interruptible(ab->dev, + AB5500_BANK_USB, + AB5500_USB_PHY_CTRL_REG, + bit); +} + +static void ab5500_usb_phy_disable(struct ab5500_usb *ab, bool sel_host) +{ + u8 bit; + bit = sel_host ? AB5500_USB_HOST_ENABLE : + AB5500_USB_DEVICE_ENABLE; + + abx500_set_register_interruptible(ab->dev, + AB5500_BANK_USB, + AB5500_USB_PHY_CTRL_REG, + bit); + + /* Needed to disable the phy.*/ + ab5500_usb_wd_workaround(ab); + + regulator_disable(ab->v_ape); + clk_disable(ab->sysclk); + ab->usb_gpio->disable(); + if (!sel_host) { + + cancel_delayed_work_sync(&ab->work_usb_workaround); + prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP, + "usb", 25); + } + +} + +#define ab5500_usb_peri_phy_en(ab) ab5500_usb_phy_enable(ab, false) +#define ab5500_usb_peri_phy_dis(ab) ab5500_usb_phy_disable(ab, false) +#define ab5500_usb_host_phy_en(ab) ab5500_usb_phy_enable(ab, true) +#define ab5500_usb_host_phy_dis(ab) ab5500_usb_phy_disable(ab, true) + +/* Work created after an link status update handler*/ +static int ab5500_usb_link_status_update(struct ab5500_usb *ab) +{ + u8 val = 0; + int ret = 0; + int gpioval = 0; + enum ab8500_usb_link_status lsts; + enum usb_xceiv_events event; + + (void)abx500_get_register_interruptible(ab->dev, + AB5500_BANK_USB, AB5500_USB_LINE_STAT_REG, &val); + + lsts = (val >> 3) & 0x0F; + + switch (lsts) { + + case USB_LINK_STD_HOST_NC: + case USB_LINK_STD_HOST_C_NS: + case USB_LINK_STD_HOST_C_S: + case USB_LINK_HOST_CHG_NM: + case USB_LINK_HOST_CHG_HS: + case USB_LINK_HOST_CHG_HS_CHIRP: + if (ab->otg.gadget) { + ab5500_usb_peri_phy_en(ab); + ab->mode = USB_DEVICE; + } + gpio_set_value(ab->usb_cs_gpio, 1); + event = USB_EVENT_VBUS; + break; + + case USB_LINK_HM_IDGND: + if (ab->rev == AB5500_2_0) + break; + + /* enable usb chip Select */ + ret = gpio_direction_output(ab->usb_cs_gpio, gpioval); + if (ret < 0) { + dev_err(ab->dev, "usb_cs_gpio: gpio direction failed\n"); + gpio_free(ab->usb_cs_gpio); + return ret; + } + gpio_set_value(ab->usb_cs_gpio, 1); + + ab5500_usb_host_phy_en(ab); + + break; + + case USB_LINK_HM_IDGND_V2: + if (!(ab->rev == AB5500_2_0)) + break; + + /* enable usb chip Select */ + ret = gpio_direction_output(ab->usb_cs_gpio, gpioval); + if (ret < 0) { + dev_err(ab->dev, "usb_cs_gpio: gpio direction failed\n"); + gpio_free(ab->usb_cs_gpio); + return ret; + } + gpio_set_value(ab->usb_cs_gpio, 1); + + ab5500_usb_host_phy_en(ab); + + break; + default: + break; + } + + atomic_notifier_call_chain(&ab->otg.notifier, event, &ab->vbus_draw); + + return 0; +} + +static void ab5500_usb_delayed_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct ab5500_usb *ab = container_of(dwork, struct ab5500_usb, dwork); + + ab5500_usb_link_status_update(ab); +} + +/** + * This function is used to signal the completion of + * USB Link status register update + */ +static irqreturn_t ab5500_usb_link_status_irq(int irq, void *data) +{ + struct ab5500_usb *ab = (struct ab5500_usb *) data; + ab5500_usb_link_status_update(ab); + + return IRQ_HANDLED; +} + +static irqreturn_t ab5500_usb_device_insert_irq(int irq, void *data) +{ + int ret = 0, val = 1; + struct ab5500_usb *ab = (struct ab5500_usb *) data; + ab->mode = USB_DEVICE; + + ab5500_usb_peri_phy_en(ab); + /* enable usb chip Select */ + ret = gpio_direction_output(ab->usb_cs_gpio, val); + if (ret < 0) { + dev_err(ab->dev, "usb_cs_gpio: gpio direction failed\n"); + gpio_free(ab->usb_cs_gpio); + return ret; + } + gpio_set_value(ab->usb_cs_gpio, 1); + + return IRQ_HANDLED; +} + +/** + * This function used to remove the voltage for USB ab->dev mode. + */ +static irqreturn_t ab5500_usb_device_disconnect_irq(int irq, void *data) +{ + struct ab5500_usb *ab = (struct ab5500_usb *) data; + /* disable usb chip Select */ + gpio_set_value(ab->usb_cs_gpio, 0); + ab5500_usb_peri_phy_dis(ab); + return IRQ_HANDLED; +} + +/** + * ab5500_usb_host_disconnect_irq : work handler for host cable insert. + * @work: work structure + * + * This function is used to handle the host cable insert work. + */ +static void ab5500_usb_host_disconnect_irq(int irq, void *data) +{ + struct ab5500_usb *ab = (struct ab5500_usb *) data; + /* disable usb chip Select */ + gpio_set_value(ab->usb_cs_gpio, 0); + ab5500_usb_host_phy_dis(ab); + return IRQ_HANDLED; +} + +static void ab5500_usb_irq_free(struct ab5500_usb *ab) +{ + if (ab->irq_num_id_rise) + free_irq(ab->irq_num_id_rise, ab); + + if (ab->irq_num_id_fall) + free_irq(ab->irq_num_id_fall, ab); + + if (ab->irq_num_vbus_rise) + free_irq(ab->irq_num_vbus_rise, ab); + + if (ab->irq_num_vbus_fall) + free_irq(ab->irq_num_vbus_fall, ab); + + if (ab->irq_num_link_status) + free_irq(ab->irq_num_link_status, ab); +} + +/** + * ab5500_usb_irq_setup : register USB callback handlers for ab5500 + * @mode: value for mode. + * + * This function is used to register USB callback handlers for ab5500. + */ +static int ab5500_usb_irq_setup(struct platform_device *pdev, + struct ab5500_usb *ab) +{ + int ret = 0; + int irq, err; + + if (!ab->dev) + return -EINVAL; + + irq = platform_get_irq_byname(pdev, "usb_idgnd_f"); + if (irq < 0) { + dev_err(&pdev->dev, "ID fall irq not found\n"); + err = irq; + goto irq_fail; + } + ab->irq_num_id_fall = irq; + + irq = platform_get_irq_byname(pdev, "VBUS_F"); + if (irq < 0) { + dev_err(&pdev->dev, "VBUS fall irq not found\n"); + err = irq; + goto irq_fail; + + } + ab->irq_num_vbus_fall = irq; + + irq = platform_get_irq_byname(pdev, "VBUS_R"); + if (irq < 0) { + dev_err(&pdev->dev, "VBUS raise irq not found\n"); + err = irq; + goto irq_fail; + + } + ab->irq_num_vbus_rise = irq; + + irq = platform_get_irq_byname(pdev, "Link_Update"); + if (irq < 0) { + dev_err(&pdev->dev, "Link Update irq not found\n"); + err = irq; + goto irq_fail; + } + ab->irq_num_link_status = irq; + + + ret = request_threaded_irq(ab->irq_num_link_status, + NULL, ab5500_usb_link_status_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-link-status-update", ab); + if (ret < 0) { + printk(KERN_ERR "failed to set the callback" + " handler for usb charge" + " detect done\n"); + err = ret; + goto irq_fail; + } + + ret = request_threaded_irq(ab->irq_num_vbus_rise, NULL, + ab5500_usb_device_insert_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-vbus-rise", ab); + if (ret < 0) { + printk(KERN_ERR "failed to set the callback" + " handler for usb ab->dev" + " insertion\n"); + err = ret; + goto irq_fail; + } + + ret = request_threaded_irq(ab->irq_num_vbus_fall, NULL, + ab5500_usb_device_disconnect_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-vbus-fall", ab); + if (ret < 0) { + printk(KERN_ERR "failed to set the callback" + " handler for usb ab->dev" + " removal\n"); + err = ret; + goto irq_fail; + } + + ret = request_threaded_irq((ab->irq_num_id_fall), NULL, + ab5500_usb_host_disconnect_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-id-fall", ab); + if (ret < 0) { + printk(KERN_ERR "failed to set the callback" + " handler for usb host" + " removal\n"); + err = ret; + goto irq_fail; + } + + ab5500_usb_wd_workaround(ab); + return 0; + +irq_fail: + ab5500_usb_irq_free(ab); + return err; +} + +/** + * ab5500_usb_boot_detect : detect the USB cable during boot time. + * @mode: value for mode. + * + * This function is used to detect the USB cable during boot time. + */ +static int ab5500_usb_boot_detect(struct ab5500_usb *ab) +{ + int ret; + int val = 1; + int usb_status = 0; + int gpioval = 0; + enum ab8500_usb_link_status lsts; + if (!ab->dev) + return -EINVAL; + + abx500_set_register_interruptible(ab->dev, + AB5500_BANK_USB, + AB5500_USB_PHY_CTRL_REG, + AB5500_USB_DEVICE_ENABLE); + + udelay(AB5500_PHY_DELAY_US); + + abx500_set_register_interruptible(ab->dev, + AB5500_BANK_USB, + AB5500_USB_PHY_CTRL_REG, + AB5500_USB_DEVICE_DISABLE); + + abx500_set_register_interruptible(ab->dev, + AB5500_BANK_USB, + AB5500_USB_PHY_CTRL_REG, + AB5500_USB_HOST_ENABLE); + + udelay(AB5500_PHY_DELAY_US); + + abx500_set_register_interruptible(ab->dev, + AB5500_BANK_USB, + AB5500_USB_PHY_CTRL_REG, + AB5500_USB_HOST_DISABLE); + + ab5500_usb_wd_workaround(ab); + + (void)abx500_get_register_interruptible(ab->dev, + AB5500_BANK_USB, AB5500_USB_LINE_STAT_REG, &usb_status); + + lsts = (usb_status >> 3) & 0x0F; + + switch (lsts) { + + case USB_LINK_STD_HOST_NC: + case USB_LINK_STD_HOST_C_NS: + case USB_LINK_STD_HOST_C_S: + case USB_LINK_HOST_CHG_NM: + case USB_LINK_HOST_CHG_HS: + case USB_LINK_HOST_CHG_HS_CHIRP: + + ab5500_usb_peri_phy_en(ab); + + /* enable usb chip Select */ + ret = gpio_direction_output(ab->usb_cs_gpio, val); + if (ret < 0) { + dev_err(ab->dev, "usb_cs_gpio: gpio direction failed\n"); + gpio_free(ab->usb_cs_gpio); + return ret; + } + gpio_set_value(ab->usb_cs_gpio, 1); + + break; + + case USB_LINK_HM_IDGND: + case USB_LINK_HM_IDGND_V2: + /* enable usb chip Select */ + ret = gpio_direction_output(ab->usb_cs_gpio, gpioval); + if (ret < 0) { + dev_err(ab->dev, "usb_cs_gpio: gpio direction failed\n"); + gpio_free(ab->usb_cs_gpio); + return ret; + } + gpio_set_value(ab->usb_cs_gpio, 1); + ab5500_usb_host_phy_en(ab); + + break; + default: + break; + } + + return 0; +} + +static int ab5500_usb_set_power(struct otg_transceiver *otg, unsigned mA) +{ + struct ab5500_usb *ab; + + if (!otg) + return -ENODEV; + + ab = xceiv_to_ab(otg); + + ab->vbus_draw = mA; + + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_VBUS, &ab->vbus_draw); + return 0; +} + +static int ab5500_usb_set_suspend(struct otg_transceiver *x, int suspend) +{ + /* TODO */ + return 0; +} + +static int ab5500_usb_set_host(struct otg_transceiver *otg, + struct usb_bus *host) +{ + struct ab5500_usb *ab; + + if (!otg) + return -ENODEV; + + ab = xceiv_to_ab(otg); + + /* Some drivers call this function in atomic context. + * Do not update ab5500 registers directly till this + * is fixed. + */ + + if (!host) { + ab->otg.host = NULL; + schedule_work(&ab->phy_dis_work); + } else { + ab->otg.host = host; + /* Phy will not be enabled if cable is already + * plugged-in. Schedule to enable phy. + * Use same delay to avoid any race condition. + */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + } + + return 0; +} + +static int ab5500_usb_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + struct ab5500_usb *ab; + + if (!otg) + return -ENODEV; + + ab = xceiv_to_ab(otg); + + /* Some drivers call this function in atomic context. + * Do not update ab5500 registers directly till this + * is fixed. + */ + + if (!gadget) { + ab->otg.gadget = NULL; + schedule_work(&ab->phy_dis_work); + } else { + ab->otg.gadget = gadget; + /* Phy will not be enabled if cable is already + * plugged-in. Schedule to enable phy. + * Use same delay to avoid any race condition. + */ + schedule_delayed_work(&ab->dwork, ab->link_status_wait); + } + + return 0; +} + +static int __devinit ab5500_usb_probe(struct platform_device *pdev) +{ + struct ab5500_usb *ab; + struct abx500_usbgpio_platform_data *usb_pdata = + pdev->dev.platform_data; + int err; + int rev; + int ret = -1; + int irq; + ab = kzalloc(sizeof *ab, GFP_KERNEL); + if (!ab) + return -ENOMEM; + + ab->dev = &pdev->dev; + ab->rev = rev; + ab->otg.dev = ab->dev; + ab->otg.label = "ab5500"; + ab->otg.state = OTG_STATE_B_IDLE; + ab->otg.set_host = ab5500_usb_set_host; + ab->otg.set_peripheral = ab5500_usb_set_peripheral; + ab->otg.set_suspend = ab5500_usb_set_suspend; + ab->otg.set_power = ab5500_usb_set_power; + ab->usb_gpio = usb_pdata; + ab->mode = USB_IDLE; + + platform_set_drvdata(pdev, ab); + + ATOMIC_INIT_NOTIFIER_HEAD(&ab->otg.notifier); + + /* v1: Wait for link status to become stable. + * all: Updates form set_host and set_peripheral as they are atomic. + */ + INIT_DELAYED_WORK(&ab->dwork, ab5500_usb_delayed_work); + + err = otg_set_transceiver(&ab->otg); + if (err) + dev_err(&pdev->dev, "Can't register transceiver\n"); + + ab->usb_cs_gpio = ab->usb_gpio->usb_cs; + + ab->rev = abx500_get_chip_id(ab->dev); + + ab->sysclk = clk_get(ab->dev, "sysclk"); + if (IS_ERR(ab->sysclk)) { + ret = PTR_ERR(ab->sysclk); + ab->sysclk = NULL; + return ret; + } + + ab->v_ape = regulator_get(ab->dev, "v-ape"); + if (!ab->v_ape) { + dev_err(ab->dev, "Could not get v-ape supply\n"); + + return -EINVAL; + } + + ab5500_usb_irq_setup(pdev, ab); + + ret = gpio_request(ab->usb_cs_gpio, "usb-cs"); + if (ret < 0) + dev_err(&pdev->dev, "usb gpio request fail\n"); + + /* Aquire GPIO alternate config struct for USB */ + err = ab->usb_gpio->get(ab->dev); + if (err < 0) + goto fail1; + + err = ab5500_usb_boot_detect(ab); + if (err < 0) + goto fail1; + + return 0; + +fail1: + ab5500_usb_irq_free(ab); + kfree(ab); + return err; +} + +static int __devexit ab5500_usb_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver ab5500_usb_driver = { + .driver = { + .name = "ab5500-usb", + .owner = THIS_MODULE, + }, + .probe = ab5500_usb_probe, + .remove = __devexit_p(ab5500_usb_remove), +}; + +static int __init ab5500_usb_init(void) +{ + return platform_driver_register(&ab5500_usb_driver); +} +subsys_initcall(ab5500_usb_init); + +static void __exit ab5500_usb_exit(void) +{ + platform_driver_unregister(&ab5500_usb_driver); +} +module_exit(ab5500_usb_exit); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 4a9602ea9bf8ad1d5a57c8811647cef22de784cd Mon Sep 17 00:00:00 2001 From: Sakethram Bommisetti Date: Sat, 1 Oct 2011 18:40:39 +0530 Subject: ux500:USB: Fix for CPU Idle from USB USB context is stored and restored at each time when the usb cable is pluged in. ST-Ericsson Linux next: NA ST-Ericsson ID: 363987 ST-Ericsson FOSS-OUT ID: NA Change-Id: I5fa7b32dd4f67dcc81f2418e872ed5107f8624c0 Signed-off-by: Sakethram Bommisetti Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32785 Reviewed-by: Praveena NADAHALLY Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/include/mach/usb.h | 1 + drivers/usb/musb/ux500.c | 148 ++++++++++++++++++++++++++++++++- drivers/usb/otg/ab8500-usb.c | 4 + 3 files changed, 152 insertions(+), 1 deletion(-) (limited to 'arch/arm/mach-ux500/include/mach') diff --git a/arch/arm/mach-ux500/include/mach/usb.h b/arch/arm/mach-ux500/include/mach/usb.h index a9aa06190eb..2d1e54070f2 100644 --- a/arch/arm/mach-ux500/include/mach/usb.h +++ b/arch/arm/mach-ux500/include/mach/usb.h @@ -31,4 +31,5 @@ struct abx500_usbgpio_platform_data { int usb_cs; }; +void ux500_restore_context(void); #endif diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index c1381a5a460..17da4e290f4 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -25,9 +25,11 @@ #include #include #include +#include #include "musb_core.h" +#define DEFAULT_DEVCTL 0x81 static void ux500_musb_set_vbus(struct musb *musb, int is_on); struct ux500_glue { @@ -38,6 +40,145 @@ struct ux500_glue { #define glue_to_musb(g) platform_get_drvdata(g->musb) static struct timer_list notify_timer; +struct musb_context_registers context; +struct musb *_musb; +void ux500_store_context(struct musb *musb) +{ +#ifdef CONFIG_PM + int i; + void __iomem *musb_base = musb->mregs; + void __iomem *epio; + _musb = musb; + + if (is_host_enabled(musb)) { + context.frame = musb_readw(musb_base, MUSB_FRAME); + context.testmode = musb_readb(musb_base, MUSB_TESTMODE); + } + context.power = musb_readb(musb_base, MUSB_POWER); + context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE); + context.intrrxe = musb_readw(musb_base, MUSB_INTRRXE); + context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE); + context.index = musb_readb(musb_base, MUSB_INDEX); + context.devctl = DEFAULT_DEVCTL; + for (i = 0; i < musb->config->num_eps; ++i) { + epio = musb->endpoints[i].regs; + context.index_regs[i].txmaxp = + musb_readw(epio, MUSB_TXMAXP); + context.index_regs[i].txcsr = + musb_readw(epio, MUSB_TXCSR); + context.index_regs[i].rxmaxp = + musb_readw(epio, MUSB_RXMAXP); + context.index_regs[i].rxcsr = + musb_readw(epio, MUSB_RXCSR); + + if (musb->dyn_fifo) { + context.index_regs[i].txfifoadd = + musb_read_txfifoadd(musb_base); + context.index_regs[i].rxfifoadd = + musb_read_rxfifoadd(musb_base); + context.index_regs[i].txfifosz = + musb_read_txfifosz(musb_base); + context.index_regs[i].rxfifosz = + musb_read_rxfifosz(musb_base); + } + if (is_host_enabled(musb)) { + context.index_regs[i].txtype = + musb_readb(epio, MUSB_TXTYPE); + context.index_regs[i].txinterval = + musb_readb(epio, MUSB_TXINTERVAL); + context.index_regs[i].rxtype = + musb_readb(epio, MUSB_RXTYPE); + context.index_regs[i].rxinterval = + musb_readb(epio, MUSB_RXINTERVAL); + + context.index_regs[i].txfunaddr = + musb_read_txfunaddr(musb_base, i); + context.index_regs[i].txhubaddr = + musb_read_txhubaddr(musb_base, i); + context.index_regs[i].txhubport = + musb_read_txhubport(musb_base, i); + + context.index_regs[i].rxfunaddr = + musb_read_rxfunaddr(musb_base, i); + context.index_regs[i].rxhubaddr = + musb_read_rxhubaddr(musb_base, i); + context.index_regs[i].rxhubport = + musb_read_rxhubport(musb_base, i); + } + } +#endif +} +void ux500_restore_context(void) +{ +#ifdef CONFIG_PM + int i; + struct musb *musb = _musb; + void __iomem *musb_base = musb->mregs; + void __iomem *ep_target_regs; + void __iomem *epio; + + if (is_host_enabled(musb)) { + musb_writew(musb_base, MUSB_FRAME, context.frame); + musb_writeb(musb_base, MUSB_TESTMODE, context.testmode); + } + musb_writeb(musb_base, MUSB_POWER, context.power); + musb_writew(musb_base, MUSB_INTRTXE, context.intrtxe); + musb_writew(musb_base, MUSB_INTRRXE, context.intrrxe); + musb_writeb(musb_base, MUSB_INTRUSBE, context.intrusbe); + musb_writeb(musb_base, MUSB_DEVCTL, context.devctl); + + for (i = 0; i < musb->config->num_eps; ++i) { + epio = musb->endpoints[i].regs; + musb_writew(epio, MUSB_TXMAXP, + context.index_regs[i].txmaxp); + musb_writew(epio, MUSB_TXCSR, + context.index_regs[i].txcsr); + musb_writew(epio, MUSB_RXMAXP, + context.index_regs[i].rxmaxp); + musb_writew(epio, MUSB_RXCSR, + context.index_regs[i].rxcsr); + + if (musb->dyn_fifo) { + musb_write_txfifosz(musb_base, + context.index_regs[i].txfifosz); + musb_write_rxfifosz(musb_base, + context.index_regs[i].rxfifosz); + musb_write_txfifoadd(musb_base, + context.index_regs[i].txfifoadd); + musb_write_rxfifoadd(musb_base, + context.index_regs[i].rxfifoadd); + } + + if (is_host_enabled(musb)) { + musb_writeb(epio, MUSB_TXTYPE, + context.index_regs[i].txtype); + musb_writeb(epio, MUSB_TXINTERVAL, + context.index_regs[i].txinterval); + musb_writeb(epio, MUSB_RXTYPE, + context.index_regs[i].rxtype); + musb_writeb(epio, MUSB_RXINTERVAL, + + musb->context.index_regs[i].rxinterval); + musb_write_txfunaddr(musb_base, i, + context.index_regs[i].txfunaddr); + musb_write_txhubaddr(musb_base, i, + context.index_regs[i].txhubaddr); + musb_write_txhubport(musb_base, i, + context.index_regs[i].txhubport); + + ep_target_regs = + musb_read_target_reg_base(i, musb_base); + + musb_write_rxfunaddr(ep_target_regs, + context.index_regs[i].rxfunaddr); + musb_write_rxhubaddr(ep_target_regs, + context.index_regs[i].rxhubaddr); + musb_write_rxhubport(ep_target_regs, + context.index_regs[i].rxhubport); + } + } +#endif +} static void musb_notify_idle(unsigned long _musb) { @@ -185,7 +326,10 @@ static void ux500_musb_try_idle(struct musb *musb, unsigned long timeout) (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(¬ify_timer, timeout); } - +static void ux500_musb_enable(struct musb *musb) +{ + ux500_store_context(musb); +} static int ux500_musb_init(struct musb *musb) { int status; @@ -229,6 +373,8 @@ static const struct musb_platform_ops ux500_ops = { .set_vbus = ux500_musb_set_vbus, .try_idle = ux500_musb_try_idle, + + .enable = ux500_musb_enable, }; /** diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 661b978a0dc..eb4c6537532 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -340,10 +340,12 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) if (ab->mode == USB_HOST) { ab->mode = USB_PERIPHERAL; ab8500_usb_host_phy_dis(ab); + ux500_restore_context(); ab8500_usb_peri_phy_en(ab); } if (ab->mode == USB_IDLE) { ab->mode = USB_PERIPHERAL; + ux500_restore_context(); ab8500_usb_peri_phy_en(ab); } event = USB_EVENT_VBUS; @@ -354,10 +356,12 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) if (ab->mode == USB_PERIPHERAL) { ab->mode = USB_HOST; ab8500_usb_peri_phy_dis(ab); + ux500_restore_context(); ab8500_usb_host_phy_en(ab); } if (ab->mode == USB_IDLE) { ab->mode = USB_HOST; + ux500_restore_context(); ab8500_usb_host_phy_en(ab); } ab->phy.otg->default_a = true; -- cgit v1.2.3 From 30600535dfe60c8092d799fdfa3fc16f47393861 Mon Sep 17 00:00:00 2001 From: Jonas Aberg Date: Tue, 24 Jan 2012 11:08:39 +0530 Subject: u8500: musb: Add pm runtime support Add pm runtime support for musb, including both pin and clock handling. ST-Ericsson Linux next: - ST-Ericsson ID: 370128, 375498 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Idf72a6f226d1309d5a13b358e653ee97469e5ae6 Signed-off-by: Jonas Aaberg Signed-off-by: Sakethram Bommisetti Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44709 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-mop500.c | 2 - arch/arm/mach-ux500/include/mach/usb.h | 6 +- arch/arm/mach-ux500/usb.c | 4 ++ drivers/usb/musb/musb_core.c | 6 +- drivers/usb/musb/ux500.c | 117 ++++++++++++++++++++++++++------- drivers/usb/otg/ab8500-usb.c | 35 ++++------ 6 files changed, 119 insertions(+), 51 deletions(-) (limited to 'arch/arm/mach-ux500/include/mach') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 5c1da28427b..77d03c1fbd0 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -53,7 +53,6 @@ #include "devices-db8500.h" #include "board-mop500.h" #include "board-mop500-regulators.h" -#include "board-ux500-usb.h" static struct gpio_led snowball_led_array[] = { { @@ -195,7 +194,6 @@ static struct ab8500_platform_data ab8500_platdata = { .regulator = ab8500_regulators, .num_regulator = ARRAY_SIZE(ab8500_regulators), .gpio = &ab8500_gpio_pdata, - .usb = &abx500_usbgpio_plat_data, }; static struct resource ab8500_resources[] = { diff --git a/arch/arm/mach-ux500/include/mach/usb.h b/arch/arm/mach-ux500/include/mach/usb.h index 2d1e54070f2..700d9dbb1be 100644 --- a/arch/arm/mach-ux500/include/mach/usb.h +++ b/arch/arm/mach-ux500/include/mach/usb.h @@ -12,6 +12,8 @@ #define UX500_MUSB_DMA_NUM_RX_CHANNELS 8 #define UX500_MUSB_DMA_NUM_TX_CHANNELS 8 +struct musb; + struct ux500_musb_board_data { void **dma_rx_param_array; void **dma_tx_param_array; @@ -23,6 +25,7 @@ struct ux500_musb_board_data { void ux500_add_usb(struct device *parent, resource_size_t base, int irq, int *dma_rx_cfg, int *dma_tx_cfg); +/* Only used for u5500 */ struct abx500_usbgpio_platform_data { int (*get)(struct device *device); void (*enable)(void); @@ -30,6 +33,5 @@ struct abx500_usbgpio_platform_data { void (*put)(void); int usb_cs; }; - -void ux500_restore_context(void); +void ux500_restore_context(struct musb *musb); #endif diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c index ca84e040e99..fb1f6775154 100644 --- a/arch/arm/mach-ux500/usb.c +++ b/arch/arm/mach-ux500/usb.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "pins.h" #include "board-ux500-usb.h" @@ -170,6 +171,9 @@ struct platform_device ux500_musb_device = { .platform_data = &musb_platform_data, .dma_mask = &ux500_musb_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), +#ifdef CONFIG_UX500_SOC_DB8500 + .pm_domain = &ux500_dev_power_domain, +#endif }, .num_resources = ARRAY_SIZE(usb_resources), .resource = usb_resources, diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 5faac98ca30..c5bf28afd86 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2371,8 +2371,12 @@ static const struct dev_pm_ops musb_dev_pm_ops = { .runtime_suspend = musb_runtime_suspend, .runtime_resume = musb_runtime_resume, }; - +#ifdef CONFIG_UX500_SOC_DB8500 +#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops) +#else #define MUSB_DEV_PM_OPS NULL +#endif + #else #define MUSB_DEV_PM_OPS NULL #endif diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index ae6be2eaa09..5d0c5eda128 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include "musb_core.h" @@ -40,19 +42,23 @@ struct ux500_glue { #define glue_to_musb(g) platform_get_drvdata(g->musb) static struct timer_list notify_timer; -struct musb_context_registers context; +static struct musb_context_registers context; +static bool context_stored; struct musb *_musb; -void ux500_store_context(struct musb *musb) + +static void ux500_store_context(struct musb *musb) { #ifdef CONFIG_PM int i; void __iomem *musb_base; void __iomem *epio; - if (musb != NULL) - _musb = musb; - else - return; + if (cpu_is_u5500()) { + if (musb != NULL) + _musb = musb; + else + return; + } musb_base = musb->mregs; @@ -124,17 +130,21 @@ void ux500_store_context(struct musb *musb) musb_read_rxhubport(musb_base, i); } } + context_stored = true; #endif } -void ux500_restore_context(void) + +void ux500_restore_context(struct musb *musb) { #ifdef CONFIG_PM int i; - struct musb *musb; void __iomem *musb_base; void __iomem *ep_target_regs; void __iomem *epio; + if (!context_stored) + return; + if (_musb != NULL) musb = _musb; else @@ -222,7 +232,8 @@ static void musb_notify_idle(unsigned long _musb) unsigned long flags; u8 devctl; - + dev_dbg(musb->controller, "musb_notify_idle %s", + otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); devctl = musb_readb(musb->mregs, MUSB_DEVCTL); @@ -235,6 +246,10 @@ static void musb_notify_idle(unsigned long _musb) musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); } + if (cpu_is_u8500()) { + pm_runtime_mark_last_busy(musb->controller); + pm_runtime_put_autosuspend(musb->controller); + } break; case OTG_STATE_A_SUSPEND: @@ -250,7 +265,14 @@ static int musb_otg_notifications(struct notifier_block *nb, { struct musb *musb = container_of(nb, struct musb, nb); + dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n", + event, otg_state_string(musb->xceiv->state)); switch (event) { + + case USB_EVENT_PREPARE: + pm_runtime_get_sync(musb->controller); + ux500_restore_context(musb); + break; case USB_EVENT_ID: case USB_EVENT_RIDA: dev_dbg(musb->controller, "ID GND\n"); @@ -263,12 +285,17 @@ static int musb_otg_notifications(struct notifier_block *nb, dev_dbg(musb->controller, "VBUS Connect\n"); break; - +/* case USB_EVENT_RIDB: FIXME, not yet managed */ case USB_EVENT_NONE: dev_dbg(musb->controller, "VBUS Disconnect\n"); - if (is_otg_enabled(musb)) + if (is_otg_enabled(musb) && musb->is_host) ux500_musb_set_vbus(musb, 0); - + else + musb->xceiv->state = OTG_STATE_B_IDLE; + break; + case USB_EVENT_CLEAN: + pm_runtime_mark_last_busy(musb->controller); + pm_runtime_put_autosuspend(musb->controller); break; default: dev_dbg(musb->controller, "ID float\n"); @@ -321,11 +348,9 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on) /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and * jumping right to B_IDLE... */ - musb->xceiv->default_a = 0; musb->xceiv->state = OTG_STATE_B_IDLE; devctl &= ~MUSB_DEVCTL_SESSION; - MUSB_DEV_MODE(musb); } musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); @@ -400,12 +425,13 @@ static struct usb_ep *ux500_musb_configure_endpoints(struct musb *musb, static int ux500_musb_init(struct musb *musb) { int status; + musb->xceiv = usb_get_transceiver(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; } - + pm_runtime_get_noresume(musb->controller); musb->nb.notifier_call = musb_otg_notifications; status = otg_register_notifier(musb->xceiv, &musb->nb); @@ -418,6 +444,7 @@ static int ux500_musb_init(struct musb *musb) return 0; err1: + pm_runtime_disable(musb->controller); return status; } @@ -516,12 +543,12 @@ static int __devinit ux500_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register musb device\n"); goto err4; } + pm_runtime_enable(&pdev->dev); return 0; - err4: - clk_disable(clk); - + if (cpu_is_u5500()) + clk_disable(clk); err3: clk_put(clk); @@ -541,8 +568,12 @@ static int __devexit ux500_remove(struct platform_device *pdev) platform_device_del(glue->musb); platform_device_put(glue->musb); - clk_disable(glue->clk); + if (cpu_is_u5500()) + clk_disable(glue->clk); clk_put(glue->clk); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + kfree(glue); return 0; @@ -562,8 +593,15 @@ static int ux500_suspend(struct device *dev) struct musb *musb = glue_to_musb(glue); usb_phy_set_suspend(musb->xceiv, 1); - clk_disable(glue->clk); + if (cpu_is_u5500()) + /* + * Since this clock is in the APE domain, it will + * automatically be disabled on suspend. + * (And enabled on resume automatically.) + */ + clk_disable(glue->clk); + dev_dbg(dev, "ux500_suspend\n"); return 0; } @@ -578,20 +616,51 @@ static int ux500_resume(struct device *dev) { struct ux500_glue *glue = dev_get_drvdata(dev); struct musb *musb = glue_to_musb(glue); - int ret; + + if (cpu_is_u5500()) + /* No point in propagating errors on resume */ + (void) clk_enable(glue->clk); + dev_dbg(dev, "ux500_resume\n"); + + usb_phy_set_suspend(musb->xceiv, 0); + + return 0; +} +#ifdef CONFIG_UX500_SOC_DB8500 +static int ux500_musb_runtime_resume(struct device *dev) +{ + struct ux500_glue *glue = dev_get_drvdata(dev); + int ret; + + if (cpu_is_u5500()) + return 0; ret = clk_enable(glue->clk); if (ret) { - dev_err(dev, "failed to enable clock\n"); + dev_dbg(dev, "Unable to enable clk\n"); return ret; } + dev_dbg(dev, "ux500_musb_runtime_resume\n"); + return 0; +} - usb_phy_set_suspend(musb->xceiv, 0); +static int ux500_musb_runtime_suspend(struct device *dev) +{ + struct ux500_glue *glue = dev_get_drvdata(dev); + if (cpu_is_u5500()) + return 0; + + clk_disable(glue->clk); + dev_dbg(dev, "ux500_musb_runtime_suspend\n"); return 0; } - +#endif static const struct dev_pm_ops ux500_pm_ops = { +#ifdef CONFIG_UX500_SOC_DB8500 + SET_RUNTIME_PM_OPS(ux500_musb_runtime_suspend, + ux500_musb_runtime_resume, NULL) +#endif .suspend = ux500_suspend, .resume = ux500_resume, }; diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 5c5b6a9dcdb..659fca15976 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -29,19 +29,15 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include -#include - -#include - #define AB8500_MAIN_WD_CTRL_REG 0x01 #define AB8500_USB_LINE_STAT_REG 0x80 #define AB8500_USB_PHY_CTRL_REG 0x8A @@ -126,7 +122,6 @@ struct ab8500_usb { struct regulator *v_ape; struct regulator *v_musb; struct regulator *v_ulpi; - struct abx500_usbgpio_platform_data *usb_gpio; struct delayed_work work_usb_workaround; bool sysfs_flag; }; @@ -245,7 +240,6 @@ static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host) bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN : AB8500_BIT_PHY_CTRL_DEVICE_EN; - ab->usb_gpio->enable(); clk_enable(ab->sysclk); ab8500_usb_regulator_ctrl(ab, sel_host, true); @@ -299,8 +293,6 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) ab8500_usb_regulator_ctrl(ab, sel_host, false); - ab->usb_gpio->disable(); - prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, (char *)dev_name(ab->dev), 50); @@ -360,12 +352,16 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) if (ab->mode == USB_HOST) { ab->mode = USB_PERIPHERAL; ab8500_usb_host_phy_dis(ab); - ux500_restore_context(); + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); ab8500_usb_peri_phy_en(ab); } if (ab->mode == USB_IDLE) { ab->mode = USB_PERIPHERAL; - ux500_restore_context(); + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); ab8500_usb_peri_phy_en(ab); } if (event != USB_EVENT_RIDC) @@ -378,12 +374,16 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) if (ab->mode == USB_PERIPHERAL) { ab->mode = USB_HOST; ab8500_usb_peri_phy_dis(ab); - ux500_restore_context(); + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); ab8500_usb_host_phy_en(ab); } if (ab->mode == USB_IDLE) { ab->mode = USB_HOST; - ux500_restore_context(); + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); ab8500_usb_host_phy_en(ab); } ab->phy.otg->default_a = true; @@ -794,8 +794,6 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) { struct ab8500_usb *ab; struct usb_otg *otg; - struct ab8500_platform_data *ab8500_pdata = - dev_get_platdata(pdev->dev.parent); int err; int rev; int ret = -1; @@ -831,7 +829,6 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) otg->phy = &ab->phy; otg->set_host = ab8500_usb_set_host; otg->set_peripheral = ab8500_usb_set_peripheral; - ab->usb_gpio = ab8500_pdata->usb; ab->sysfs_flag = true; platform_set_drvdata(pdev, ab); @@ -917,10 +914,6 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); - err = ab->usb_gpio->get(ab->dev); - if (err < 0) - goto fail3; - prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)dev_name(ab->dev), 50); dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", ab->rev); @@ -969,8 +962,6 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) ab8500_usb_regulator_put(ab); - ab->usb_gpio->put(); - platform_set_drvdata(pdev, NULL); kfree(ab->phy.otg); -- cgit v1.2.3