1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b98e66faSJames Bottomley /*
3b98e66faSJames Bottomley * Serial Attached SCSI (SAS) Expander discovery and configuration
4b98e66faSJames Bottomley *
5b98e66faSJames Bottomley * Copyright (C) 2007 James E.J. Bottomley
6b98e66faSJames Bottomley * <James.Bottomley@HansenPartnership.com>
7b98e66faSJames Bottomley */
8b98e66faSJames Bottomley #include <linux/scatterlist.h>
9b98e66faSJames Bottomley #include <linux/blkdev.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
1109703660SPaul Gortmaker #include <linux/export.h>
12b98e66faSJames Bottomley
13b98e66faSJames Bottomley #include "sas_internal.h"
14b98e66faSJames Bottomley
15b98e66faSJames Bottomley #include <scsi/scsi_transport.h>
16b98e66faSJames Bottomley #include <scsi/scsi_transport_sas.h>
17e15f669cSJason Yan #include "scsi_sas_internal.h"
18b98e66faSJames Bottomley
sas_host_smp_discover(struct sas_ha_struct * sas_ha,u8 * resp_data,u8 phy_id)19b98e66faSJames Bottomley static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data,
20b98e66faSJames Bottomley u8 phy_id)
21b98e66faSJames Bottomley {
22b98e66faSJames Bottomley struct sas_phy *phy;
23b98e66faSJames Bottomley struct sas_rphy *rphy;
24b98e66faSJames Bottomley
25b98e66faSJames Bottomley if (phy_id >= sas_ha->num_phys) {
26b98e66faSJames Bottomley resp_data[2] = SMP_RESP_NO_PHY;
27b98e66faSJames Bottomley return;
28b98e66faSJames Bottomley }
29b98e66faSJames Bottomley resp_data[2] = SMP_RESP_FUNC_ACC;
30b98e66faSJames Bottomley
31b98e66faSJames Bottomley phy = sas_ha->sas_phy[phy_id]->phy;
32b98e66faSJames Bottomley resp_data[9] = phy_id;
33b98e66faSJames Bottomley resp_data[13] = phy->negotiated_linkrate;
34b98e66faSJames Bottomley memcpy(resp_data + 16, sas_ha->sas_addr, SAS_ADDR_SIZE);
35b98e66faSJames Bottomley memcpy(resp_data + 24, sas_ha->sas_phy[phy_id]->attached_sas_addr,
36b98e66faSJames Bottomley SAS_ADDR_SIZE);
37b98e66faSJames Bottomley resp_data[40] = (phy->minimum_linkrate << 4) |
38b98e66faSJames Bottomley phy->minimum_linkrate_hw;
39b98e66faSJames Bottomley resp_data[41] = (phy->maximum_linkrate << 4) |
40b98e66faSJames Bottomley phy->maximum_linkrate_hw;
41b98e66faSJames Bottomley
42b98e66faSJames Bottomley if (!sas_ha->sas_phy[phy_id]->port ||
43b98e66faSJames Bottomley !sas_ha->sas_phy[phy_id]->port->port_dev)
44b98e66faSJames Bottomley return;
45b98e66faSJames Bottomley
46b98e66faSJames Bottomley rphy = sas_ha->sas_phy[phy_id]->port->port_dev->rphy;
47b98e66faSJames Bottomley resp_data[12] = rphy->identify.device_type << 4;
48b98e66faSJames Bottomley resp_data[14] = rphy->identify.initiator_port_protocols;
49b98e66faSJames Bottomley resp_data[15] = rphy->identify.target_port_protocols;
50b98e66faSJames Bottomley }
51b98e66faSJames Bottomley
528ec6552fSDan Williams /**
538ec6552fSDan Williams * to_sas_gpio_gp_bit - given the gpio frame data find the byte/bit position of 'od'
548ec6552fSDan Williams * @od: od bit to find
558ec6552fSDan Williams * @data: incoming bitstream (from frame)
568ec6552fSDan Williams * @index: requested data register index (from frame)
578ec6552fSDan Williams * @count: total number of registers in the bitstream (from frame)
588ec6552fSDan Williams * @bit: bit position of 'od' in the returned byte
598ec6552fSDan Williams *
608ec6552fSDan Williams * returns NULL if 'od' is not in 'data'
618ec6552fSDan Williams *
628ec6552fSDan Williams * From SFF-8485 v0.7:
638ec6552fSDan Williams * "In GPIO_TX[1], bit 0 of byte 3 contains the first bit (i.e., OD0.0)
648ec6552fSDan Williams * and bit 7 of byte 0 contains the 32nd bit (i.e., OD10.1).
658ec6552fSDan Williams *
668ec6552fSDan Williams * In GPIO_TX[2], bit 0 of byte 3 contains the 33rd bit (i.e., OD10.2)
678ec6552fSDan Williams * and bit 7 of byte 0 contains the 64th bit (i.e., OD21.0)."
688ec6552fSDan Williams *
698ec6552fSDan Williams * The general-purpose (raw-bitstream) RX registers have the same layout
708ec6552fSDan Williams * although 'od' is renamed 'id' for 'input data'.
718ec6552fSDan Williams *
728ec6552fSDan Williams * SFF-8489 defines the behavior of the LEDs in response to the 'od' values.
738ec6552fSDan Williams */
to_sas_gpio_gp_bit(unsigned int od,u8 * data,u8 index,u8 count,u8 * bit)748ec6552fSDan Williams static u8 *to_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count, u8 *bit)
758ec6552fSDan Williams {
768ec6552fSDan Williams unsigned int reg;
778ec6552fSDan Williams u8 byte;
788ec6552fSDan Williams
798ec6552fSDan Williams /* gp registers start at index 1 */
808ec6552fSDan Williams if (index == 0)
818ec6552fSDan Williams return NULL;
828ec6552fSDan Williams
838ec6552fSDan Williams index--; /* make index 0-based */
848ec6552fSDan Williams if (od < index * 32)
858ec6552fSDan Williams return NULL;
868ec6552fSDan Williams
878ec6552fSDan Williams od -= index * 32;
888ec6552fSDan Williams reg = od >> 5;
898ec6552fSDan Williams
908ec6552fSDan Williams if (reg >= count)
918ec6552fSDan Williams return NULL;
928ec6552fSDan Williams
938ec6552fSDan Williams od &= (1 << 5) - 1;
948ec6552fSDan Williams byte = 3 - (od >> 3);
958ec6552fSDan Williams *bit = od & ((1 << 3) - 1);
968ec6552fSDan Williams
978ec6552fSDan Williams return &data[reg * 4 + byte];
988ec6552fSDan Williams }
998ec6552fSDan Williams
try_test_sas_gpio_gp_bit(unsigned int od,u8 * data,u8 index,u8 count)1008ec6552fSDan Williams int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count)
1018ec6552fSDan Williams {
1028ec6552fSDan Williams u8 *byte;
1038ec6552fSDan Williams u8 bit;
1048ec6552fSDan Williams
1058ec6552fSDan Williams byte = to_sas_gpio_gp_bit(od, data, index, count, &bit);
1068ec6552fSDan Williams if (!byte)
1078ec6552fSDan Williams return -1;
1088ec6552fSDan Williams
1098ec6552fSDan Williams return (*byte >> bit) & 1;
1108ec6552fSDan Williams }
1118ec6552fSDan Williams EXPORT_SYMBOL(try_test_sas_gpio_gp_bit);
1128ec6552fSDan Williams
sas_host_smp_write_gpio(struct sas_ha_struct * sas_ha,u8 * resp_data,u8 reg_type,u8 reg_index,u8 reg_count,u8 * req_data)1138ec6552fSDan Williams static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data,
1148ec6552fSDan Williams u8 reg_type, u8 reg_index, u8 reg_count,
1158ec6552fSDan Williams u8 *req_data)
1168ec6552fSDan Williams {
117*1136a022SJohn Garry struct sas_internal *i = to_sas_internal(sas_ha->shost->transportt);
1188ec6552fSDan Williams int written;
1198ec6552fSDan Williams
1208ec6552fSDan Williams if (i->dft->lldd_write_gpio == NULL) {
1218ec6552fSDan Williams resp_data[2] = SMP_RESP_FUNC_UNK;
1228ec6552fSDan Williams return 0;
1238ec6552fSDan Williams }
1248ec6552fSDan Williams
1258ec6552fSDan Williams written = i->dft->lldd_write_gpio(sas_ha, reg_type, reg_index,
1268ec6552fSDan Williams reg_count, req_data);
1278ec6552fSDan Williams
1288ec6552fSDan Williams if (written < 0) {
1298ec6552fSDan Williams resp_data[2] = SMP_RESP_FUNC_FAILED;
1308ec6552fSDan Williams written = 0;
1318ec6552fSDan Williams } else
1328ec6552fSDan Williams resp_data[2] = SMP_RESP_FUNC_ACC;
1338ec6552fSDan Williams
1348ec6552fSDan Williams return written;
1358ec6552fSDan Williams }
1368ec6552fSDan Williams
sas_report_phy_sata(struct sas_ha_struct * sas_ha,u8 * resp_data,u8 phy_id)137b98e66faSJames Bottomley static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data,
138b98e66faSJames Bottomley u8 phy_id)
139b98e66faSJames Bottomley {
140b98e66faSJames Bottomley struct sas_rphy *rphy;
141b98e66faSJames Bottomley struct dev_to_host_fis *fis;
142b98e66faSJames Bottomley int i;
143b98e66faSJames Bottomley
144b98e66faSJames Bottomley if (phy_id >= sas_ha->num_phys) {
145b98e66faSJames Bottomley resp_data[2] = SMP_RESP_NO_PHY;
146b98e66faSJames Bottomley return;
147b98e66faSJames Bottomley }
148b98e66faSJames Bottomley
149b98e66faSJames Bottomley resp_data[2] = SMP_RESP_PHY_NO_SATA;
150b98e66faSJames Bottomley
151b98e66faSJames Bottomley if (!sas_ha->sas_phy[phy_id]->port)
152b98e66faSJames Bottomley return;
153b98e66faSJames Bottomley
154b98e66faSJames Bottomley rphy = sas_ha->sas_phy[phy_id]->port->port_dev->rphy;
155b98e66faSJames Bottomley fis = (struct dev_to_host_fis *)
156b98e66faSJames Bottomley sas_ha->sas_phy[phy_id]->port->port_dev->frame_rcvd;
157b98e66faSJames Bottomley if (rphy->identify.target_port_protocols != SAS_PROTOCOL_SATA)
158b98e66faSJames Bottomley return;
159b98e66faSJames Bottomley
160b98e66faSJames Bottomley resp_data[2] = SMP_RESP_FUNC_ACC;
161b98e66faSJames Bottomley resp_data[9] = phy_id;
162b98e66faSJames Bottomley memcpy(resp_data + 16, sas_ha->sas_phy[phy_id]->attached_sas_addr,
163b98e66faSJames Bottomley SAS_ADDR_SIZE);
164b98e66faSJames Bottomley
165b98e66faSJames Bottomley /* check to see if we have a valid d2h fis */
166b98e66faSJames Bottomley if (fis->fis_type != 0x34)
167b98e66faSJames Bottomley return;
168b98e66faSJames Bottomley
169b98e66faSJames Bottomley /* the d2h fis is required by the standard to be in LE format */
170b98e66faSJames Bottomley for (i = 0; i < 20; i += 4) {
171b98e66faSJames Bottomley u8 *dst = resp_data + 24 + i, *src =
172b98e66faSJames Bottomley &sas_ha->sas_phy[phy_id]->port->port_dev->frame_rcvd[i];
173b98e66faSJames Bottomley dst[0] = src[3];
174b98e66faSJames Bottomley dst[1] = src[2];
175b98e66faSJames Bottomley dst[2] = src[1];
176b98e66faSJames Bottomley dst[3] = src[0];
177b98e66faSJames Bottomley }
178b98e66faSJames Bottomley }
179b98e66faSJames Bottomley
sas_phy_control(struct sas_ha_struct * sas_ha,u8 phy_id,u8 phy_op,enum sas_linkrate min,enum sas_linkrate max,u8 * resp_data)180b98e66faSJames Bottomley static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
181b98e66faSJames Bottomley u8 phy_op, enum sas_linkrate min,
182b98e66faSJames Bottomley enum sas_linkrate max, u8 *resp_data)
183b98e66faSJames Bottomley {
184b98e66faSJames Bottomley struct sas_internal *i =
185*1136a022SJohn Garry to_sas_internal(sas_ha->shost->transportt);
186b98e66faSJames Bottomley struct sas_phy_linkrates rates;
187ab526633SDan Williams struct asd_sas_phy *asd_phy;
188b98e66faSJames Bottomley
189b98e66faSJames Bottomley if (phy_id >= sas_ha->num_phys) {
190b98e66faSJames Bottomley resp_data[2] = SMP_RESP_NO_PHY;
191b98e66faSJames Bottomley return;
192b98e66faSJames Bottomley }
193ab526633SDan Williams
194ab526633SDan Williams asd_phy = sas_ha->sas_phy[phy_id];
195b98e66faSJames Bottomley switch (phy_op) {
196b98e66faSJames Bottomley case PHY_FUNC_NOP:
197b98e66faSJames Bottomley case PHY_FUNC_LINK_RESET:
198b98e66faSJames Bottomley case PHY_FUNC_HARD_RESET:
199b98e66faSJames Bottomley case PHY_FUNC_DISABLE:
200b98e66faSJames Bottomley case PHY_FUNC_CLEAR_ERROR_LOG:
201b98e66faSJames Bottomley case PHY_FUNC_CLEAR_AFFIL:
202b98e66faSJames Bottomley case PHY_FUNC_TX_SATA_PS_SIGNAL:
203b98e66faSJames Bottomley break;
204b98e66faSJames Bottomley
205b98e66faSJames Bottomley default:
206b98e66faSJames Bottomley resp_data[2] = SMP_RESP_PHY_UNK_OP;
207b98e66faSJames Bottomley return;
208b98e66faSJames Bottomley }
209b98e66faSJames Bottomley
210b98e66faSJames Bottomley rates.minimum_linkrate = min;
211b98e66faSJames Bottomley rates.maximum_linkrate = max;
212b98e66faSJames Bottomley
213ab526633SDan Williams /* filter reset requests through libata eh */
214ab526633SDan Williams if (phy_op == PHY_FUNC_LINK_RESET && sas_try_ata_reset(asd_phy) == 0) {
215ab526633SDan Williams resp_data[2] = SMP_RESP_FUNC_ACC;
216ab526633SDan Williams return;
217ab526633SDan Williams }
218ab526633SDan Williams
219ab526633SDan Williams if (i->dft->lldd_control_phy(asd_phy, phy_op, &rates))
220b98e66faSJames Bottomley resp_data[2] = SMP_RESP_FUNC_FAILED;
221b98e66faSJames Bottomley else
222b98e66faSJames Bottomley resp_data[2] = SMP_RESP_FUNC_ACC;
223b98e66faSJames Bottomley }
224b98e66faSJames Bottomley
sas_smp_host_handler(struct bsg_job * job,struct Scsi_Host * shost)225651a0136SChristoph Hellwig void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost)
226b98e66faSJames Bottomley {
227b98e66faSJames Bottomley struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
228651a0136SChristoph Hellwig u8 *req_data, *resp_data;
229651a0136SChristoph Hellwig unsigned int reslen = 0;
230c3a4d78cSTejun Heo int error = -EINVAL;
231b98e66faSJames Bottomley
232b98e66faSJames Bottomley /* eight is the minimum size for request and response frames */
233651a0136SChristoph Hellwig if (job->request_payload.payload_len < 8 ||
234651a0136SChristoph Hellwig job->reply_payload.payload_len < 8)
235b98e66faSJames Bottomley goto out;
236b98e66faSJames Bottomley
237651a0136SChristoph Hellwig error = -ENOMEM;
238651a0136SChristoph Hellwig req_data = kzalloc(job->request_payload.payload_len, GFP_KERNEL);
239651a0136SChristoph Hellwig if (!req_data)
240b98e66faSJames Bottomley goto out;
241651a0136SChristoph Hellwig sg_copy_to_buffer(job->request_payload.sg_list,
242651a0136SChristoph Hellwig job->request_payload.sg_cnt, req_data,
243651a0136SChristoph Hellwig job->request_payload.payload_len);
244b98e66faSJames Bottomley
245b98e66faSJames Bottomley /* make sure frame can always be built ... we copy
246b98e66faSJames Bottomley * back only the requested length */
247651a0136SChristoph Hellwig resp_data = kzalloc(max(job->reply_payload.payload_len, 128U),
248651a0136SChristoph Hellwig GFP_KERNEL);
249651a0136SChristoph Hellwig if (!resp_data)
250651a0136SChristoph Hellwig goto out_free_req;
251b98e66faSJames Bottomley
252651a0136SChristoph Hellwig error = -EINVAL;
253b98e66faSJames Bottomley if (req_data[0] != SMP_REQUEST)
254651a0136SChristoph Hellwig goto out_free_resp;
255b98e66faSJames Bottomley
256b98e66faSJames Bottomley /* set up default don't know response */
257b98e66faSJames Bottomley resp_data[0] = SMP_RESPONSE;
258b98e66faSJames Bottomley resp_data[1] = req_data[1];
259b98e66faSJames Bottomley resp_data[2] = SMP_RESP_FUNC_UNK;
260b98e66faSJames Bottomley
261b98e66faSJames Bottomley switch (req_data[1]) {
262b98e66faSJames Bottomley case SMP_REPORT_GENERAL:
263b98e66faSJames Bottomley resp_data[2] = SMP_RESP_FUNC_ACC;
264b98e66faSJames Bottomley resp_data[9] = sas_ha->num_phys;
265651a0136SChristoph Hellwig reslen = 32;
266b98e66faSJames Bottomley break;
267b98e66faSJames Bottomley
268b98e66faSJames Bottomley case SMP_REPORT_MANUF_INFO:
269b98e66faSJames Bottomley resp_data[2] = SMP_RESP_FUNC_ACC;
270b98e66faSJames Bottomley memcpy(resp_data + 12, shost->hostt->name,
271b98e66faSJames Bottomley SAS_EXPANDER_VENDOR_ID_LEN);
272b98e66faSJames Bottomley memcpy(resp_data + 20, "libsas virt phy",
273b98e66faSJames Bottomley SAS_EXPANDER_PRODUCT_ID_LEN);
274651a0136SChristoph Hellwig reslen = 64;
275b98e66faSJames Bottomley break;
276b98e66faSJames Bottomley
277b98e66faSJames Bottomley case SMP_READ_GPIO_REG:
278b98e66faSJames Bottomley /* FIXME: need GPIO support in the transport class */
279b98e66faSJames Bottomley break;
280b98e66faSJames Bottomley
281b98e66faSJames Bottomley case SMP_DISCOVER:
282651a0136SChristoph Hellwig if (job->request_payload.payload_len < 16)
283651a0136SChristoph Hellwig goto out_free_resp;
284b98e66faSJames Bottomley sas_host_smp_discover(sas_ha, resp_data, req_data[9]);
285651a0136SChristoph Hellwig reslen = 56;
286b98e66faSJames Bottomley break;
287b98e66faSJames Bottomley
288b98e66faSJames Bottomley case SMP_REPORT_PHY_ERR_LOG:
289b98e66faSJames Bottomley /* FIXME: could implement this with additional
290b98e66faSJames Bottomley * libsas callbacks providing the HW supports it */
291b98e66faSJames Bottomley break;
292b98e66faSJames Bottomley
293b98e66faSJames Bottomley case SMP_REPORT_PHY_SATA:
294651a0136SChristoph Hellwig if (job->request_payload.payload_len < 16)
295651a0136SChristoph Hellwig goto out_free_resp;
296b98e66faSJames Bottomley sas_report_phy_sata(sas_ha, resp_data, req_data[9]);
297651a0136SChristoph Hellwig reslen = 60;
298b98e66faSJames Bottomley break;
299b98e66faSJames Bottomley
300b98e66faSJames Bottomley case SMP_REPORT_ROUTE_INFO:
301b98e66faSJames Bottomley /* Can't implement; hosts have no routes */
302b98e66faSJames Bottomley break;
303b98e66faSJames Bottomley
3048ec6552fSDan Williams case SMP_WRITE_GPIO_REG: {
3058ec6552fSDan Williams /* SFF-8485 v0.7 */
3068ec6552fSDan Williams const int base_frame_size = 11;
3078ec6552fSDan Williams int to_write = req_data[4];
3088ec6552fSDan Williams
309651a0136SChristoph Hellwig if (job->request_payload.payload_len <
310651a0136SChristoph Hellwig base_frame_size + to_write * 4) {
3118ec6552fSDan Williams resp_data[2] = SMP_RESP_INV_FRM_LEN;
312b98e66faSJames Bottomley break;
3138ec6552fSDan Williams }
3148ec6552fSDan Williams
3158ec6552fSDan Williams to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2],
3168ec6552fSDan Williams req_data[3], to_write, &req_data[8]);
317651a0136SChristoph Hellwig reslen = 8;
3188ec6552fSDan Williams break;
3198ec6552fSDan Williams }
320b98e66faSJames Bottomley
321b98e66faSJames Bottomley case SMP_CONF_ROUTE_INFO:
322b98e66faSJames Bottomley /* Can't implement; hosts have no routes */
323b98e66faSJames Bottomley break;
324b98e66faSJames Bottomley
325b98e66faSJames Bottomley case SMP_PHY_CONTROL:
326651a0136SChristoph Hellwig if (job->request_payload.payload_len < 44)
327651a0136SChristoph Hellwig goto out_free_resp;
328b98e66faSJames Bottomley sas_phy_control(sas_ha, req_data[9], req_data[10],
329b98e66faSJames Bottomley req_data[32] >> 4, req_data[33] >> 4,
330b98e66faSJames Bottomley resp_data);
331651a0136SChristoph Hellwig reslen = 8;
332b98e66faSJames Bottomley break;
333b98e66faSJames Bottomley
334b98e66faSJames Bottomley case SMP_PHY_TEST_FUNCTION:
335b98e66faSJames Bottomley /* FIXME: should this be implemented? */
336b98e66faSJames Bottomley break;
337b98e66faSJames Bottomley
338b98e66faSJames Bottomley default:
339b98e66faSJames Bottomley /* probably a 2.0 function */
340b98e66faSJames Bottomley break;
341b98e66faSJames Bottomley }
342b98e66faSJames Bottomley
343651a0136SChristoph Hellwig sg_copy_from_buffer(job->reply_payload.sg_list,
344651a0136SChristoph Hellwig job->reply_payload.sg_cnt, resp_data,
345651a0136SChristoph Hellwig job->reply_payload.payload_len);
346b98e66faSJames Bottomley
347651a0136SChristoph Hellwig error = 0;
348651a0136SChristoph Hellwig out_free_resp:
349b98e66faSJames Bottomley kfree(resp_data);
350651a0136SChristoph Hellwig out_free_req:
351651a0136SChristoph Hellwig kfree(req_data);
352651a0136SChristoph Hellwig out:
353651a0136SChristoph Hellwig bsg_job_done(job, error, reslen);
354b98e66faSJames Bottomley }
355