blob: 3ea7fb0e3819a2b65ea3e0e2428c258227ad6e0d [file] [log] [blame]
/*
* fusb302 usb phy driver for type-c and PD
*
* Copyright (C) 2015, 2016 Fairchild Semiconductor Corporation
*
* 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 3 of the License, or
* 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. Seee 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, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/printk.h> // pr_err, printk, etc
#include <linux/delay.h>
#include <linux/power_supply.h>
#include "../core/PD_Types.h"
#include "fusb30x_global.h" // Chip structure
#include "platform_helpers.h" // Implementation details
#include "../core/platform.h"
#include "../core/TypeC_Types.h"
/*******************************************************************************
* Function: platform_set/get_vbus_lvl_enable
* Input: VBUS_LVL - requested voltage
* Boolean - enable this voltage level
* Boolean - turn off other supported voltages
* Return: Boolean - on or off
* Description: Provide access to the VBUS control pins.
******************************************************************************/
void platform_set_vbus_lvl_enable(VBUS_LVL level, FSC_BOOL blnEnable, FSC_BOOL blnDisableOthers)
{
FSC_U32 i;
// Additional VBUS levels can be added here as needed.
switch (level)
{
case VBUS_LVL_5V:
// Enable/Disable the 5V Source
fusb_GPIO_Set_VBus5v(blnEnable == TRUE ? true : false);
break;
case VBUS_LVL_12V:
// Enable/Disable the 12V Source
fusb_GPIO_Set_VBusOther(blnEnable == TRUE ? true : false);
break;
default:
// Otherwise, do nothing.
break;
}
// Turn off other levels, if requested
if (blnDisableOthers || ((level == VBUS_LVL_ALL) && (blnEnable == FALSE)))
{
i = 0;
do {
// Skip the current level
if( i == level ) continue;
// Turn off the other level(s)
platform_set_vbus_lvl_enable( i, FALSE, FALSE );
} while (++i < VBUS_LVL_COUNT);
}
return;
}
FSC_BOOL platform_get_vbus_lvl_enable(VBUS_LVL level)
{
// Additional VBUS levels can be added here as needed.
switch (level)
{
case VBUS_LVL_5V:
// Return the state of the 5V VBUS Source.
return fusb_GPIO_Get_VBus5v() ? TRUE : FALSE;
case VBUS_LVL_12V:
// Return the state of the 12V VBUS Source.
return fusb_GPIO_Get_VBusOther() ? TRUE : FALSE;
default:
// Otherwise, return FALSE.
return FALSE;
}
}
extern FSC_BOOL VCONN_enabled;
/*******************************************************************************
* Function: platform_set_vconn_enable
* Input: blnEnable - enable or disable VCONN
* Return: Boolean - State of VCONN GPIO
* Description: Provide access to the VCONN control pin(s).
******************************************************************************/
FSC_BOOL platform_set_vconn_enable(FSC_BOOL blnEnable)
{
struct fusb30x_chip *chip = fusb30x_GetChip();
FSC_BOOL ret;
ret = fusb_Power_Vconn(blnEnable);
if (ret && VCONN_enabled != blnEnable) {
chip->vconn = blnEnable ? DUAL_ROLE_PROP_VCONN_SUPPLY_YES : DUAL_ROLE_PROP_VCONN_SUPPLY_NO;
if (chip->fusb_instance)
dual_role_instance_changed(chip->fusb_instance);
}
return ret;
}
/*******************************************************************************
* Function: platform_set_vbus_discharge
* Input: Boolean
* Return: None
* Description: Enable/Disable Vbus Discharge Path
******************************************************************************/
void platform_set_vbus_discharge(FSC_BOOL blnEnable)
{
// TODO - Implement if required for platform
}
/*******************************************************************************
* Function: platform_get_device_irq_state
* Input: None
* Return: Boolean. TRUE = Interrupt Active
* Description: Get the state of the INT_N pin. INT_N is active low. This
* function handles that by returning TRUE if the pin is
* pulled low indicating an active interrupt signal.
******************************************************************************/
FSC_BOOL platform_get_device_irq_state(void)
{
return fusb_GPIO_Get_IntN() ? TRUE : FALSE;
}
/*******************************************************************************
* Function: platform_i2c_write
* Input: SlaveAddress - Slave device bus address
* RegAddrLength - Register Address Byte Length
* DataLength - Length of data to transmit
* PacketSize - Maximum size of each transmitted packet
* IncSize - Number of bytes to send before incrementing addr
* RegisterAddress - Internal register address
* Data - Buffer of char data to transmit
* Return: Error state
* Description: Write a char buffer to the I2C peripheral.
******************************************************************************/
FSC_BOOL platform_i2c_write(FSC_U8 SlaveAddress,
FSC_U8 RegAddrLength,
FSC_U8 DataLength,
FSC_U8 PacketSize,
FSC_U8 IncSize,
FSC_U32 RegisterAddress,
FSC_U8* Data)
{
FSC_BOOL ret = FALSE;
if (Data == NULL)
{
pr_err("%s - Error: Write data buffer is NULL!\n", __func__);
ret = FALSE;
}
else if (fusb_I2C_WriteData((FSC_U8)RegisterAddress, DataLength, Data))
{
ret = TRUE;
}
else // I2C Write failure
{
ret = FALSE; // Write data block to the device
}
return ret;
}
/*******************************************************************************
* Function: platform_i2c_read
* Input: SlaveAddress - Slave device bus address
* RegAddrLength - Register Address Byte Length
* DataLength - Length of data to attempt to read
* PacketSize - Maximum size of each received packet
* IncSize - Number of bytes to recv before incrementing addr
* RegisterAddress - Internal register address
* Data - Buffer for received char data
* Return: Error state.
* Description: Read char data from the I2C peripheral.
******************************************************************************/
FSC_BOOL platform_i2c_read(FSC_U8 SlaveAddress,
FSC_U8 RegAddrLength,
FSC_U8 DataLength,
FSC_U8 PacketSize,
FSC_U8 IncSize,
FSC_U32 RegisterAddress,
FSC_U8* Data)
{
FSC_BOOL ret = FALSE;
FSC_S32 i = 0;
FSC_U8 temp = 0;
struct fusb30x_chip* chip = fusb30x_GetChip();
if (!chip)
{
pr_err("FUSB %s - Error: Chip structure is NULL!\n", __func__);
return FALSE;
}
if (Data == NULL)
{
pr_err("%s - Error: Read data buffer is NULL!\n", __func__);
ret = FALSE;
}
else if (DataLength > 1 && chip->use_i2c_blocks) // Do block reads if able and necessary
{
if (!fusb_I2C_ReadBlockData(RegisterAddress, DataLength, Data))
{
ret = FALSE;
}
else
{
ret = TRUE;
}
}
else
{
for (i = 0; i < DataLength; i++)
{
if (fusb_I2C_ReadData((FSC_U8)RegisterAddress + i, &temp))
{
Data[i] = temp;
ret = TRUE;
}
else
{
ret = FALSE;
break;
}
}
}
return ret;
}
/*****************************************************************************
* Function: platform_enable_timer
* Input: enable - TRUE to enable platform timer, FALSE to disable
* Return: None
* Description: Enables or disables platform timer
******************************************************************************/
void platform_enable_timer(FSC_BOOL enable)
{
if (enable == TRUE)
{
fusb_StartTimers();
}
else
{
fusb_StopTimers();
}
}
/*****************************************************************************
* Function: platform_delay_10us
* Input: delayCount - Number of 10us delays to wait
* Return: None
* Description: Perform a software delay in intervals of 10us.
******************************************************************************/
void platform_delay_10us(FSC_U32 delayCount)
{
fusb_Delay10us(delayCount);
}
extern FSC_BOOL IsPRSwap;
extern CCTermType CC1TermCCDebounce;
extern CCTermType CC2TermCCDebounce;
/*******************************************************************************
* Function: platform_notify_cc_orientation
* Input: orientation - Orientation of CC (NONE, CC1, CC2)
* Return: None
* Description: A callback used by the core to report to the platform the
* current CC orientation. Called in SetStateAttached... and
* SetStateUnattached functions.
******************************************************************************/
void platform_notify_cc_orientation(CC_ORIENTATION orientation)
{
struct pinctrl_state *set_state;
struct fusb30x_chip* chip = fusb30x_GetChip();
// Optional: Notify platform of CC orientation
pr_info("FUSB %s: orientation=[%d], CC1=[%d], CC2=[%d]\n", __func__, orientation, CC1TermCCDebounce, CC2TermCCDebounce);
if (orientation == CC1) {
if (chip->fusb302_pinctrl) {
set_state = pinctrl_lookup_state(chip->fusb302_pinctrl, "usb3_switch_sel_0");
if (IS_ERR(set_state)) {
pr_err("cannot get fusb302 pinctrl usb3_switch_sel_0 state");
return;
}
pinctrl_select_state(chip->fusb302_pinctrl, set_state);
}
} else if (orientation == CC2) {
if (chip->fusb302_pinctrl) {
set_state = pinctrl_lookup_state(chip->fusb302_pinctrl, "usb3_switch_sel_1");
if (IS_ERR(set_state)) {
pr_err("cannot get fusb302 pinctrl usb3_switch_sel_1 state");
return;
}
pinctrl_select_state(chip->fusb302_pinctrl, set_state);
}
} else if (orientation == NONE) {
if (chip && chip->uc && chip->uc->pd_vbus_ctrl) {
chip->uc->pd_vbus_ctrl(-1, FALSE);
if (!IsPRSwap)
platform_notify_attached_source(0, false);
}
}
}
extern ConnectionState ConnState; // Variable indicating the current connection state
extern PolicyState_t PolicyState;
/*******************************************************************************
* Function: platform_notify_pd_contract
* Input: contract - TRUE: Contract, FALSE: No Contract
* Return: None
* Description: A callback used by the core to report to the platform the
* current PD contract status. Called in PDPolicy.
*******************************************************************************/
void platform_notify_pd_contract(FSC_BOOL contract)
{
struct fusb30x_chip* chip = fusb30x_GetChip();
// Optional: Notify platform of PD contract
pr_info("FUSB %s: Contract=[%d], typec_state(0x%x), pd_state(0x%x)\n", __func__, contract, ConnState, PolicyState);
if (contract) {
if (ConnState == AttachedSink) {
if (chip && chip->uc && chip->uc->pd_vbus_ctrl) {
chip->uc->pd_vbus_ctrl(0, FALSE);
}
} else if (ConnState == AttachedSource) {
if (chip && chip->uc && chip->uc->pd_vbus_ctrl) {
chip->uc->pd_vbus_ctrl(1, FALSE);
}
}
}
}
/*******************************************************************************
* Function: platform_notify_unsupported_accessory
* Input: None
* Return: None
* Description: A callback used by the core to report entry to the
* Unsupported Accessory state. The platform may implement
* USB Billboard.
*******************************************************************************/
void platform_notify_unsupported_accessory(void)
{
// Optional: Implement USB Billboard
printk(KERN_INFO "FUSB %s: invoked\n", __func__);
}
extern FSC_BOOL PolicyIsDFP;
void platform_notify_attached_source(int value, bool connected)
{
struct fusb30x_chip* chip = fusb30x_GetChip();
int notify_retry_count = 0;
pr_info("FUSB %s: value(%d), typec_state(0x%x), pd_state(0x%x)\n", __func__, value, ConnState, PolicyState);
do {
if (chip != NULL && chip->uc != NULL && chip->uc->notify_attached_source != NULL) {
chip->uc->notify_attached_source(chip->uc, value);
break;
} else {
printk(KERN_INFO "FUSB %s: structure null, retry_count = %d\n", __func__, notify_retry_count);
notify_retry_count++;
msleep(5000);
}
} while (notify_retry_count <= 3);
PolicyIsDFP = value ? TRUE : FALSE;
if (connected) {
chip->pmode = value ? DUAL_ROLE_PROP_MODE_DFP : DUAL_ROLE_PROP_MODE_UFP;
chip->drole = value ? DUAL_ROLE_PROP_DR_HOST : DUAL_ROLE_PROP_DR_DEVICE;
} else {
chip->pmode = DUAL_ROLE_PROP_MODE_NONE;
chip->drole = DUAL_ROLE_PROP_DR_NONE;
chip->prole = DUAL_ROLE_PROP_PR_NONE;
}
if (chip->fusb_instance)
dual_role_instance_changed(chip->fusb_instance);
}
FSC_S32 platform_select_source_capability(u8 obj_cnt, doDataObject_t pd_data[7], int *device_max_ma)
{
return fusb_battery_select_source_capability(obj_cnt, pd_data, device_max_ma);
}
int platform_set_sink_current_previous(CCTermType Termination)
{
struct fusb30x_chip* chip = fusb30x_GetChip();
USBTypeCCurrent sinkCurrent;
int ret = -ENODEV;
switch (Termination)
{
case CCTypeRdUSB: // If we detect the default...
sinkCurrent = utccDefault;
break;
case CCTypeRd1p5: // If we detect 1.5A
sinkCurrent = utcc1p5A;
break;
case CCTypeRd3p0: // If we detect 3.0A
sinkCurrent = utcc3p0A;
break;
default:
sinkCurrent = utccNone;
break;
}
if (!chip->batt_psy)
chip->batt_psy = power_supply_get_by_name("battery");
#ifdef CONFIG_HTC_BATT
if (chip->batt_psy && chip->batt_psy->set_property)
ret = chip->batt_psy->set_property(chip->batt_psy, POWER_SUPPLY_PROP_TYPEC_SINK_CURRENT, (const union power_supply_propval *)&sinkCurrent);
#endif
return ret;
}
int usb_controller_register(struct device *parent, struct usb_controller *uc)
{
struct fusb30x_chip* chip = fusb30x_GetChip();
if (chip == NULL)
return -ENODEV;
chip->uc = uc;
return 0;
}
EXPORT_SYMBOL_GPL(usb_controller_register);
int usb_typec_ctrl_register(struct device *parent, struct usb_typec_ctrl *utc)
{
struct fusb30x_chip* chip = fusb30x_GetChip();
if (chip == NULL)
return -ENODEV;
chip->utc = utc;
return 0;
}
EXPORT_SYMBOL_GPL(usb_typec_ctrl_register);