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