2010-12-07 17:00:09 +03:00
/*
* drivers / usb / otg / ab8500_usb . c
*
2013-05-15 16:03:32 +04:00
* USB transceiver driver for AB8500 family chips
2010-12-07 17:00:09 +03:00
*
2013-05-15 16:03:32 +04:00
* Copyright ( C ) 2010 - 2013 ST - Ericsson AB
2010-12-07 17:00:09 +03:00
* Mian Yousaf Kaukab < mian . yousaf . kaukab @ stericsson . com >
2013-05-15 16:03:32 +04:00
* Avinash Kumar < avinash . kumar @ stericsson . com >
2013-05-15 16:03:33 +04:00
* Thirupathi Chippakurthy < thirupathi . chippakurthy @ stericsson . com >
2010-12-07 17:00:09 +03:00
*
* 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 0213 9 , USA .
*
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/usb/otg.h>
# include <linux/slab.h>
# include <linux/notifier.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
2013-05-15 16:03:26 +04:00
# include <linux/clk.h>
# include <linux/err.h>
2010-12-07 17:00:09 +03:00
# include <linux/mfd/abx500.h>
2011-12-02 17:16:33 +04:00
# include <linux/mfd/abx500/ab8500.h>
2013-03-08 06:27:09 +04:00
# include <linux/usb/musb-ux500.h>
2013-04-03 12:45:06 +04:00
# include <linux/regulator/consumer.h>
2013-04-03 12:45:11 +04:00
# include <linux/pinctrl/consumer.h>
2010-12-07 17:00:09 +03:00
2013-04-03 12:45:04 +04:00
/* Bank AB8500_SYS_CTRL2_BLOCK */
2010-12-07 17:00:09 +03:00
# define AB8500_MAIN_WD_CTRL_REG 0x01
2013-04-03 12:45:04 +04:00
/* Bank AB8500_USB */
2010-12-07 17:00:09 +03:00
# define AB8500_USB_LINE_STAT_REG 0x80
2013-03-08 06:27:09 +04:00
# define AB8505_USB_LINE_STAT_REG 0x94
2013-05-15 16:03:32 +04:00
# define AB8540_USB_LINK_STAT_REG 0x94
2013-05-15 16:03:33 +04:00
# define AB9540_USB_LINK_STAT_REG 0x94
2013-05-15 16:03:32 +04:00
# define AB8540_USB_OTG_CTL_REG 0x87
2010-12-07 17:00:09 +03:00
# define AB8500_USB_PHY_CTRL_REG 0x8A
2013-05-15 16:03:32 +04:00
# define AB8540_VBUS_CTRL_REG 0x82
2010-12-07 17:00:09 +03:00
2013-04-03 12:45:04 +04:00
/* Bank AB8500_DEVELOPMENT */
# define AB8500_BANK12_ACCESS 0x00
/* Bank AB8500_DEBUG */
2013-05-15 16:03:32 +04:00
# define AB8540_DEBUG 0x32
2013-04-03 12:45:04 +04:00
# define AB8500_USB_PHY_TUNE1 0x05
# define AB8500_USB_PHY_TUNE2 0x06
# define AB8500_USB_PHY_TUNE3 0x07
2013-05-15 16:03:32 +04:00
/* Bank AB8500_INTERRUPT */
# define AB8500_IT_SOURCE2_REG 0x01
2010-12-07 17:00:09 +03:00
# define AB8500_BIT_OTG_STAT_ID (1 << 0)
# define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0)
# define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1)
# define AB8500_BIT_WD_CTRL_ENABLE (1 << 0)
# define AB8500_BIT_WD_CTRL_KICK (1 << 1)
2013-05-15 16:03:32 +04:00
# define AB8500_BIT_SOURCE2_VBUSDET (1 << 7)
# define AB8540_BIT_OTG_CTL_VBUS_VALID_ENA (1 << 0)
# define AB8540_BIT_OTG_CTL_ID_HOST_ENA (1 << 1)
# define AB8540_BIT_OTG_CTL_ID_DEV_ENA (1 << 5)
# define AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA (1 << 0)
2010-12-07 17:00:09 +03:00
# define AB8500_WD_KICK_DELAY_US 100 /* usec */
# define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */
2013-03-08 06:27:09 +04:00
# define AB8500_V20_31952_DISABLE_DELAY_US 100 /* usec */
2010-12-07 17:00:09 +03:00
/* Usb line status register */
enum ab8500_usb_link_status {
2013-03-08 06:27:09 +04:00
USB_LINK_NOT_CONFIGURED_8500 = 0 ,
USB_LINK_STD_HOST_NC_8500 ,
USB_LINK_STD_HOST_C_NS_8500 ,
USB_LINK_STD_HOST_C_S_8500 ,
USB_LINK_HOST_CHG_NM_8500 ,
USB_LINK_HOST_CHG_HS_8500 ,
USB_LINK_HOST_CHG_HS_CHIRP_8500 ,
USB_LINK_DEDICATED_CHG_8500 ,
USB_LINK_ACA_RID_A_8500 ,
USB_LINK_ACA_RID_B_8500 ,
USB_LINK_ACA_RID_C_NM_8500 ,
USB_LINK_ACA_RID_C_HS_8500 ,
USB_LINK_ACA_RID_C_HS_CHIRP_8500 ,
USB_LINK_HM_IDGND_8500 ,
USB_LINK_RESERVED_8500 ,
USB_LINK_NOT_VALID_LINK_8500 ,
} ;
enum ab8505_usb_link_status {
USB_LINK_NOT_CONFIGURED_8505 = 0 ,
USB_LINK_STD_HOST_NC_8505 ,
USB_LINK_STD_HOST_C_NS_8505 ,
USB_LINK_STD_HOST_C_S_8505 ,
USB_LINK_CDP_8505 ,
USB_LINK_RESERVED0_8505 ,
USB_LINK_RESERVED1_8505 ,
USB_LINK_DEDICATED_CHG_8505 ,
USB_LINK_ACA_RID_A_8505 ,
USB_LINK_ACA_RID_B_8505 ,
USB_LINK_ACA_RID_C_NM_8505 ,
USB_LINK_RESERVED2_8505 ,
USB_LINK_RESERVED3_8505 ,
USB_LINK_HM_IDGND_8505 ,
USB_LINK_CHARGERPORT_NOT_OK_8505 ,
USB_LINK_CHARGER_DM_HIGH_8505 ,
USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8505 ,
USB_LINK_STD_UPSTREAM_NO_IDGNG_NO_VBUS_8505 ,
USB_LINK_STD_UPSTREAM_8505 ,
USB_LINK_CHARGER_SE1_8505 ,
USB_LINK_CARKIT_CHGR_1_8505 ,
USB_LINK_CARKIT_CHGR_2_8505 ,
USB_LINK_ACA_DOCK_CHGR_8505 ,
USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8505 ,
USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8505 ,
USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8505 ,
USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8505 ,
USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505 ,
} ;
2013-05-15 16:03:32 +04:00
enum ab8540_usb_link_status {
USB_LINK_NOT_CONFIGURED_8540 = 0 ,
USB_LINK_STD_HOST_NC_8540 ,
USB_LINK_STD_HOST_C_NS_8540 ,
USB_LINK_STD_HOST_C_S_8540 ,
USB_LINK_CDP_8540 ,
USB_LINK_RESERVED0_8540 ,
USB_LINK_RESERVED1_8540 ,
USB_LINK_DEDICATED_CHG_8540 ,
USB_LINK_ACA_RID_A_8540 ,
USB_LINK_ACA_RID_B_8540 ,
USB_LINK_ACA_RID_C_NM_8540 ,
USB_LINK_RESERVED2_8540 ,
USB_LINK_RESERVED3_8540 ,
USB_LINK_HM_IDGND_8540 ,
USB_LINK_CHARGERPORT_NOT_OK_8540 ,
USB_LINK_CHARGER_DM_HIGH_8540 ,
USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540 ,
USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_8540 ,
USB_LINK_STD_UPSTREAM_8540 ,
USB_LINK_CHARGER_SE1_8540 ,
USB_LINK_CARKIT_CHGR_1_8540 ,
USB_LINK_CARKIT_CHGR_2_8540 ,
USB_LINK_ACA_DOCK_CHGR_8540 ,
USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8540 ,
USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8540 ,
USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8540 ,
USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8540 ,
USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8540
} ;
2013-05-15 16:03:33 +04:00
enum ab9540_usb_link_status {
USB_LINK_NOT_CONFIGURED_9540 = 0 ,
USB_LINK_STD_HOST_NC_9540 ,
USB_LINK_STD_HOST_C_NS_9540 ,
USB_LINK_STD_HOST_C_S_9540 ,
USB_LINK_CDP_9540 ,
USB_LINK_RESERVED0_9540 ,
USB_LINK_RESERVED1_9540 ,
USB_LINK_DEDICATED_CHG_9540 ,
USB_LINK_ACA_RID_A_9540 ,
USB_LINK_ACA_RID_B_9540 ,
USB_LINK_ACA_RID_C_NM_9540 ,
USB_LINK_RESERVED2_9540 ,
USB_LINK_RESERVED3_9540 ,
USB_LINK_HM_IDGND_9540 ,
USB_LINK_CHARGERPORT_NOT_OK_9540 ,
USB_LINK_CHARGER_DM_HIGH_9540 ,
USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540 ,
USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_9540 ,
USB_LINK_STD_UPSTREAM_9540 ,
USB_LINK_CHARGER_SE1_9540 ,
USB_LINK_CARKIT_CHGR_1_9540 ,
USB_LINK_CARKIT_CHGR_2_9540 ,
USB_LINK_ACA_DOCK_CHGR_9540 ,
USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_9540 ,
USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_9540 ,
USB_LINK_SAMSUNG_UART_CBL_PHY_EN_9540 ,
USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_9540 ,
USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_9540
} ;
2013-03-08 06:27:09 +04:00
enum ab8500_usb_mode {
USB_IDLE = 0 ,
USB_PERIPHERAL ,
USB_HOST ,
USB_DEDICATED_CHG
2010-12-07 17:00:09 +03:00
} ;
2013-05-15 16:03:31 +04:00
/* Register USB_LINK_STATUS interrupt */
# define AB8500_USB_FLAG_USE_LINK_STATUS_IRQ (1 << 0)
/* Register ID_WAKEUP_F interrupt */
# define AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ (1 << 1)
/* Register VBUS_DET_F interrupt */
# define AB8500_USB_FLAG_USE_VBUS_DET_IRQ (1 << 2)
/* Driver is using the ab-iddet driver*/
# define AB8500_USB_FLAG_USE_AB_IDDET (1 << 3)
/* Enable setting regulators voltage */
# define AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE (1 << 4)
2013-05-15 16:03:32 +04:00
/* Enable the check_vbus_status workaround */
# define AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS (1 << 5)
/* Enable the vbus host workaround */
# define AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK (1 << 6)
2013-05-15 16:03:31 +04:00
2010-12-07 17:00:09 +03:00
struct ab8500_usb {
2012-02-13 15:24:05 +04:00
struct usb_phy phy ;
2010-12-07 17:00:09 +03:00
struct device * dev ;
2013-03-08 06:27:08 +04:00
struct ab8500 * ab8500 ;
2010-12-07 17:00:09 +03:00
unsigned vbus_draw ;
struct work_struct phy_dis_work ;
2013-05-15 16:03:32 +04:00
struct work_struct vbus_event_work ;
2013-03-08 06:27:09 +04:00
enum ab8500_usb_mode mode ;
2013-05-15 16:03:26 +04:00
struct clk * sysclk ;
2013-04-03 12:45:06 +04:00
struct regulator * v_ape ;
struct regulator * v_musb ;
struct regulator * v_ulpi ;
2013-04-03 12:45:08 +04:00
int saved_v_ulpi ;
2013-03-08 06:27:09 +04:00
int previous_link_status_state ;
2013-04-03 12:45:11 +04:00
struct pinctrl * pinctrl ;
struct pinctrl_state * pins_sleep ;
2013-05-15 16:03:32 +04:00
bool enabled_charging_detection ;
2013-05-15 16:03:31 +04:00
unsigned int flags ;
2010-12-07 17:00:09 +03:00
} ;
2012-02-13 15:24:05 +04:00
static inline struct ab8500_usb * phy_to_ab ( struct usb_phy * x )
2010-12-07 17:00:09 +03:00
{
2012-02-13 15:24:05 +04:00
return container_of ( x , struct ab8500_usb , phy ) ;
2010-12-07 17:00:09 +03:00
}
static void ab8500_usb_wd_workaround ( struct ab8500_usb * ab )
{
abx500_set_register_interruptible ( ab - > dev ,
AB8500_SYS_CTRL2_BLOCK ,
AB8500_MAIN_WD_CTRL_REG ,
AB8500_BIT_WD_CTRL_ENABLE ) ;
udelay ( AB8500_WD_KICK_DELAY_US ) ;
abx500_set_register_interruptible ( ab - > dev ,
AB8500_SYS_CTRL2_BLOCK ,
AB8500_MAIN_WD_CTRL_REG ,
( AB8500_BIT_WD_CTRL_ENABLE
| AB8500_BIT_WD_CTRL_KICK ) ) ;
2013-03-08 06:27:08 +04:00
udelay ( AB8500_WD_V11_DISABLE_DELAY_US ) ;
2010-12-07 17:00:09 +03:00
abx500_set_register_interruptible ( ab - > dev ,
AB8500_SYS_CTRL2_BLOCK ,
AB8500_MAIN_WD_CTRL_REG ,
0 ) ;
}
2013-04-03 12:45:08 +04:00
static void ab8500_usb_regulator_enable ( struct ab8500_usb * ab )
{
int ret , volt ;
2013-04-03 18:02:25 +04:00
ret = regulator_enable ( ab - > v_ape ) ;
if ( ret )
dev_err ( ab - > dev , " Failed to enable v-ape \n " ) ;
2013-04-03 12:45:08 +04:00
2013-05-15 16:03:31 +04:00
if ( ab - > flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE ) {
2013-04-03 12:45:08 +04:00
ab - > saved_v_ulpi = regulator_get_voltage ( ab - > v_ulpi ) ;
if ( ab - > saved_v_ulpi < 0 )
dev_err ( ab - > dev , " Failed to get v_ulpi voltage \n " ) ;
ret = regulator_set_voltage ( ab - > v_ulpi , 1300000 , 1350000 ) ;
if ( ret < 0 )
dev_err ( ab - > dev , " Failed to set the Vintcore to 1.3V, ret=%d \n " ,
ret ) ;
ret = regulator_set_optimum_mode ( ab - > v_ulpi , 28000 ) ;
if ( ret < 0 )
dev_err ( ab - > dev , " Failed to set optimum mode (ret=%d) \n " ,
ret ) ;
}
2013-04-03 18:02:25 +04:00
ret = regulator_enable ( ab - > v_ulpi ) ;
if ( ret )
dev_err ( ab - > dev , " Failed to enable vddulpivio18 \n " ) ;
2013-04-03 12:45:08 +04:00
2013-05-15 16:03:31 +04:00
if ( ab - > flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE ) {
2013-04-03 12:45:08 +04:00
volt = regulator_get_voltage ( ab - > v_ulpi ) ;
if ( ( volt ! = 1300000 ) & & ( volt ! = 1350000 ) )
dev_err ( ab - > dev , " Vintcore is not set to 1.3V volt=%d \n " ,
volt ) ;
}
2013-04-03 18:02:25 +04:00
ret = regulator_enable ( ab - > v_musb ) ;
if ( ret )
dev_err ( ab - > dev , " Failed to enable musb_1v8 \n " ) ;
2013-04-03 12:45:08 +04:00
}
static void ab8500_usb_regulator_disable ( struct ab8500_usb * ab )
{
int ret ;
regulator_disable ( ab - > v_musb ) ;
regulator_disable ( ab - > v_ulpi ) ;
/* USB is not the only consumer of Vintcore, restore old settings */
2013-05-15 16:03:31 +04:00
if ( ab - > flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE ) {
2013-04-03 12:45:08 +04:00
if ( ab - > saved_v_ulpi > 0 ) {
ret = regulator_set_voltage ( ab - > v_ulpi ,
ab - > saved_v_ulpi , ab - > saved_v_ulpi ) ;
if ( ret < 0 )
dev_err ( ab - > dev , " Failed to set the Vintcore to %duV, ret=%d \n " ,
ab - > saved_v_ulpi , ret ) ;
}
ret = regulator_set_optimum_mode ( ab - > v_ulpi , 0 ) ;
if ( ret < 0 )
dev_err ( ab - > dev , " Failed to set optimum mode (ret=%d) \n " ,
ret ) ;
}
regulator_disable ( ab - > v_ape ) ;
}
2013-03-08 06:27:09 +04:00
static void ab8500_usb_wd_linkstatus ( struct ab8500_usb * ab , u8 bit )
{
/* Workaround for v2.0 bug # 31952 */
if ( is_ab8500_2p0 ( ab - > ab8500 ) ) {
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_PHY_CTRL_REG ,
bit , bit ) ;
udelay ( AB8500_V20_31952_DISABLE_DELAY_US ) ;
}
}
2013-04-03 12:45:07 +04:00
static void ab8500_usb_phy_enable ( struct ab8500_usb * ab , bool sel_host )
2010-12-07 17:00:09 +03:00
{
2013-04-03 12:45:07 +04:00
u8 bit ;
bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN :
AB8500_BIT_PHY_CTRL_DEVICE_EN ;
2010-12-07 17:00:09 +03:00
2013-04-03 12:45:11 +04:00
/* mux and configure USB pins to DEFAULT state */
ab - > pinctrl = pinctrl_get_select ( ab - > dev , PINCTRL_STATE_DEFAULT ) ;
if ( IS_ERR ( ab - > pinctrl ) )
dev_err ( ab - > dev , " could not get/set default pinstate \n " ) ;
2013-05-15 16:03:26 +04:00
if ( clk_prepare_enable ( ab - > sysclk ) )
dev_err ( ab - > dev , " can't prepare/enable clock \n " ) ;
2013-04-03 12:45:08 +04:00
ab8500_usb_regulator_enable ( ab ) ;
2013-04-03 12:45:07 +04:00
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_PHY_CTRL_REG ,
bit , bit ) ;
2013-05-15 16:03:32 +04:00
if ( ab - > flags & AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK ) {
if ( sel_host )
abx500_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8540_USB_OTG_CTL_REG ,
AB8540_BIT_OTG_CTL_VBUS_VALID_ENA |
AB8540_BIT_OTG_CTL_ID_HOST_ENA |
AB8540_BIT_OTG_CTL_ID_DEV_ENA ) ;
}
2013-04-03 12:45:07 +04:00
}
static void ab8500_usb_phy_disable ( struct ab8500_usb * ab , bool sel_host )
{
u8 bit ;
bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN :
AB8500_BIT_PHY_CTRL_DEVICE_EN ;
ab8500_usb_wd_linkstatus ( ab , bit ) ;
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_PHY_CTRL_REG ,
bit , 0 ) ;
2010-12-07 17:00:09 +03:00
2013-04-03 12:45:07 +04:00
/* Needed to disable the phy.*/
ab8500_usb_wd_workaround ( ab ) ;
2013-04-03 12:45:08 +04:00
2013-05-15 16:03:26 +04:00
clk_disable_unprepare ( ab - > sysclk ) ;
2013-04-03 12:45:08 +04:00
ab8500_usb_regulator_disable ( ab ) ;
2013-04-03 12:45:11 +04:00
if ( ! IS_ERR ( ab - > pinctrl ) ) {
/* configure USB pins to SLEEP state */
ab - > pins_sleep = pinctrl_lookup_state ( ab - > pinctrl ,
PINCTRL_STATE_SLEEP ) ;
if ( IS_ERR ( ab - > pins_sleep ) )
dev_dbg ( ab - > dev , " could not get sleep pinstate \n " ) ;
else if ( pinctrl_select_state ( ab - > pinctrl , ab - > pins_sleep ) )
dev_err ( ab - > dev , " could not set pins to sleep state \n " ) ;
2013-05-15 16:03:25 +04:00
/*
* as USB pins are shared with iddet , release them to allow
2013-04-03 12:45:11 +04:00
* iddet to request them
*/
pinctrl_put ( ab - > pinctrl ) ;
}
2010-12-07 17:00:09 +03:00
}
2013-04-03 12:45:07 +04:00
# define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_enable(ab, true)
# define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_disable(ab, true)
# define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_enable(ab, false)
# define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_disable(ab, false)
2010-12-07 17:00:09 +03:00
2013-05-15 16:03:33 +04:00
static int ab9540_usb_link_status_update ( struct ab8500_usb * ab ,
enum ab9540_usb_link_status lsts )
{
enum ux500_musb_vbus_id_status event = 0 ;
dev_dbg ( ab - > dev , " ab9540_usb_link_status_update %d \n " , lsts ) ;
if ( ab - > previous_link_status_state = = USB_LINK_HM_IDGND_9540 & &
( lsts = = USB_LINK_STD_HOST_C_NS_9540 | |
lsts = = USB_LINK_STD_HOST_NC_9540 ) )
return 0 ;
if ( ab - > previous_link_status_state = = USB_LINK_ACA_RID_A_9540 & &
( lsts = = USB_LINK_STD_HOST_NC_9540 ) )
return 0 ;
ab - > previous_link_status_state = lsts ;
switch ( lsts ) {
case USB_LINK_ACA_RID_B_9540 :
event = UX500_MUSB_RIDB ;
case USB_LINK_NOT_CONFIGURED_9540 :
case USB_LINK_RESERVED0_9540 :
case USB_LINK_RESERVED1_9540 :
case USB_LINK_RESERVED2_9540 :
case USB_LINK_RESERVED3_9540 :
if ( ab - > mode = = USB_PERIPHERAL )
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_CLEAN , & ab - > vbus_draw ) ;
ab - > mode = USB_IDLE ;
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
if ( event ! = UX500_MUSB_RIDB )
event = UX500_MUSB_NONE ;
/* Fallback to default B_IDLE as nothing is connected. */
ab - > phy . state = OTG_STATE_B_IDLE ;
break ;
case USB_LINK_ACA_RID_C_NM_9540 :
event = UX500_MUSB_RIDC ;
case USB_LINK_STD_HOST_NC_9540 :
case USB_LINK_STD_HOST_C_NS_9540 :
case USB_LINK_STD_HOST_C_S_9540 :
case USB_LINK_CDP_9540 :
if ( ab - > mode = = USB_HOST ) {
ab - > mode = USB_PERIPHERAL ;
ab8500_usb_host_phy_dis ( ab ) ;
ab8500_usb_peri_phy_en ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
}
if ( ab - > mode = = USB_IDLE ) {
ab - > mode = USB_PERIPHERAL ;
ab8500_usb_peri_phy_en ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
}
if ( event ! = UX500_MUSB_RIDC )
event = UX500_MUSB_VBUS ;
break ;
case USB_LINK_ACA_RID_A_9540 :
event = UX500_MUSB_RIDA ;
case USB_LINK_HM_IDGND_9540 :
case USB_LINK_STD_UPSTREAM_9540 :
if ( ab - > mode = = USB_PERIPHERAL ) {
ab - > mode = USB_HOST ;
ab8500_usb_peri_phy_dis ( ab ) ;
ab8500_usb_host_phy_en ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
}
if ( ab - > mode = = USB_IDLE ) {
ab - > mode = USB_HOST ;
ab8500_usb_host_phy_en ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
}
ab - > phy . otg - > default_a = true ;
if ( event ! = UX500_MUSB_RIDA )
event = UX500_MUSB_ID ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
break ;
case USB_LINK_DEDICATED_CHG_9540 :
ab - > mode = USB_DEDICATED_CHG ;
event = UX500_MUSB_CHARGER ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
break ;
case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540 :
case USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_9540 :
if ( ! ( is_ab9540_2p0_or_earlier ( ab - > ab8500 ) ) ) {
event = UX500_MUSB_NONE ;
if ( ab - > mode = = USB_HOST ) {
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
ab8500_usb_host_phy_dis ( ab ) ;
ab - > mode = USB_IDLE ;
}
if ( ab - > mode = = USB_PERIPHERAL ) {
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
ab8500_usb_peri_phy_dis ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_CLEAN ,
& ab - > vbus_draw ) ;
ab - > mode = USB_IDLE ;
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
}
}
break ;
default :
break ;
}
return 0 ;
}
2013-05-15 16:03:32 +04:00
static int ab8540_usb_link_status_update ( struct ab8500_usb * ab ,
enum ab8540_usb_link_status lsts )
{
enum ux500_musb_vbus_id_status event = 0 ;
dev_dbg ( ab - > dev , " ab8540_usb_link_status_update %d \n " , lsts ) ;
if ( ab - > enabled_charging_detection ) {
/* Disable USB Charger detection */
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8540_VBUS_CTRL_REG ,
AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA , 0x00 ) ;
ab - > enabled_charging_detection = false ;
}
/*
* Spurious link_status interrupts are seen in case of a
* disconnection of a device in IDGND and RIDA stage
*/
if ( ab - > previous_link_status_state = = USB_LINK_HM_IDGND_8540 & &
( lsts = = USB_LINK_STD_HOST_C_NS_8540 | |
lsts = = USB_LINK_STD_HOST_NC_8540 ) )
return 0 ;
if ( ab - > previous_link_status_state = = USB_LINK_ACA_RID_A_8540 & &
( lsts = = USB_LINK_STD_HOST_NC_8540 ) )
return 0 ;
ab - > previous_link_status_state = lsts ;
switch ( lsts ) {
case USB_LINK_ACA_RID_B_8540 :
event = UX500_MUSB_RIDB ;
case USB_LINK_NOT_CONFIGURED_8540 :
case USB_LINK_RESERVED0_8540 :
case USB_LINK_RESERVED1_8540 :
case USB_LINK_RESERVED2_8540 :
case USB_LINK_RESERVED3_8540 :
ab - > mode = USB_IDLE ;
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
if ( event ! = UX500_MUSB_RIDB )
event = UX500_MUSB_NONE ;
/*
* Fallback to default B_IDLE as nothing
* is connected
*/
ab - > phy . state = OTG_STATE_B_IDLE ;
break ;
case USB_LINK_ACA_RID_C_NM_8540 :
event = UX500_MUSB_RIDC ;
case USB_LINK_STD_HOST_NC_8540 :
case USB_LINK_STD_HOST_C_NS_8540 :
case USB_LINK_STD_HOST_C_S_8540 :
case USB_LINK_CDP_8540 :
if ( ab - > mode = = USB_IDLE ) {
ab - > mode = USB_PERIPHERAL ;
ab8500_usb_peri_phy_en ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
}
if ( event ! = UX500_MUSB_RIDC )
event = UX500_MUSB_VBUS ;
break ;
case USB_LINK_ACA_RID_A_8540 :
case USB_LINK_ACA_DOCK_CHGR_8540 :
event = UX500_MUSB_RIDA ;
case USB_LINK_HM_IDGND_8540 :
case USB_LINK_STD_UPSTREAM_8540 :
if ( ab - > mode = = USB_IDLE ) {
ab - > mode = USB_HOST ;
ab8500_usb_host_phy_en ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
}
ab - > phy . otg - > default_a = true ;
if ( event ! = UX500_MUSB_RIDA )
event = UX500_MUSB_ID ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
break ;
case USB_LINK_DEDICATED_CHG_8540 :
ab - > mode = USB_DEDICATED_CHG ;
event = UX500_MUSB_CHARGER ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
break ;
case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540 :
case USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_8540 :
event = UX500_MUSB_NONE ;
if ( ab - > mode = = USB_HOST ) {
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
ab8500_usb_host_phy_dis ( ab ) ;
ab - > mode = USB_IDLE ;
}
if ( ab - > mode = = USB_PERIPHERAL ) {
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
ab8500_usb_peri_phy_dis ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_CLEAN , & ab - > vbus_draw ) ;
ab - > mode = USB_IDLE ;
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
}
break ;
default :
event = UX500_MUSB_NONE ;
break ;
}
return 0 ;
}
2013-03-08 06:27:09 +04:00
static int ab8505_usb_link_status_update ( struct ab8500_usb * ab ,
enum ab8505_usb_link_status lsts )
2010-12-07 17:00:09 +03:00
{
2013-03-08 06:27:09 +04:00
enum ux500_musb_vbus_id_status event = 0 ;
2010-12-07 17:00:09 +03:00
2013-03-08 06:27:09 +04:00
dev_dbg ( ab - > dev , " ab8505_usb_link_status_update %d \n " , lsts ) ;
2010-12-07 17:00:09 +03:00
2013-03-08 06:27:09 +04:00
/*
* Spurious link_status interrupts are seen at the time of
* disconnection of a device in RIDA state
*/
if ( ab - > previous_link_status_state = = USB_LINK_ACA_RID_A_8505 & &
( lsts = = USB_LINK_STD_HOST_NC_8505 ) )
return 0 ;
ab - > previous_link_status_state = lsts ;
2010-12-07 17:00:09 +03:00
switch ( lsts ) {
2013-03-08 06:27:09 +04:00
case USB_LINK_ACA_RID_B_8505 :
event = UX500_MUSB_RIDB ;
case USB_LINK_NOT_CONFIGURED_8505 :
case USB_LINK_RESERVED0_8505 :
case USB_LINK_RESERVED1_8505 :
case USB_LINK_RESERVED2_8505 :
case USB_LINK_RESERVED3_8505 :
ab - > mode = USB_IDLE ;
2012-02-13 15:24:05 +04:00
ab - > phy . otg - > default_a = false ;
2010-12-07 17:00:09 +03:00
ab - > vbus_draw = 0 ;
2013-03-08 06:27:09 +04:00
if ( event ! = UX500_MUSB_RIDB )
event = UX500_MUSB_NONE ;
/*
* Fallback to default B_IDLE as nothing
* is connected
*/
ab - > phy . state = OTG_STATE_B_IDLE ;
2010-12-07 17:00:09 +03:00
break ;
2013-03-08 06:27:09 +04:00
case USB_LINK_ACA_RID_C_NM_8505 :
event = UX500_MUSB_RIDC ;
case USB_LINK_STD_HOST_NC_8505 :
case USB_LINK_STD_HOST_C_NS_8505 :
case USB_LINK_STD_HOST_C_S_8505 :
case USB_LINK_CDP_8505 :
if ( ab - > mode = = USB_IDLE ) {
ab - > mode = USB_PERIPHERAL ;
2010-12-07 17:00:09 +03:00
ab8500_usb_peri_phy_en ( ab ) ;
2013-03-08 06:27:09 +04:00
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
2010-12-07 17:00:09 +03:00
}
2013-03-08 06:27:09 +04:00
if ( event ! = UX500_MUSB_RIDC )
event = UX500_MUSB_VBUS ;
2010-12-07 17:00:09 +03:00
break ;
2013-03-08 06:27:09 +04:00
case USB_LINK_ACA_RID_A_8505 :
case USB_LINK_ACA_DOCK_CHGR_8505 :
event = UX500_MUSB_RIDA ;
case USB_LINK_HM_IDGND_8505 :
if ( ab - > mode = = USB_IDLE ) {
ab - > mode = USB_HOST ;
2010-12-07 17:00:09 +03:00
ab8500_usb_host_phy_en ( ab ) ;
2013-03-08 06:27:09 +04:00
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
2010-12-07 17:00:09 +03:00
}
2012-02-13 15:24:05 +04:00
ab - > phy . otg - > default_a = true ;
2013-03-08 06:27:09 +04:00
if ( event ! = UX500_MUSB_RIDA )
event = UX500_MUSB_ID ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
2010-12-07 17:00:09 +03:00
break ;
2013-03-08 06:27:09 +04:00
case USB_LINK_DEDICATED_CHG_8505 :
ab - > mode = USB_DEDICATED_CHG ;
event = UX500_MUSB_CHARGER ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
break ;
default :
2010-12-07 17:00:09 +03:00
break ;
}
2013-03-08 06:27:09 +04:00
return 0 ;
}
static int ab8500_usb_link_status_update ( struct ab8500_usb * ab ,
enum ab8500_usb_link_status lsts )
{
enum ux500_musb_vbus_id_status event = 0 ;
dev_dbg ( ab - > dev , " ab8500_usb_link_status_update %d \n " , lsts ) ;
/*
* Spurious link_status interrupts are seen in case of a
* disconnection of a device in IDGND and RIDA stage
*/
if ( ab - > previous_link_status_state = = USB_LINK_HM_IDGND_8500 & &
( lsts = = USB_LINK_STD_HOST_C_NS_8500 | |
lsts = = USB_LINK_STD_HOST_NC_8500 ) )
return 0 ;
if ( ab - > previous_link_status_state = = USB_LINK_ACA_RID_A_8500 & &
lsts = = USB_LINK_STD_HOST_NC_8500 )
return 0 ;
ab - > previous_link_status_state = lsts ;
switch ( lsts ) {
case USB_LINK_ACA_RID_B_8500 :
event = UX500_MUSB_RIDB ;
case USB_LINK_NOT_CONFIGURED_8500 :
case USB_LINK_NOT_VALID_LINK_8500 :
ab - > mode = USB_IDLE ;
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
if ( event ! = UX500_MUSB_RIDB )
event = UX500_MUSB_NONE ;
/* Fallback to default B_IDLE as nothing is connected */
ab - > phy . state = OTG_STATE_B_IDLE ;
break ;
case USB_LINK_ACA_RID_C_NM_8500 :
case USB_LINK_ACA_RID_C_HS_8500 :
case USB_LINK_ACA_RID_C_HS_CHIRP_8500 :
event = UX500_MUSB_RIDC ;
case USB_LINK_STD_HOST_NC_8500 :
case USB_LINK_STD_HOST_C_NS_8500 :
case USB_LINK_STD_HOST_C_S_8500 :
case USB_LINK_HOST_CHG_NM_8500 :
case USB_LINK_HOST_CHG_HS_8500 :
case USB_LINK_HOST_CHG_HS_CHIRP_8500 :
if ( ab - > mode = = USB_IDLE ) {
ab - > mode = USB_PERIPHERAL ;
ab8500_usb_peri_phy_en ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
}
if ( event ! = UX500_MUSB_RIDC )
event = UX500_MUSB_VBUS ;
break ;
case USB_LINK_ACA_RID_A_8500 :
event = UX500_MUSB_RIDA ;
case USB_LINK_HM_IDGND_8500 :
if ( ab - > mode = = USB_IDLE ) {
ab - > mode = USB_HOST ;
ab8500_usb_host_phy_en ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_PREPARE , & ab - > vbus_draw ) ;
}
ab - > phy . otg - > default_a = true ;
if ( event ! = UX500_MUSB_RIDA )
event = UX500_MUSB_ID ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
break ;
case USB_LINK_DEDICATED_CHG_8500 :
ab - > mode = USB_DEDICATED_CHG ;
event = UX500_MUSB_CHARGER ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
break ;
case USB_LINK_RESERVED_8500 :
break ;
}
2010-12-07 17:00:09 +03:00
return 0 ;
}
2013-03-08 06:27:09 +04:00
/*
* Connection Sequence :
* 1. Link Status Interrupt
* 2. Enable AB clock
* 3. Enable AB regulators
* 4. Enable USB phy
* 5. Reset the musb controller
* 6. Switch the ULPI GPIO pins to fucntion mode
* 7. Enable the musb Peripheral5 clock
* 8. Restore MUSB context
*/
static int abx500_usb_link_status_update ( struct ab8500_usb * ab )
2010-12-07 17:00:09 +03:00
{
2013-03-08 06:27:09 +04:00
u8 reg ;
int ret = 0 ;
if ( is_ab8500 ( ab - > ab8500 ) ) {
enum ab8500_usb_link_status lsts ;
abx500_get_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_LINE_STAT_REG , & reg ) ;
lsts = ( reg > > 3 ) & 0x0F ;
ret = ab8500_usb_link_status_update ( ab , lsts ) ;
} else if ( is_ab8505 ( ab - > ab8500 ) ) {
enum ab8505_usb_link_status lsts ;
abx500_get_register_interruptible ( ab - > dev ,
AB8500_USB , AB8505_USB_LINE_STAT_REG , & reg ) ;
lsts = ( reg > > 3 ) & 0x1F ;
ret = ab8505_usb_link_status_update ( ab , lsts ) ;
2013-05-15 16:03:32 +04:00
} else if ( is_ab8540 ( ab - > ab8500 ) ) {
enum ab8540_usb_link_status lsts ;
abx500_get_register_interruptible ( ab - > dev ,
AB8500_USB , AB8540_USB_LINK_STAT_REG , & reg ) ;
lsts = ( reg > > 3 ) & 0xFF ;
ret = ab8540_usb_link_status_update ( ab , lsts ) ;
2013-05-15 16:03:33 +04:00
} else if ( is_ab9540 ( ab - > ab8500 ) ) {
enum ab9540_usb_link_status lsts ;
abx500_get_register_interruptible ( ab - > dev ,
AB8500_USB , AB9540_USB_LINK_STAT_REG , & reg ) ;
lsts = ( reg > > 3 ) & 0xFF ;
ret = ab9540_usb_link_status_update ( ab , lsts ) ;
2013-03-08 06:27:09 +04:00
}
return ret ;
}
/*
* Disconnection Sequence :
* 1. Disconect Interrupt
* 2. Disable regulators
* 3. Disable AB clock
* 4. Disable the Phy
* 5. Link Status Interrupt
* 6. Disable Musb Clock
*/
static irqreturn_t ab8500_usb_disconnect_irq ( int irq , void * data )
{
struct ab8500_usb * ab = ( struct ab8500_usb * ) data ;
enum usb_phy_events event = UX500_MUSB_NONE ;
/* Link status will not be updated till phy is disabled. */
if ( ab - > mode = = USB_HOST ) {
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
ab8500_usb_host_phy_dis ( ab ) ;
ab - > mode = USB_IDLE ;
}
if ( ab - > mode = = USB_PERIPHERAL ) {
atomic_notifier_call_chain ( & ab - > phy . notifier ,
event , & ab - > vbus_draw ) ;
ab8500_usb_peri_phy_dis ( ab ) ;
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_CLEAN , & ab - > vbus_draw ) ;
ab - > mode = USB_IDLE ;
ab - > phy . otg - > default_a = false ;
ab - > vbus_draw = 0 ;
}
if ( is_ab8500_2p0 ( ab - > ab8500 ) ) {
if ( ab - > mode = = USB_DEDICATED_CHG ) {
ab8500_usb_wd_linkstatus ( ab ,
AB8500_BIT_PHY_CTRL_DEVICE_EN ) ;
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_PHY_CTRL_REG ,
AB8500_BIT_PHY_CTRL_DEVICE_EN , 0 ) ;
}
}
2010-12-07 17:00:09 +03:00
2013-03-08 06:27:09 +04:00
return IRQ_HANDLED ;
2010-12-07 17:00:09 +03:00
}
2013-03-08 06:27:09 +04:00
static irqreturn_t ab8500_usb_link_status_irq ( int irq , void * data )
2010-12-07 17:00:09 +03:00
{
2013-05-15 16:03:25 +04:00
struct ab8500_usb * ab = ( struct ab8500_usb * ) data ;
2010-12-07 17:00:09 +03:00
2013-03-08 06:27:09 +04:00
abx500_usb_link_status_update ( ab ) ;
2010-12-07 17:00:09 +03:00
return IRQ_HANDLED ;
}
static void ab8500_usb_phy_disable_work ( struct work_struct * work )
{
struct ab8500_usb * ab = container_of ( work , struct ab8500_usb ,
phy_dis_work ) ;
2012-02-13 15:24:05 +04:00
if ( ! ab - > phy . otg - > host )
2010-12-07 17:00:09 +03:00
ab8500_usb_host_phy_dis ( ab ) ;
2012-02-13 15:24:05 +04:00
if ( ! ab - > phy . otg - > gadget )
2010-12-07 17:00:09 +03:00
ab8500_usb_peri_phy_dis ( ab ) ;
}
2013-05-15 16:03:32 +04:00
/* Check if VBUS is set and linkstatus has not detected a cable. */
static bool ab8500_usb_check_vbus_status ( struct ab8500_usb * ab )
{
u8 isource2 ;
u8 reg ;
enum ab8540_usb_link_status lsts ;
abx500_get_register_interruptible ( ab - > dev ,
AB8500_INTERRUPT , AB8500_IT_SOURCE2_REG ,
& isource2 ) ;
/* If Vbus is below 3.6V abort */
if ( ! ( isource2 & AB8500_BIT_SOURCE2_VBUSDET ) )
return false ;
abx500_get_register_interruptible ( ab - > dev ,
AB8500_USB , AB8540_USB_LINK_STAT_REG ,
& reg ) ;
lsts = ( reg > > 3 ) & 0xFF ;
/* Check if linkstatus has detected a cable */
if ( lsts )
return false ;
return true ;
}
/* re-trigger charger detection again with watchdog re-kick. */
static void ab8500_usb_vbus_turn_on_event_work ( struct work_struct * work )
{
struct ab8500_usb * ab = container_of ( work , struct ab8500_usb ,
vbus_event_work ) ;
if ( ab - > mode ! = USB_IDLE )
return ;
abx500_set_register_interruptible ( ab - > dev ,
AB8500_SYS_CTRL2_BLOCK , AB8500_MAIN_WD_CTRL_REG ,
AB8500_BIT_WD_CTRL_ENABLE ) ;
udelay ( 100 ) ;
abx500_set_register_interruptible ( ab - > dev ,
AB8500_SYS_CTRL2_BLOCK , AB8500_MAIN_WD_CTRL_REG ,
AB8500_BIT_WD_CTRL_ENABLE | AB8500_BIT_WD_CTRL_KICK ) ;
udelay ( 100 ) ;
/* Disable Main watchdog */
abx500_set_register_interruptible ( ab - > dev ,
AB8500_SYS_CTRL2_BLOCK , AB8500_MAIN_WD_CTRL_REG ,
0x0 ) ;
/* Enable USB Charger detection */
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8540_VBUS_CTRL_REG ,
AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA ,
AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA ) ;
ab - > enabled_charging_detection = true ;
}
2013-04-03 12:45:05 +04:00
static unsigned ab8500_eyediagram_workaroud ( struct ab8500_usb * ab , unsigned mA )
{
/*
* AB8500 V2 has eye diagram issues when drawing more than 100 mA from
* VBUS . Set charging current to 100 mA in case of standard host
*/
if ( is_ab8500_2p0_or_earlier ( ab - > ab8500 ) )
if ( mA > 100 )
mA = 100 ;
return mA ;
}
2012-02-13 15:24:05 +04:00
static int ab8500_usb_set_power ( struct usb_phy * phy , unsigned mA )
2010-12-07 17:00:09 +03:00
{
struct ab8500_usb * ab ;
2012-02-13 15:24:05 +04:00
if ( ! phy )
2010-12-07 17:00:09 +03:00
return - ENODEV ;
2012-02-13 15:24:05 +04:00
ab = phy_to_ab ( phy ) ;
2010-12-07 17:00:09 +03:00
2013-04-03 12:45:05 +04:00
mA = ab8500_eyediagram_workaroud ( ab , mA ) ;
2010-12-07 17:00:09 +03:00
ab - > vbus_draw = mA ;
2013-04-03 12:45:10 +04:00
atomic_notifier_call_chain ( & ab - > phy . notifier ,
UX500_MUSB_VBUS , & ab - > vbus_draw ) ;
2010-12-07 17:00:09 +03:00
return 0 ;
}
2012-02-13 15:24:02 +04:00
static int ab8500_usb_set_suspend ( struct usb_phy * x , int suspend )
2010-12-07 17:00:09 +03:00
{
/* TODO */
return 0 ;
}
2012-02-13 15:24:05 +04:00
static int ab8500_usb_set_peripheral ( struct usb_otg * otg ,
struct usb_gadget * gadget )
2010-12-07 17:00:09 +03:00
{
struct ab8500_usb * ab ;
if ( ! otg )
return - ENODEV ;
2012-02-13 15:24:05 +04:00
ab = phy_to_ab ( otg - > phy ) ;
2010-12-07 17:00:09 +03:00
2013-04-03 12:45:13 +04:00
ab - > phy . otg - > gadget = gadget ;
2010-12-07 17:00:09 +03:00
/* Some drivers call this function in atomic context.
* Do not update ab8500 registers directly till this
* is fixed .
*/
2013-05-15 16:03:25 +04:00
if ( ( ab - > mode ! = USB_IDLE ) & & ! gadget ) {
2013-04-03 12:45:13 +04:00
ab - > mode = USB_IDLE ;
2010-12-07 17:00:09 +03:00
schedule_work ( & ab - > phy_dis_work ) ;
}
return 0 ;
}
2012-02-13 15:24:05 +04:00
static int ab8500_usb_set_host ( struct usb_otg * otg , struct usb_bus * host )
2010-12-07 17:00:09 +03:00
{
struct ab8500_usb * ab ;
if ( ! otg )
return - ENODEV ;
2012-02-13 15:24:05 +04:00
ab = phy_to_ab ( otg - > phy ) ;
2010-12-07 17:00:09 +03:00
2013-04-03 12:45:13 +04:00
ab - > phy . otg - > host = host ;
2010-12-07 17:00:09 +03:00
/* Some drivers call this function in atomic context.
* Do not update ab8500 registers directly till this
* is fixed .
*/
2013-05-15 16:03:25 +04:00
if ( ( ab - > mode ! = USB_IDLE ) & & ! host ) {
2013-04-03 12:45:13 +04:00
ab - > mode = USB_IDLE ;
2010-12-07 17:00:09 +03:00
schedule_work ( & ab - > phy_dis_work ) ;
}
return 0 ;
}
2013-05-15 16:03:27 +04:00
static void ab8500_usb_restart_phy ( struct ab8500_usb * ab )
{
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_PHY_CTRL_REG ,
AB8500_BIT_PHY_CTRL_DEVICE_EN ,
AB8500_BIT_PHY_CTRL_DEVICE_EN ) ;
udelay ( 100 ) ;
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_PHY_CTRL_REG ,
AB8500_BIT_PHY_CTRL_DEVICE_EN ,
0 ) ;
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_PHY_CTRL_REG ,
AB8500_BIT_PHY_CTRL_HOST_EN ,
AB8500_BIT_PHY_CTRL_HOST_EN ) ;
udelay ( 100 ) ;
abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_USB , AB8500_USB_PHY_CTRL_REG ,
AB8500_BIT_PHY_CTRL_HOST_EN ,
0 ) ;
}
2013-04-03 12:45:06 +04:00
static int ab8500_usb_regulator_get ( struct ab8500_usb * ab )
{
int err ;
ab - > v_ape = devm_regulator_get ( ab - > dev , " v-ape " ) ;
if ( IS_ERR ( ab - > v_ape ) ) {
dev_err ( ab - > dev , " Could not get v-ape supply \n " ) ;
err = PTR_ERR ( ab - > v_ape ) ;
return err ;
}
ab - > v_ulpi = devm_regulator_get ( ab - > dev , " vddulpivio18 " ) ;
if ( IS_ERR ( ab - > v_ulpi ) ) {
dev_err ( ab - > dev , " Could not get vddulpivio18 supply \n " ) ;
err = PTR_ERR ( ab - > v_ulpi ) ;
return err ;
}
ab - > v_musb = devm_regulator_get ( ab - > dev , " musb_1v8 " ) ;
if ( IS_ERR ( ab - > v_musb ) ) {
dev_err ( ab - > dev , " Could not get musb_1v8 supply \n " ) ;
err = PTR_ERR ( ab - > v_musb ) ;
return err ;
}
return 0 ;
}
2013-03-08 06:27:09 +04:00
static int ab8500_usb_irq_setup ( struct platform_device * pdev ,
struct ab8500_usb * ab )
2010-12-07 17:00:09 +03:00
{
int err ;
2013-03-08 06:27:09 +04:00
int irq ;
2010-12-07 17:00:09 +03:00
2013-05-15 16:03:31 +04:00
if ( ab - > flags & AB8500_USB_FLAG_USE_LINK_STATUS_IRQ ) {
irq = platform_get_irq_byname ( pdev , " USB_LINK_STATUS " ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " Link status irq not found \n " ) ;
return irq ;
}
err = devm_request_threaded_irq ( & pdev - > dev , irq , NULL ,
ab8500_usb_link_status_irq ,
IRQF_NO_SUSPEND | IRQF_SHARED ,
" usb-link-status " , ab ) ;
if ( err < 0 ) {
dev_err ( ab - > dev , " request_irq failed for link status irq \n " ) ;
return err ;
}
2010-12-07 17:00:09 +03:00
}
2013-05-15 16:03:31 +04:00
if ( ab - > flags & AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ ) {
irq = platform_get_irq_byname ( pdev , " ID_WAKEUP_F " ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " ID fall irq not found \n " ) ;
return irq ;
}
err = devm_request_threaded_irq ( & pdev - > dev , irq , NULL ,
ab8500_usb_disconnect_irq ,
IRQF_NO_SUSPEND | IRQF_SHARED ,
" usb-id-fall " , ab ) ;
if ( err < 0 ) {
dev_err ( ab - > dev , " request_irq failed for ID fall irq \n " ) ;
return err ;
}
2013-03-08 06:27:09 +04:00
}
2013-05-15 16:03:31 +04:00
if ( ab - > flags & AB8500_USB_FLAG_USE_VBUS_DET_IRQ ) {
irq = platform_get_irq_byname ( pdev , " VBUS_DET_F " ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " VBUS fall irq not found \n " ) ;
return irq ;
}
err = devm_request_threaded_irq ( & pdev - > dev , irq , NULL ,
ab8500_usb_disconnect_irq ,
IRQF_NO_SUSPEND | IRQF_SHARED ,
" usb-vbus-fall " , ab ) ;
if ( err < 0 ) {
dev_err ( ab - > dev , " request_irq failed for Vbus fall irq \n " ) ;
return err ;
}
2010-12-07 17:00:09 +03:00
}
return 0 ;
}
2013-05-15 16:03:30 +04:00
static void ab8500_usb_set_ab8500_tuning_values ( struct ab8500_usb * ab )
{
int err ;
/* Enable the PBT/Bank 0x12 access */
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEVELOPMENT , AB8500_BANK12_ACCESS , 0x01 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to enable bank12 access err=%d \n " ,
err ) ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE1 , 0xC8 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE1 register err=%d \n " ,
err ) ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE2 , 0x00 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE2 register err=%d \n " ,
err ) ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE3 , 0x78 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE3 regester err=%d \n " ,
err ) ;
/* Switch to normal mode/disable Bank 0x12 access */
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEVELOPMENT , AB8500_BANK12_ACCESS , 0x00 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to switch bank12 access err=%d \n " ,
err ) ;
}
static void ab8500_usb_set_ab8505_tuning_values ( struct ab8500_usb * ab )
{
int err ;
/* Enable the PBT/Bank 0x12 access */
err = abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_DEVELOPMENT , AB8500_BANK12_ACCESS ,
0x01 , 0x01 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to enable bank12 access err=%d \n " ,
err ) ;
err = abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE1 ,
0xC8 , 0xC8 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE1 register err=%d \n " ,
err ) ;
err = abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE2 ,
0x60 , 0x60 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE2 register err=%d \n " ,
err ) ;
err = abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE3 ,
0xFC , 0x80 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE3 regester err=%d \n " ,
err ) ;
/* Switch to normal mode/disable Bank 0x12 access */
err = abx500_mask_and_set_register_interruptible ( ab - > dev ,
AB8500_DEVELOPMENT , AB8500_BANK12_ACCESS ,
0x00 , 0x00 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to switch bank12 access err=%d \n " ,
err ) ;
}
2013-05-15 16:03:32 +04:00
static void ab8500_usb_set_ab8540_tuning_values ( struct ab8500_usb * ab )
{
int err ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8540_DEBUG , AB8500_USB_PHY_TUNE1 , 0xCC ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE1 register ret=%d \n " ,
err ) ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8540_DEBUG , AB8500_USB_PHY_TUNE2 , 0x60 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE2 register ret=%d \n " ,
err ) ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8540_DEBUG , AB8500_USB_PHY_TUNE3 , 0x90 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE3 regester ret=%d \n " ,
err ) ;
}
2013-05-15 16:03:33 +04:00
static void ab8500_usb_set_ab9540_tuning_values ( struct ab8500_usb * ab )
{
int err ;
/* Enable the PBT/Bank 0x12 access */
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEVELOPMENT , AB8500_BANK12_ACCESS , 0x01 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to enable bank12 access err=%d \n " ,
err ) ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE1 , 0xC8 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE1 register err=%d \n " ,
err ) ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE2 , 0x60 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE2 register err=%d \n " ,
err ) ;
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEBUG , AB8500_USB_PHY_TUNE3 , 0x80 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to set PHY_TUNE3 regester err=%d \n " ,
err ) ;
/* Switch to normal mode/disable Bank 0x12 access */
err = abx500_set_register_interruptible ( ab - > dev ,
AB8500_DEVELOPMENT , AB8500_BANK12_ACCESS , 0x00 ) ;
if ( err < 0 )
dev_err ( ab - > dev , " Failed to switch bank12 access err=%d \n " ,
err ) ;
}
2012-11-19 22:21:48 +04:00
static int ab8500_usb_probe ( struct platform_device * pdev )
2010-12-07 17:00:09 +03:00
{
struct ab8500_usb * ab ;
2013-03-08 06:27:08 +04:00
struct ab8500 * ab8500 ;
2012-02-13 15:24:05 +04:00
struct usb_otg * otg ;
2010-12-07 17:00:09 +03:00
int err ;
int rev ;
2013-03-08 06:27:08 +04:00
ab8500 = dev_get_drvdata ( pdev - > dev . parent ) ;
2010-12-07 17:00:09 +03:00
rev = abx500_get_chip_id ( & pdev - > dev ) ;
2013-03-08 06:27:08 +04:00
if ( is_ab8500_1p1_or_earlier ( ab8500 ) ) {
dev_err ( & pdev - > dev , " Unsupported AB8500 chip rev=%d \n " , rev ) ;
2010-12-07 17:00:09 +03:00
return - ENODEV ;
}
2013-04-03 12:45:03 +04:00
ab = devm_kzalloc ( & pdev - > dev , sizeof ( * ab ) , GFP_KERNEL ) ;
2010-12-07 17:00:09 +03:00
if ( ! ab )
return - ENOMEM ;
2013-04-03 12:45:03 +04:00
otg = devm_kzalloc ( & pdev - > dev , sizeof ( * otg ) , GFP_KERNEL ) ;
if ( ! otg )
2012-02-13 15:24:05 +04:00
return - ENOMEM ;
2010-12-07 17:00:09 +03:00
ab - > dev = & pdev - > dev ;
2013-03-08 06:27:08 +04:00
ab - > ab8500 = ab8500 ;
2012-02-13 15:24:05 +04:00
ab - > phy . dev = ab - > dev ;
ab - > phy . otg = otg ;
ab - > phy . label = " ab8500 " ;
ab - > phy . set_suspend = ab8500_usb_set_suspend ;
ab - > phy . set_power = ab8500_usb_set_power ;
ab - > phy . state = OTG_STATE_UNDEFINED ;
otg - > phy = & ab - > phy ;
otg - > set_host = ab8500_usb_set_host ;
otg - > set_peripheral = ab8500_usb_set_peripheral ;
2010-12-07 17:00:09 +03:00
2013-05-15 16:03:31 +04:00
if ( is_ab8500 ( ab - > ab8500 ) ) {
ab - > flags | = AB8500_USB_FLAG_USE_LINK_STATUS_IRQ |
AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ |
AB8500_USB_FLAG_USE_VBUS_DET_IRQ |
AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE ;
} else if ( is_ab8505 ( ab - > ab8500 ) ) {
ab - > flags | = AB8500_USB_FLAG_USE_LINK_STATUS_IRQ |
AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ |
AB8500_USB_FLAG_USE_VBUS_DET_IRQ |
AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE ;
2013-05-15 16:03:32 +04:00
} else if ( is_ab8540 ( ab - > ab8500 ) ) {
ab - > flags | = AB8500_USB_FLAG_USE_LINK_STATUS_IRQ |
AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS |
AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK |
AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE ;
2013-05-15 16:03:33 +04:00
} else if ( is_ab9540 ( ab - > ab8500 ) ) {
ab - > flags | = AB8500_USB_FLAG_USE_LINK_STATUS_IRQ |
AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE ;
if ( is_ab9540_2p0_or_earlier ( ab - > ab8500 ) )
ab - > flags | = AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ |
AB8500_USB_FLAG_USE_VBUS_DET_IRQ ;
2013-05-15 16:03:31 +04:00
}
/* Disable regulator voltage setting for AB8500 <= v2.0 */
if ( is_ab8500_2p0_or_earlier ( ab - > ab8500 ) )
ab - > flags & = ~ AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE ;
2010-12-07 17:00:09 +03:00
platform_set_drvdata ( pdev , ab ) ;
/* all: Disable phy when called from set_host and set_peripheral */
INIT_WORK ( & ab - > phy_dis_work , ab8500_usb_phy_disable_work ) ;
2013-05-15 16:03:32 +04:00
INIT_WORK ( & ab - > vbus_event_work , ab8500_usb_vbus_turn_on_event_work ) ;
2013-04-03 12:45:06 +04:00
err = ab8500_usb_regulator_get ( ab ) ;
if ( err )
return err ;
2013-05-15 16:03:26 +04:00
ab - > sysclk = devm_clk_get ( ab - > dev , " sysclk " ) ;
if ( IS_ERR ( ab - > sysclk ) ) {
dev_err ( ab - > dev , " Could not get sysclk. \n " ) ;
return PTR_ERR ( ab - > sysclk ) ;
}
2013-03-08 06:27:09 +04:00
err = ab8500_usb_irq_setup ( pdev , ab ) ;
2010-12-07 17:00:09 +03:00
if ( err < 0 )
2013-04-03 12:45:03 +04:00
return err ;
2010-12-07 17:00:09 +03:00
2012-06-22 15:32:46 +04:00
err = usb_add_phy ( & ab - > phy , USB_PHY_TYPE_USB2 ) ;
2010-12-07 17:00:09 +03:00
if ( err ) {
dev_err ( & pdev - > dev , " Can't register transceiver \n " ) ;
2013-04-03 12:45:03 +04:00
return err ;
2010-12-07 17:00:09 +03:00
}
2013-05-15 16:03:30 +04:00
if ( is_ab8500 ( ab - > ab8500 ) & & ! is_ab8500_2p0_or_earlier ( ab - > ab8500 ) )
/* Phy tuning values for AB8500 > v2.0 */
ab8500_usb_set_ab8500_tuning_values ( ab ) ;
else if ( is_ab8505 ( ab - > ab8500 ) )
/* Phy tuning values for AB8505 */
ab8500_usb_set_ab8505_tuning_values ( ab ) ;
2013-05-15 16:03:32 +04:00
else if ( is_ab8540 ( ab - > ab8500 ) )
/* Phy tuning values for AB8540 */
ab8500_usb_set_ab8540_tuning_values ( ab ) ;
2013-05-15 16:03:33 +04:00
else if ( is_ab9540 ( ab - > ab8500 ) )
/* Phy tuning values for AB9540 */
ab8500_usb_set_ab9540_tuning_values ( ab ) ;
2013-04-03 12:45:04 +04:00
2013-03-08 06:27:09 +04:00
/* Needed to enable ID detection. */
ab8500_usb_wd_workaround ( ab ) ;
2013-05-15 16:03:27 +04:00
/*
* This is required for usb - link - status to work properly when a
* cable is connected at boot time .
*/
ab8500_usb_restart_phy ( ab ) ;
2013-05-15 16:03:32 +04:00
if ( ab - > flags & AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS ) {
if ( ab8500_usb_check_vbus_status ( ab ) )
schedule_work ( & ab - > vbus_event_work ) ;
}
2013-04-03 12:45:12 +04:00
abx500_usb_link_status_update ( ab ) ;
2013-03-08 06:27:08 +04:00
dev_info ( & pdev - > dev , " revision 0x%2x driver initialized \n " , rev ) ;
2010-12-07 17:00:09 +03:00
return 0 ;
}
2012-11-19 22:26:20 +04:00
static int ab8500_usb_remove ( struct platform_device * pdev )
2010-12-07 17:00:09 +03:00
{
struct ab8500_usb * ab = platform_get_drvdata ( pdev ) ;
cancel_work_sync ( & ab - > phy_dis_work ) ;
2013-05-15 16:03:32 +04:00
cancel_work_sync ( & ab - > vbus_event_work ) ;
2010-12-07 17:00:09 +03:00
2012-06-22 15:32:46 +04:00
usb_remove_phy ( & ab - > phy ) ;
2010-12-07 17:00:09 +03:00
2013-04-03 12:45:09 +04:00
if ( ab - > mode = = USB_HOST )
ab8500_usb_host_phy_dis ( ab ) ;
else if ( ab - > mode = = USB_PERIPHERAL )
ab8500_usb_peri_phy_dis ( ab ) ;
2010-12-07 17:00:09 +03:00
return 0 ;
}
2013-05-15 16:03:29 +04:00
static struct platform_device_id ab8500_usb_devtype [ ] = {
{ . name = " ab8500-usb " , } ,
2013-05-15 16:03:32 +04:00
{ . name = " ab8540-usb " , } ,
2013-05-15 16:03:33 +04:00
{ . name = " ab9540-usb " , } ,
2013-05-15 16:03:29 +04:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( platform , ab8500_usb_devtype ) ;
2010-12-07 17:00:09 +03:00
static struct platform_driver ab8500_usb_driver = {
. probe = ab8500_usb_probe ,
2012-11-19 22:21:08 +04:00
. remove = ab8500_usb_remove ,
2013-05-15 16:03:29 +04:00
. id_table = ab8500_usb_devtype ,
2010-12-07 17:00:09 +03:00
. driver = {
2013-05-15 16:03:29 +04:00
. name = " abx5x0-usb " ,
2010-12-07 17:00:09 +03:00
. owner = THIS_MODULE ,
} ,
} ;
static int __init ab8500_usb_init ( void )
{
return platform_driver_register ( & ab8500_usb_driver ) ;
}
subsys_initcall ( ab8500_usb_init ) ;
static void __exit ab8500_usb_exit ( void )
{
platform_driver_unregister ( & ab8500_usb_driver ) ;
}
module_exit ( ab8500_usb_exit ) ;
MODULE_AUTHOR ( " ST-Ericsson AB " ) ;
2013-05-15 16:03:32 +04:00
MODULE_DESCRIPTION ( " AB8500 family usb transceiver driver " ) ;
2010-12-07 17:00:09 +03:00
MODULE_LICENSE ( " GPL " ) ;