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