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 v4.11-rc1 379 lines 9.8 kB view raw
1/* 2 * Copyright (c) 2015, Linaro Limited 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 and 6 * only version 2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14#include <linux/clk.h> 15#include <linux/delay.h> 16#include <linux/device.h> 17#include <linux/err.h> 18#include <linux/extcon.h> 19#include <linux/gpio/consumer.h> 20#include <linux/io.h> 21#include <linux/module.h> 22#include <linux/of.h> 23#include <linux/platform_device.h> 24#include <linux/reboot.h> 25#include <linux/regulator/consumer.h> 26#include <linux/reset.h> 27#include <linux/slab.h> 28#include <linux/usb.h> 29#include <linux/usb/ulpi.h> 30 31#define HSPHY_AHBBURST 0x0090 32#define HSPHY_AHBMODE 0x0098 33#define HSPHY_GENCONFIG 0x009c 34#define HSPHY_GENCONFIG_2 0x00a0 35 36#define HSPHY_USBCMD 0x0140 37#define HSPHY_ULPI_VIEWPORT 0x0170 38#define HSPHY_CTRL 0x0240 39 40#define HSPHY_TXFIFO_IDLE_FORCE_DIS BIT(4) 41#define HSPHY_SESS_VLD_CTRL_EN BIT(7) 42#define HSPHY_POR_ASSERT BIT(0) 43#define HSPHY_RETEN BIT(1) 44 45#define HSPHY_SESS_VLD_CTRL BIT(25) 46 47#define ULPI_PWR_CLK_MNG_REG 0x88 48#define ULPI_PWR_OTG_COMP_DISABLE BIT(0) 49 50#define ULPI_MISC_A 0x96 51#define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) 52#define ULPI_MISC_A_VBUSVLDEXT BIT(0) 53 54#define HSPHY_3P3_MIN 3050000 /* uV */ 55#define HSPHY_3P3_MAX 3300000 /* uV */ 56 57#define HSPHY_1P8_MIN 1800000 /* uV */ 58#define HSPHY_1P8_MAX 1800000 /* uV */ 59 60#define HSPHY_VDD_MIN 5 61#define HSPHY_VDD_MAX 7 62 63struct phy_8x16 { 64 struct usb_phy phy; 65 void __iomem *regs; 66 struct clk *core_clk; 67 struct clk *iface_clk; 68 struct regulator_bulk_data regulator[3]; 69 70 struct reset_control *phy_reset; 71 72 struct extcon_dev *vbus_edev; 73 struct notifier_block vbus_notify; 74 75 struct gpio_desc *switch_gpio; 76 struct notifier_block reboot_notify; 77}; 78 79static int phy_8x16_notify_connect(struct usb_phy *phy, 80 enum usb_device_speed speed) 81{ 82 struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy); 83 u32 val; 84 85 val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; 86 usb_phy_io_write(&qphy->phy, val, ULPI_SET(ULPI_MISC_A)); 87 88 val = readl(qphy->regs + HSPHY_USBCMD); 89 val |= HSPHY_SESS_VLD_CTRL; 90 writel(val, qphy->regs + HSPHY_USBCMD); 91 92 return 0; 93} 94 95static int phy_8x16_notify_disconnect(struct usb_phy *phy, 96 enum usb_device_speed speed) 97{ 98 struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy); 99 u32 val; 100 101 val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; 102 usb_phy_io_write(&qphy->phy, val, ULPI_CLR(ULPI_MISC_A)); 103 104 val = readl(qphy->regs + HSPHY_USBCMD); 105 val &= ~HSPHY_SESS_VLD_CTRL; 106 writel(val, qphy->regs + HSPHY_USBCMD); 107 108 return 0; 109} 110 111static int phy_8x16_vbus_on(struct phy_8x16 *qphy) 112{ 113 phy_8x16_notify_connect(&qphy->phy, USB_SPEED_UNKNOWN); 114 115 /* Switch D+/D- lines to Device connector */ 116 gpiod_set_value_cansleep(qphy->switch_gpio, 0); 117 118 return 0; 119} 120 121static int phy_8x16_vbus_off(struct phy_8x16 *qphy) 122{ 123 phy_8x16_notify_disconnect(&qphy->phy, USB_SPEED_UNKNOWN); 124 125 /* Switch D+/D- lines to USB HUB */ 126 gpiod_set_value_cansleep(qphy->switch_gpio, 1); 127 128 return 0; 129} 130 131static int phy_8x16_vbus_notify(struct notifier_block *nb, unsigned long event, 132 void *ptr) 133{ 134 struct phy_8x16 *qphy = container_of(nb, struct phy_8x16, vbus_notify); 135 136 if (event) 137 phy_8x16_vbus_on(qphy); 138 else 139 phy_8x16_vbus_off(qphy); 140 141 return NOTIFY_DONE; 142} 143 144static int phy_8x16_init(struct usb_phy *phy) 145{ 146 struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy); 147 u32 val, init[] = {0x44, 0x6B, 0x24, 0x13}; 148 u32 addr = ULPI_EXT_VENDOR_SPECIFIC; 149 int idx, state; 150 151 for (idx = 0; idx < ARRAY_SIZE(init); idx++) 152 usb_phy_io_write(phy, init[idx], addr + idx); 153 154 reset_control_reset(qphy->phy_reset); 155 156 /* Assert USB HSPHY_POR */ 157 val = readl(qphy->regs + HSPHY_CTRL); 158 val |= HSPHY_POR_ASSERT; 159 writel(val, qphy->regs + HSPHY_CTRL); 160 161 /* 162 * wait for minimum 10 microseconds as suggested in HPG. 163 * Use a slightly larger value since the exact value didn't 164 * work 100% of the time. 165 */ 166 usleep_range(12, 15); 167 168 /* Deassert USB HSPHY_POR */ 169 val = readl(qphy->regs + HSPHY_CTRL); 170 val &= ~HSPHY_POR_ASSERT; 171 writel(val, qphy->regs + HSPHY_CTRL); 172 173 usleep_range(10, 15); 174 175 writel(0x00, qphy->regs + HSPHY_AHBBURST); 176 writel(0x08, qphy->regs + HSPHY_AHBMODE); 177 178 /* workaround for rx buffer collision issue */ 179 val = readl(qphy->regs + HSPHY_GENCONFIG); 180 val &= ~HSPHY_TXFIFO_IDLE_FORCE_DIS; 181 writel(val, qphy->regs + HSPHY_GENCONFIG); 182 183 val = readl(qphy->regs + HSPHY_GENCONFIG_2); 184 val |= HSPHY_SESS_VLD_CTRL_EN; 185 writel(val, qphy->regs + HSPHY_GENCONFIG_2); 186 187 val = ULPI_PWR_OTG_COMP_DISABLE; 188 usb_phy_io_write(phy, val, ULPI_SET(ULPI_PWR_CLK_MNG_REG)); 189 190 state = extcon_get_state(qphy->vbus_edev, EXTCON_USB); 191 if (state) 192 phy_8x16_vbus_on(qphy); 193 else 194 phy_8x16_vbus_off(qphy); 195 196 val = usb_phy_io_read(&qphy->phy, ULPI_FUNC_CTRL); 197 val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; 198 val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; 199 usb_phy_io_write(&qphy->phy, val, ULPI_FUNC_CTRL); 200 201 return 0; 202} 203 204static void phy_8x16_shutdown(struct usb_phy *phy) 205{ 206 u32 val; 207 208 /* Put the controller in non-driving mode */ 209 val = usb_phy_io_read(phy, ULPI_FUNC_CTRL); 210 val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; 211 val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; 212 usb_phy_io_write(phy, val, ULPI_FUNC_CTRL); 213} 214 215static int phy_8x16_read_devicetree(struct phy_8x16 *qphy) 216{ 217 struct device *dev = qphy->phy.dev; 218 int ret; 219 220 qphy->core_clk = devm_clk_get(dev, "core"); 221 if (IS_ERR(qphy->core_clk)) 222 return PTR_ERR(qphy->core_clk); 223 224 qphy->iface_clk = devm_clk_get(dev, "iface"); 225 if (IS_ERR(qphy->iface_clk)) 226 return PTR_ERR(qphy->iface_clk); 227 228 qphy->regulator[0].supply = "v3p3"; 229 qphy->regulator[1].supply = "v1p8"; 230 qphy->regulator[2].supply = "vddcx"; 231 232 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(qphy->regulator), 233 qphy->regulator); 234 if (ret) 235 return ret; 236 237 qphy->phy_reset = devm_reset_control_get(dev, "phy"); 238 if (IS_ERR(qphy->phy_reset)) 239 return PTR_ERR(qphy->phy_reset); 240 241 qphy->switch_gpio = devm_gpiod_get_optional(dev, "switch", 242 GPIOD_OUT_LOW); 243 return PTR_ERR_OR_ZERO(qphy->switch_gpio); 244} 245 246static int phy_8x16_reboot_notify(struct notifier_block *this, 247 unsigned long code, void *unused) 248{ 249 struct phy_8x16 *qphy; 250 251 qphy = container_of(this, struct phy_8x16, reboot_notify); 252 253 /* 254 * Ensure that D+/D- lines are routed to uB connector, so 255 * we could load bootloader/kernel at next reboot_notify 256 */ 257 gpiod_set_value_cansleep(qphy->switch_gpio, 0); 258 return NOTIFY_DONE; 259} 260 261static int phy_8x16_probe(struct platform_device *pdev) 262{ 263 struct phy_8x16 *qphy; 264 struct resource *res; 265 struct usb_phy *phy; 266 int ret; 267 268 qphy = devm_kzalloc(&pdev->dev, sizeof(*qphy), GFP_KERNEL); 269 if (!qphy) 270 return -ENOMEM; 271 272 platform_set_drvdata(pdev, qphy); 273 274 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 275 if (!res) 276 return -EINVAL; 277 278 qphy->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 279 if (!qphy->regs) 280 return -ENOMEM; 281 282 phy = &qphy->phy; 283 phy->dev = &pdev->dev; 284 phy->label = dev_name(&pdev->dev); 285 phy->init = phy_8x16_init; 286 phy->shutdown = phy_8x16_shutdown; 287 phy->notify_connect = phy_8x16_notify_connect; 288 phy->notify_disconnect = phy_8x16_notify_disconnect; 289 phy->io_priv = qphy->regs + HSPHY_ULPI_VIEWPORT; 290 phy->io_ops = &ulpi_viewport_access_ops; 291 phy->type = USB_PHY_TYPE_USB2; 292 293 ret = phy_8x16_read_devicetree(qphy); 294 if (ret < 0) 295 return ret; 296 297 qphy->vbus_edev = extcon_get_edev_by_phandle(phy->dev, 0); 298 if (IS_ERR(qphy->vbus_edev)) 299 return PTR_ERR(qphy->vbus_edev); 300 301 ret = clk_set_rate(qphy->core_clk, INT_MAX); 302 if (ret < 0) 303 dev_dbg(phy->dev, "Can't boost core clock\n"); 304 305 ret = clk_prepare_enable(qphy->core_clk); 306 if (ret < 0) 307 return ret; 308 309 ret = clk_prepare_enable(qphy->iface_clk); 310 if (ret < 0) 311 goto off_core; 312 313 ret = regulator_bulk_enable(ARRAY_SIZE(qphy->regulator), 314 qphy->regulator); 315 if (WARN_ON(ret)) 316 goto off_clks; 317 318 qphy->vbus_notify.notifier_call = phy_8x16_vbus_notify; 319 ret = devm_extcon_register_notifier(&pdev->dev, qphy->vbus_edev, 320 EXTCON_USB, &qphy->vbus_notify); 321 if (ret < 0) 322 goto off_power; 323 324 ret = usb_add_phy_dev(&qphy->phy); 325 if (ret) 326 goto off_power; 327 328 qphy->reboot_notify.notifier_call = phy_8x16_reboot_notify; 329 register_reboot_notifier(&qphy->reboot_notify); 330 331 return 0; 332 333off_power: 334 regulator_bulk_disable(ARRAY_SIZE(qphy->regulator), qphy->regulator); 335off_clks: 336 clk_disable_unprepare(qphy->iface_clk); 337off_core: 338 clk_disable_unprepare(qphy->core_clk); 339 return ret; 340} 341 342static int phy_8x16_remove(struct platform_device *pdev) 343{ 344 struct phy_8x16 *qphy = platform_get_drvdata(pdev); 345 346 unregister_reboot_notifier(&qphy->reboot_notify); 347 348 /* 349 * Ensure that D+/D- lines are routed to uB connector, so 350 * we could load bootloader/kernel at next reboot_notify 351 */ 352 gpiod_set_value_cansleep(qphy->switch_gpio, 0); 353 354 usb_remove_phy(&qphy->phy); 355 356 clk_disable_unprepare(qphy->iface_clk); 357 clk_disable_unprepare(qphy->core_clk); 358 regulator_bulk_disable(ARRAY_SIZE(qphy->regulator), qphy->regulator); 359 return 0; 360} 361 362static const struct of_device_id phy_8x16_dt_match[] = { 363 { .compatible = "qcom,usb-8x16-phy" }, 364 { } 365}; 366MODULE_DEVICE_TABLE(of, phy_8x16_dt_match); 367 368static struct platform_driver phy_8x16_driver = { 369 .probe = phy_8x16_probe, 370 .remove = phy_8x16_remove, 371 .driver = { 372 .name = "phy-qcom-8x16-usb", 373 .of_match_table = phy_8x16_dt_match, 374 }, 375}; 376module_platform_driver(phy_8x16_driver); 377 378MODULE_LICENSE("GPL v2"); 379MODULE_DESCRIPTION("Qualcomm APQ8016/MSM8916 chipsets USB transceiver driver");