Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.14 180 lines 4.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/** 3 * tusb1210.c - TUSB1210 USB ULPI PHY driver 4 * 5 * Copyright (C) 2015 Intel Corporation 6 * 7 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 8 */ 9#include <linux/module.h> 10#include <linux/bitfield.h> 11#include <linux/ulpi/driver.h> 12#include <linux/ulpi/regs.h> 13#include <linux/gpio/consumer.h> 14#include <linux/phy/ulpi_phy.h> 15 16#define TUSB1210_VENDOR_SPECIFIC2 0x80 17#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK GENMASK(3, 0) 18#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK GENMASK(5, 4) 19#define TUSB1210_VENDOR_SPECIFIC2_DP_MASK BIT(6) 20 21struct tusb1210 { 22 struct ulpi *ulpi; 23 struct phy *phy; 24 struct gpio_desc *gpio_reset; 25 struct gpio_desc *gpio_cs; 26 u8 vendor_specific2; 27}; 28 29static int tusb1210_power_on(struct phy *phy) 30{ 31 struct tusb1210 *tusb = phy_get_drvdata(phy); 32 33 gpiod_set_value_cansleep(tusb->gpio_reset, 1); 34 gpiod_set_value_cansleep(tusb->gpio_cs, 1); 35 36 /* Restore the optional eye diagram optimization value */ 37 if (tusb->vendor_specific2) 38 ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, 39 tusb->vendor_specific2); 40 41 return 0; 42} 43 44static int tusb1210_power_off(struct phy *phy) 45{ 46 struct tusb1210 *tusb = phy_get_drvdata(phy); 47 48 gpiod_set_value_cansleep(tusb->gpio_reset, 0); 49 gpiod_set_value_cansleep(tusb->gpio_cs, 0); 50 51 return 0; 52} 53 54static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode) 55{ 56 struct tusb1210 *tusb = phy_get_drvdata(phy); 57 int ret; 58 59 ret = ulpi_read(tusb->ulpi, ULPI_OTG_CTRL); 60 if (ret < 0) 61 return ret; 62 63 switch (mode) { 64 case PHY_MODE_USB_HOST: 65 ret |= (ULPI_OTG_CTRL_DRVVBUS_EXT 66 | ULPI_OTG_CTRL_ID_PULLUP 67 | ULPI_OTG_CTRL_DP_PULLDOWN 68 | ULPI_OTG_CTRL_DM_PULLDOWN); 69 ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); 70 ret |= ULPI_OTG_CTRL_DRVVBUS; 71 break; 72 case PHY_MODE_USB_DEVICE: 73 ret &= ~(ULPI_OTG_CTRL_DRVVBUS 74 | ULPI_OTG_CTRL_DP_PULLDOWN 75 | ULPI_OTG_CTRL_DM_PULLDOWN); 76 ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); 77 ret &= ~ULPI_OTG_CTRL_DRVVBUS_EXT; 78 break; 79 default: 80 /* nothing */ 81 return 0; 82 } 83 84 return ulpi_write(tusb->ulpi, ULPI_OTG_CTRL, ret); 85} 86 87static const struct phy_ops phy_ops = { 88 .power_on = tusb1210_power_on, 89 .power_off = tusb1210_power_off, 90 .set_mode = tusb1210_set_mode, 91 .owner = THIS_MODULE, 92}; 93 94static int tusb1210_probe(struct ulpi *ulpi) 95{ 96 struct tusb1210 *tusb; 97 u8 val, reg; 98 99 tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); 100 if (!tusb) 101 return -ENOMEM; 102 103 tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", 104 GPIOD_OUT_LOW); 105 if (IS_ERR(tusb->gpio_reset)) 106 return PTR_ERR(tusb->gpio_reset); 107 108 gpiod_set_value_cansleep(tusb->gpio_reset, 1); 109 110 tusb->gpio_cs = devm_gpiod_get_optional(&ulpi->dev, "cs", 111 GPIOD_OUT_LOW); 112 if (IS_ERR(tusb->gpio_cs)) 113 return PTR_ERR(tusb->gpio_cs); 114 115 gpiod_set_value_cansleep(tusb->gpio_cs, 1); 116 117 /* 118 * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye 119 * diagram optimization and DP/DM swap. 120 */ 121 122 reg = ulpi_read(ulpi, TUSB1210_VENDOR_SPECIFIC2); 123 124 /* High speed output drive strength configuration */ 125 if (!device_property_read_u8(&ulpi->dev, "ihstx", &val)) 126 u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_IHSTX_MASK); 127 128 /* High speed output impedance configuration */ 129 if (!device_property_read_u8(&ulpi->dev, "zhsdrv", &val)) 130 u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_MASK); 131 132 /* DP/DM swap control */ 133 if (!device_property_read_u8(&ulpi->dev, "datapolarity", &val)) 134 u8p_replace_bits(&reg, val, (u8)TUSB1210_VENDOR_SPECIFIC2_DP_MASK); 135 136 ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); 137 tusb->vendor_specific2 = reg; 138 139 tusb->phy = ulpi_phy_create(ulpi, &phy_ops); 140 if (IS_ERR(tusb->phy)) 141 return PTR_ERR(tusb->phy); 142 143 tusb->ulpi = ulpi; 144 145 phy_set_drvdata(tusb->phy, tusb); 146 ulpi_set_drvdata(ulpi, tusb); 147 return 0; 148} 149 150static void tusb1210_remove(struct ulpi *ulpi) 151{ 152 struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); 153 154 ulpi_phy_destroy(ulpi, tusb->phy); 155} 156 157#define TI_VENDOR_ID 0x0451 158 159static const struct ulpi_device_id tusb1210_ulpi_id[] = { 160 { TI_VENDOR_ID, 0x1507, }, /* TUSB1210 */ 161 { TI_VENDOR_ID, 0x1508, }, /* TUSB1211 */ 162 { }, 163}; 164MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); 165 166static struct ulpi_driver tusb1210_driver = { 167 .id_table = tusb1210_ulpi_id, 168 .probe = tusb1210_probe, 169 .remove = tusb1210_remove, 170 .driver = { 171 .name = "tusb1210", 172 .owner = THIS_MODULE, 173 }, 174}; 175 176module_ulpi_driver(tusb1210_driver); 177 178MODULE_AUTHOR("Intel Corporation"); 179MODULE_LICENSE("GPL v2"); 180MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver");