Commit | Line | Data |
---|---|---|
4b9ffb5a JP |
1 | /* |
2 | * UFS Host driver for Synopsys Designware Core | |
3 | * | |
4 | * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com) | |
5 | * | |
6 | * Authors: Joao Pinto <jpinto@synopsys.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include "ufshcd.h" | |
14 | #include "unipro.h" | |
15 | ||
16 | #include "ufshcd-dwc.h" | |
17 | #include "ufshci-dwc.h" | |
18 | ||
19 | int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba, | |
20 | const struct ufshcd_dme_attr_val *v, int n) | |
21 | { | |
22 | int ret = 0; | |
23 | int attr_node = 0; | |
24 | ||
25 | for (attr_node = 0; attr_node < n; attr_node++) { | |
26 | ret = ufshcd_dme_set_attr(hba, v[attr_node].attr_sel, | |
27 | ATTR_SET_NOR, v[attr_node].mib_val, v[attr_node].peer); | |
28 | ||
29 | if (ret) | |
30 | return ret; | |
31 | } | |
32 | ||
33 | return 0; | |
34 | } | |
35 | EXPORT_SYMBOL(ufshcd_dwc_dme_set_attrs); | |
36 | ||
37 | /** | |
38 | * ufshcd_dwc_program_clk_div() | |
39 | * This function programs the clk divider value. This value is needed to | |
40 | * provide 1 microsecond tick to unipro layer. | |
41 | * @hba: Private Structure pointer | |
42 | * @divider_val: clock divider value to be programmed | |
43 | * | |
44 | */ | |
45 | static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val) | |
46 | { | |
47 | ufshcd_writel(hba, divider_val, DWC_UFS_REG_HCLKDIV); | |
48 | } | |
49 | ||
50 | /** | |
51 | * ufshcd_dwc_link_is_up() | |
52 | * Check if link is up | |
53 | * @hba: private structure poitner | |
54 | * | |
55 | * Returns 0 on success, non-zero value on failure | |
56 | */ | |
57 | static int ufshcd_dwc_link_is_up(struct ufs_hba *hba) | |
58 | { | |
59 | int dme_result = 0; | |
60 | ||
61 | ufshcd_dme_get(hba, UIC_ARG_MIB(VS_POWERSTATE), &dme_result); | |
62 | ||
63 | if (dme_result == UFSHCD_LINK_IS_UP) { | |
64 | ufshcd_set_link_active(hba); | |
65 | return 0; | |
66 | } | |
67 | ||
68 | return 1; | |
69 | } | |
70 | ||
71 | /** | |
72 | * ufshcd_dwc_connection_setup() | |
73 | * This function configures both the local side (host) and the peer side | |
74 | * (device) unipro attributes to establish the connection to application/ | |
75 | * cport. | |
76 | * This function is not required if the hardware is properly configured to | |
77 | * have this connection setup on reset. But invoking this function does no | |
78 | * harm and should be fine even working with any ufs device. | |
79 | * | |
80 | * @hba: pointer to drivers private data | |
81 | * | |
82 | * Returns 0 on success non-zero value on failure | |
83 | */ | |
84 | static int ufshcd_dwc_connection_setup(struct ufs_hba *hba) | |
85 | { | |
86 | const struct ufshcd_dme_attr_val setup_attrs[] = { | |
87 | { UIC_ARG_MIB(T_CONNECTIONSTATE), 0, DME_LOCAL }, | |
88 | { UIC_ARG_MIB(N_DEVICEID), 0, DME_LOCAL }, | |
89 | { UIC_ARG_MIB(N_DEVICEID_VALID), 0, DME_LOCAL }, | |
90 | { UIC_ARG_MIB(T_PEERDEVICEID), 1, DME_LOCAL }, | |
91 | { UIC_ARG_MIB(T_PEERCPORTID), 0, DME_LOCAL }, | |
92 | { UIC_ARG_MIB(T_TRAFFICCLASS), 0, DME_LOCAL }, | |
93 | { UIC_ARG_MIB(T_CPORTFLAGS), 0x6, DME_LOCAL }, | |
94 | { UIC_ARG_MIB(T_CPORTMODE), 1, DME_LOCAL }, | |
95 | { UIC_ARG_MIB(T_CONNECTIONSTATE), 1, DME_LOCAL }, | |
96 | { UIC_ARG_MIB(T_CONNECTIONSTATE), 0, DME_PEER }, | |
97 | { UIC_ARG_MIB(N_DEVICEID), 1, DME_PEER }, | |
98 | { UIC_ARG_MIB(N_DEVICEID_VALID), 1, DME_PEER }, | |
99 | { UIC_ARG_MIB(T_PEERDEVICEID), 1, DME_PEER }, | |
100 | { UIC_ARG_MIB(T_PEERCPORTID), 0, DME_PEER }, | |
101 | { UIC_ARG_MIB(T_TRAFFICCLASS), 0, DME_PEER }, | |
102 | { UIC_ARG_MIB(T_CPORTFLAGS), 0x6, DME_PEER }, | |
103 | { UIC_ARG_MIB(T_CPORTMODE), 1, DME_PEER }, | |
104 | { UIC_ARG_MIB(T_CONNECTIONSTATE), 1, DME_PEER } | |
105 | }; | |
106 | ||
107 | return ufshcd_dwc_dme_set_attrs(hba, setup_attrs, ARRAY_SIZE(setup_attrs)); | |
108 | } | |
109 | ||
110 | /** | |
111 | * ufshcd_dwc_link_startup_notify() | |
112 | * UFS Host DWC specific link startup sequence | |
113 | * @hba: private structure poitner | |
114 | * @status: Callback notify status | |
115 | * | |
116 | * Returns 0 on success, non-zero value on failure | |
117 | */ | |
118 | int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba, | |
119 | enum ufs_notify_change_status status) | |
120 | { | |
121 | int err = 0; | |
122 | ||
123 | if (status == PRE_CHANGE) { | |
124 | ufshcd_dwc_program_clk_div(hba, DWC_UFS_REG_HCLKDIV_DIV_125); | |
125 | ||
126 | if (hba->vops->phy_initialization) { | |
127 | err = hba->vops->phy_initialization(hba); | |
128 | if (err) { | |
129 | dev_err(hba->dev, "Phy setup failed (%d)\n", | |
130 | err); | |
131 | goto out; | |
132 | } | |
133 | } | |
134 | } else { /* POST_CHANGE */ | |
135 | err = ufshcd_dwc_link_is_up(hba); | |
136 | if (err) { | |
137 | dev_err(hba->dev, "Link is not up\n"); | |
138 | goto out; | |
139 | } | |
140 | ||
141 | err = ufshcd_dwc_connection_setup(hba); | |
142 | if (err) | |
143 | dev_err(hba->dev, "Connection setup failed (%d)\n", | |
144 | err); | |
145 | } | |
146 | ||
147 | out: | |
148 | return err; | |
149 | } | |
150 | EXPORT_SYMBOL(ufshcd_dwc_link_startup_notify); | |
87ee3ab9 JP |
151 | |
152 | MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>"); | |
153 | MODULE_DESCRIPTION("UFS Host driver for Synopsys Designware Core"); | |
154 | MODULE_LICENSE("Dual BSD/GPL"); |