1 | // SPDX-License-Identifier: GPL-2.0-only |
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 | * |
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 | |
21 | static int asd_get_ddb(struct asd_ha_struct *asd_ha) |
22 | { |
23 | int ddb, i; |
24 | |
25 | ddb = FIND_FREE_DDB(asd_ha); |
26 | if (ddb >= asd_ha->hw_prof.max_ddbs) { |
27 | ddb = -ENOMEM; |
28 | goto out; |
29 | } |
30 | SET_DDB(ddb, asd_ha); |
31 | |
32 | for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4) |
33 | asd_ddbsite_write_dword(asd_ha, ddb_site_no: ddb, offs: i, val: 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 | |
57 | static void asd_free_ddb(struct asd_ha_struct *asd_ha, int ddb) |
58 | { |
59 | if (!ddb || ddb >= 0xFFFF) |
60 | return; |
61 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, DDB_TYPE, DDB_TYPE_UNUSED); |
62 | CLEAR_DDB(ddb, asd_ha); |
63 | } |
64 | |
65 | static void asd_set_ddb_type(struct domain_device *dev) |
66 | { |
67 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
68 | int ddb = (int) (unsigned long) dev->lldd_dev; |
69 | |
70 | if (dev->dev_type == SAS_SATA_PM_PORT) |
71 | asd_ddbsite_write_byte(asd_ha,ddb_site_no: ddb, DDB_TYPE, DDB_TYPE_PM_PORT); |
72 | else if (dev->tproto) |
73 | asd_ddbsite_write_byte(asd_ha,ddb_site_no: ddb, DDB_TYPE, DDB_TYPE_TARGET); |
74 | else |
75 | asd_ddbsite_write_byte(asd_ha,ddb_site_no: 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_site_no: ddb, offs: i, val: 0xFFFF); |
89 | |
90 | asd_ddbsite_write_word(asd_ha, ddb_site_no: (int) (unsigned long) dev->lldd_dev, |
91 | SISTER_DDB, val: ddb); |
92 | return 0; |
93 | } |
94 | |
95 | void asd_set_dmamode(struct domain_device *dev) |
96 | { |
97 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
98 | struct ata_device *ata_dev = sas_to_ata_dev(dev); |
99 | int ddb = (int) (unsigned long) dev->lldd_dev; |
100 | u32 qdepth = 0; |
101 | |
102 | if (dev->dev_type == SAS_SATA_DEV || dev->dev_type == SAS_SATA_PM_PORT) { |
103 | if (ata_id_has_ncq(ata_dev->id)) |
104 | qdepth = ata_id_queue_depth(ata_dev->id); |
105 | asd_ddbsite_write_dword(asd_ha, ddb_site_no: ddb, SATA_TAG_ALLOC_MASK, |
106 | val: (1ULL<<qdepth)-1); |
107 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, NUM_SATA_TAGS, val: qdepth); |
108 | } |
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(lock: 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_site_no: ddb, ATA_CMD_SCBPTR, val: 0xFFFF); |
126 | if (dev->dev_type == SAS_SATA_DEV || dev->dev_type == SAS_SATA_PM || |
127 | dev->dev_type == SAS_SATA_PM_PORT) { |
128 | struct dev_to_host_fis *fis = (struct dev_to_host_fis *) |
129 | dev->frame_rcvd; |
130 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, SATA_STATUS, val: fis->status); |
131 | } |
132 | asd_ddbsite_write_word(asd_ha, ddb_site_no: ddb, NCQ_DATA_SCB_PTR, val: 0xFFFF); |
133 | |
134 | return 0; |
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_site_no: ddb, offs: 0, DDB_TP_CONN_TYPE); |
150 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, offs: 1, val: 0); |
151 | asd_ddbsite_write_word(asd_ha, ddb_site_no: ddb, INIT_CONN_TAG, val: 0xFFFF); |
152 | for (i = 0; i < SAS_ADDR_SIZE; i++) |
153 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, DEST_SAS_ADDR+i, |
154 | val: dev->sas_addr[i]); |
155 | asd_ddbsite_write_word(asd_ha, ddb_site_no: ddb, SEND_QUEUE_HEAD, val: 0xFFFF); |
156 | asd_set_ddb_type(dev); |
157 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, CONN_MASK, val: dev->port->phy_mask); |
158 | if (dev->port->oob_mode != SATA_OOB_MODE) { |
159 | flags |= OPEN_REQUIRED; |
160 | if ((dev->dev_type == SAS_SATA_DEV) || |
161 | (dev->tproto & SAS_PROTOCOL_STP)) { |
162 | struct smp_rps_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; |
173 | if (!dev->parent && dev_is_expander(type: dev->dev_type)) |
174 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, MAX_CCONN, |
175 | val: 4); |
176 | else |
177 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, MAX_CCONN, |
178 | val: dev->pathways); |
179 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, NUM_CTX, val: 1); |
180 | } |
181 | } |
182 | if (dev->dev_type == SAS_SATA_PM) |
183 | flags |= SATA_MULTIPORT; |
184 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, DDB_TARG_FLAGS, val: flags); |
185 | |
186 | flags = 0; |
187 | if (dev->tproto & SAS_PROTOCOL_STP) |
188 | flags |= STP_CL_POL_NO_TX; |
189 | asd_ddbsite_write_byte(asd_ha, ddb_site_no: ddb, DDB_TARG_FLAGS2, val: flags); |
190 | |
191 | asd_ddbsite_write_word(asd_ha, ddb_site_no: ddb, EXEC_QUEUE_TAIL, val: 0xFFFF); |
192 | asd_ddbsite_write_word(asd_ha, ddb_site_no: ddb, SEND_QUEUE_TAIL, val: 0xFFFF); |
193 | asd_ddbsite_write_word(asd_ha, ddb_site_no: ddb, SISTER_DDB, val: 0xFFFF); |
194 | |
195 | if (dev->dev_type == SAS_SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { |
196 | i = asd_init_sata(dev); |
197 | if (i < 0) { |
198 | asd_free_ddb(asd_ha, ddb); |
199 | return i; |
200 | } |
201 | } |
202 | |
203 | if (dev->dev_type == SAS_END_DEVICE) { |
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_site_no: 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_site_no: ddb, ITNL_TIMEOUT, |
211 | val: (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_site_no: ddb, offs: i, val: 0xFFFF); |
227 | |
228 | asd_ddbsite_write_word(asd_ha, ddb_site_no: (int) (unsigned long) dev->lldd_dev, |
229 | SISTER_DDB, val: 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_site_no: ddb, PM_PORT_FLAGS, val: flags); |
258 | asd_ddbsite_write_word(asd_ha, ddb_site_no: ddb, SISTER_DDB, val: 0xFFFF); |
259 | asd_ddbsite_write_word(asd_ha, ddb_site_no: ddb, ATA_CMD_SCBPTR, val: 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_site_no: ddb, PARENT_DDB, val: parent_ddb); |
264 | pmtable_ddb = asd_ddbsite_read_word(asd_ha, ddb_site_no: parent_ddb, SISTER_DDB); |
265 | asd_ddbsite_write_word(asd_ha, ddb_site_no: pmtable_ddb, offs: dev->sata_dev.port_no,val: ddb); |
266 | |
267 | if (asd_ddbsite_read_byte(asd_ha, ddb_site_no: 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(asd_ha: dev->port->ha->lldd_ha, |
299 | ddb: (int) (unsigned long) dev->lldd_dev); |
300 | out: |
301 | return res; |
302 | } |
303 | |
304 | int asd_dev_found(struct domain_device *dev) |
305 | { |
306 | unsigned long flags; |
307 | int res = 0; |
308 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
309 | |
310 | spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags); |
311 | switch (dev->dev_type) { |
312 | case SAS_SATA_PM: |
313 | res = asd_init_sata_pm_ddb(dev); |
314 | break; |
315 | case SAS_SATA_PM_PORT: |
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 | } |
324 | spin_unlock_irqrestore(lock: &asd_ha->hw_prof.ddb_lock, flags); |
325 | |
326 | return res; |
327 | } |
328 | |
329 | void asd_dev_gone(struct domain_device *dev) |
330 | { |
331 | int ddb, sister_ddb; |
332 | unsigned long flags; |
333 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
334 | |
335 | spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags); |
336 | ddb = (int) (unsigned long) dev->lldd_dev; |
337 | sister_ddb = asd_ddbsite_read_word(asd_ha, ddb_site_no: ddb, SISTER_DDB); |
338 | |
339 | if (sister_ddb != 0xFFFF) |
340 | asd_free_ddb(asd_ha, ddb: sister_ddb); |
341 | asd_free_ddb(asd_ha, ddb); |
342 | dev->lldd_dev = NULL; |
343 | spin_unlock_irqrestore(lock: &asd_ha->hw_prof.ddb_lock, flags); |
344 | } |
345 | |