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