]>
Commit | Line | Data |
---|---|---|
4b0ea24b VYA |
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 | */ | |
8 | #include <clk.h> | |
9 | #include <dm.h> | |
10 | #include <ufs.h> | |
11 | #include <asm/io.h> | |
12 | #include <dm/device_compat.h> | |
13 | #include <linux/bitops.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/err.h> | |
16 | #include <linux/time.h> | |
17 | ||
18 | #include "ufs.h" | |
19 | #include "ufshci-dwc.h" | |
20 | #include "ufshcd-dwc.h" | |
21 | ||
22 | int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba, | |
23 | const struct ufshcd_dme_attr_val *v, int n) | |
24 | { | |
25 | int ret = 0; | |
26 | int attr_node = 0; | |
27 | ||
28 | for (attr_node = 0; attr_node < n; attr_node++) { | |
29 | ret = ufshcd_dme_set_attr(hba, v[attr_node].attr_sel, | |
30 | ATTR_SET_NOR, v[attr_node].mib_val, v[attr_node].peer); | |
31 | if (ret) | |
32 | return ret; | |
33 | } | |
34 | ||
35 | return 0; | |
36 | } | |
37 | ||
38 | /** | |
39 | * ufshcd_dwc_program_clk_div() - program clock divider. | |
40 | * @hba: Private Structure pointer | |
41 | * @divider_val: clock divider value to be programmed | |
42 | * | |
43 | */ | |
44 | static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val) | |
45 | { | |
46 | ufshcd_writel(hba, divider_val, DWC_UFS_REG_HCLKDIV); | |
47 | } | |
48 | ||
49 | /** | |
50 | * ufshcd_dwc_link_is_up() - check if link is up. | |
51 | * @hba: private structure pointer | |
52 | * | |
53 | * Return: 0 on success, non-zero value on failure. | |
54 | */ | |
55 | static int ufshcd_dwc_link_is_up(struct ufs_hba *hba) | |
56 | { | |
57 | int dme_result = 0; | |
58 | ||
59 | ufshcd_dme_get(hba, UIC_ARG_MIB(VS_POWERSTATE), &dme_result); | |
60 | ||
61 | if (dme_result == UFSHCD_LINK_IS_UP) | |
62 | return 0; | |
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 | return ufshcd_dwc_dme_set_attrs(hba, setup_attrs, ARRAY_SIZE(setup_attrs)); | |
103 | } | |
104 | ||
105 | /** | |
106 | * ufshcd_dwc_link_startup_notify() - program clock divider. | |
107 | * @hba: private structure pointer | |
108 | * @status: Callback notify status | |
109 | * | |
110 | * Return: 0 on success, non-zero value on failure. | |
111 | */ | |
112 | int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba, | |
113 | enum ufs_notify_change_status status) | |
114 | { | |
115 | int err = 0; | |
116 | ||
117 | if (status == PRE_CHANGE) { | |
118 | ufshcd_dwc_program_clk_div(hba, DWC_UFS_REG_HCLKDIV_DIV_125); | |
119 | } else { /* POST_CHANGE */ | |
120 | err = ufshcd_dwc_link_is_up(hba); | |
121 | if (err) { | |
122 | dev_err(hba->dev, "Link is not up\n"); | |
123 | return err; | |
124 | } | |
125 | ||
126 | err = ufshcd_dwc_connection_setup(hba); | |
127 | if (err) | |
128 | dev_err(hba->dev, "Connection setup failed (%d)\n", | |
129 | err); | |
130 | } | |
131 | ||
132 | return err; | |
133 | } |