diff options
Diffstat (limited to 'drivers/scsi/libsas/sas_host_smp.c')
-rw-r--r-- | drivers/scsi/libsas/sas_host_smp.c | 104 |
1 files changed, 102 insertions, 2 deletions
diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 04ad8dd1a74c..bb8f49269a68 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/scatterlist.h> | 11 | #include <linux/scatterlist.h> |
12 | #include <linux/blkdev.h> | 12 | #include <linux/blkdev.h> |
13 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
14 | #include <linux/export.h> | ||
14 | 15 | ||
15 | #include "sas_internal.h" | 16 | #include "sas_internal.h" |
16 | 17 | ||
@@ -51,6 +52,91 @@ static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data, | |||
51 | resp_data[15] = rphy->identify.target_port_protocols; | 52 | resp_data[15] = rphy->identify.target_port_protocols; |
52 | } | 53 | } |
53 | 54 | ||
55 | /** | ||
56 | * to_sas_gpio_gp_bit - given the gpio frame data find the byte/bit position of 'od' | ||
57 | * @od: od bit to find | ||
58 | * @data: incoming bitstream (from frame) | ||
59 | * @index: requested data register index (from frame) | ||
60 | * @count: total number of registers in the bitstream (from frame) | ||
61 | * @bit: bit position of 'od' in the returned byte | ||
62 | * | ||
63 | * returns NULL if 'od' is not in 'data' | ||
64 | * | ||
65 | * From SFF-8485 v0.7: | ||
66 | * "In GPIO_TX[1], bit 0 of byte 3 contains the first bit (i.e., OD0.0) | ||
67 | * and bit 7 of byte 0 contains the 32nd bit (i.e., OD10.1). | ||
68 | * | ||
69 | * In GPIO_TX[2], bit 0 of byte 3 contains the 33rd bit (i.e., OD10.2) | ||
70 | * and bit 7 of byte 0 contains the 64th bit (i.e., OD21.0)." | ||
71 | * | ||
72 | * The general-purpose (raw-bitstream) RX registers have the same layout | ||
73 | * although 'od' is renamed 'id' for 'input data'. | ||
74 | * | ||
75 | * SFF-8489 defines the behavior of the LEDs in response to the 'od' values. | ||
76 | */ | ||
77 | static u8 *to_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count, u8 *bit) | ||
78 | { | ||
79 | unsigned int reg; | ||
80 | u8 byte; | ||
81 | |||
82 | /* gp registers start at index 1 */ | ||
83 | if (index == 0) | ||
84 | return NULL; | ||
85 | |||
86 | index--; /* make index 0-based */ | ||
87 | if (od < index * 32) | ||
88 | return NULL; | ||
89 | |||
90 | od -= index * 32; | ||
91 | reg = od >> 5; | ||
92 | |||
93 | if (reg >= count) | ||
94 | return NULL; | ||
95 | |||
96 | od &= (1 << 5) - 1; | ||
97 | byte = 3 - (od >> 3); | ||
98 | *bit = od & ((1 << 3) - 1); | ||
99 | |||
100 | return &data[reg * 4 + byte]; | ||
101 | } | ||
102 | |||
103 | int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count) | ||
104 | { | ||
105 | u8 *byte; | ||
106 | u8 bit; | ||
107 | |||
108 | byte = to_sas_gpio_gp_bit(od, data, index, count, &bit); | ||
109 | if (!byte) | ||
110 | return -1; | ||
111 | |||
112 | return (*byte >> bit) & 1; | ||
113 | } | ||
114 | EXPORT_SYMBOL(try_test_sas_gpio_gp_bit); | ||
115 | |||
116 | static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data, | ||
117 | u8 reg_type, u8 reg_index, u8 reg_count, | ||
118 | u8 *req_data) | ||
119 | { | ||
120 | struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); | ||
121 | int written; | ||
122 | |||
123 | if (i->dft->lldd_write_gpio == NULL) { | ||
124 | resp_data[2] = SMP_RESP_FUNC_UNK; | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | written = i->dft->lldd_write_gpio(sas_ha, reg_type, reg_index, | ||
129 | reg_count, req_data); | ||
130 | |||
131 | if (written < 0) { | ||
132 | resp_data[2] = SMP_RESP_FUNC_FAILED; | ||
133 | written = 0; | ||
134 | } else | ||
135 | resp_data[2] = SMP_RESP_FUNC_ACC; | ||
136 | |||
137 | return written; | ||
138 | } | ||
139 | |||
54 | static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data, | 140 | static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data, |
55 | u8 phy_id) | 141 | u8 phy_id) |
56 | { | 142 | { |
@@ -230,9 +316,23 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, | |||
230 | /* Can't implement; hosts have no routes */ | 316 | /* Can't implement; hosts have no routes */ |
231 | break; | 317 | break; |
232 | 318 | ||
233 | case SMP_WRITE_GPIO_REG: | 319 | case SMP_WRITE_GPIO_REG: { |
234 | /* FIXME: need GPIO support in the transport class */ | 320 | /* SFF-8485 v0.7 */ |
321 | const int base_frame_size = 11; | ||
322 | int to_write = req_data[4]; | ||
323 | |||
324 | if (blk_rq_bytes(req) < base_frame_size + to_write * 4 || | ||
325 | req->resid_len < base_frame_size + to_write * 4) { | ||
326 | resp_data[2] = SMP_RESP_INV_FRM_LEN; | ||
327 | break; | ||
328 | } | ||
329 | |||
330 | to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2], | ||
331 | req_data[3], to_write, &req_data[8]); | ||
332 | req->resid_len -= base_frame_size + to_write * 4; | ||
333 | rsp->resid_len -= 8; | ||
235 | break; | 334 | break; |
335 | } | ||
236 | 336 | ||
237 | case SMP_CONF_ROUTE_INFO: | 337 | case SMP_CONF_ROUTE_INFO: |
238 | /* Can't implement; hosts have no routes */ | 338 | /* Can't implement; hosts have no routes */ |