Commit | Line | Data |
---|---|---|
65c85c83 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
2908d778 JB |
2 | /* |
3 | * Aic94xx SAS/SATA DDB management | |
4 | * | |
5 | * Copyright (C) 2005 Adaptec, Inc. All rights reserved. | |
6 | * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> | |
7 | * | |
2908d778 JB |
8 | * $Id: //depot/aic94xx/aic94xx_dev.c#21 $ |
9 | */ | |
10 | ||
11 | #include "aic94xx.h" | |
12 | #include "aic94xx_hwi.h" | |
13 | #include "aic94xx_reg.h" | |
14 | #include "aic94xx_sas.h" | |
15 | ||
16 | #define FIND_FREE_DDB(_ha) find_first_zero_bit((_ha)->hw_prof.ddb_bitmap, \ | |
17 | (_ha)->hw_prof.max_ddbs) | |
18 | #define SET_DDB(_ddb, _ha) set_bit(_ddb, (_ha)->hw_prof.ddb_bitmap) | |
19 | #define CLEAR_DDB(_ddb, _ha) clear_bit(_ddb, (_ha)->hw_prof.ddb_bitmap) | |
20 | ||
81e56ded | 21 | static int asd_get_ddb(struct asd_ha_struct *asd_ha) |
2908d778 | 22 | { |
2908d778 JB |
23 | int ddb, i; |
24 | ||
2908d778 JB |
25 | ddb = FIND_FREE_DDB(asd_ha); |
26 | if (ddb >= asd_ha->hw_prof.max_ddbs) { | |
27 | ddb = -ENOMEM; | |
2908d778 JB |
28 | goto out; |
29 | } | |
30 | SET_DDB(ddb, asd_ha); | |
2908d778 JB |
31 | |
32 | for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4) | |
33 | asd_ddbsite_write_dword(asd_ha, ddb, i, 0); | |
34 | out: | |
35 | return ddb; | |
36 | } | |
37 | ||
38 | #define INIT_CONN_TAG offsetof(struct asd_ddb_ssp_smp_target_port, init_conn_tag) | |
39 | #define DEST_SAS_ADDR offsetof(struct asd_ddb_ssp_smp_target_port, dest_sas_addr) | |
40 | #define SEND_QUEUE_HEAD offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_head) | |
41 | #define DDB_TYPE offsetof(struct asd_ddb_ssp_smp_target_port, ddb_type) | |
42 | #define CONN_MASK offsetof(struct asd_ddb_ssp_smp_target_port, conn_mask) | |
43 | #define DDB_TARG_FLAGS offsetof(struct asd_ddb_ssp_smp_target_port, flags) | |
44 | #define DDB_TARG_FLAGS2 offsetof(struct asd_ddb_stp_sata_target_port, flags2) | |
45 | #define EXEC_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, exec_queue_tail) | |
46 | #define SEND_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_tail) | |
47 | #define SISTER_DDB offsetof(struct asd_ddb_ssp_smp_target_port, sister_ddb) | |
48 | #define MAX_CCONN offsetof(struct asd_ddb_ssp_smp_target_port, max_concurrent_conn) | |
49 | #define NUM_CTX offsetof(struct asd_ddb_ssp_smp_target_port, num_contexts) | |
50 | #define ATA_CMD_SCBPTR offsetof(struct asd_ddb_stp_sata_target_port, ata_cmd_scbptr) | |
51 | #define SATA_TAG_ALLOC_MASK offsetof(struct asd_ddb_stp_sata_target_port, sata_tag_alloc_mask) | |
52 | #define NUM_SATA_TAGS offsetof(struct asd_ddb_stp_sata_target_port, num_sata_tags) | |
53 | #define SATA_STATUS offsetof(struct asd_ddb_stp_sata_target_port, sata_status) | |
54 | #define NCQ_DATA_SCB_PTR offsetof(struct asd_ddb_stp_sata_target_port, ncq_data_scb_ptr) | |
55 | #define ITNL_TIMEOUT offsetof(struct asd_ddb_ssp_smp_target_port, itnl_timeout) | |
56 | ||
81e56ded | 57 | static void asd_free_ddb(struct asd_ha_struct *asd_ha, int ddb) |
2908d778 | 58 | { |
2908d778 JB |
59 | if (!ddb || ddb >= 0xFFFF) |
60 | return; | |
61 | asd_ddbsite_write_byte(asd_ha, ddb, DDB_TYPE, DDB_TYPE_UNUSED); | |
2908d778 | 62 | CLEAR_DDB(ddb, asd_ha); |
2908d778 JB |
63 | } |
64 | ||
81e56ded | 65 | static void asd_set_ddb_type(struct domain_device *dev) |
2908d778 JB |
66 | { |
67 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | |
68 | int ddb = (int) (unsigned long) dev->lldd_dev; | |
69 | ||
aa9f8328 | 70 | if (dev->dev_type == SAS_SATA_PM_PORT) |
2908d778 JB |
71 | asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_PM_PORT); |
72 | else if (dev->tproto) | |
73 | asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_TARGET); | |
74 | else | |
75 | asd_ddbsite_write_byte(asd_ha,ddb,DDB_TYPE,DDB_TYPE_INITIATOR); | |
76 | } | |
77 | ||
78 | static int asd_init_sata_tag_ddb(struct domain_device *dev) | |
79 | { | |
80 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | |
81 | int ddb, i; | |
82 | ||
83 | ddb = asd_get_ddb(asd_ha); | |
84 | if (ddb < 0) | |
85 | return ddb; | |
86 | ||
87 | for (i = 0; i < sizeof(struct asd_ddb_sata_tag); i += 2) | |
88 | asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF); | |
89 | ||
90 | asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev, | |
91 | SISTER_DDB, ddb); | |
92 | return 0; | |
93 | } | |
94 | ||
b91bb296 | 95 | void asd_set_dmamode(struct domain_device *dev) |
2908d778 JB |
96 | { |
97 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | |
b91bb296 | 98 | struct ata_device *ata_dev = sas_to_ata_dev(dev); |
2908d778 JB |
99 | int ddb = (int) (unsigned long) dev->lldd_dev; |
100 | u32 qdepth = 0; | |
2908d778 | 101 | |
aa9f8328 | 102 | if (dev->dev_type == SAS_SATA_DEV || dev->dev_type == SAS_SATA_PM_PORT) { |
b91bb296 DW |
103 | if (ata_id_has_ncq(ata_dev->id)) |
104 | qdepth = ata_id_queue_depth(ata_dev->id); | |
2908d778 | 105 | asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK, |
797f49de | 106 | (1ULL<<qdepth)-1); |
2908d778 JB |
107 | asd_ddbsite_write_byte(asd_ha, ddb, NUM_SATA_TAGS, qdepth); |
108 | } | |
b91bb296 DW |
109 | |
110 | if (qdepth > 0) | |
111 | if (asd_init_sata_tag_ddb(dev) != 0) { | |
112 | unsigned long flags; | |
113 | ||
114 | spin_lock_irqsave(dev->sata_dev.ap->lock, flags); | |
115 | ata_dev->flags |= ATA_DFLAG_NCQ_OFF; | |
116 | spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); | |
117 | } | |
118 | } | |
119 | ||
120 | static int asd_init_sata(struct domain_device *dev) | |
121 | { | |
122 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | |
123 | int ddb = (int) (unsigned long) dev->lldd_dev; | |
124 | ||
125 | asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF); | |
aa9f8328 JB |
126 | if (dev->dev_type == SAS_SATA_DEV || dev->dev_type == SAS_SATA_PM || |
127 | dev->dev_type == SAS_SATA_PM_PORT) { | |
2908d778 JB |
128 | struct dev_to_host_fis *fis = (struct dev_to_host_fis *) |
129 | dev->frame_rcvd; | |
130 | asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status); | |
131 | } | |
132 | asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF); | |
b91bb296 DW |
133 | |
134 | return 0; | |
2908d778 JB |
135 | } |
136 | ||
137 | static int asd_init_target_ddb(struct domain_device *dev) | |
138 | { | |
139 | int ddb, i; | |
140 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | |
141 | u8 flags = 0; | |
142 | ||
143 | ddb = asd_get_ddb(asd_ha); | |
144 | if (ddb < 0) | |
145 | return ddb; | |
146 | ||
147 | dev->lldd_dev = (void *) (unsigned long) ddb; | |
148 | ||
149 | asd_ddbsite_write_byte(asd_ha, ddb, 0, DDB_TP_CONN_TYPE); | |
150 | asd_ddbsite_write_byte(asd_ha, ddb, 1, 0); | |
151 | asd_ddbsite_write_word(asd_ha, ddb, INIT_CONN_TAG, 0xFFFF); | |
152 | for (i = 0; i < SAS_ADDR_SIZE; i++) | |
153 | asd_ddbsite_write_byte(asd_ha, ddb, DEST_SAS_ADDR+i, | |
154 | dev->sas_addr[i]); | |
155 | asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_HEAD, 0xFFFF); | |
156 | asd_set_ddb_type(dev); | |
157 | asd_ddbsite_write_byte(asd_ha, ddb, CONN_MASK, dev->port->phy_mask); | |
158 | if (dev->port->oob_mode != SATA_OOB_MODE) { | |
159 | flags |= OPEN_REQUIRED; | |
aa9f8328 | 160 | if ((dev->dev_type == SAS_SATA_DEV) || |
5929faf3 | 161 | (dev->tproto & SAS_PROTOCOL_STP)) { |
2908d778 JB |
162 | struct smp_resp *rps_resp = &dev->sata_dev.rps_resp; |
163 | if (rps_resp->frame_type == SMP_RESPONSE && | |
164 | rps_resp->function == SMP_REPORT_PHY_SATA && | |
165 | rps_resp->result == SMP_RESP_FUNC_ACC) { | |
166 | if (rps_resp->rps.affil_valid) | |
167 | flags |= STP_AFFIL_POL; | |
168 | if (rps_resp->rps.affil_supp) | |
169 | flags |= SUPPORTS_AFFIL; | |
170 | } | |
171 | } else { | |
172 | flags |= CONCURRENT_CONN_SUPP; | |
924a3541 | 173 | if (!dev->parent && dev_is_expander(dev->dev_type)) |
2908d778 JB |
174 | asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN, |
175 | 4); | |
176 | else | |
177 | asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN, | |
178 | dev->pathways); | |
179 | asd_ddbsite_write_byte(asd_ha, ddb, NUM_CTX, 1); | |
180 | } | |
181 | } | |
aa9f8328 | 182 | if (dev->dev_type == SAS_SATA_PM) |
2908d778 JB |
183 | flags |= SATA_MULTIPORT; |
184 | asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS, flags); | |
185 | ||
186 | flags = 0; | |
5929faf3 | 187 | if (dev->tproto & SAS_PROTOCOL_STP) |
2908d778 JB |
188 | flags |= STP_CL_POL_NO_TX; |
189 | asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS2, flags); | |
190 | ||
191 | asd_ddbsite_write_word(asd_ha, ddb, EXEC_QUEUE_TAIL, 0xFFFF); | |
192 | asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_TAIL, 0xFFFF); | |
193 | asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF); | |
194 | ||
aa9f8328 | 195 | if (dev->dev_type == SAS_SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { |
2908d778 JB |
196 | i = asd_init_sata(dev); |
197 | if (i < 0) { | |
198 | asd_free_ddb(asd_ha, ddb); | |
199 | return i; | |
200 | } | |
201 | } | |
202 | ||
aa9f8328 | 203 | if (dev->dev_type == SAS_END_DEVICE) { |
2908d778 JB |
204 | struct sas_end_device *rdev = rphy_to_end_device(dev->rphy); |
205 | if (rdev->I_T_nexus_loss_timeout > 0) | |
206 | asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT, | |
207 | min(rdev->I_T_nexus_loss_timeout, | |
208 | (u16)ITNL_TIMEOUT_CONST)); | |
209 | else | |
210 | asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT, | |
211 | (u16)ITNL_TIMEOUT_CONST); | |
212 | } | |
213 | return 0; | |
214 | } | |
215 | ||
216 | static int asd_init_sata_pm_table_ddb(struct domain_device *dev) | |
217 | { | |
218 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | |
219 | int ddb, i; | |
220 | ||
221 | ddb = asd_get_ddb(asd_ha); | |
222 | if (ddb < 0) | |
223 | return ddb; | |
224 | ||
225 | for (i = 0; i < 32; i += 2) | |
226 | asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF); | |
227 | ||
228 | asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev, | |
229 | SISTER_DDB, ddb); | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | #define PM_PORT_FLAGS offsetof(struct asd_ddb_sata_pm_port, pm_port_flags) | |
235 | #define PARENT_DDB offsetof(struct asd_ddb_sata_pm_port, parent_ddb) | |
236 | ||
237 | /** | |
238 | * asd_init_sata_pm_port_ddb -- SATA Port Multiplier Port | |
239 | * dev: pointer to domain device | |
240 | * | |
241 | * For SATA Port Multiplier Ports we need to allocate one SATA Port | |
242 | * Multiplier Port DDB and depending on whether the target on it | |
243 | * supports SATA II NCQ, one SATA Tag DDB. | |
244 | */ | |
245 | static int asd_init_sata_pm_port_ddb(struct domain_device *dev) | |
246 | { | |
247 | int ddb, i, parent_ddb, pmtable_ddb; | |
248 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | |
249 | u8 flags; | |
250 | ||
251 | ddb = asd_get_ddb(asd_ha); | |
252 | if (ddb < 0) | |
253 | return ddb; | |
254 | ||
255 | asd_set_ddb_type(dev); | |
256 | flags = (dev->sata_dev.port_no << 4) | PM_PORT_SET; | |
257 | asd_ddbsite_write_byte(asd_ha, ddb, PM_PORT_FLAGS, flags); | |
258 | asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF); | |
259 | asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF); | |
260 | asd_init_sata(dev); | |
261 | ||
262 | parent_ddb = (int) (unsigned long) dev->parent->lldd_dev; | |
263 | asd_ddbsite_write_word(asd_ha, ddb, PARENT_DDB, parent_ddb); | |
264 | pmtable_ddb = asd_ddbsite_read_word(asd_ha, parent_ddb, SISTER_DDB); | |
265 | asd_ddbsite_write_word(asd_ha, pmtable_ddb, dev->sata_dev.port_no,ddb); | |
266 | ||
267 | if (asd_ddbsite_read_byte(asd_ha, ddb, NUM_SATA_TAGS) > 0) { | |
268 | i = asd_init_sata_tag_ddb(dev); | |
269 | if (i < 0) { | |
270 | asd_free_ddb(asd_ha, ddb); | |
271 | return i; | |
272 | } | |
273 | } | |
274 | return 0; | |
275 | } | |
276 | ||
277 | static int asd_init_initiator_ddb(struct domain_device *dev) | |
278 | { | |
279 | return -ENODEV; | |
280 | } | |
281 | ||
282 | /** | |
283 | * asd_init_sata_pm_ddb -- SATA Port Multiplier | |
284 | * dev: pointer to domain device | |
285 | * | |
286 | * For STP and direct-attached SATA Port Multipliers we need | |
287 | * one target port DDB entry and one SATA PM table DDB entry. | |
288 | */ | |
289 | static int asd_init_sata_pm_ddb(struct domain_device *dev) | |
290 | { | |
291 | int res = 0; | |
292 | ||
293 | res = asd_init_target_ddb(dev); | |
294 | if (res) | |
295 | goto out; | |
296 | res = asd_init_sata_pm_table_ddb(dev); | |
297 | if (res) | |
298 | asd_free_ddb(dev->port->ha->lldd_ha, | |
299 | (int) (unsigned long) dev->lldd_dev); | |
300 | out: | |
301 | return res; | |
302 | } | |
303 | ||
304 | int asd_dev_found(struct domain_device *dev) | |
305 | { | |
57ba07dc | 306 | unsigned long flags; |
2908d778 | 307 | int res = 0; |
57ba07dc | 308 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
2908d778 | 309 | |
57ba07dc | 310 | spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags); |
2908d778 | 311 | switch (dev->dev_type) { |
aa9f8328 | 312 | case SAS_SATA_PM: |
2908d778 JB |
313 | res = asd_init_sata_pm_ddb(dev); |
314 | break; | |
aa9f8328 | 315 | case SAS_SATA_PM_PORT: |
2908d778 JB |
316 | res = asd_init_sata_pm_port_ddb(dev); |
317 | break; | |
318 | default: | |
319 | if (dev->tproto) | |
320 | res = asd_init_target_ddb(dev); | |
321 | else | |
322 | res = asd_init_initiator_ddb(dev); | |
323 | } | |
57ba07dc DW |
324 | spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags); |
325 | ||
2908d778 JB |
326 | return res; |
327 | } | |
328 | ||
329 | void asd_dev_gone(struct domain_device *dev) | |
330 | { | |
331 | int ddb, sister_ddb; | |
57ba07dc | 332 | unsigned long flags; |
2908d778 JB |
333 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
334 | ||
57ba07dc | 335 | spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags); |
2908d778 JB |
336 | ddb = (int) (unsigned long) dev->lldd_dev; |
337 | sister_ddb = asd_ddbsite_read_word(asd_ha, ddb, SISTER_DDB); | |
338 | ||
339 | if (sister_ddb != 0xFFFF) | |
340 | asd_free_ddb(asd_ha, sister_ddb); | |
341 | asd_free_ddb(asd_ha, ddb); | |
342 | dev->lldd_dev = NULL; | |
57ba07dc | 343 | spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags); |
2908d778 | 344 | } |