Greg Kroah-Hartman | 5fd54ac | 2017-11-03 11:28:30 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
Stephen Boyd | 7bb7e9b | 2016-12-28 14:56:55 -0800 | [diff] [blame] | 2 | /* |
| 3 | * Copyright (c) 2016 Linaro Ltd. |
Stephen Boyd | 7bb7e9b | 2016-12-28 14:56:55 -0800 | [diff] [blame] | 4 | */ |
| 5 | |
| 6 | #include <linux/device.h> |
| 7 | #include <linux/usb/chipidea.h> |
| 8 | #include <linux/ulpi/interface.h> |
| 9 | |
| 10 | #include "ci.h" |
| 11 | |
| 12 | #define ULPI_WAKEUP BIT(31) |
| 13 | #define ULPI_RUN BIT(30) |
| 14 | #define ULPI_WRITE BIT(29) |
| 15 | #define ULPI_SYNC_STATE BIT(27) |
| 16 | #define ULPI_ADDR(n) ((n) << 16) |
| 17 | #define ULPI_DATA(n) (n) |
| 18 | |
| 19 | static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask) |
| 20 | { |
| 21 | unsigned long usec = 10000; |
| 22 | |
| 23 | while (usec--) { |
| 24 | if (!hw_read(ci, OP_ULPI_VIEWPORT, mask)) |
| 25 | return 0; |
| 26 | |
| 27 | udelay(1); |
| 28 | } |
| 29 | |
| 30 | return -ETIMEDOUT; |
| 31 | } |
| 32 | |
| 33 | static int ci_ulpi_read(struct device *dev, u8 addr) |
| 34 | { |
| 35 | struct ci_hdrc *ci = dev_get_drvdata(dev); |
| 36 | int ret; |
| 37 | |
| 38 | hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP); |
| 39 | ret = ci_ulpi_wait(ci, ULPI_WAKEUP); |
| 40 | if (ret) |
| 41 | return ret; |
| 42 | |
| 43 | hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr)); |
| 44 | ret = ci_ulpi_wait(ci, ULPI_RUN); |
| 45 | if (ret) |
| 46 | return ret; |
| 47 | |
| 48 | return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8; |
| 49 | } |
| 50 | |
| 51 | static int ci_ulpi_write(struct device *dev, u8 addr, u8 val) |
| 52 | { |
| 53 | struct ci_hdrc *ci = dev_get_drvdata(dev); |
| 54 | int ret; |
| 55 | |
| 56 | hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP); |
| 57 | ret = ci_ulpi_wait(ci, ULPI_WAKEUP); |
| 58 | if (ret) |
| 59 | return ret; |
| 60 | |
| 61 | hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, |
| 62 | ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val); |
| 63 | return ci_ulpi_wait(ci, ULPI_RUN); |
| 64 | } |
| 65 | |
| 66 | int ci_ulpi_init(struct ci_hdrc *ci) |
| 67 | { |
| 68 | if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI) |
| 69 | return 0; |
| 70 | |
| 71 | /* |
| 72 | * Set PORTSC correctly so we can read/write ULPI registers for |
| 73 | * identification purposes |
| 74 | */ |
| 75 | hw_phymode_configure(ci); |
| 76 | |
| 77 | ci->ulpi_ops.read = ci_ulpi_read; |
| 78 | ci->ulpi_ops.write = ci_ulpi_write; |
| 79 | ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops); |
| 80 | if (IS_ERR(ci->ulpi)) |
| 81 | dev_err(ci->dev, "failed to register ULPI interface"); |
| 82 | |
| 83 | return PTR_ERR_OR_ZERO(ci->ulpi); |
| 84 | } |
| 85 | |
| 86 | void ci_ulpi_exit(struct ci_hdrc *ci) |
| 87 | { |
| 88 | if (ci->ulpi) { |
| 89 | ulpi_unregister_interface(ci->ulpi); |
| 90 | ci->ulpi = NULL; |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | int ci_ulpi_resume(struct ci_hdrc *ci) |
| 95 | { |
| 96 | int cnt = 100000; |
| 97 | |
Fabio Estevam | a930d8bd | 2018-07-04 10:09:58 -0300 | [diff] [blame] | 98 | if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI) |
| 99 | return 0; |
| 100 | |
Stephen Boyd | 7bb7e9b | 2016-12-28 14:56:55 -0800 | [diff] [blame] | 101 | while (cnt-- > 0) { |
| 102 | if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE)) |
| 103 | return 0; |
| 104 | udelay(1); |
| 105 | } |
| 106 | |
| 107 | return -ETIMEDOUT; |
| 108 | } |