/* * drivers/usb/sunxi_usb/usbc/usbc_phy.c * (C) Copyright 2010-2015 * Allwinner Technology Co., Ltd. * daniel, 2009.10.21 * * usb common ops. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include "usbc_i.h" #include "linux/sunxi-sid.h" /** * define USB PHY controller reg bit */ /* Common Control Bits for Both PHYs */ #define USBC_PHY_PLL_BW 0x03 #define USBC_PHY_RES45_CAL_EN 0x0c /* Private Control Bits for Each PHY */ #define USBC_PHY_TX_AMPLITUDE_TUNE 0x20 #define USBC_PHY_TX_SLEWRATE_TUNE 0x22 #define USBC_PHY_VBUSVALID_TH_SEL 0x25 #define USBC_PHY_PULLUP_RES_SEL 0x27 #define USBC_PHY_OTG_FUNC_EN 0x28 #define USBC_PHY_VBUS_DET_EN 0x29 #define USBC_PHY_DISCON_TH_SEL 0x2a /* usb PHY common set, initialize */ void USBC_PHY_SetCommonConfig(void) { } /** * usb PHY specific set * @hUSB: handle returned by USBC_open_otg, * include some key data that the USBC need. * */ void USBC_PHY_SetPrivateConfig(__hdle hUSB) { } /** * get PHY's common setting. for debug, to see if PHY is set correctly. * * return the 32bit usb PHY common setting value. */ __u32 USBC_PHY_GetCommonConfig(void) { __u32 reg_val = 0; return reg_val; } /** * write usb PHY0's phy reg setting. mainly for phy0 standby. * * return the data wrote */ static __u32 usb_phy0_write(__u32 addr, __u32 data, __u32 dmask, void __iomem *usbc_base_addr) { __u32 i = 0; data = data & 0x0f; addr = addr & 0x0f; dmask = dmask & 0x0f; USBC_Writeb((dmask<<4)|data, usbc_base_addr + 0x404 + 2); USBC_Writeb(addr|0x10, usbc_base_addr + 0x404); for (i = 0; i < 5 ; i++) ; USBC_Writeb(addr|0x30, usbc_base_addr + 0x404); for (i = 0 ; i < 5 ; i++) ; USBC_Writeb(addr|0x10, usbc_base_addr + 0x404); for (i = 0 ; i < 5 ; i++) ; return (USBC_Readb(usbc_base_addr + 0x404 + 3) & 0x0f); } /** * Standby the usb phy with the input usb phy index number * @phy_index: usb phy index number, which used to select the phy to standby * */ void USBC_phy_Standby(__hdle hUSB, __u32 phy_index) { __usbc_otg_t *usbc_otg = (__usbc_otg_t *)hUSB; if (phy_index == 0) { usb_phy0_write(0xB, 0x8, 0xf, usbc_otg->base_addr); usb_phy0_write(0x7, 0xf, 0xf, usbc_otg->base_addr); usb_phy0_write(0x1, 0xf, 0xf, usbc_otg->base_addr); usb_phy0_write(0x2, 0xf, 0xf, usbc_otg->base_addr); } } /** * Recover the standby phy with the input index number * @phy_index: usb phy index number * */ void USBC_Phy_Standby_Recover(__hdle hUSB, __u32 phy_index) { __u32 i; if (phy_index == 0) { for (i = 0; i < 0x10; i++) ; } } static __u32 USBC_Phy_TpWrite(__u32 usbc_no, __u32 addr, __u32 data, __u32 len) { void __iomem *otgc_base = NULL; void __iomem *phyctl_val = NULL; __u32 temp = 0, dtmp = 0; __u32 j = 0; otgc_base = get_otgc_vbase(); if (otgc_base == NULL) return 0; phyctl_val = otgc_base + USBPHYC_REG_o_PHYCTL; dtmp = data; for (j = 0; j < len; j++) { /* set the bit address to be write */ temp = USBC_Readl(phyctl_val); temp &= ~(0xff << 8); temp |= ((addr + j) << 8); USBC_Writel(temp, phyctl_val); temp = USBC_Readb(phyctl_val); temp &= ~(0x1 << 7); temp |= (dtmp & 0x1) << 7; temp &= ~(0x1 << (usbc_no << 1)); USBC_Writeb(temp, phyctl_val); temp = USBC_Readb(phyctl_val); temp |= (0x1 << (usbc_no << 1)); USBC_Writeb(temp, phyctl_val); temp = USBC_Readb(phyctl_val); temp &= ~(0x1 << (usbc_no << 1)); USBC_Writeb(temp, phyctl_val); dtmp >>= 1; } return data; } static __u32 USBC_Phy_Write(__u32 usbc_no, __u32 addr, __u32 data, __u32 len) { return USBC_Phy_TpWrite(usbc_no, addr, data, len); } void UsbPhyCtl(void __iomem *regs) { __u32 reg_val = 0; reg_val = USBC_Readl(regs + USBPHYC_REG_o_PHYCTL); reg_val |= (0x01 << USBC_PHY_CTL_VBUSVLDEXT); USBC_Writel(reg_val, (regs + USBPHYC_REG_o_PHYCTL)); } void USBC_PHY_Set_Ctl(void __iomem *regs, __u32 mask) { __u32 reg_val = 0; reg_val = USBC_Readl(regs + USBPHYC_REG_o_PHYCTL); reg_val |= (0x01 << mask); USBC_Writel(reg_val, (regs + USBPHYC_REG_o_PHYCTL)); } void USBC_PHY_Clear_Ctl(void __iomem *regs, __u32 mask) { __u32 reg_val = 0; reg_val = USBC_Readl(regs + USBPHYC_REG_o_PHYCTL); reg_val &= ~(0x01 << mask); USBC_Writel(reg_val, (regs + USBPHYC_REG_o_PHYCTL)); } void UsbPhyInit(__u32 usbc_no) { /* adjust the 45 ohm resistor */ if (usbc_no == 0) USBC_Phy_Write(usbc_no, 0x0c, 0x01, 1); /* adjust USB0 PHY range and rate */ USBC_Phy_Write(usbc_no, 0x20, 0x14, 5); /* adjust disconnect threshold */ USBC_Phy_Write(usbc_no, 0x2a, 3, 2); /* by wangjx */ } void UsbPhyEndReset(__u32 usbc_no) { int i; if (usbc_no == 0) { /** * Disable Sequelch Detect for a while * before Release USB Reset. */ USBC_Phy_Write(usbc_no, 0x3c, 0x2, 2); for (i = 0; i < 0x100; i++) ; USBC_Phy_Write(usbc_no, 0x3c, 0x0, 2); } } void usb_otg_phy_txtune(void __iomem *regs) { __u32 reg_val = 0; reg_val = USBC_Readl(regs + USBC_REG_o_PHYTUNE); #if defined(CONFIG_ARCH_SUN8IW18) reg_val |= (0x01 << 1); #else reg_val |= 0x03 << 2; /* TXRESTUNE */ #endif reg_val &= ~(0xf << 8); reg_val |= 0xc << 8; /* TXVREFTUNE */ USBC_Writel(reg_val, (regs + USBC_REG_o_PHYTUNE)); } #if defined(CONFIG_ARCH_SUN8IW21) /*for new phy*/ static int usbc_new_phyx_tp_write(void __iomem *regs, int addr, int data, int len) { int temp = 0; int j = 0; int dtmp = 0; /*device: 0x410(phy_ctl)*/ dtmp = data; for (j = 0; j < len; j++) { temp = USBC_Readb(regs + USBPHYC_REG_o_PHYCTL); temp |= (0x1 << 1); USBC_Writeb(temp, regs + USBPHYC_REG_o_PHYCTL); USBC_Writeb(addr + j, regs + USBPHYC_REG_o_PHYCTL + 1); temp = USBC_Readb(regs + USBPHYC_REG_o_PHYCTL); temp &= ~(0x1 << 0); USBC_Writeb(temp, regs + USBPHYC_REG_o_PHYCTL); temp = USBC_Readb(regs + USBPHYC_REG_o_PHYCTL); temp &= ~(0x1 << 7); temp |= (dtmp & 0x1) << 7; USBC_Writeb(temp, regs + USBPHYC_REG_o_PHYCTL); temp |= (0x1 << 0); USBC_Writeb(temp, regs + USBPHYC_REG_o_PHYCTL); temp &= ~(0x1 << 0); USBC_Writeb(temp, regs + USBPHYC_REG_o_PHYCTL); temp = USBC_Readb(regs + USBPHYC_REG_o_PHYCTL); temp &= ~(0x1 << 1); USBC_Writeb(temp, regs + USBPHYC_REG_o_PHYCTL); dtmp >>= 1; } return 0; } static int usbc_new_phyx_tp_read(void __iomem *regs, int addr, int len) { int temp = 0; int i = 0; int j = 0; int ret = 0; temp = USBC_Readb(regs + USBPHYC_REG_o_PHYCTL); temp |= (0x1 << 1); USBC_Writeb(temp, regs + USBPHYC_REG_o_PHYCTL); for (j = len; j > 0; j--) { USBC_Writeb((addr + j - 1), regs + USBPHYC_REG_o_PHYCTL + 1); for (i = 0; i < 0x4; i++) ; temp = USBC_Readb(regs + USBC_REG_o_PHYSTATUS); ret <<= 1; ret |= (temp & 0x1); } temp = USBC_Readb(regs + USBPHYC_REG_o_PHYCTL); temp &= ~(0x1 << 1); USBC_Writeb(temp, regs + USBPHYC_REG_o_PHYCTL); return ret; } void usbc_new_phyx_write(void __iomem *regs, u32 data) { u32 temp = 0, ptmp = 0, rtmp = 0; temp = data & PHY_RANGE_TRAN_MASK; temp >>= (2 + 4); ptmp = data & PHY_RANGE_PREE_MASK; ptmp >>= 4; rtmp = data & PHY_RANGE_RESI_MASK; /* tranceive data */ usbc_new_phyx_tp_write(regs, 0x60, temp, 0x4); DMSG_INFO("write to trancevie data: 0x%x\n", temp); usbc_new_phyx_tp_write(regs, 0x64, ptmp, 0x2); DMSG_INFO("write to preemphasis data: 0x%x\n", ptmp); usbc_new_phyx_tp_write(regs, 0x43, 0x0, 0x1); usbc_new_phyx_tp_write(regs, 0x41, 0x0, 0x1); usbc_new_phyx_tp_write(regs, 0x40, 0x0, 0x1); usbc_new_phyx_tp_write(regs, 0x44, rtmp, 0x4); DMSG_INFO("write to resistance data: 0x%x\n", rtmp); usbc_new_phyx_tp_write(regs, 0x43, 0x1, 0x1); } u32 usbc_new_phyx_read(void __iomem *regs) { u32 temp = 0, ptmp = 0, rtmp = 0, ret = 0; temp = usbc_new_phyx_tp_read(regs, 0x60, 0x4); ptmp = usbc_new_phyx_tp_read(regs, 0x64, 0x2); rtmp = usbc_new_phyx_tp_read(regs, 0x44, 0x4); DMSG_INFO("trancevie[9:6]:0x%x, preemphasis[5:4]:0x%x, resistance[3:0]:0x%x\n", temp, ptmp, rtmp); temp <<= (2 + 4); ptmp <<= 4; ret = temp | ptmp | rtmp; return ret; } void usbc_new_phy_init(void __iomem *regs) { int value = 0; u32 efuse_val = 0; pr_debug("addr:%x,len:%x,value:%x\n", 0x03, 0x06, usbc_new_phyx_tp_read(regs, 0x03, 0x06)); pr_debug("addr:%x,len:%x,value:%x\n", 0x16, 0x03, usbc_new_phyx_tp_read(regs, 0x16, 0x03)); pr_debug("addr:%x,len:%x,value:%x\n", 0x0b, 0x08, usbc_new_phyx_tp_read(regs, 0x0b, 0x08)); pr_debug("addr:%x,len:%x,value:%x\n", 0x09, 0x03, usbc_new_phyx_tp_read(regs, 0x09, 0x03)); sunxi_get_module_param_from_sid(&efuse_val, EFUSE_OFFSET, 4); pr_debug("efuse_val:0x%x\n", efuse_val); usbc_new_phyx_tp_write(regs, 0x03, 0x3f, 0x06); pr_debug("addr:%x,len:%x,value:%x\n", 0x03, 0x3f, usbc_new_phyx_tp_read(regs, 0x03, 0x06)); usbc_new_phyx_tp_write(regs, 0x1c, 0x03, 0x03); pr_debug("addr:%x,len:%x,value:%x\n", 0x1c, 0x03, usbc_new_phyx_tp_read(regs, 0x1c, 0x03)); if (efuse_val & SUNXI_USB_PHY_EFUSE_ADJUST) { /* Already calibrate completely, don't have to distinguish iref mode and vref mode */ pr_debug("USB phy already calibrate completely\n"); /* usbc-0 */ value = (efuse_val & SUNXI_USB_PHY_EFUSE_USB0TX) >> 9; usbc_new_phyx_tp_write(regs, 0x60, value, 0x04); value = (efuse_val & SUNXI_USB_PHY_EFUSE_RES) >> 5; usbc_new_phyx_tp_write(regs, 0x44, value, 0x04); pr_debug("addr:%x,len:%x,value:%x\n", 0x60, 0x04, usbc_new_phyx_tp_read(regs, 0x60, 0x04)); pr_debug("addr:%x,len:%x,value:%x\n", 0x44, 0x04, usbc_new_phyx_tp_read(regs, 0x44, 0x04)); } else { pr_debug("USB phy don't calibrate\n"); } pr_debug("addr:%x,len:%x,value:%x\n", 0x03, 0x06, usbc_new_phyx_tp_read(regs, 0x03, 0x06)); pr_debug("addr:%x,len:%x,value:%x\n", 0x16, 0x03, usbc_new_phyx_tp_read(regs, 0x16, 0x03)); pr_debug("addr:%x,len:%x,value:%x\n", 0x0b, 0x08, usbc_new_phyx_tp_read(regs, 0x0b, 0x08)); pr_debug("addr:%x,len:%x,value:%x\n", 0x09, 0x03, usbc_new_phyx_tp_read(regs, 0x09, 0x03)); } void usbc_new_phy_res_cal(void __iomem *regs) { int value; /*clear software res cail*/ usbc_new_phyx_tp_write(regs, 0x43, 0x0, 0x01); usbc_new_phyx_tp_write(regs, 0x41, 0x0, 0x01); usbc_new_phyx_tp_write(regs, 0x40, 0x0, 0x01); /*res cail*/ usbc_new_phyx_tp_write(regs, 0x40, 0x01, 0x01); mdelay(1); usbc_new_phyx_tp_write(regs, 0x41, 0x01, 0x01); while (1) { if (usbc_new_phyx_tp_read(regs, 0x42, 0x01)) break; } /*set res*/ value = usbc_new_phyx_tp_read(regs, 0x49, 0x04); pr_debug("addr:%x,,value:%x\n", 0x49, value); usbc_new_phyx_tp_write(regs, 0x44, value, 0x04); usbc_new_phyx_tp_write(regs, 0x41, 0x0, 0x01); usbc_new_phyx_tp_write(regs, 0x40, 0x0, 0x01); usbc_new_phyx_tp_write(regs, 0x43, 0x01, 0x01); } #endif