]>
Commit | Line | Data |
---|---|---|
e8899fad JG |
1 | /* |
2 | * Copyright (c) 2015 Linaro Ltd. | |
3 | * Copyright (c) 2015 Hisilicon Limited. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | */ | |
11 | ||
12 | #include "hisi_sas.h" | |
13 | #define DRV_NAME "hisi_sas" | |
14 | ||
257efd1f JG |
15 | static void hisi_sas_slot_index_clear(struct hisi_hba *hisi_hba, int slot_idx) |
16 | { | |
17 | void *bitmap = hisi_hba->slot_index_tags; | |
18 | ||
19 | clear_bit(slot_idx, bitmap); | |
20 | } | |
21 | ||
22 | static void hisi_sas_slot_index_init(struct hisi_hba *hisi_hba) | |
23 | { | |
24 | int i; | |
25 | ||
26 | for (i = 0; i < hisi_hba->slot_index_count; ++i) | |
27 | hisi_sas_slot_index_clear(hisi_hba, i); | |
28 | } | |
29 | ||
976867e6 JG |
30 | |
31 | static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) | |
32 | { | |
33 | struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; | |
34 | struct asd_sas_phy *sas_phy = &phy->sas_phy; | |
35 | ||
36 | phy->hisi_hba = hisi_hba; | |
37 | phy->port = NULL; | |
38 | init_timer(&phy->timer); | |
39 | sas_phy->enabled = (phy_no < hisi_hba->n_phy) ? 1 : 0; | |
40 | sas_phy->class = SAS; | |
41 | sas_phy->iproto = SAS_PROTOCOL_ALL; | |
42 | sas_phy->tproto = 0; | |
43 | sas_phy->type = PHY_TYPE_PHYSICAL; | |
44 | sas_phy->role = PHY_ROLE_INITIATOR; | |
45 | sas_phy->oob_mode = OOB_NOT_CONNECTED; | |
46 | sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; | |
47 | sas_phy->id = phy_no; | |
48 | sas_phy->sas_addr = &hisi_hba->sas_addr[0]; | |
49 | sas_phy->frame_rcvd = &phy->frame_rcvd[0]; | |
50 | sas_phy->ha = (struct sas_ha_struct *)hisi_hba->shost->hostdata; | |
51 | sas_phy->lldd_phy = phy; | |
52 | } | |
53 | ||
e8899fad JG |
54 | static struct scsi_transport_template *hisi_sas_stt; |
55 | ||
7eb7869f JG |
56 | static struct scsi_host_template hisi_sas_sht = { |
57 | .module = THIS_MODULE, | |
58 | .name = DRV_NAME, | |
59 | .queuecommand = sas_queuecommand, | |
60 | .target_alloc = sas_target_alloc, | |
61 | .slave_configure = sas_slave_configure, | |
62 | .change_queue_depth = sas_change_queue_depth, | |
63 | .bios_param = sas_bios_param, | |
64 | .can_queue = 1, | |
65 | .this_id = -1, | |
66 | .sg_tablesize = SG_ALL, | |
67 | .max_sectors = SCSI_DEFAULT_MAX_SECTORS, | |
68 | .use_clustering = ENABLE_CLUSTERING, | |
69 | .eh_device_reset_handler = sas_eh_device_reset_handler, | |
70 | .eh_bus_reset_handler = sas_eh_bus_reset_handler, | |
71 | .target_destroy = sas_target_destroy, | |
72 | .ioctl = sas_ioctl, | |
73 | }; | |
74 | ||
e8899fad JG |
75 | static struct sas_domain_function_template hisi_sas_transport_ops = { |
76 | }; | |
77 | ||
6be6de18 JG |
78 | static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) |
79 | { | |
80 | int i, s; | |
81 | struct platform_device *pdev = hisi_hba->pdev; | |
82 | struct device *dev = &pdev->dev; | |
83 | ||
976867e6 JG |
84 | for (i = 0; i < hisi_hba->n_phy; i++) { |
85 | hisi_sas_phy_init(hisi_hba, i); | |
86 | hisi_hba->port[i].port_attached = 0; | |
87 | hisi_hba->port[i].id = -1; | |
88 | INIT_LIST_HEAD(&hisi_hba->port[i].list); | |
89 | } | |
90 | ||
af740dbe JG |
91 | for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { |
92 | hisi_hba->devices[i].dev_type = SAS_PHY_UNUSED; | |
93 | hisi_hba->devices[i].device_id = i; | |
94 | hisi_hba->devices[i].dev_status = HISI_SAS_DEV_NORMAL; | |
95 | } | |
96 | ||
6be6de18 | 97 | for (i = 0; i < hisi_hba->queue_count; i++) { |
9101a079 JG |
98 | struct hisi_sas_cq *cq = &hisi_hba->cq[i]; |
99 | ||
100 | /* Completion queue structure */ | |
101 | cq->id = i; | |
102 | cq->hisi_hba = hisi_hba; | |
103 | ||
6be6de18 JG |
104 | /* Delivery queue */ |
105 | s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS; | |
106 | hisi_hba->cmd_hdr[i] = dma_alloc_coherent(dev, s, | |
107 | &hisi_hba->cmd_hdr_dma[i], GFP_KERNEL); | |
108 | if (!hisi_hba->cmd_hdr[i]) | |
109 | goto err_out; | |
110 | memset(hisi_hba->cmd_hdr[i], 0, s); | |
111 | ||
112 | /* Completion queue */ | |
113 | s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS; | |
114 | hisi_hba->complete_hdr[i] = dma_alloc_coherent(dev, s, | |
115 | &hisi_hba->complete_hdr_dma[i], GFP_KERNEL); | |
116 | if (!hisi_hba->complete_hdr[i]) | |
117 | goto err_out; | |
118 | memset(hisi_hba->complete_hdr[i], 0, s); | |
119 | } | |
120 | ||
121 | s = HISI_SAS_STATUS_BUF_SZ; | |
122 | hisi_hba->status_buffer_pool = dma_pool_create("status_buffer", | |
123 | dev, s, 16, 0); | |
124 | if (!hisi_hba->status_buffer_pool) | |
125 | goto err_out; | |
126 | ||
127 | s = HISI_SAS_COMMAND_TABLE_SZ; | |
128 | hisi_hba->command_table_pool = dma_pool_create("command_table", | |
129 | dev, s, 16, 0); | |
130 | if (!hisi_hba->command_table_pool) | |
131 | goto err_out; | |
132 | ||
133 | s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct); | |
134 | hisi_hba->itct = dma_alloc_coherent(dev, s, &hisi_hba->itct_dma, | |
135 | GFP_KERNEL); | |
136 | if (!hisi_hba->itct) | |
137 | goto err_out; | |
138 | ||
139 | memset(hisi_hba->itct, 0, s); | |
140 | ||
141 | hisi_hba->slot_info = devm_kcalloc(dev, HISI_SAS_COMMAND_ENTRIES, | |
142 | sizeof(struct hisi_sas_slot), | |
143 | GFP_KERNEL); | |
144 | if (!hisi_hba->slot_info) | |
145 | goto err_out; | |
146 | ||
147 | s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_iost); | |
148 | hisi_hba->iost = dma_alloc_coherent(dev, s, &hisi_hba->iost_dma, | |
149 | GFP_KERNEL); | |
150 | if (!hisi_hba->iost) | |
151 | goto err_out; | |
152 | ||
153 | memset(hisi_hba->iost, 0, s); | |
154 | ||
155 | s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_breakpoint); | |
156 | hisi_hba->breakpoint = dma_alloc_coherent(dev, s, | |
157 | &hisi_hba->breakpoint_dma, GFP_KERNEL); | |
158 | if (!hisi_hba->breakpoint) | |
159 | goto err_out; | |
160 | ||
161 | memset(hisi_hba->breakpoint, 0, s); | |
162 | ||
257efd1f JG |
163 | hisi_hba->slot_index_count = HISI_SAS_COMMAND_ENTRIES; |
164 | s = hisi_hba->slot_index_count / sizeof(unsigned long); | |
165 | hisi_hba->slot_index_tags = devm_kzalloc(dev, s, GFP_KERNEL); | |
166 | if (!hisi_hba->slot_index_tags) | |
167 | goto err_out; | |
168 | ||
6be6de18 JG |
169 | hisi_hba->sge_page_pool = dma_pool_create("status_sge", dev, |
170 | sizeof(struct hisi_sas_sge_page), 16, 0); | |
171 | if (!hisi_hba->sge_page_pool) | |
172 | goto err_out; | |
173 | ||
174 | s = sizeof(struct hisi_sas_initial_fis) * HISI_SAS_MAX_PHYS; | |
175 | hisi_hba->initial_fis = dma_alloc_coherent(dev, s, | |
176 | &hisi_hba->initial_fis_dma, GFP_KERNEL); | |
177 | if (!hisi_hba->initial_fis) | |
178 | goto err_out; | |
179 | memset(hisi_hba->initial_fis, 0, s); | |
180 | ||
181 | s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_breakpoint) * 2; | |
182 | hisi_hba->sata_breakpoint = dma_alloc_coherent(dev, s, | |
183 | &hisi_hba->sata_breakpoint_dma, GFP_KERNEL); | |
184 | if (!hisi_hba->sata_breakpoint) | |
185 | goto err_out; | |
186 | memset(hisi_hba->sata_breakpoint, 0, s); | |
187 | ||
257efd1f JG |
188 | hisi_sas_slot_index_init(hisi_hba); |
189 | ||
7e9080e1 JG |
190 | hisi_hba->wq = create_singlethread_workqueue(dev_name(dev)); |
191 | if (!hisi_hba->wq) { | |
192 | dev_err(dev, "sas_alloc: failed to create workqueue\n"); | |
193 | goto err_out; | |
194 | } | |
195 | ||
6be6de18 JG |
196 | return 0; |
197 | err_out: | |
198 | return -ENOMEM; | |
199 | } | |
200 | ||
89d53322 JG |
201 | static void hisi_sas_free(struct hisi_hba *hisi_hba) |
202 | { | |
203 | struct device *dev = &hisi_hba->pdev->dev; | |
204 | int i, s; | |
205 | ||
206 | for (i = 0; i < hisi_hba->queue_count; i++) { | |
207 | s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS; | |
208 | if (hisi_hba->cmd_hdr[i]) | |
209 | dma_free_coherent(dev, s, | |
210 | hisi_hba->cmd_hdr[i], | |
211 | hisi_hba->cmd_hdr_dma[i]); | |
212 | ||
213 | s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS; | |
214 | if (hisi_hba->complete_hdr[i]) | |
215 | dma_free_coherent(dev, s, | |
216 | hisi_hba->complete_hdr[i], | |
217 | hisi_hba->complete_hdr_dma[i]); | |
218 | } | |
219 | ||
220 | dma_pool_destroy(hisi_hba->status_buffer_pool); | |
221 | dma_pool_destroy(hisi_hba->command_table_pool); | |
222 | dma_pool_destroy(hisi_hba->sge_page_pool); | |
223 | ||
224 | s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct); | |
225 | if (hisi_hba->itct) | |
226 | dma_free_coherent(dev, s, | |
227 | hisi_hba->itct, hisi_hba->itct_dma); | |
228 | ||
229 | s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_iost); | |
230 | if (hisi_hba->iost) | |
231 | dma_free_coherent(dev, s, | |
232 | hisi_hba->iost, hisi_hba->iost_dma); | |
233 | ||
234 | s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_breakpoint); | |
235 | if (hisi_hba->breakpoint) | |
236 | dma_free_coherent(dev, s, | |
237 | hisi_hba->breakpoint, | |
238 | hisi_hba->breakpoint_dma); | |
239 | ||
240 | ||
241 | s = sizeof(struct hisi_sas_initial_fis) * HISI_SAS_MAX_PHYS; | |
242 | if (hisi_hba->initial_fis) | |
243 | dma_free_coherent(dev, s, | |
244 | hisi_hba->initial_fis, | |
245 | hisi_hba->initial_fis_dma); | |
246 | ||
247 | s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_breakpoint) * 2; | |
248 | if (hisi_hba->sata_breakpoint) | |
249 | dma_free_coherent(dev, s, | |
250 | hisi_hba->sata_breakpoint, | |
251 | hisi_hba->sata_breakpoint_dma); | |
252 | ||
7e9080e1 JG |
253 | if (hisi_hba->wq) |
254 | destroy_workqueue(hisi_hba->wq); | |
89d53322 | 255 | } |
6be6de18 | 256 | |
7eb7869f JG |
257 | static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev, |
258 | const struct hisi_sas_hw *hw) | |
259 | { | |
e26b2f40 | 260 | struct resource *res; |
7eb7869f JG |
261 | struct Scsi_Host *shost; |
262 | struct hisi_hba *hisi_hba; | |
263 | struct device *dev = &pdev->dev; | |
e26b2f40 JG |
264 | struct device_node *np = pdev->dev.of_node; |
265 | struct property *sas_addr_prop; | |
266 | int num; | |
7eb7869f JG |
267 | |
268 | shost = scsi_host_alloc(&hisi_sas_sht, sizeof(*hisi_hba)); | |
269 | if (!shost) | |
270 | goto err_out; | |
271 | hisi_hba = shost_priv(shost); | |
272 | ||
273 | hisi_hba->hw = hw; | |
274 | hisi_hba->pdev = pdev; | |
275 | hisi_hba->shost = shost; | |
276 | SHOST_TO_SAS_HA(shost) = &hisi_hba->sha; | |
277 | ||
e26b2f40 JG |
278 | sas_addr_prop = of_find_property(np, "sas-addr", NULL); |
279 | if (!sas_addr_prop || (sas_addr_prop->length != SAS_ADDR_SIZE)) | |
280 | goto err_out; | |
281 | memcpy(hisi_hba->sas_addr, sas_addr_prop->value, SAS_ADDR_SIZE); | |
282 | ||
283 | if (of_property_read_u32(np, "ctrl-reset-reg", | |
284 | &hisi_hba->ctrl_reset_reg)) | |
285 | goto err_out; | |
286 | ||
287 | if (of_property_read_u32(np, "ctrl-reset-sts-reg", | |
288 | &hisi_hba->ctrl_reset_sts_reg)) | |
289 | goto err_out; | |
290 | ||
291 | if (of_property_read_u32(np, "ctrl-clock-ena-reg", | |
292 | &hisi_hba->ctrl_clock_ena_reg)) | |
293 | goto err_out; | |
294 | ||
295 | if (of_property_read_u32(np, "phy-count", &hisi_hba->n_phy)) | |
296 | goto err_out; | |
297 | ||
298 | if (of_property_read_u32(np, "queue-count", &hisi_hba->queue_count)) | |
299 | goto err_out; | |
300 | ||
301 | num = of_irq_count(np); | |
302 | hisi_hba->int_names = devm_kcalloc(dev, num, | |
303 | HISI_SAS_NAME_LEN, | |
304 | GFP_KERNEL); | |
305 | if (!hisi_hba->int_names) | |
306 | goto err_out; | |
307 | ||
308 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
309 | hisi_hba->regs = devm_ioremap_resource(dev, res); | |
310 | if (IS_ERR(hisi_hba->regs)) | |
311 | goto err_out; | |
312 | ||
313 | hisi_hba->ctrl = syscon_regmap_lookup_by_phandle( | |
314 | np, "hisilicon,sas-syscon"); | |
315 | if (IS_ERR(hisi_hba->ctrl)) | |
316 | goto err_out; | |
317 | ||
89d53322 JG |
318 | if (hisi_sas_alloc(hisi_hba, shost)) { |
319 | hisi_sas_free(hisi_hba); | |
6be6de18 | 320 | goto err_out; |
89d53322 | 321 | } |
6be6de18 | 322 | |
7eb7869f JG |
323 | return shost; |
324 | err_out: | |
325 | dev_err(dev, "shost alloc failed\n"); | |
326 | return NULL; | |
327 | } | |
328 | ||
5d74242e JG |
329 | static void hisi_sas_init_add(struct hisi_hba *hisi_hba) |
330 | { | |
331 | int i; | |
332 | ||
333 | for (i = 0; i < hisi_hba->n_phy; i++) | |
334 | memcpy(&hisi_hba->phy[i].dev_sas_addr, | |
335 | hisi_hba->sas_addr, | |
336 | SAS_ADDR_SIZE); | |
337 | } | |
338 | ||
7eb7869f JG |
339 | int hisi_sas_probe(struct platform_device *pdev, |
340 | const struct hisi_sas_hw *hw) | |
341 | { | |
342 | struct Scsi_Host *shost; | |
343 | struct hisi_hba *hisi_hba; | |
344 | struct device *dev = &pdev->dev; | |
345 | struct asd_sas_phy **arr_phy; | |
346 | struct asd_sas_port **arr_port; | |
347 | struct sas_ha_struct *sha; | |
348 | int rc, phy_nr, port_nr, i; | |
349 | ||
350 | shost = hisi_sas_shost_alloc(pdev, hw); | |
351 | if (!shost) { | |
352 | rc = -ENOMEM; | |
353 | goto err_out_ha; | |
354 | } | |
355 | ||
356 | sha = SHOST_TO_SAS_HA(shost); | |
357 | hisi_hba = shost_priv(shost); | |
358 | platform_set_drvdata(pdev, sha); | |
50cb916f JG |
359 | |
360 | if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)) && | |
361 | dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) { | |
362 | dev_err(dev, "No usable DMA addressing method\n"); | |
363 | rc = -EIO; | |
364 | goto err_out_ha; | |
365 | } | |
366 | ||
7eb7869f JG |
367 | phy_nr = port_nr = hisi_hba->n_phy; |
368 | ||
369 | arr_phy = devm_kcalloc(dev, phy_nr, sizeof(void *), GFP_KERNEL); | |
370 | arr_port = devm_kcalloc(dev, port_nr, sizeof(void *), GFP_KERNEL); | |
371 | if (!arr_phy || !arr_port) | |
372 | return -ENOMEM; | |
373 | ||
374 | sha->sas_phy = arr_phy; | |
375 | sha->sas_port = arr_port; | |
376 | sha->core.shost = shost; | |
377 | sha->lldd_ha = hisi_hba; | |
378 | ||
379 | shost->transportt = hisi_sas_stt; | |
380 | shost->max_id = HISI_SAS_MAX_DEVICES; | |
381 | shost->max_lun = ~0; | |
382 | shost->max_channel = 1; | |
383 | shost->max_cmd_len = 16; | |
384 | shost->sg_tablesize = min_t(u16, SG_ALL, HISI_SAS_SGE_PAGE_CNT); | |
385 | shost->can_queue = HISI_SAS_COMMAND_ENTRIES; | |
386 | shost->cmd_per_lun = HISI_SAS_COMMAND_ENTRIES; | |
387 | ||
388 | sha->sas_ha_name = DRV_NAME; | |
389 | sha->dev = &hisi_hba->pdev->dev; | |
390 | sha->lldd_module = THIS_MODULE; | |
391 | sha->sas_addr = &hisi_hba->sas_addr[0]; | |
392 | sha->num_phys = hisi_hba->n_phy; | |
393 | sha->core.shost = hisi_hba->shost; | |
394 | ||
395 | for (i = 0; i < hisi_hba->n_phy; i++) { | |
396 | sha->sas_phy[i] = &hisi_hba->phy[i].sas_phy; | |
397 | sha->sas_port[i] = &hisi_hba->port[i].sas_port; | |
398 | } | |
399 | ||
5d74242e JG |
400 | hisi_sas_init_add(hisi_hba); |
401 | ||
7eb7869f JG |
402 | rc = scsi_add_host(shost, &pdev->dev); |
403 | if (rc) | |
404 | goto err_out_ha; | |
405 | ||
406 | rc = sas_register_ha(sha); | |
407 | if (rc) | |
408 | goto err_out_register_ha; | |
409 | ||
410 | scsi_scan_host(shost); | |
411 | ||
412 | return 0; | |
413 | ||
414 | err_out_register_ha: | |
415 | scsi_remove_host(shost); | |
416 | err_out_ha: | |
417 | kfree(shost); | |
418 | return rc; | |
419 | } | |
420 | EXPORT_SYMBOL_GPL(hisi_sas_probe); | |
421 | ||
89d53322 JG |
422 | int hisi_sas_remove(struct platform_device *pdev) |
423 | { | |
424 | struct sas_ha_struct *sha = platform_get_drvdata(pdev); | |
425 | struct hisi_hba *hisi_hba = sha->lldd_ha; | |
426 | ||
427 | scsi_remove_host(sha->core.shost); | |
428 | sas_unregister_ha(sha); | |
429 | sas_remove_host(sha->core.shost); | |
430 | ||
431 | hisi_sas_free(hisi_hba); | |
432 | return 0; | |
433 | } | |
434 | EXPORT_SYMBOL_GPL(hisi_sas_remove); | |
435 | ||
e8899fad JG |
436 | static __init int hisi_sas_init(void) |
437 | { | |
438 | pr_info("hisi_sas: driver version %s\n", DRV_VERSION); | |
439 | ||
440 | hisi_sas_stt = sas_domain_attach_transport(&hisi_sas_transport_ops); | |
441 | if (!hisi_sas_stt) | |
442 | return -ENOMEM; | |
443 | ||
444 | return 0; | |
445 | } | |
446 | ||
447 | static __exit void hisi_sas_exit(void) | |
448 | { | |
449 | sas_release_transport(hisi_sas_stt); | |
450 | } | |
451 | ||
452 | module_init(hisi_sas_init); | |
453 | module_exit(hisi_sas_exit); | |
454 | ||
455 | MODULE_VERSION(DRV_VERSION); | |
456 | MODULE_LICENSE("GPL"); | |
457 | MODULE_AUTHOR("John Garry <john.garry@huawei.com>"); | |
458 | MODULE_DESCRIPTION("HISILICON SAS controller driver"); | |
459 | MODULE_ALIAS("platform:" DRV_NAME); |