123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * tusb1210.c - TUSB1210 USB ULPI PHY driver
- *
- * Copyright (C) 2015 Intel Corporation
- *
- * Author: Heikki Krogerus <[email protected]>
- */
- #include <linux/module.h>
- #include <linux/bitfield.h>
- #include <linux/delay.h>
- #include <linux/ulpi/driver.h>
- #include <linux/ulpi/regs.h>
- #include <linux/gpio/consumer.h>
- #include <linux/phy/ulpi_phy.h>
- #include <linux/power_supply.h>
- #include <linux/workqueue.h>
- #define TUSB1211_POWER_CONTROL 0x3d
- #define TUSB1211_POWER_CONTROL_SET 0x3e
- #define TUSB1211_POWER_CONTROL_CLEAR 0x3f
- #define TUSB1211_POWER_CONTROL_SW_CONTROL BIT(0)
- #define TUSB1211_POWER_CONTROL_DET_COMP BIT(1)
- #define TUSB1211_POWER_CONTROL_DP_VSRC_EN BIT(6)
- #define TUSB1210_VENDOR_SPECIFIC2 0x80
- #define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0)
- #define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4)
- #define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6)
- #define TUSB1211_VENDOR_SPECIFIC3 0x85
- #define TUSB1211_VENDOR_SPECIFIC3_SET 0x86
- #define TUSB1211_VENDOR_SPECIFIC3_CLEAR 0x87
- #define TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET BIT(4)
- #define TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN BIT(6)
- #define TUSB1210_RESET_TIME_MS 50
- #define TUSB1210_CHG_DET_MAX_RETRIES 5
- /* TUSB1210 charger detection work states */
- enum tusb1210_chg_det_state {
- TUSB1210_CHG_DET_CONNECTING,
- TUSB1210_CHG_DET_START_DET,
- TUSB1210_CHG_DET_READ_DET,
- TUSB1210_CHG_DET_FINISH_DET,
- TUSB1210_CHG_DET_CONNECTED,
- TUSB1210_CHG_DET_DISCONNECTING,
- TUSB1210_CHG_DET_DISCONNECTING_DONE,
- TUSB1210_CHG_DET_DISCONNECTED,
- };
- struct tusb1210 {
- struct ulpi *ulpi;
- struct phy *phy;
- struct gpio_desc *gpio_reset;
- struct gpio_desc *gpio_cs;
- u8 otg_ctrl;
- u8 vendor_specific2;
- #ifdef CONFIG_POWER_SUPPLY
- enum power_supply_usb_type chg_type;
- enum tusb1210_chg_det_state chg_det_state;
- int chg_det_retries;
- struct delayed_work chg_det_work;
- struct notifier_block psy_nb;
- struct power_supply *psy;
- struct power_supply *charger;
- #endif
- };
- static int tusb1210_ulpi_write(struct tusb1210 *tusb, u8 reg, u8 val)
- {
- int ret;
- ret = ulpi_write(tusb->ulpi, reg, val);
- if (ret)
- dev_err(&tusb->ulpi->dev, "error %d writing val 0x%02x to reg 0x%02x\n",
- ret, val, reg);
- return ret;
- }
- static int tusb1210_ulpi_read(struct tusb1210 *tusb, u8 reg, u8 *val)
- {
- int ret;
- ret = ulpi_read(tusb->ulpi, reg);
- if (ret >= 0) {
- *val = ret;
- ret = 0;
- } else {
- dev_err(&tusb->ulpi->dev, "error %d reading reg 0x%02x\n", ret, reg);
- }
- return ret;
- }
- static int tusb1210_power_on(struct phy *phy)
- {
- struct tusb1210 *tusb = phy_get_drvdata(phy);
- gpiod_set_value_cansleep(tusb->gpio_reset, 1);
- gpiod_set_value_cansleep(tusb->gpio_cs, 1);
- msleep(TUSB1210_RESET_TIME_MS);
- /* Restore the optional eye diagram optimization value */
- tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, tusb->vendor_specific2);
- return 0;
- }
- static int tusb1210_power_off(struct phy *phy)
- {
- struct tusb1210 *tusb = phy_get_drvdata(phy);
- gpiod_set_value_cansleep(tusb->gpio_reset, 0);
- gpiod_set_value_cansleep(tusb->gpio_cs, 0);
- return 0;
- }
- static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode)
- {
- struct tusb1210 *tusb = phy_get_drvdata(phy);
- int ret;
- u8 reg;
- ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, ®);
- if (ret < 0)
- return ret;
- switch (mode) {
- case PHY_MODE_USB_HOST:
- reg |= (ULPI_OTG_CTRL_DRVVBUS_EXT
- | ULPI_OTG_CTRL_ID_PULLUP
- | ULPI_OTG_CTRL_DP_PULLDOWN
- | ULPI_OTG_CTRL_DM_PULLDOWN);
- tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
- reg |= ULPI_OTG_CTRL_DRVVBUS;
- break;
- case PHY_MODE_USB_DEVICE:
- reg &= ~(ULPI_OTG_CTRL_DRVVBUS
- | ULPI_OTG_CTRL_DP_PULLDOWN
- | ULPI_OTG_CTRL_DM_PULLDOWN);
- tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
- reg &= ~ULPI_OTG_CTRL_DRVVBUS_EXT;
- break;
- default:
- /* nothing */
- return 0;
- }
- tusb->otg_ctrl = reg;
- return tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, reg);
- }
- #ifdef CONFIG_POWER_SUPPLY
- static const char * const tusb1210_chg_det_states[] = {
- "CHG_DET_CONNECTING",
- "CHG_DET_START_DET",
- "CHG_DET_READ_DET",
- "CHG_DET_FINISH_DET",
- "CHG_DET_CONNECTED",
- "CHG_DET_DISCONNECTING",
- "CHG_DET_DISCONNECTING_DONE",
- "CHG_DET_DISCONNECTED",
- };
- static void tusb1210_reset(struct tusb1210 *tusb)
- {
- gpiod_set_value_cansleep(tusb->gpio_reset, 0);
- usleep_range(200, 500);
- gpiod_set_value_cansleep(tusb->gpio_reset, 1);
- }
- static void tusb1210_chg_det_set_type(struct tusb1210 *tusb,
- enum power_supply_usb_type type)
- {
- dev_dbg(&tusb->ulpi->dev, "charger type: %d\n", type);
- tusb->chg_type = type;
- tusb->chg_det_retries = 0;
- power_supply_changed(tusb->psy);
- }
- static void tusb1210_chg_det_set_state(struct tusb1210 *tusb,
- enum tusb1210_chg_det_state new_state,
- int delay_ms)
- {
- if (delay_ms)
- dev_dbg(&tusb->ulpi->dev, "chg_det new state %s in %d ms\n",
- tusb1210_chg_det_states[new_state], delay_ms);
- tusb->chg_det_state = new_state;
- mod_delayed_work(system_long_wq, &tusb->chg_det_work,
- msecs_to_jiffies(delay_ms));
- }
- static void tusb1210_chg_det_handle_ulpi_error(struct tusb1210 *tusb)
- {
- tusb1210_reset(tusb);
- if (tusb->chg_det_retries < TUSB1210_CHG_DET_MAX_RETRIES) {
- tusb->chg_det_retries++;
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET,
- TUSB1210_RESET_TIME_MS);
- } else {
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET,
- TUSB1210_RESET_TIME_MS);
- }
- }
- /*
- * Boards using a TUSB121x for charger-detection have 3 power_supply class devs:
- *
- * tusb1211-charger-detect(1) -> charger -> fuel-gauge
- *
- * To determine if an USB charger is connected to the board, the online prop of
- * the charger psy needs to be read. Since the tusb1211-charger-detect psy is
- * the start of the supplier -> supplied-to chain, power_supply_am_i_supplied()
- * cannot be used here.
- *
- * Instead, below is a list of the power_supply names of known chargers for
- * these boards and the charger psy is looked up by name from this list.
- *
- * (1) modelling the external USB charger
- */
- static const char * const tusb1210_chargers[] = {
- "bq24190-charger",
- };
- static bool tusb1210_get_online(struct tusb1210 *tusb)
- {
- union power_supply_propval val;
- int i;
- for (i = 0; i < ARRAY_SIZE(tusb1210_chargers) && !tusb->charger; i++)
- tusb->charger = power_supply_get_by_name(tusb1210_chargers[i]);
- if (!tusb->charger)
- return false;
- if (power_supply_get_property(tusb->charger, POWER_SUPPLY_PROP_ONLINE, &val))
- return false;
- return val.intval;
- }
- static void tusb1210_chg_det_work(struct work_struct *work)
- {
- struct tusb1210 *tusb = container_of(work, struct tusb1210, chg_det_work.work);
- bool vbus_present = tusb1210_get_online(tusb);
- int ret;
- u8 val;
- dev_dbg(&tusb->ulpi->dev, "chg_det state %s vbus_present %d\n",
- tusb1210_chg_det_states[tusb->chg_det_state], vbus_present);
- switch (tusb->chg_det_state) {
- case TUSB1210_CHG_DET_CONNECTING:
- tusb->chg_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
- tusb->chg_det_retries = 0;
- /* Power on USB controller for ulpi_read()/_write() */
- ret = pm_runtime_resume_and_get(tusb->ulpi->dev.parent);
- if (ret < 0) {
- dev_err(&tusb->ulpi->dev, "error %d runtime-resuming\n", ret);
- /* Should never happen, skip charger detection */
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0);
- return;
- }
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_START_DET, 0);
- break;
- case TUSB1210_CHG_DET_START_DET:
- /*
- * Use the builtin charger detection FSM to keep things simple.
- * This only detects DCP / SDP. This is good enough for the few
- * boards which actually rely on the phy for charger detection.
- */
- mutex_lock(&tusb->phy->mutex);
- ret = tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_SET,
- TUSB1211_VENDOR_SPECIFIC3_SW_USB_DET);
- mutex_unlock(&tusb->phy->mutex);
- if (ret) {
- tusb1210_chg_det_handle_ulpi_error(tusb);
- break;
- }
- /* Wait 400 ms for the charger detection FSM to finish */
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_READ_DET, 400);
- break;
- case TUSB1210_CHG_DET_READ_DET:
- mutex_lock(&tusb->phy->mutex);
- ret = tusb1210_ulpi_read(tusb, TUSB1211_POWER_CONTROL, &val);
- mutex_unlock(&tusb->phy->mutex);
- if (ret) {
- tusb1210_chg_det_handle_ulpi_error(tusb);
- break;
- }
- if (val & TUSB1211_POWER_CONTROL_DET_COMP)
- tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_DCP);
- else
- tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_SDP);
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_FINISH_DET, 0);
- break;
- case TUSB1210_CHG_DET_FINISH_DET:
- mutex_lock(&tusb->phy->mutex);
- /* Set SW_CONTROL to stop the charger-det FSM */
- ret = tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_SET,
- TUSB1211_POWER_CONTROL_SW_CONTROL);
- /* Clear DP_VSRC_EN which may have been enabled by the charger-det FSM */
- ret |= tusb1210_ulpi_write(tusb, TUSB1211_POWER_CONTROL_CLEAR,
- TUSB1211_POWER_CONTROL_DP_VSRC_EN);
- /* Clear CHGD_IDP_SRC_EN (may have been enabled by the charger-det FSM) */
- ret |= tusb1210_ulpi_write(tusb, TUSB1211_VENDOR_SPECIFIC3_CLEAR,
- TUSB1211_VENDOR_SPECIFIC3_CHGD_IDP_SRC_EN);
- /* If any of the above fails reset the phy */
- if (ret) {
- tusb1210_reset(tusb);
- msleep(TUSB1210_RESET_TIME_MS);
- }
- /* Restore phy-parameters and OTG_CTRL register */
- tusb1210_ulpi_write(tusb, ULPI_OTG_CTRL, tusb->otg_ctrl);
- tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2,
- tusb->vendor_specific2);
- mutex_unlock(&tusb->phy->mutex);
- pm_runtime_put(tusb->ulpi->dev.parent);
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTED, 0);
- break;
- case TUSB1210_CHG_DET_CONNECTED:
- if (!vbus_present)
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING, 0);
- break;
- case TUSB1210_CHG_DET_DISCONNECTING:
- /*
- * The phy seems to take approx. 600ms longer then the charger
- * chip (which is used to get vbus_present) to determine Vbus
- * session end. Wait 800ms to ensure the phy has detected and
- * signalled Vbus session end.
- */
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTING_DONE, 800);
- break;
- case TUSB1210_CHG_DET_DISCONNECTING_DONE:
- /*
- * The phy often stops reacting to ulpi_read()/_write requests
- * after a Vbus-session end. Reset it to work around this.
- */
- tusb1210_reset(tusb);
- tusb1210_chg_det_set_type(tusb, POWER_SUPPLY_USB_TYPE_UNKNOWN);
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_DISCONNECTED, 0);
- break;
- case TUSB1210_CHG_DET_DISCONNECTED:
- if (vbus_present)
- tusb1210_chg_det_set_state(tusb, TUSB1210_CHG_DET_CONNECTING, 0);
- break;
- }
- }
- static int tusb1210_psy_notifier(struct notifier_block *nb,
- unsigned long event, void *ptr)
- {
- struct tusb1210 *tusb = container_of(nb, struct tusb1210, psy_nb);
- struct power_supply *psy = ptr;
- if (psy != tusb->psy && psy->desc->type == POWER_SUPPLY_TYPE_USB)
- queue_delayed_work(system_long_wq, &tusb->chg_det_work, 0);
- return NOTIFY_OK;
- }
- static int tusb1210_psy_get_prop(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
- {
- struct tusb1210 *tusb = power_supply_get_drvdata(psy);
- switch (psp) {
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = tusb1210_get_online(tusb);
- break;
- case POWER_SUPPLY_PROP_USB_TYPE:
- val->intval = tusb->chg_type;
- break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- if (tusb->chg_type == POWER_SUPPLY_USB_TYPE_DCP)
- val->intval = 2000000;
- else
- val->intval = 500000;
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static const enum power_supply_usb_type tusb1210_psy_usb_types[] = {
- POWER_SUPPLY_USB_TYPE_SDP,
- POWER_SUPPLY_USB_TYPE_DCP,
- POWER_SUPPLY_USB_TYPE_UNKNOWN,
- };
- static const enum power_supply_property tusb1210_psy_props[] = {
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_USB_TYPE,
- POWER_SUPPLY_PROP_CURRENT_MAX,
- };
- static const struct power_supply_desc tusb1210_psy_desc = {
- .name = "tusb1211-charger-detect",
- .type = POWER_SUPPLY_TYPE_USB,
- .usb_types = tusb1210_psy_usb_types,
- .num_usb_types = ARRAY_SIZE(tusb1210_psy_usb_types),
- .properties = tusb1210_psy_props,
- .num_properties = ARRAY_SIZE(tusb1210_psy_props),
- .get_property = tusb1210_psy_get_prop,
- };
- /* Setup charger detection if requested, on errors continue without chg-det */
- static void tusb1210_probe_charger_detect(struct tusb1210 *tusb)
- {
- struct power_supply_config psy_cfg = { .drv_data = tusb };
- struct device *dev = &tusb->ulpi->dev;
- int ret;
- if (!device_property_read_bool(dev->parent, "linux,phy_charger_detect"))
- return;
- if (tusb->ulpi->id.product != 0x1508) {
- dev_err(dev, "error charger detection is only supported on the TUSB1211\n");
- return;
- }
- ret = tusb1210_ulpi_read(tusb, ULPI_OTG_CTRL, &tusb->otg_ctrl);
- if (ret)
- return;
- tusb->psy = power_supply_register(dev, &tusb1210_psy_desc, &psy_cfg);
- if (IS_ERR(tusb->psy))
- return;
- /*
- * Delay initial run by 2 seconds to allow the charger driver,
- * which is used to determine vbus_present, to load.
- */
- tusb->chg_det_state = TUSB1210_CHG_DET_DISCONNECTED;
- INIT_DELAYED_WORK(&tusb->chg_det_work, tusb1210_chg_det_work);
- queue_delayed_work(system_long_wq, &tusb->chg_det_work, 2 * HZ);
- tusb->psy_nb.notifier_call = tusb1210_psy_notifier;
- power_supply_reg_notifier(&tusb->psy_nb);
- }
- static void tusb1210_remove_charger_detect(struct tusb1210 *tusb)
- {
- if (!IS_ERR_OR_NULL(tusb->psy)) {
- power_supply_unreg_notifier(&tusb->psy_nb);
- cancel_delayed_work_sync(&tusb->chg_det_work);
- power_supply_unregister(tusb->psy);
- }
- if (tusb->charger)
- power_supply_put(tusb->charger);
- }
- #else
- static void tusb1210_probe_charger_detect(struct tusb1210 *tusb) { }
- static void tusb1210_remove_charger_detect(struct tusb1210 *tusb) { }
- #endif
- static const struct phy_ops phy_ops = {
- .power_on = tusb1210_power_on,
- .power_off = tusb1210_power_off,
- .set_mode = tusb1210_set_mode,
- .owner = THIS_MODULE,
- };
- static int tusb1210_probe(struct ulpi *ulpi)
- {
- struct tusb1210 *tusb;
- u8 val, reg;
- int ret;
- tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL);
- if (!tusb)
- return -ENOMEM;
- tusb->ulpi = ulpi;
- tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset",
- GPIOD_OUT_LOW);
- if (IS_ERR(tusb->gpio_reset))
- return PTR_ERR(tusb->gpio_reset);
- gpiod_set_value_cansleep(tusb->gpio_reset, 1);
- tusb->gpio_cs = devm_gpiod_get_optional(&ulpi->dev, "cs",
- GPIOD_OUT_LOW);
- if (IS_ERR(tusb->gpio_cs))
- return PTR_ERR(tusb->gpio_cs);
- gpiod_set_value_cansleep(tusb->gpio_cs, 1);
- /*
- * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye
- * diagram optimization and DP/DM swap.
- */
- ret = tusb1210_ulpi_read(tusb, TUSB1210_VENDOR_SPECIFIC2, ®);
- if (ret)
- return ret;
- /* High speed output drive strength configuration */
- if (!device_property_read_u8(&ulpi->dev, "ihstx", &val))
- u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK);
- /* High speed output impedance configuration */
- if (!device_property_read_u8(&ulpi->dev, "zhsdrv", &val))
- u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK);
- /* DP/DM swap control */
- if (!device_property_read_u8(&ulpi->dev, "datapolarity", &val))
- u8p_replace_bits(®, val, (u8)TUSB1210_VENDOR_SPECIFIC2_DP_MASK);
- ret = tusb1210_ulpi_write(tusb, TUSB1210_VENDOR_SPECIFIC2, reg);
- if (ret)
- return ret;
- tusb->vendor_specific2 = reg;
- tusb1210_probe_charger_detect(tusb);
- tusb->phy = ulpi_phy_create(ulpi, &phy_ops);
- if (IS_ERR(tusb->phy)) {
- ret = PTR_ERR(tusb->phy);
- goto err_remove_charger;
- }
- phy_set_drvdata(tusb->phy, tusb);
- ulpi_set_drvdata(ulpi, tusb);
- return 0;
- err_remove_charger:
- tusb1210_remove_charger_detect(tusb);
- return ret;
- }
- static void tusb1210_remove(struct ulpi *ulpi)
- {
- struct tusb1210 *tusb = ulpi_get_drvdata(ulpi);
- ulpi_phy_destroy(ulpi, tusb->phy);
- tusb1210_remove_charger_detect(tusb);
- }
- #define TI_VENDOR_ID 0x0451
- static const struct ulpi_device_id tusb1210_ulpi_id[] = {
- { TI_VENDOR_ID, 0x1507, }, /* TUSB1210 */
- { TI_VENDOR_ID, 0x1508, }, /* TUSB1211 */
- { },
- };
- MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id);
- static struct ulpi_driver tusb1210_driver = {
- .id_table = tusb1210_ulpi_id,
- .probe = tusb1210_probe,
- .remove = tusb1210_remove,
- .driver = {
- .name = "tusb1210",
- .owner = THIS_MODULE,
- },
- };
- module_ulpi_driver(tusb1210_driver);
- MODULE_AUTHOR("Intel Corporation");
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver");
|