On Fri, 14 Dec 2007 19:33:14 +0530, "Gadiyar, Anand" <gadiyar@xxxxxx> wrote: > From: Anand Gadiyar <gadiyar@xxxxxx> > > Added support for TWL4030 USB transceiver chip. > > Signed-off-by: Anand Gadiyar <gadiyar@xxxxxx> > Signed-off-by: Vikram Pandita <vikram.pandita@xxxxxx> > Signed-off-by: Nishant Kamat <nskamat@xxxxxx> > --- > drivers/i2c/chips/Kconfig | 19 + > drivers/i2c/chips/Makefile | 3 > drivers/i2c/chips/twl4030_usb.c | 700 > ++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 721 insertions(+), 1 deletion(-) > > Index: linux-omap-dec10/drivers/i2c/chips/Kconfig > =================================================================== > --- linux-omap-dec10.orig/drivers/i2c/chips/Kconfig 2007-12-14 > 19:06:41.112941439 +0530 > +++ linux-omap-dec10/drivers/i2c/chips/Kconfig 2007-12-14 > 19:12:45.826336129 +0530 > @@ -140,6 +140,25 @@ > bool "TWL4030 GPIO Driver" > depends on TWL4030_CORE > > +config TWL4030_USB > + bool "TWL4030 USB Transceiver Driver" > + depends on TWL4030_CORE > + > +choice > + prompt "Transceiver mode" > + depends on TWL4030_USB > + help > + TWL4030 USB transceiver can operate in various > + mutually-exclusive modes. Select one of them. > + > +config TWL4030_USB_HS_ULPI > + depends on TWL4030_USB > + bool "High-speed ULPI" > + help > + Say Y here if the TWL4030 is connected to high-speed USB > + controller through a ULPI interface. > +endchoice > + > config SENSORS_M41T00 > tristate "ST M41T00 RTC chip (DEPRECATED)" > depends on PPC32 > Index: linux-omap-dec10/drivers/i2c/chips/Makefile > =================================================================== > --- linux-omap-dec10.orig/drivers/i2c/chips/Makefile 2007-12-14 > 19:06:41.112941439 +0530 > +++ linux-omap-dec10/drivers/i2c/chips/Makefile 2007-12-14 > 19:12:45.826336129 +0530 > @@ -17,8 +17,9 @@ > obj-$(CONFIG_GPIOEXPANDER_OMAP) += gpio_expander_omap.o > obj-$(CONFIG_MENELAUS) += menelaus.o > obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o > -obj-$(CONFIG_TWL4030_CORE) += twl4030_core.o > +obj-$(CONFIG_TWL4030_CORE) += twl4030_core.o Changing spaces into tab?!? It should come as an extra-patch before or after usb series. > obj-$(CONFIG_TWL4030_GPIO) += twl4030_gpio.o > +obj-$(CONFIG_TWL4030_USB) += twl4030_usb.o > obj-$(CONFIG_RTC_X1205_I2C) += x1205.o > > ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) > Index: linux-omap-dec10/drivers/i2c/chips/twl4030_usb.c > =================================================================== > --- /dev/null 1970-01-01 00:00:00.000000000 +0000 > +++ linux-omap-dec10/drivers/i2c/chips/twl4030_usb.c 2007-12-14 > 19:30:44.115082813 +0530 > @@ -0,0 +1,700 @@ > +/* > + * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller > + * > + * Copyright (C) 2004-2007 Texas Instruments > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + * > + * Current status: > + * - HS USB ULPI mode works. > + * - 3-pin mode support may be added in future. > + */ > + > + > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/time.h> > +#include <linux/interrupt.h> > +#include <linux/usb.h> > + > +#include <asm/arch/twl4030.h> > + > +/* Register defines */ > + > +#define VENDOR_ID_LO 0x00 > +#define VENDOR_ID_HI 0x01 > +#define PRODUCT_ID_LO 0x02 > +#define PRODUCT_ID_HI 0x03 > + > +#define FUNC_CTRL 0x04 > +#define FUNC_CTRL_SET 0x05 > +#define FUNC_CTRL_CLR 0x06 > +#define FUNC_CTRL_SUSPENDM (1 << 6) > +#define FUNC_CTRL_RESET (1 << 5) > +#define FUNC_CTRL_OPMODE_MASK (3 << 3) /* bits 3 and 4 */ > +#define FUNC_CTRL_OPMODE_NORMAL (0 << 3) > +#define FUNC_CTRL_OPMODE_NONDRIVING (1 << 3) > +#define FUNC_CTRL_OPMODE_DISABLE_BIT_NRZI (2 << 3) > +#define FUNC_CTRL_TERMSELECT (1 << 2) > +#define FUNC_CTRL_XCVRSELECT_MASK (3 << 0) /* bits 0 and 1 */ > +#define FUNC_CTRL_XCVRSELECT_HS (0 << 0) > +#define FUNC_CTRL_XCVRSELECT_FS (1 << 0) > +#define FUNC_CTRL_XCVRSELECT_LS (2 << 0) > +#define FUNC_CTRL_XCVRSELECT_FS4LS (3 << 0) > + > +#define IFC_CTRL 0x07 > +#define IFC_CTRL_SET 0x08 > +#define IFC_CTRL_CLR 0x09 > +#define IFC_CTRL_INTERFACE_PROTECT_DISABLE (1 << 7) > +#define IFC_CTRL_AUTORESUME (1 << 4) > +#define IFC_CTRL_CLOCKSUSPENDM (1 << 3) > +#define IFC_CTRL_CARKITMODE (1 << 2) > +#define IFC_CTRL_FSLSSERIALMODE_3PIN (1 << 1) > + > +#define OTG_CTRL 0x0A > +#define OTG_CTRL_SET 0x0B > +#define OTG_CTRL_CLR 0x0C > +#define OTG_CTRL_DRVVBUS (1 << 5) > +#define OTG_CTRL_CHRGVBUS (1 << 4) > +#define OTG_CTRL_DISCHRGVBUS (1 << 3) > +#define OTG_CTRL_DMPULLDOWN (1 << 2) > +#define OTG_CTRL_DPPULLDOWN (1 << 1) > +#define OTG_CTRL_IDPULLUP (1 << 0) > + > +#define USB_INT_EN_RISE 0x0D > +#define USB_INT_EN_RISE_SET 0x0E > +#define USB_INT_EN_RISE_CLR 0x0F > +#define USB_INT_EN_FALL 0x10 > +#define USB_INT_EN_FALL_SET 0x11 > +#define USB_INT_EN_FALL_CLR 0x12 > +#define USB_INT_STS 0x13 > +#define USB_INT_LATCH 0x14 > +#define USB_INT_IDGND (1 << 4) > +#define USB_INT_SESSEND (1 << 3) > +#define USB_INT_SESSVALID (1 << 2) > +#define USB_INT_VBUSVALID (1 << 1) > +#define USB_INT_HOSTDISCONNECT (1 << 0) > + > +#define CARKIT_CTRL 0x19 > +#define CARKIT_CTRL_SET 0x1A > +#define CARKIT_CTRL_CLR 0x1B > +#define CARKIT_CTRL_MICEN (1 << 6) > +#define CARKIT_CTRL_SPKRIGHTEN (1 << 5) > +#define CARKIT_CTRL_SPKLEFTEN (1 << 4) > +#define CARKIT_CTRL_RXDEN (1 << 3) > +#define CARKIT_CTRL_TXDEN (1 << 2) > +#define CARKIT_CTRL_IDGNDDRV (1 << 1) > +#define CARKIT_CTRL_CARKITPWR (1 << 0) > +#define CARKIT_PLS_CTRL 0x22 > +#define CARKIT_PLS_CTRL_SET 0x23 > +#define CARKIT_PLS_CTRL_CLR 0x24 > +#define CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) > +#define CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) > +#define CARKIT_PLS_CTRL_RXPLSEN (1 << 1) > +#define CARKIT_PLS_CTRL_TXPLSEN (1 << 0) > + > +#define MCPC_CTRL 0x30 > +#define MCPC_CTRL_SET 0x31 > +#define MCPC_CTRL_CLR 0x32 > +#define MCPC_CTRL_RTSOL (1 << 7) > +#define MCPC_CTRL_EXTSWR (1 << 6) > +#define MCPC_CTRL_EXTSWC (1 << 5) > +#define MCPC_CTRL_VOICESW (1 << 4) > +#define MCPC_CTRL_OUT64K (1 << 3) > +#define MCPC_CTRL_RTSCTSSW (1 << 2) > +#define MCPC_CTRL_HS_UART (1 << 0) > + > +#define MCPC_IO_CTRL 0x33 > +#define MCPC_IO_CTRL_SET 0x34 > +#define MCPC_IO_CTRL_CLR 0x35 > +#define MCPC_IO_CTRL_MICBIASEN (1 << 5) > +#define MCPC_IO_CTRL_CTS_NPU (1 << 4) > +#define MCPC_IO_CTRL_RXD_PU (1 << 3) > +#define MCPC_IO_CTRL_TXDTYP (1 << 2) > +#define MCPC_IO_CTRL_CTSTYP (1 << 1) > +#define MCPC_IO_CTRL_RTSTYP (1 << 0) > + > +#define MCPC_CTRL2 0x36 > +#define MCPC_CTRL2_SET 0x37 > +#define MCPC_CTRL2_CLR 0x38 > +#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) > + > +#define OTHER_FUNC_CTRL 0x80 > +#define OTHER_FUNC_CTRL_SET 0x81 > +#define OTHER_FUNC_CTRL_CLR 0x82 > +#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) > +#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) > + > +#define OTHER_IFC_CTRL 0x83 > +#define OTHER_IFC_CTRL_SET 0x84 > +#define OTHER_IFC_CTRL_CLR 0x85 > +#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) > +#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) > +#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) > +#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) > +#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) > +#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) > + > +#define OTHER_INT_EN_RISE 0x86 > +#define OTHER_INT_EN_RISE_SET 0x87 > +#define OTHER_INT_EN_RISE_CLR 0x88 > +#define OTHER_INT_EN_FALL 0x89 > +#define OTHER_INT_EN_FALL_SET 0x8A > +#define OTHER_INT_EN_FALL_CLR 0x8B > +#define OTHER_INT_STS 0x8C > +#define OTHER_INT_LATCH 0x8D > +#define OTHER_INT_VB_SESS_VLD (1 << 7) > +#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ > +#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ > +#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ > +#define OTHER_INT_MANU (1 << 1) > +#define OTHER_INT_ABNORMAL_STRESS (1 << 0) > + > +#define ID_STATUS 0x96 > +#define ID_RES_FLOAT (1 << 4) > +#define ID_RES_440K (1 << 3) > +#define ID_RES_200K (1 << 2) > +#define ID_RES_102K (1 << 1) > +#define ID_RES_GND (1 << 0) > + > +#define POWER_CTRL 0xAC > +#define POWER_CTRL_SET 0xAD > +#define POWER_CTRL_CLR 0xAE > +#define POWER_CTRL_OTG_ENAB (1 << 5) > + > +#define OTHER_IFC_CTRL2 0xAF > +#define OTHER_IFC_CTRL2_SET 0xB0 > +#define OTHER_IFC_CTRL2_CLR 0xB1 > +#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) > +#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) > +#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) > +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ > +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) > +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) > + > +#define REG_CTRL_EN 0xB2 > +#define REG_CTRL_EN_SET 0xB3 > +#define REG_CTRL_EN_CLR 0xB4 > +#define REG_CTRL_ERROR 0xB5 > +#define ULPI_I2C_CONFLICT_INTEN (1 << 0) > + > +#define OTHER_FUNC_CTRL2 0xB8 > +#define OTHER_FUNC_CTRL2_SET 0xB9 > +#define OTHER_FUNC_CTRL2_CLR 0xBA > +#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) > + > +/* following registers do not have separate _clr and _set registers */ > +#define VBUS_DEBOUNCE 0xC0 > +#define ID_DEBOUNCE 0xC1 > +#define VBAT_TIMER 0xD3 > +#define PHY_PWR_CTRL 0xFD > +#define PHY_PWR_PHYPWD (1 << 0) > +#define PHY_CLK_CTRL 0xFE > +#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) > +#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) there is extra white space here after PHY_CLK_CTRL_CLK32K_EN, could you remove ? > +#define REQ_PHY_DPLL_CLK (1 << 0) > +#define PHY_CLK_CTRL_STS 0xFF > +#define PHY_DPLL_CLK (1 << 0) > + > +/* In module TWL4030_MODULE_PM_MASTER */ > +#define PROTECT_KEY 0x0E > + > +/* In module TWL4030_MODULE_PM_RECEIVER */ > +#define VUSB_DEDICATED1 0x7D > +#define VUSB_DEDICATED2 0x7E > +#define VUSB1V5_DEV_GRP 0x71 > +#define VUSB1V5_TYPE 0x72 > +#define VUSB1V5_REMAP 0x73 > +#define VUSB1V8_DEV_GRP 0x74 > +#define VUSB1V8_TYPE 0x75 > +#define VUSB1V8_REMAP 0x76 > +#define VUSB3V1_DEV_GRP 0x77 > +#define VUSB3V1_TYPE 0x78 > +#define VUSB3V1_REMAP 0x79 > + > +#define ID_STATUS 0x96 > +#define ID_RES_FLOAT (1 << 4) /* mini-B */ > +#define ID_RES_440K (1 << 3) /* type 2 charger */ > +#define ID_RES_200K (1 << 2) /* 5-wire carkit or > + type 1 charger */ > +#define ID_RES_102K (1 << 1) /* phone */ > +#define ID_RES_GND (1 << 0) /* mini-A */ > + > +/* In module TWL4030_MODULE_INTBR */ > +#define PMBR1 0x0D > +#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) > + > +/* In module TWL4030_MODULE_INT */ > +#define REG_PWR_ISR1 0x00 > +#define REG_PWR_IMR1 0x01 > +#define USB_PRES (1 << 2) > +#define REG_PWR_EDR1 0x05 > +#define USB_PRES_FALLING (1 << 4) > +#define USB_PRES_RISING (1 << 5) > +#define REG_PWR_SIH_CTRL 0x07 > +#define COR (1 << 2) > + > +/*-------------------------------------------------------------------------*/ > + > +static int twl4030_i2c_write_u8_verify(u8 module, u8 data, u8 address) > +{ > + u8 check; Adding an extra blank line after variable definition increase code readability. > + if ((twl4030_i2c_write_u8(module, data, address) >= 0) && > + (twl4030_i2c_read_u8(module, &check, address) >= 0) && > + (check == data)) > + return 0; > + /* Failed once: Try again */ > + if ((twl4030_i2c_write_u8(module, data, address) >= 0) && > + (twl4030_i2c_read_u8(module, &check, address) >= 0) && > + (check == data)) > + return 0; > + /* Failed again: Return error */ > + return -EBUSY; > +} > + > +#define twl4030_usb_write_verify(address, data) \ > + twl4030_i2c_write_u8_verify(TWL4030_MODULE_USB, (data), (address)) > + > +static inline int twl4030_usb_write(u8 address, u8 data) > +{ > + int ret = 0; > + ret = twl4030_i2c_write_u8(TWL4030_MODULE_USB, data, address); > + if (ret >= 0) { > +#if 0 /* debug */ > + u8 data1; > + if (twl4030_i2c_read_u8(TWL4030_MODULE_USB, &data1, > + address) < 0) > + printk(KERN_ERR "re-read failed\n"); > + else > + printk(KERN_INFO > + "Write %s wrote %x read %x from reg %x\n", > + (data1 == data) ? "succeed" : "mismatch", > + data, data1, address); > +#endif > + } else { > + printk(KERN_WARNING > + "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); > + } > + return ret; > +} > + > +static inline int twl4030_usb_read(u8 address) > +{ > + u8 data; > + int ret = 0; > + ret = twl4030_i2c_read_u8(TWL4030_MODULE_USB, &data, address); > + if (ret >= 0) { > + ret = data; > + } else { > + printk(KERN_WARNING > + "TWL4030:USB:Read[0x%x] Error %d\n", address, ret); > + } > + return ret; > +} > + > +/*-------------------------------------------------------------------------*/ > + > +struct twl4030_usb { > + int irq; > + u8 usb_mode; /* pin configuration */ extra whitespace after u8. > +#define T2_USB_MODE_ULPI 1 > +/* #define T2_USB_MODE_CEA2011_3PIN 2 */ > + u8 asleep; > +}; > + > +static struct twl4030_usb *the_transceiver; > + > +/*-------------------------------------------------------------------------*/ > + > +static inline int > +twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) > +{ > + return twl4030_usb_write(reg + 1, bits); > +} > + > +static inline int > +twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) > +{ > + return twl4030_usb_write(reg + 2, bits); > +} > + > +/*-------------------------------------------------------------------------*/ > + > +static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) > +{ > + twl->usb_mode = mode; > + > + switch (mode) { > + case T2_USB_MODE_ULPI: > + twl4030_usb_clear_bits(twl, IFC_CTRL, IFC_CTRL_CARKITMODE); > + twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); > + twl4030_usb_clear_bits(twl, FUNC_CTRL, > + FUNC_CTRL_XCVRSELECT_MASK | > + FUNC_CTRL_OPMODE_MASK); > + break; > +/* > + case T2_USB_MODE_CEA2011_3PIN: > + twl4030_cea2011_3_pin_FS_setup(twl); > + break; > +*/ > + default: > + /* FIXME: power on defaults */ > + break; > + }; > +} > + > +#ifdef CONFIG_TWL4030_USB_HS_ULPI > +static void hs_usb_init(struct twl4030_usb *twl) > +{ > + twl->usb_mode = T2_USB_MODE_ULPI; > + return; > +} > + > +#endif > + > +static void twl4030_i2c_access(int on) > +{ > + unsigned long timeout; > + int val = twl4030_usb_read(PHY_CLK_CTRL); > + > + if (val >= 0) { > + if (on) { > + /* enable DPLL to access PHY registers over I2C */ > + val |= REQ_PHY_DPLL_CLK; > + if (twl4030_usb_write_verify(PHY_CLK_CTRL, > + (u8)val) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c write failed," > + "line %d\n", __LINE__); The second line has no log level defined. Change it to: printk(KERN_ERR "twl4030_usb: i2c write failed," KERN_ERR "line %d\n", __LINE__); > + return; > + } > + > + timeout = jiffies + HZ; > + while (!(twl4030_usb_read(PHY_CLK_CTRL_STS) & > + PHY_DPLL_CLK) > + && time_before(jiffies, timeout)) > + udelay(10); > + if (!(twl4030_usb_read(PHY_CLK_CTRL_STS) & > + PHY_DPLL_CLK)) > + printk(KERN_ERR "Timeout setting T2 HSUSB " > + "PHY DPLL clock\n"); same here > + } else { > + /* let ULPI control the DPLL clock */ > + val &= ~REQ_PHY_DPLL_CLK; > + if (twl4030_usb_write_verify(PHY_CLK_CTRL, > + (u8)val) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c write failed," > + "line %d\n", __LINE__); same here > + } > + } > + } > + return; > +} > + > +static void usb_irq_enable(int rising, int falling) > +{ > + u8 val; > + > + /* edge setup */ > + if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_EDR1) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c read failed," > + "line %d\n", __LINE__); same here > + return; > + } > + val &= ~(USB_PRES_RISING | USB_PRES_FALLING); > + if (rising) > + val = val | USB_PRES_RISING; > + if (falling) > + val = val | USB_PRES_FALLING; > + if (twl4030_i2c_write_u8_verify(TWL4030_MODULE_INT, val, > + REG_PWR_EDR1) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c write failed," > + "line %d\n", __LINE__); same here > + return; > + } > + > + /* un-mask interrupt */ > + if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_IMR1) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c read failed," > + "line %d\n", __LINE__); same here > + return; > + } > + val &= ~USB_PRES; > + if (twl4030_i2c_write_u8_verify(TWL4030_MODULE_INT, val, > + REG_PWR_IMR1) < 0) > + printk(KERN_ERR "twl4030_usb: i2c write failed," > + "line %d\n", __LINE__); same here > + > + return; > +} > + > +static void usb_irq_disable(void) > +{ > + u8 val; > + > + /* undo edge setup */ > + if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_EDR1) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c read failed," > + "line %d\n", __LINE__); same here > + return; > + } > + val &= ~(USB_PRES_RISING | USB_PRES_FALLING); > + if (twl4030_i2c_write_u8_verify(TWL4030_MODULE_INT, val, > + REG_PWR_EDR1) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c write failed," > + "line %d\n", __LINE__); same here > + return; > + } > + > + /* mask interrupt */ > + if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_IMR1) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c read failed," > + "line %d\n", __LINE__); same here > + return; > + } > + val |= USB_PRES; > + if (twl4030_i2c_write_u8_verify(TWL4030_MODULE_INT, val, > + REG_PWR_IMR1) < 0) > + printk(KERN_ERR "twl4030_usb: i2c write failed," > + "line %d\n", __LINE__); same here > + > + return; > +} > + > +void twl4030_phy_suspend(int controller_off); > +void twl4030_phy_resume(void); > + > +static irqreturn_t twl4030_usb_irq(int irq, void *_twl) > +{ > + int ret = IRQ_NONE; > + u8 val; > + u8 sih_ctrl; > + > + /* save previous value of SIH_CTRL and disable clear_on_read */ > + twl4030_i2c_read_u8(TWL4030_MODULE_INT, &sih_ctrl, REG_PWR_SIH_CTRL); > + twl4030_i2c_write_u8(TWL4030_MODULE_INT, (sih_ctrl & ~COR), > + REG_PWR_SIH_CTRL); > + > + if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_ISR1) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c read failed," > + "line %d\n", __LINE__); same here > + goto done; > + } > + > + /* this interrupt line may be shared */ > + if (!(val & USB_PRES)) > + goto done; > + > + /* clear the interrupt */ > + twl4030_i2c_write_u8(TWL4030_MODULE_INT, USB_PRES, REG_PWR_ISR1); > + > + /* action based on cable attach or detach */ > + if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_EDR1) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c read failed," > + "line %d\n", __LINE__); same here > + goto done; > + } > + > + if (val & USB_PRES_RISING) > + twl4030_phy_resume(); > + else > + twl4030_phy_suspend(0); > + > + ret = IRQ_HANDLED; > + > +done: > + /* restore previous value of SIH_CTRL */ > + twl4030_i2c_write_u8(TWL4030_MODULE_INT, sih_ctrl, REG_PWR_SIH_CTRL); > + return ret; > +} > + > +static void twl4030_phy_power(struct twl4030_usb *twl, int on) > +{ > + u8 pwr; > + > + pwr = twl4030_usb_read(PHY_PWR_CTRL); > + if (on) { > + pwr &= ~PHY_PWR_PHYPWD; > + if (twl4030_usb_write_verify(PHY_PWR_CTRL, pwr) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c write failed," > + "line %d\n", __LINE__); same here > + return; > + } > + twl4030_usb_write(PHY_CLK_CTRL, > + twl4030_usb_read(PHY_CLK_CTRL) | > + (PHY_CLK_CTRL_CLOCKGATING_EN | > + PHY_CLK_CTRL_CLK32K_EN)); > + } else { > + pwr |= PHY_PWR_PHYPWD; > + if (twl4030_usb_write_verify(PHY_PWR_CTRL, pwr) < 0) { > + printk(KERN_ERR "twl4030_usb: i2c write failed," > + "line %d\n", __LINE__); same here > + } > + } > + return; > +} > + > +static void twl4030_usb_ldo_init(struct twl4030_usb *twl) > +{ > + /* Enable writing to power configuration registers */ > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY); > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY); > + > + /* put VUSB3V1 LDO in active state */ > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); > + > + /* input to VUSB3V1 LDO is from VBAT, not VBUS */ > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); > + > + /* turn on 3.1V regulator */ > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB3V1_DEV_GRP); > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); > + > + /* turn on 1.5V regulator */ > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V5_DEV_GRP); > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); > + > + /* turn on 1.8V regulator */ > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V8_DEV_GRP); > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); > + > + /* disable access to power configuration registers */ > + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, PROTECT_KEY); > +} > + > +void twl4030_phy_suspend(int controller_off) > +{ > + struct twl4030_usb *twl = the_transceiver; > + > + if (controller_off) > + usb_irq_disable(); > + > + if (twl->asleep) > + return; > + > + if (!controller_off) > + /* enable rising edge interrupt to detect cable attach */ > + usb_irq_enable(1, 0); > + > + twl4030_phy_power(twl, 0); > + twl->asleep = 1; > + return; > +} > +EXPORT_SYMBOL(twl4030_phy_suspend); > + > + > +void twl4030_phy_resume(void) > +{ > + struct twl4030_usb *twl = the_transceiver; > + > + if (!twl->asleep) > + return; > + > + /* enable falling edge interrupt to detect cable detach */ > + usb_irq_enable(0, 1); > + > + twl4030_phy_power(twl, 1); > + twl4030_i2c_access(1); > + twl4030_usb_set_mode(twl, twl->usb_mode); > + if (twl->usb_mode == T2_USB_MODE_ULPI) > + twl4030_i2c_access(0); > + twl->asleep = 0; > + return; > +} > +EXPORT_SYMBOL(twl4030_phy_resume); > + > + > +static int __init twl4030_usb_init(void) > +{ > + struct twl4030_usb *twl; > + int status; > + > + if (the_transceiver) > + return 0; > + > + twl = kcalloc(1, sizeof *twl, GFP_KERNEL); > + if (!twl) > + return 0; > + > + the_transceiver = twl; > + > + twl->irq = TWL4030_MODIRQ_PWR; > + > + usb_irq_disable(); > + status = request_irq(twl->irq, twl4030_usb_irq, > + IRQF_DISABLED | IRQF_SHARED, "twl4030_usb", twl); > + if (status < 0) { > + printk(KERN_DEBUG "can't get IRQ %d, err %d\n", > + twl->irq, status); same here > + kfree(twl); > + return -ENODEV; > + } > + > +#if defined(CONFIG_TWL4030_USB_HS_ULPI) #ifdef CONFIG_TWL4030_USB_HS_ULPI > + hs_usb_init(twl); > +#endif > + twl4030_usb_ldo_init(twl); > + twl4030_phy_power(twl, 1); > + twl4030_i2c_access(1); > + twl4030_usb_set_mode(twl, twl->usb_mode); > + if (twl->usb_mode == T2_USB_MODE_ULPI) > + twl4030_i2c_access(0); > + > + twl->asleep = 0; > + > + if (twl->usb_mode == T2_USB_MODE_ULPI) > + twl4030_phy_suspend(1); > + > + printk(KERN_INFO "Initialized TWL4030 USB module"); > + > + return 0; > +} > + > + > +static void __exit twl4030_usb_exit(void) > +{ > + struct twl4030_usb *twl = the_transceiver; > + int val; > + > + usb_irq_disable(); > + free_irq(twl->irq, twl); > + > + /* set transceiver mode to power on defaults */ > + twl4030_usb_set_mode(twl, -1); > + > + /* autogate 60MHz ULPI clock, > + * clear dpll clock request for i2c access, > + * disable 32KHz > + */ > + val = twl4030_usb_read(PHY_CLK_CTRL); > + if (val >= 0) { > + val |= PHY_CLK_CTRL_CLOCKGATING_EN; > + val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); > + twl4030_usb_write(PHY_CLK_CTRL, (u8)val); > + } > + > + /* disable complete OTG block */ > + twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); > + > + twl4030_phy_power(twl, 0); > + > + kfree(twl); > +} > + > +subsys_initcall(twl4030_usb_init); > +module_exit(twl4030_usb_exit); > + > +MODULE_AUTHOR("Texas Instruments, Inc."); > +MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); > +MODULE_LICENSE("GPL"); I'm only looking at cosmetic here as I already tested the previous patch and it was working fine. Another cosmetic I'd like to see (although I can live without it) is a better variable name convention. "val" has no meaning at all, better names would even help us searching data sheets, TRMs and other hw documents. -- Best Regards, Felipe Balbi http://felipebalbi.com me@xxxxxxxxxxxxxxx - To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html