18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * tusb1210.c - TUSB1210 USB ULPI PHY driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/ulpi/driver.h> 118c2ecf20Sopenharmony_ci#include <linux/ulpi/regs.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 138c2ecf20Sopenharmony_ci#include <linux/phy/ulpi_phy.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define TUSB1210_VENDOR_SPECIFIC2 0x80 168c2ecf20Sopenharmony_ci#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 178c2ecf20Sopenharmony_ci#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 188c2ecf20Sopenharmony_ci#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct tusb1210 { 218c2ecf20Sopenharmony_ci struct ulpi *ulpi; 228c2ecf20Sopenharmony_ci struct phy *phy; 238c2ecf20Sopenharmony_ci struct gpio_desc *gpio_reset; 248c2ecf20Sopenharmony_ci struct gpio_desc *gpio_cs; 258c2ecf20Sopenharmony_ci u8 vendor_specific2; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int tusb1210_power_on(struct phy *phy) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct tusb1210 *tusb = phy_get_drvdata(phy); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_reset, 1); 338c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_cs, 1); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* Restore the optional eye diagram optimization value */ 368c2ecf20Sopenharmony_ci if (tusb->vendor_specific2) 378c2ecf20Sopenharmony_ci ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, 388c2ecf20Sopenharmony_ci tusb->vendor_specific2); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int tusb1210_power_off(struct phy *phy) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct tusb1210 *tusb = phy_get_drvdata(phy); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_reset, 0); 488c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_cs, 0); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return 0; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct tusb1210 *tusb = phy_get_drvdata(phy); 568c2ecf20Sopenharmony_ci int ret; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci ret = ulpi_read(tusb->ulpi, ULPI_OTG_CTRL); 598c2ecf20Sopenharmony_ci if (ret < 0) 608c2ecf20Sopenharmony_ci return ret; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci switch (mode) { 638c2ecf20Sopenharmony_ci case PHY_MODE_USB_HOST: 648c2ecf20Sopenharmony_ci ret |= (ULPI_OTG_CTRL_DRVVBUS_EXT 658c2ecf20Sopenharmony_ci | ULPI_OTG_CTRL_ID_PULLUP 668c2ecf20Sopenharmony_ci | ULPI_OTG_CTRL_DP_PULLDOWN 678c2ecf20Sopenharmony_ci | ULPI_OTG_CTRL_DM_PULLDOWN); 688c2ecf20Sopenharmony_ci ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); 698c2ecf20Sopenharmony_ci ret |= ULPI_OTG_CTRL_DRVVBUS; 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci case PHY_MODE_USB_DEVICE: 728c2ecf20Sopenharmony_ci ret &= ~(ULPI_OTG_CTRL_DRVVBUS 738c2ecf20Sopenharmony_ci | ULPI_OTG_CTRL_DP_PULLDOWN 748c2ecf20Sopenharmony_ci | ULPI_OTG_CTRL_DM_PULLDOWN); 758c2ecf20Sopenharmony_ci ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); 768c2ecf20Sopenharmony_ci ret &= ~ULPI_OTG_CTRL_DRVVBUS_EXT; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci default: 798c2ecf20Sopenharmony_ci /* nothing */ 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct phy_ops phy_ops = { 878c2ecf20Sopenharmony_ci .power_on = tusb1210_power_on, 888c2ecf20Sopenharmony_ci .power_off = tusb1210_power_off, 898c2ecf20Sopenharmony_ci .set_mode = tusb1210_set_mode, 908c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int tusb1210_probe(struct ulpi *ulpi) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct tusb1210 *tusb; 968c2ecf20Sopenharmony_ci u8 val, reg; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); 998c2ecf20Sopenharmony_ci if (!tusb) 1008c2ecf20Sopenharmony_ci return -ENOMEM; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", 1038c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 1048c2ecf20Sopenharmony_ci if (IS_ERR(tusb->gpio_reset)) 1058c2ecf20Sopenharmony_ci return PTR_ERR(tusb->gpio_reset); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_reset, 1); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci tusb->gpio_cs = devm_gpiod_get_optional(&ulpi->dev, "cs", 1108c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 1118c2ecf20Sopenharmony_ci if (IS_ERR(tusb->gpio_cs)) 1128c2ecf20Sopenharmony_ci return PTR_ERR(tusb->gpio_cs); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(tusb->gpio_cs, 1); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye 1188c2ecf20Sopenharmony_ci * diagram optimization and DP/DM swap. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* High speed output drive strength configuration */ 1228c2ecf20Sopenharmony_ci device_property_read_u8(&ulpi->dev, "ihstx", &val); 1238c2ecf20Sopenharmony_ci reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* High speed output impedance configuration */ 1268c2ecf20Sopenharmony_ci device_property_read_u8(&ulpi->dev, "zhsdrv", &val); 1278c2ecf20Sopenharmony_ci reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* DP/DM swap control */ 1308c2ecf20Sopenharmony_ci device_property_read_u8(&ulpi->dev, "datapolarity", &val); 1318c2ecf20Sopenharmony_ci reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (reg) { 1348c2ecf20Sopenharmony_ci ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); 1358c2ecf20Sopenharmony_ci tusb->vendor_specific2 = reg; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci tusb->phy = ulpi_phy_create(ulpi, &phy_ops); 1398c2ecf20Sopenharmony_ci if (IS_ERR(tusb->phy)) 1408c2ecf20Sopenharmony_ci return PTR_ERR(tusb->phy); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci tusb->ulpi = ulpi; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci phy_set_drvdata(tusb->phy, tusb); 1458c2ecf20Sopenharmony_ci ulpi_set_drvdata(ulpi, tusb); 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void tusb1210_remove(struct ulpi *ulpi) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ulpi_phy_destroy(ulpi, tusb->phy); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#define TI_VENDOR_ID 0x0451 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct ulpi_device_id tusb1210_ulpi_id[] = { 1598c2ecf20Sopenharmony_ci { TI_VENDOR_ID, 0x1507, }, /* TUSB1210 */ 1608c2ecf20Sopenharmony_ci { TI_VENDOR_ID, 0x1508, }, /* TUSB1211 */ 1618c2ecf20Sopenharmony_ci { }, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct ulpi_driver tusb1210_driver = { 1668c2ecf20Sopenharmony_ci .id_table = tusb1210_ulpi_id, 1678c2ecf20Sopenharmony_ci .probe = tusb1210_probe, 1688c2ecf20Sopenharmony_ci .remove = tusb1210_remove, 1698c2ecf20Sopenharmony_ci .driver = { 1708c2ecf20Sopenharmony_ci .name = "tusb1210", 1718c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1728c2ecf20Sopenharmony_ci }, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cimodule_ulpi_driver(tusb1210_driver); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 1788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); 180