162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * tusb1210.c - TUSB1210 USB ULPI PHY driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Intel Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/ulpi/driver.h> 1362306a36Sopenharmony_ci#include <linux/ulpi/regs.h> 1462306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1562306a36Sopenharmony_ci#include <linux/phy/ulpi_phy.h> 1662306a36Sopenharmony_ci#include <linux/power_supply.h> 1762306a36Sopenharmony_ci#include <linux/property.h> 1862306a36Sopenharmony_ci#include <linux/workqueue.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define TUSB1211_POWER_CONTROL 0x3d 2162306a36Sopenharmony_ci#define TUSB1211_POWER_CONTROL_SET 0x3e 2262306a36Sopenharmony_ci#define TUSB1211_POWER_CONTROL_CLEAR 0x3f 2362306a36Sopenharmony_ci#define TUSB1211_POWER_CONTROL_SW_CONTROL BIT(0) 2462306a36Sopenharmony_ci#define TUSB1211_POWER_CONTROL_DET_COMP BIT(1) 2562306a36Sopenharmony_ci#define TUSB1211_POWER_CONTROL_DP_VSRC_EN BIT(6) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define TUSB1210_VENDOR_SPECIFIC2 0x80 2862306a36Sopenharmony_ci#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0) 2962306a36Sopenharmony_ci#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4) 3062306a36Sopenharmony_ci#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define TUSB1211_VENDOR_SPECIFIC3 0x85 3362306a36Sopenharmony_ci#define TUSB1211_VENDOR_SPECIFIC3_SET 0x86 3462306a36Sopenharmony_ci#define TUSB1211_VENDOR_SPECIFIC3_CLEAR 0x87 3562306a36Sopenharmony_ci#define TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET BIT(4) 3662306a36Sopenharmony_ci#define TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN BIT(6) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define TUSB1210_RESET_TIME_MS 50 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define TUSB1210_CHG_DET_MAX_RETRIES 5 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* TUSB1210 charger detection work states */ 4362306a36Sopenharmony_cienum tusb1210_chg_det_state { 4462306a36Sopenharmony_ci TUSB1210_CHG_DET_CONNECTING, 4562306a36Sopenharmony_ci TUSB1210_CHG_DET_START_DET, 4662306a36Sopenharmony_ci TUSB1210_CHG_DET_READ_DET, 4762306a36Sopenharmony_ci TUSB1210_CHG_DET_FINISH_DET, 4862306a36Sopenharmony_ci TUSB1210_CHG_DET_CONNECTED, 4962306a36Sopenharmony_ci TUSB1210_CHG_DET_DISCONNECTING, 5062306a36Sopenharmony_ci TUSB1210_CHG_DET_DISCONNECTING_DONE, 5162306a36Sopenharmony_ci TUSB1210_CHG_DET_DISCONNECTED, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct tusb1210 { 5562306a36Sopenharmony_ci struct ulpi *ulpi; 5662306a36Sopenharmony_ci struct phy *phy; 5762306a36Sopenharmony_ci struct gpio_desc *gpio_reset; 5862306a36Sopenharmony_ci struct gpio_desc *gpio_cs; 5962306a36Sopenharmony_ci u8 otg_ctrl; 6062306a36Sopenharmony_ci u8 vendor_specific2; 6162306a36Sopenharmony_ci#ifdef CONFIG_POWER_SUPPLY 6262306a36Sopenharmony_ci enum power_supply_usb_type chg_type; 6362306a36Sopenharmony_ci enum tusb1210_chg_det_state chg_det_state; 6462306a36Sopenharmony_ci int chg_det_retries; 6562306a36Sopenharmony_ci struct delayed_work chg_det_work; 6662306a36Sopenharmony_ci struct notifier_block psy_nb; 6762306a36Sopenharmony_ci struct power_supply *psy; 6862306a36Sopenharmony_ci struct power_supply *charger; 6962306a36Sopenharmony_ci#endif 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int tusb1210_ulpi_write(struct tusb1210 *tusb, u8 reg, u8 val) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int ret; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ret = ulpi_write(tusb->ulpi, reg, val); 7762306a36Sopenharmony_ci if (ret) 7862306a36Sopenharmony_ci dev_err(&tusb->ulpi->dev, "error %d writing val 0x%02x to reg 0x%02x\n", 7962306a36Sopenharmony_ci ret, val, reg); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int tusb1210_ulpi_read(struct tusb1210 *tusb, u8 reg, u8 *val) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ret = ulpi_read(tusb->ulpi, reg); 8962306a36Sopenharmony_ci if (ret >= 0) { 9062306a36Sopenharmony_ci *val = ret; 9162306a36Sopenharmony_ci ret = 0; 9262306a36Sopenharmony_ci } else { 9362306a36Sopenharmony_ci dev_err(&tusb->ulpi->dev, "error %d reading reg 0x%02x\n", ret, reg); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int tusb1210_power_on(struct phy *phy) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct tusb1210 *tusb = phy_get_drvdata(phy); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_reset, 1); 10462306a36Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_cs, 1); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci msleep(TUSB1210_RESET_TIME_MS); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Restore the optional eye diagram optimization value */ 10962306a36Sopenharmony_ci tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, tusb->vendor_specific2); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int tusb1210_power_off(struct phy *phy) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct tusb1210 *tusb = phy_get_drvdata(phy); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_reset, 0); 11962306a36Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_cs, 0); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct tusb1210 *tusb = phy_get_drvdata(phy); 12762306a36Sopenharmony_ci int ret; 12862306a36Sopenharmony_ci u8 reg; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, ®); 13162306a36Sopenharmony_ci if (ret < 0) 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci switch (mode) { 13562306a36Sopenharmony_ci case PHY_MODE_USB_HOST: 13662306a36Sopenharmony_ci reg |= (ULPI_OTG_CTRL_DRVVBUS_EXT 13762306a36Sopenharmony_ci | ULPI_OTG_CTRL_ID_PULLUP 13862306a36Sopenharmony_ci | ULPI_OTG_CTRL_DP_PULLDOWN 13962306a36Sopenharmony_ci | ULPI_OTG_CTRL_DM_PULLDOWN); 14062306a36Sopenharmony_ci tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); 14162306a36Sopenharmony_ci reg |= ULPI_OTG_CTRL_DRVVBUS; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case PHY_MODE_USB_DEVICE: 14462306a36Sopenharmony_ci reg &= ~(ULPI_OTG_CTRL_DRVVBUS 14562306a36Sopenharmony_ci | ULPI_OTG_CTRL_DP_PULLDOWN 14662306a36Sopenharmony_ci | ULPI_OTG_CTRL_DM_PULLDOWN); 14762306a36Sopenharmony_ci tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); 14862306a36Sopenharmony_ci reg &= ~ULPI_OTG_CTRL_DRVVBUS_EXT; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci default: 15162306a36Sopenharmony_ci /* nothing */ 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci tusb->otg_ctrl = reg; 15662306a36Sopenharmony_ci return tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#ifdef CONFIG_POWER_SUPPLY 16062306a36Sopenharmony_cistatic const char * const tusb1210_chg_det_states[] = { 16162306a36Sopenharmony_ci "CHG_DET_CONNECTING", 16262306a36Sopenharmony_ci "CHG_DET_START_DET", 16362306a36Sopenharmony_ci "CHG_DET_READ_DET", 16462306a36Sopenharmony_ci "CHG_DET_FINISH_DET", 16562306a36Sopenharmony_ci "CHG_DET_CONNECTED", 16662306a36Sopenharmony_ci "CHG_DET_DISCONNECTING", 16762306a36Sopenharmony_ci "CHG_DET_DISCONNECTING_DONE", 16862306a36Sopenharmony_ci "CHG_DET_DISCONNECTED", 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void tusb1210_reset(struct tusb1210 *tusb) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_reset, 0); 17462306a36Sopenharmony_ci usleep_range(200, 500); 17562306a36Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_reset, 1); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void tusb1210_chg_det_set_type(struct tusb1210 *tusb, 17962306a36Sopenharmony_ci enum power_supply_usb_type type) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci dev_dbg(&tusb->ulpi->dev, "charger type: %d\n", type); 18262306a36Sopenharmony_ci tusb->chg_type = type; 18362306a36Sopenharmony_ci tusb->chg_det_retries = 0; 18462306a36Sopenharmony_ci power_supply_changed(tusb->psy); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void tusb1210_chg_det_set_state(struct tusb1210 *tusb, 18862306a36Sopenharmony_ci enum tusb1210_chg_det_state new_state, 18962306a36Sopenharmony_ci int delay_ms) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci if (delay_ms) 19262306a36Sopenharmony_ci dev_dbg(&tusb->ulpi->dev, "chg_det new state %s in %d ms\n", 19362306a36Sopenharmony_ci tusb1210_chg_det_states[new_state], delay_ms); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci tusb->chg_det_state = new_state; 19662306a36Sopenharmony_ci mod_delayed_work(system_long_wq, &tusb->chg_det_work, 19762306a36Sopenharmony_ci msecs_to_jiffies(delay_ms)); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void tusb1210_chg_det_handle_ulpi_error(struct tusb1210 *tusb) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci tusb1210_reset(tusb); 20362306a36Sopenharmony_ci if (tusb->chg_det_retries < TUSB1210_CHG_DET_MAX_RETRIES) { 20462306a36Sopenharmony_ci tusb->chg_det_retries++; 20562306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, 20662306a36Sopenharmony_ci TUSB1210_RESET_TIME_MS); 20762306a36Sopenharmony_ci } else { 20862306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, 20962306a36Sopenharmony_ci TUSB1210_RESET_TIME_MS); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * Boards using a TUSB121x for charger-detection have 3 power_supply class devs: 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * tusb1211-charger-detect(1) -> charger -> fuel-gauge 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * To determine if an USB charger is connected to the board, the online prop of 21962306a36Sopenharmony_ci * the charger psy needs to be read. Since the tusb1211-charger-detect psy is 22062306a36Sopenharmony_ci * the start of the supplier -> supplied-to chain, power_supply_am_i_supplied() 22162306a36Sopenharmony_ci * cannot be used here. 22262306a36Sopenharmony_ci * 22362306a36Sopenharmony_ci * Instead, below is a list of the power_supply names of known chargers for 22462306a36Sopenharmony_ci * these boards and the charger psy is looked up by name from this list. 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * (1) modelling the external USB charger 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic const char * const tusb1210_chargers[] = { 22962306a36Sopenharmony_ci "bq24190-charger", 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic bool tusb1210_get_online(struct tusb1210 *tusb) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci union power_supply_propval val; 23562306a36Sopenharmony_ci int i; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tusb1210_chargers) && !tusb->charger; i++) 23862306a36Sopenharmony_ci tusb->charger = power_supply_get_by_name(tusb1210_chargers[i]); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!tusb->charger) 24162306a36Sopenharmony_ci return false; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (power_supply_get_property(tusb->charger, POWER_SUPPLY_PROP_ONLINE, &val)) 24462306a36Sopenharmony_ci return false; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return val.intval; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void tusb1210_chg_det_work(struct work_struct *work) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct tusb1210 *tusb = container_of(work, struct tusb1210, chg_det_work.work); 25262306a36Sopenharmony_ci bool vbus_present = tusb1210_get_online(tusb); 25362306a36Sopenharmony_ci int ret; 25462306a36Sopenharmony_ci u8 val; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci dev_dbg(&tusb->ulpi->dev, "chg_det state %s vbus_present %d\n", 25762306a36Sopenharmony_ci tusb1210_chg_det_states[tusb->chg_det_state], vbus_present); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci switch (tusb->chg_det_state) { 26062306a36Sopenharmony_ci case TUSB1210_CHG_DET_CONNECTING: 26162306a36Sopenharmony_ci tusb->chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; 26262306a36Sopenharmony_ci tusb->chg_det_retries = 0; 26362306a36Sopenharmony_ci /* Power on USB controller for ulpi_read()/_write() */ 26462306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(tusb->ulpi->dev.parent); 26562306a36Sopenharmony_ci if (ret < 0) { 26662306a36Sopenharmony_ci dev_err(&tusb->ulpi->dev, "error %d runtime-resuming\n", ret); 26762306a36Sopenharmony_ci /* Should never happen, skip charger detection */ 26862306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0); 26962306a36Sopenharmony_ci return; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, 0); 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case TUSB1210_CHG_DET_START_DET: 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * Use the builtin charger detection FSM to keep things simple. 27662306a36Sopenharmony_ci * This only detects DCP / SDP. This is good enough for the few 27762306a36Sopenharmony_ci * boards which actually rely on the phy for charger detection. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci mutex_lock(&tusb->phy->mutex); 28062306a36Sopenharmony_ci ret = tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_SET, 28162306a36Sopenharmony_ci TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET); 28262306a36Sopenharmony_ci mutex_unlock(&tusb->phy->mutex); 28362306a36Sopenharmony_ci if (ret) { 28462306a36Sopenharmony_ci tusb1210_chg_det_handle_ulpi_error(tusb); 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Wait 400 ms for the charger detection FSM to finish */ 28962306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_READ_DET, 400); 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case TUSB1210_CHG_DET_READ_DET: 29262306a36Sopenharmony_ci mutex_lock(&tusb->phy->mutex); 29362306a36Sopenharmony_ci ret = tusb1210_ulpi_read(tusb, TUSB1211_POWER_CONTROL, &val); 29462306a36Sopenharmony_ci mutex_unlock(&tusb->phy->mutex); 29562306a36Sopenharmony_ci if (ret) { 29662306a36Sopenharmony_ci tusb1210_chg_det_handle_ulpi_error(tusb); 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (val & TUSB1211_POWER_CONTROL_DET_COMP) 30162306a36Sopenharmony_ci tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_DCP); 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_SDP); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, 0); 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci case TUSB1210_CHG_DET_FINISH_DET: 30862306a36Sopenharmony_ci mutex_lock(&tusb->phy->mutex); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Set SW_CONTROL to stop the charger-det FSM */ 31162306a36Sopenharmony_ci ret = tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_SET, 31262306a36Sopenharmony_ci TUSB1211_POWER_CONTROL_SW_CONTROL); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Clear DP_VSRC_EN which may have been enabled by the charger-det FSM */ 31562306a36Sopenharmony_ci ret |= tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_CLEAR, 31662306a36Sopenharmony_ci TUSB1211_POWER_CONTROL_DP_VSRC_EN); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Clear CHGD_IDP_SRC_EN (may have been enabled by the charger-det FSM) */ 31962306a36Sopenharmony_ci ret |= tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_CLEAR, 32062306a36Sopenharmony_ci TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* If any of the above fails reset the phy */ 32362306a36Sopenharmony_ci if (ret) { 32462306a36Sopenharmony_ci tusb1210_reset(tusb); 32562306a36Sopenharmony_ci msleep(TUSB1210_RESET_TIME_MS); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Restore phy-parameters and OTG_CTRL register */ 32962306a36Sopenharmony_ci tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, tusb->otg_ctrl); 33062306a36Sopenharmony_ci tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, 33162306a36Sopenharmony_ci tusb->vendor_specific2); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci mutex_unlock(&tusb->phy->mutex); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci pm_runtime_put(tusb->ulpi->dev.parent); 33662306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case TUSB1210_CHG_DET_CONNECTED: 33962306a36Sopenharmony_ci if (!vbus_present) 34062306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING, 0); 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci case TUSB1210_CHG_DET_DISCONNECTING: 34362306a36Sopenharmony_ci /* 34462306a36Sopenharmony_ci * The phy seems to take approx. 600ms longer then the charger 34562306a36Sopenharmony_ci * chip (which is used to get vbus_present) to determine Vbus 34662306a36Sopenharmony_ci * session end. Wait 800ms to ensure the phy has detected and 34762306a36Sopenharmony_ci * signalled Vbus session end. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING_DONE, 800); 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci case TUSB1210_CHG_DET_DISCONNECTING_DONE: 35262306a36Sopenharmony_ci /* 35362306a36Sopenharmony_ci * The phy often stops reacting to ulpi_read()/_write requests 35462306a36Sopenharmony_ci * after a Vbus-session end. Reset it to work around this. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci tusb1210_reset(tusb); 35762306a36Sopenharmony_ci tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_UNKNOWN); 35862306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTED, 0); 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case TUSB1210_CHG_DET_DISCONNECTED: 36162306a36Sopenharmony_ci if (vbus_present) 36262306a36Sopenharmony_ci tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTING, 0); 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int tusb1210_psy_notifier(struct notifier_block *nb, 36862306a36Sopenharmony_ci unsigned long event, void *ptr) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct tusb1210 *tusb = container_of(nb, struct tusb1210, psy_nb); 37162306a36Sopenharmony_ci struct power_supply *psy = ptr; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (psy != tusb->psy && psy->desc->type == POWER_SUPPLY_TYPE_USB) 37462306a36Sopenharmony_ci queue_delayed_work(system_long_wq, &tusb->chg_det_work, 0); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return NOTIFY_OK; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int tusb1210_psy_get_prop(struct power_supply *psy, 38062306a36Sopenharmony_ci enum power_supply_property psp, 38162306a36Sopenharmony_ci union power_supply_propval *val) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct tusb1210 *tusb = power_supply_get_drvdata(psy); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci switch (psp) { 38662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 38762306a36Sopenharmony_ci val->intval = tusb1210_get_online(tusb); 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_USB_TYPE: 39062306a36Sopenharmony_ci val->intval = tusb->chg_type; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 39362306a36Sopenharmony_ci if (tusb->chg_type == POWER_SUPPLY_USB_TYPE_DCP) 39462306a36Sopenharmony_ci val->intval = 2000000; 39562306a36Sopenharmony_ci else 39662306a36Sopenharmony_ci val->intval = 500000; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci default: 39962306a36Sopenharmony_ci return -EINVAL; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic const enum power_supply_usb_type tusb1210_psy_usb_types[] = { 40662306a36Sopenharmony_ci POWER_SUPPLY_USB_TYPE_SDP, 40762306a36Sopenharmony_ci POWER_SUPPLY_USB_TYPE_DCP, 40862306a36Sopenharmony_ci POWER_SUPPLY_USB_TYPE_UNKNOWN, 40962306a36Sopenharmony_ci}; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic const enum power_supply_property tusb1210_psy_props[] = { 41262306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 41362306a36Sopenharmony_ci POWER_SUPPLY_PROP_USB_TYPE, 41462306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic const struct power_supply_desc tusb1210_psy_desc = { 41862306a36Sopenharmony_ci .name = "tusb1211-charger-detect", 41962306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 42062306a36Sopenharmony_ci .usb_types = tusb1210_psy_usb_types, 42162306a36Sopenharmony_ci .num_usb_types = ARRAY_SIZE(tusb1210_psy_usb_types), 42262306a36Sopenharmony_ci .properties = tusb1210_psy_props, 42362306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(tusb1210_psy_props), 42462306a36Sopenharmony_ci .get_property = tusb1210_psy_get_prop, 42562306a36Sopenharmony_ci}; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* Setup charger detection if requested, on errors continue without chg-det */ 42862306a36Sopenharmony_cistatic void tusb1210_probe_charger_detect(struct tusb1210 *tusb) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct power_supply_config psy_cfg = { .drv_data = tusb }; 43162306a36Sopenharmony_ci struct device *dev = &tusb->ulpi->dev; 43262306a36Sopenharmony_ci int ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!device_property_read_bool(dev->parent, "linux,phy_charger_detect")) 43562306a36Sopenharmony_ci return; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (tusb->ulpi->id.product != 0x1508) { 43862306a36Sopenharmony_ci dev_err(dev, "error charger detection is only supported on the TUSB1211\n"); 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, &tusb->otg_ctrl); 44362306a36Sopenharmony_ci if (ret) 44462306a36Sopenharmony_ci return; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci tusb->psy = power_supply_register(dev, &tusb1210_psy_desc, &psy_cfg); 44762306a36Sopenharmony_ci if (IS_ERR(tusb->psy)) 44862306a36Sopenharmony_ci return; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Delay initial run by 2 seconds to allow the charger driver, 45262306a36Sopenharmony_ci * which is used to determine vbus_present, to load. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci tusb->chg_det_state = TUSB1210_CHG_DET_DISCONNECTED; 45562306a36Sopenharmony_ci INIT_DELAYED_WORK(&tusb->chg_det_work, tusb1210_chg_det_work); 45662306a36Sopenharmony_ci queue_delayed_work(system_long_wq, &tusb->chg_det_work, 2 * HZ); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci tusb->psy_nb.notifier_call = tusb1210_psy_notifier; 45962306a36Sopenharmony_ci power_supply_reg_notifier(&tusb->psy_nb); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void tusb1210_remove_charger_detect(struct tusb1210 *tusb) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(tusb->psy)) { 46662306a36Sopenharmony_ci power_supply_unreg_notifier(&tusb->psy_nb); 46762306a36Sopenharmony_ci cancel_delayed_work_sync(&tusb->chg_det_work); 46862306a36Sopenharmony_ci power_supply_unregister(tusb->psy); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (tusb->charger) 47262306a36Sopenharmony_ci power_supply_put(tusb->charger); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci#else 47562306a36Sopenharmony_cistatic void tusb1210_probe_charger_detect(struct tusb1210 *tusb) { } 47662306a36Sopenharmony_cistatic void tusb1210_remove_charger_detect(struct tusb1210 *tusb) { } 47762306a36Sopenharmony_ci#endif 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic const struct phy_ops phy_ops = { 48062306a36Sopenharmony_ci .power_on = tusb1210_power_on, 48162306a36Sopenharmony_ci .power_off = tusb1210_power_off, 48262306a36Sopenharmony_ci .set_mode = tusb1210_set_mode, 48362306a36Sopenharmony_ci .owner = THIS_MODULE, 48462306a36Sopenharmony_ci}; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic int tusb1210_probe(struct ulpi *ulpi) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct tusb1210 *tusb; 48962306a36Sopenharmony_ci u8 val, reg; 49062306a36Sopenharmony_ci int ret; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); 49362306a36Sopenharmony_ci if (!tusb) 49462306a36Sopenharmony_ci return -ENOMEM; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci tusb->ulpi = ulpi; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", 49962306a36Sopenharmony_ci GPIOD_OUT_LOW); 50062306a36Sopenharmony_ci if (IS_ERR(tusb->gpio_reset)) 50162306a36Sopenharmony_ci return PTR_ERR(tusb->gpio_reset); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_reset, 1); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci tusb->gpio_cs = devm_gpiod_get_optional(&ulpi->dev, "cs", 50662306a36Sopenharmony_ci GPIOD_OUT_LOW); 50762306a36Sopenharmony_ci if (IS_ERR(tusb->gpio_cs)) 50862306a36Sopenharmony_ci return PTR_ERR(tusb->gpio_cs); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_cs, 1); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* 51362306a36Sopenharmony_ci * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye 51462306a36Sopenharmony_ci * diagram optimization and DP/DM swap. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = tusb1210_ulpi_read(tusb, TUSB1210_VENDOR_SPECIFIC2, ®); 51862306a36Sopenharmony_ci if (ret) 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* High speed output drive strength configuration */ 52262306a36Sopenharmony_ci if (!device_property_read_u8(&ulpi->dev, "ihstx", &val)) 52362306a36Sopenharmony_ci u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* High speed output impedance configuration */ 52662306a36Sopenharmony_ci if (!device_property_read_u8(&ulpi->dev, "zhsdrv", &val)) 52762306a36Sopenharmony_ci u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* DP/DM swap control */ 53062306a36Sopenharmony_ci if (!device_property_read_u8(&ulpi->dev, "datapolarity", &val)) 53162306a36Sopenharmony_ci u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_DP_MASK); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci ret = tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, reg); 53462306a36Sopenharmony_ci if (ret) 53562306a36Sopenharmony_ci return ret; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci tusb->vendor_specific2 = reg; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci tusb1210_probe_charger_detect(tusb); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci tusb->phy = ulpi_phy_create(ulpi, &phy_ops); 54262306a36Sopenharmony_ci if (IS_ERR(tusb->phy)) { 54362306a36Sopenharmony_ci ret = PTR_ERR(tusb->phy); 54462306a36Sopenharmony_ci goto err_remove_charger; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci phy_set_drvdata(tusb->phy, tusb); 54862306a36Sopenharmony_ci ulpi_set_drvdata(ulpi, tusb); 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cierr_remove_charger: 55262306a36Sopenharmony_ci tusb1210_remove_charger_detect(tusb); 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic void tusb1210_remove(struct ulpi *ulpi) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ulpi_phy_destroy(ulpi, tusb->phy); 56162306a36Sopenharmony_ci tusb1210_remove_charger_detect(tusb); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci#define TI_VENDOR_ID 0x0451 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic const struct ulpi_device_id tusb1210_ulpi_id[] = { 56762306a36Sopenharmony_ci { TI_VENDOR_ID, 0x1507, }, /* TUSB1210 */ 56862306a36Sopenharmony_ci { TI_VENDOR_ID, 0x1508, }, /* TUSB1211 */ 56962306a36Sopenharmony_ci { }, 57062306a36Sopenharmony_ci}; 57162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic struct ulpi_driver tusb1210_driver = { 57462306a36Sopenharmony_ci .id_table = tusb1210_ulpi_id, 57562306a36Sopenharmony_ci .probe = tusb1210_probe, 57662306a36Sopenharmony_ci .remove = tusb1210_remove, 57762306a36Sopenharmony_ci .driver = { 57862306a36Sopenharmony_ci .name = "tusb1210", 57962306a36Sopenharmony_ci .owner = THIS_MODULE, 58062306a36Sopenharmony_ci }, 58162306a36Sopenharmony_ci}; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cimodule_ulpi_driver(tusb1210_driver); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 58662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 58762306a36Sopenharmony_ciMODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); 588