diff options
author | Kristen Carlson Accardi <kristen.c.accardi@intel.com> | 2008-06-03 13:33:55 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-07-14 15:59:33 -0400 |
commit | 18f7ba4c2f4be6b37d925931f04d6cc28d88d1ee (patch) | |
tree | 4f127510c378cba33e79d5fb71bd9fc14a28e1cb | |
parent | 87fbc5a060faf2394bee88a93519f9b9d434727c (diff) |
libata/ahci: enclosure management support
Add Enclosure Management support to libata and ahci.
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
-rw-r--r-- | drivers/ata/ahci.c | 321 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 79 | ||||
-rw-r--r-- | include/linux/libata.h | 21 |
3 files changed, 419 insertions, 2 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 5e6468a7ca4b..65d4e968feb4 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -56,6 +56,12 @@ MODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip) | |||
56 | static int ahci_enable_alpm(struct ata_port *ap, | 56 | static int ahci_enable_alpm(struct ata_port *ap, |
57 | enum link_pm policy); | 57 | enum link_pm policy); |
58 | static void ahci_disable_alpm(struct ata_port *ap); | 58 | static void ahci_disable_alpm(struct ata_port *ap); |
59 | static ssize_t ahci_led_show(struct ata_port *ap, char *buf); | ||
60 | static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, | ||
61 | size_t size); | ||
62 | static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, | ||
63 | ssize_t size); | ||
64 | #define MAX_SLOTS 8 | ||
59 | 65 | ||
60 | enum { | 66 | enum { |
61 | AHCI_PCI_BAR = 5, | 67 | AHCI_PCI_BAR = 5, |
@@ -98,6 +104,8 @@ enum { | |||
98 | HOST_IRQ_STAT = 0x08, /* interrupt status */ | 104 | HOST_IRQ_STAT = 0x08, /* interrupt status */ |
99 | HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ | 105 | HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ |
100 | HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ | 106 | HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ |
107 | HOST_EM_LOC = 0x1c, /* Enclosure Management location */ | ||
108 | HOST_EM_CTL = 0x20, /* Enclosure Management Control */ | ||
101 | 109 | ||
102 | /* HOST_CTL bits */ | 110 | /* HOST_CTL bits */ |
103 | HOST_RESET = (1 << 0), /* reset controller; self-clear */ | 111 | HOST_RESET = (1 << 0), /* reset controller; self-clear */ |
@@ -105,6 +113,7 @@ enum { | |||
105 | HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ | 113 | HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ |
106 | 114 | ||
107 | /* HOST_CAP bits */ | 115 | /* HOST_CAP bits */ |
116 | HOST_CAP_EMS = (1 << 6), /* Enclosure Management support */ | ||
108 | HOST_CAP_SSC = (1 << 14), /* Slumber capable */ | 117 | HOST_CAP_SSC = (1 << 14), /* Slumber capable */ |
109 | HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ | 118 | HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ |
110 | HOST_CAP_CLO = (1 << 24), /* Command List Override support */ | 119 | HOST_CAP_CLO = (1 << 24), /* Command List Override support */ |
@@ -202,6 +211,11 @@ enum { | |||
202 | ATA_FLAG_IPM, | 211 | ATA_FLAG_IPM, |
203 | 212 | ||
204 | ICH_MAP = 0x90, /* ICH MAP register */ | 213 | ICH_MAP = 0x90, /* ICH MAP register */ |
214 | |||
215 | /* em_ctl bits */ | ||
216 | EM_CTL_RST = (1 << 9), /* Reset */ | ||
217 | EM_CTL_TM = (1 << 8), /* Transmit Message */ | ||
218 | EM_CTL_ALHD = (1 << 26), /* Activity LED */ | ||
205 | }; | 219 | }; |
206 | 220 | ||
207 | struct ahci_cmd_hdr { | 221 | struct ahci_cmd_hdr { |
@@ -219,12 +233,21 @@ struct ahci_sg { | |||
219 | __le32 flags_size; | 233 | __le32 flags_size; |
220 | }; | 234 | }; |
221 | 235 | ||
236 | struct ahci_em_priv { | ||
237 | enum sw_activity blink_policy; | ||
238 | struct timer_list timer; | ||
239 | unsigned long saved_activity; | ||
240 | unsigned long activity; | ||
241 | unsigned long led_state; | ||
242 | }; | ||
243 | |||
222 | struct ahci_host_priv { | 244 | struct ahci_host_priv { |
223 | unsigned int flags; /* AHCI_HFLAG_* */ | 245 | unsigned int flags; /* AHCI_HFLAG_* */ |
224 | u32 cap; /* cap to use */ | 246 | u32 cap; /* cap to use */ |
225 | u32 port_map; /* port map to use */ | 247 | u32 port_map; /* port map to use */ |
226 | u32 saved_cap; /* saved initial cap */ | 248 | u32 saved_cap; /* saved initial cap */ |
227 | u32 saved_port_map; /* saved initial port_map */ | 249 | u32 saved_port_map; /* saved initial port_map */ |
250 | u32 em_loc; /* enclosure management location */ | ||
228 | }; | 251 | }; |
229 | 252 | ||
230 | struct ahci_port_priv { | 253 | struct ahci_port_priv { |
@@ -240,6 +263,8 @@ struct ahci_port_priv { | |||
240 | unsigned int ncq_saw_dmas:1; | 263 | unsigned int ncq_saw_dmas:1; |
241 | unsigned int ncq_saw_sdb:1; | 264 | unsigned int ncq_saw_sdb:1; |
242 | u32 intr_mask; /* interrupts to enable */ | 265 | u32 intr_mask; /* interrupts to enable */ |
266 | struct ahci_em_priv em_priv[MAX_SLOTS];/* enclosure management info | ||
267 | * per PM slot */ | ||
243 | }; | 268 | }; |
244 | 269 | ||
245 | static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); | 270 | static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); |
@@ -277,9 +302,20 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); | |||
277 | static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); | 302 | static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); |
278 | static int ahci_pci_device_resume(struct pci_dev *pdev); | 303 | static int ahci_pci_device_resume(struct pci_dev *pdev); |
279 | #endif | 304 | #endif |
305 | static ssize_t ahci_activity_show(struct ata_device *dev, char *buf); | ||
306 | static ssize_t ahci_activity_store(struct ata_device *dev, | ||
307 | enum sw_activity val); | ||
308 | static void ahci_init_sw_activity(struct ata_link *link); | ||
280 | 309 | ||
281 | static struct device_attribute *ahci_shost_attrs[] = { | 310 | static struct device_attribute *ahci_shost_attrs[] = { |
282 | &dev_attr_link_power_management_policy, | 311 | &dev_attr_link_power_management_policy, |
312 | &dev_attr_em_message_type, | ||
313 | &dev_attr_em_message, | ||
314 | NULL | ||
315 | }; | ||
316 | |||
317 | static struct device_attribute *ahci_sdev_attrs[] = { | ||
318 | &dev_attr_sw_activity, | ||
283 | NULL | 319 | NULL |
284 | }; | 320 | }; |
285 | 321 | ||
@@ -289,6 +325,7 @@ static struct scsi_host_template ahci_sht = { | |||
289 | .sg_tablesize = AHCI_MAX_SG, | 325 | .sg_tablesize = AHCI_MAX_SG, |
290 | .dma_boundary = AHCI_DMA_BOUNDARY, | 326 | .dma_boundary = AHCI_DMA_BOUNDARY, |
291 | .shost_attrs = ahci_shost_attrs, | 327 | .shost_attrs = ahci_shost_attrs, |
328 | .sdev_attrs = ahci_sdev_attrs, | ||
292 | }; | 329 | }; |
293 | 330 | ||
294 | static struct ata_port_operations ahci_ops = { | 331 | static struct ata_port_operations ahci_ops = { |
@@ -316,6 +353,10 @@ static struct ata_port_operations ahci_ops = { | |||
316 | 353 | ||
317 | .enable_pm = ahci_enable_alpm, | 354 | .enable_pm = ahci_enable_alpm, |
318 | .disable_pm = ahci_disable_alpm, | 355 | .disable_pm = ahci_disable_alpm, |
356 | .em_show = ahci_led_show, | ||
357 | .em_store = ahci_led_store, | ||
358 | .sw_activity_show = ahci_activity_show, | ||
359 | .sw_activity_store = ahci_activity_store, | ||
319 | #ifdef CONFIG_PM | 360 | #ifdef CONFIG_PM |
320 | .port_suspend = ahci_port_suspend, | 361 | .port_suspend = ahci_port_suspend, |
321 | .port_resume = ahci_port_resume, | 362 | .port_resume = ahci_port_resume, |
@@ -561,6 +602,11 @@ static struct pci_driver ahci_pci_driver = { | |||
561 | #endif | 602 | #endif |
562 | }; | 603 | }; |
563 | 604 | ||
605 | static int ahci_em_messages = 1; | ||
606 | module_param(ahci_em_messages, int, 0444); | ||
607 | /* add other LED protocol types when they become supported */ | ||
608 | MODULE_PARM_DESC(ahci_em_messages, | ||
609 | "Set AHCI Enclosure Management Message type (0 = disabled, 1 = LED"); | ||
564 | 610 | ||
565 | static inline int ahci_nr_ports(u32 cap) | 611 | static inline int ahci_nr_ports(u32 cap) |
566 | { | 612 | { |
@@ -1031,11 +1077,28 @@ static void ahci_power_down(struct ata_port *ap) | |||
1031 | 1077 | ||
1032 | static void ahci_start_port(struct ata_port *ap) | 1078 | static void ahci_start_port(struct ata_port *ap) |
1033 | { | 1079 | { |
1080 | struct ahci_port_priv *pp = ap->private_data; | ||
1081 | struct ata_link *link; | ||
1082 | struct ahci_em_priv *emp; | ||
1083 | |||
1034 | /* enable FIS reception */ | 1084 | /* enable FIS reception */ |
1035 | ahci_start_fis_rx(ap); | 1085 | ahci_start_fis_rx(ap); |
1036 | 1086 | ||
1037 | /* enable DMA */ | 1087 | /* enable DMA */ |
1038 | ahci_start_engine(ap); | 1088 | ahci_start_engine(ap); |
1089 | |||
1090 | /* turn on LEDs */ | ||
1091 | if (ap->flags & ATA_FLAG_EM) { | ||
1092 | ata_port_for_each_link(link, ap) { | ||
1093 | emp = &pp->em_priv[link->pmp]; | ||
1094 | ahci_transmit_led_message(ap, emp->led_state, 4); | ||
1095 | } | ||
1096 | } | ||
1097 | |||
1098 | if (ap->flags & ATA_FLAG_SW_ACTIVITY) | ||
1099 | ata_port_for_each_link(link, ap) | ||
1100 | ahci_init_sw_activity(link); | ||
1101 | |||
1039 | } | 1102 | } |
1040 | 1103 | ||
1041 | static int ahci_deinit_port(struct ata_port *ap, const char **emsg) | 1104 | static int ahci_deinit_port(struct ata_port *ap, const char **emsg) |
@@ -1116,6 +1179,230 @@ static int ahci_reset_controller(struct ata_host *host) | |||
1116 | return 0; | 1179 | return 0; |
1117 | } | 1180 | } |
1118 | 1181 | ||
1182 | static void ahci_sw_activity(struct ata_link *link) | ||
1183 | { | ||
1184 | struct ata_port *ap = link->ap; | ||
1185 | struct ahci_port_priv *pp = ap->private_data; | ||
1186 | struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; | ||
1187 | |||
1188 | if (!(link->flags & ATA_LFLAG_SW_ACTIVITY)) | ||
1189 | return; | ||
1190 | |||
1191 | emp->activity++; | ||
1192 | if (!timer_pending(&emp->timer)) | ||
1193 | mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10)); | ||
1194 | } | ||
1195 | |||
1196 | static void ahci_sw_activity_blink(unsigned long arg) | ||
1197 | { | ||
1198 | struct ata_link *link = (struct ata_link *)arg; | ||
1199 | struct ata_port *ap = link->ap; | ||
1200 | struct ahci_port_priv *pp = ap->private_data; | ||
1201 | struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; | ||
1202 | unsigned long led_message = emp->led_state; | ||
1203 | u32 activity_led_state; | ||
1204 | |||
1205 | led_message &= 0xffff0000; | ||
1206 | led_message |= ap->port_no | (link->pmp << 8); | ||
1207 | |||
1208 | /* check to see if we've had activity. If so, | ||
1209 | * toggle state of LED and reset timer. If not, | ||
1210 | * turn LED to desired idle state. | ||
1211 | */ | ||
1212 | if (emp->saved_activity != emp->activity) { | ||
1213 | emp->saved_activity = emp->activity; | ||
1214 | /* get the current LED state */ | ||
1215 | activity_led_state = led_message & 0x00010000; | ||
1216 | |||
1217 | if (activity_led_state) | ||
1218 | activity_led_state = 0; | ||
1219 | else | ||
1220 | activity_led_state = 1; | ||
1221 | |||
1222 | /* clear old state */ | ||
1223 | led_message &= 0xfff8ffff; | ||
1224 | |||
1225 | /* toggle state */ | ||
1226 | led_message |= (activity_led_state << 16); | ||
1227 | mod_timer(&emp->timer, jiffies + msecs_to_jiffies(100)); | ||
1228 | } else { | ||
1229 | /* switch to idle */ | ||
1230 | led_message &= 0xfff8ffff; | ||
1231 | if (emp->blink_policy == BLINK_OFF) | ||
1232 | led_message |= (1 << 16); | ||
1233 | } | ||
1234 | ahci_transmit_led_message(ap, led_message, 4); | ||
1235 | } | ||
1236 | |||
1237 | static void ahci_init_sw_activity(struct ata_link *link) | ||
1238 | { | ||
1239 | struct ata_port *ap = link->ap; | ||
1240 | struct ahci_port_priv *pp = ap->private_data; | ||
1241 | struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; | ||
1242 | |||
1243 | /* init activity stats, setup timer */ | ||
1244 | emp->saved_activity = emp->activity = 0; | ||
1245 | setup_timer(&emp->timer, ahci_sw_activity_blink, (unsigned long)link); | ||
1246 | |||
1247 | /* check our blink policy and set flag for link if it's enabled */ | ||
1248 | if (emp->blink_policy) | ||
1249 | link->flags |= ATA_LFLAG_SW_ACTIVITY; | ||
1250 | } | ||
1251 | |||
1252 | static int ahci_reset_em(struct ata_host *host) | ||
1253 | { | ||
1254 | void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; | ||
1255 | u32 em_ctl; | ||
1256 | |||
1257 | em_ctl = readl(mmio + HOST_EM_CTL); | ||
1258 | if ((em_ctl & EM_CTL_TM) || (em_ctl & EM_CTL_RST)) | ||
1259 | return -EINVAL; | ||
1260 | |||
1261 | writel(em_ctl | EM_CTL_RST, mmio + HOST_EM_CTL); | ||
1262 | return 0; | ||
1263 | } | ||
1264 | |||
1265 | static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, | ||
1266 | ssize_t size) | ||
1267 | { | ||
1268 | struct ahci_host_priv *hpriv = ap->host->private_data; | ||
1269 | struct ahci_port_priv *pp = ap->private_data; | ||
1270 | void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; | ||
1271 | u32 em_ctl; | ||
1272 | u32 message[] = {0, 0}; | ||
1273 | unsigned int flags; | ||
1274 | int pmp; | ||
1275 | struct ahci_em_priv *emp; | ||
1276 | |||
1277 | /* get the slot number from the message */ | ||
1278 | pmp = (state & 0x0000ff00) >> 8; | ||
1279 | if (pmp < MAX_SLOTS) | ||
1280 | emp = &pp->em_priv[pmp]; | ||
1281 | else | ||
1282 | return -EINVAL; | ||
1283 | |||
1284 | spin_lock_irqsave(ap->lock, flags); | ||
1285 | |||
1286 | /* | ||
1287 | * if we are still busy transmitting a previous message, | ||
1288 | * do not allow | ||
1289 | */ | ||
1290 | em_ctl = readl(mmio + HOST_EM_CTL); | ||
1291 | if (em_ctl & EM_CTL_TM) { | ||
1292 | spin_unlock_irqrestore(ap->lock, flags); | ||
1293 | return -EINVAL; | ||
1294 | } | ||
1295 | |||
1296 | /* | ||
1297 | * create message header - this is all zero except for | ||
1298 | * the message size, which is 4 bytes. | ||
1299 | */ | ||
1300 | message[0] |= (4 << 8); | ||
1301 | |||
1302 | /* ignore 0:4 of byte zero, fill in port info yourself */ | ||
1303 | message[1] = ((state & 0xfffffff0) | ap->port_no); | ||
1304 | |||
1305 | /* write message to EM_LOC */ | ||
1306 | writel(message[0], mmio + hpriv->em_loc); | ||
1307 | writel(message[1], mmio + hpriv->em_loc+4); | ||
1308 | |||
1309 | /* save off new led state for port/slot */ | ||
1310 | emp->led_state = message[1]; | ||
1311 | |||
1312 | /* | ||
1313 | * tell hardware to transmit the message | ||
1314 | */ | ||
1315 | writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); | ||
1316 | |||
1317 | spin_unlock_irqrestore(ap->lock, flags); | ||
1318 | return size; | ||
1319 | } | ||
1320 | |||
1321 | static ssize_t ahci_led_show(struct ata_port *ap, char *buf) | ||
1322 | { | ||
1323 | struct ahci_port_priv *pp = ap->private_data; | ||
1324 | struct ata_link *link; | ||
1325 | struct ahci_em_priv *emp; | ||
1326 | int rc = 0; | ||
1327 | |||
1328 | ata_port_for_each_link(link, ap) { | ||
1329 | emp = &pp->em_priv[link->pmp]; | ||
1330 | rc += sprintf(buf, "%lx\n", emp->led_state); | ||
1331 | } | ||
1332 | return rc; | ||
1333 | } | ||
1334 | |||
1335 | static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, | ||
1336 | size_t size) | ||
1337 | { | ||
1338 | int state; | ||
1339 | int pmp; | ||
1340 | struct ahci_port_priv *pp = ap->private_data; | ||
1341 | struct ahci_em_priv *emp; | ||
1342 | |||
1343 | state = simple_strtoul(buf, NULL, 0); | ||
1344 | |||
1345 | /* get the slot number from the message */ | ||
1346 | pmp = (state & 0x0000ff00) >> 8; | ||
1347 | if (pmp < MAX_SLOTS) | ||
1348 | emp = &pp->em_priv[pmp]; | ||
1349 | else | ||
1350 | return -EINVAL; | ||
1351 | |||
1352 | /* mask off the activity bits if we are in sw_activity | ||
1353 | * mode, user should turn off sw_activity before setting | ||
1354 | * activity led through em_message | ||
1355 | */ | ||
1356 | if (emp->blink_policy) | ||
1357 | state &= 0xfff8ffff; | ||
1358 | |||
1359 | return ahci_transmit_led_message(ap, state, size); | ||
1360 | } | ||
1361 | |||
1362 | static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) | ||
1363 | { | ||
1364 | struct ata_link *link = dev->link; | ||
1365 | struct ata_port *ap = link->ap; | ||
1366 | struct ahci_port_priv *pp = ap->private_data; | ||
1367 | struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; | ||
1368 | u32 port_led_state = emp->led_state; | ||
1369 | |||
1370 | /* save the desired Activity LED behavior */ | ||
1371 | if (val == OFF) { | ||
1372 | /* clear LFLAG */ | ||
1373 | link->flags &= ~(ATA_LFLAG_SW_ACTIVITY); | ||
1374 | |||
1375 | /* set the LED to OFF */ | ||
1376 | port_led_state &= 0xfff80000; | ||
1377 | port_led_state |= (ap->port_no | (link->pmp << 8)); | ||
1378 | ahci_transmit_led_message(ap, port_led_state, 4); | ||
1379 | } else { | ||
1380 | link->flags |= ATA_LFLAG_SW_ACTIVITY; | ||
1381 | if (val == BLINK_OFF) { | ||
1382 | /* set LED to ON for idle */ | ||
1383 | port_led_state &= 0xfff80000; | ||
1384 | port_led_state |= (ap->port_no | (link->pmp << 8)); | ||
1385 | port_led_state |= 0x00010000; /* check this */ | ||
1386 | ahci_transmit_led_message(ap, port_led_state, 4); | ||
1387 | } | ||
1388 | } | ||
1389 | emp->blink_policy = val; | ||
1390 | return 0; | ||
1391 | } | ||
1392 | |||
1393 | static ssize_t ahci_activity_show(struct ata_device *dev, char *buf) | ||
1394 | { | ||
1395 | struct ata_link *link = dev->link; | ||
1396 | struct ata_port *ap = link->ap; | ||
1397 | struct ahci_port_priv *pp = ap->private_data; | ||
1398 | struct ahci_em_priv *emp = &pp->em_priv[link->pmp]; | ||
1399 | |||
1400 | /* display the saved value of activity behavior for this | ||
1401 | * disk. | ||
1402 | */ | ||
1403 | return sprintf(buf, "%d\n", emp->blink_policy); | ||
1404 | } | ||
1405 | |||
1119 | static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap, | 1406 | static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap, |
1120 | int port_no, void __iomem *mmio, | 1407 | int port_no, void __iomem *mmio, |
1121 | void __iomem *port_mmio) | 1408 | void __iomem *port_mmio) |
@@ -1848,6 +2135,8 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) | |||
1848 | writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE); | 2135 | writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE); |
1849 | readl(port_mmio + PORT_CMD_ISSUE); /* flush */ | 2136 | readl(port_mmio + PORT_CMD_ISSUE); /* flush */ |
1850 | 2137 | ||
2138 | ahci_sw_activity(qc->dev->link); | ||
2139 | |||
1851 | return 0; | 2140 | return 0; |
1852 | } | 2141 | } |
1853 | 2142 | ||
@@ -2154,7 +2443,8 @@ static void ahci_print_info(struct ata_host *host) | |||
2154 | dev_printk(KERN_INFO, &pdev->dev, | 2443 | dev_printk(KERN_INFO, &pdev->dev, |
2155 | "flags: " | 2444 | "flags: " |
2156 | "%s%s%s%s%s%s%s" | 2445 | "%s%s%s%s%s%s%s" |
2157 | "%s%s%s%s%s%s%s\n" | 2446 | "%s%s%s%s%s%s%s" |
2447 | "%s\n" | ||
2158 | , | 2448 | , |
2159 | 2449 | ||
2160 | cap & (1 << 31) ? "64bit " : "", | 2450 | cap & (1 << 31) ? "64bit " : "", |
@@ -2171,7 +2461,8 @@ static void ahci_print_info(struct ata_host *host) | |||
2171 | cap & (1 << 17) ? "pmp " : "", | 2461 | cap & (1 << 17) ? "pmp " : "", |
2172 | cap & (1 << 15) ? "pio " : "", | 2462 | cap & (1 << 15) ? "pio " : "", |
2173 | cap & (1 << 14) ? "slum " : "", | 2463 | cap & (1 << 14) ? "slum " : "", |
2174 | cap & (1 << 13) ? "part " : "" | 2464 | cap & (1 << 13) ? "part " : "", |
2465 | cap & (1 << 6) ? "ems ": "" | ||
2175 | ); | 2466 | ); |
2176 | } | 2467 | } |
2177 | 2468 | ||
@@ -2291,6 +2582,24 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
2291 | if (hpriv->cap & HOST_CAP_PMP) | 2582 | if (hpriv->cap & HOST_CAP_PMP) |
2292 | pi.flags |= ATA_FLAG_PMP; | 2583 | pi.flags |= ATA_FLAG_PMP; |
2293 | 2584 | ||
2585 | if (ahci_em_messages && (hpriv->cap & HOST_CAP_EMS)) { | ||
2586 | u8 messages; | ||
2587 | void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; | ||
2588 | u32 em_loc = readl(mmio + HOST_EM_LOC); | ||
2589 | u32 em_ctl = readl(mmio + HOST_EM_CTL); | ||
2590 | |||
2591 | messages = (em_ctl & 0x000f0000) >> 16; | ||
2592 | |||
2593 | /* we only support LED message type right now */ | ||
2594 | if ((messages & 0x01) && (ahci_em_messages == 1)) { | ||
2595 | /* store em_loc */ | ||
2596 | hpriv->em_loc = ((em_loc >> 16) * 4); | ||
2597 | pi.flags |= ATA_FLAG_EM; | ||
2598 | if (!(em_ctl & EM_CTL_ALHD)) | ||
2599 | pi.flags |= ATA_FLAG_SW_ACTIVITY; | ||
2600 | } | ||
2601 | } | ||
2602 | |||
2294 | /* CAP.NP sometimes indicate the index of the last enabled | 2603 | /* CAP.NP sometimes indicate the index of the last enabled |
2295 | * port, at other times, that of the last possible port, so | 2604 | * port, at other times, that of the last possible port, so |
2296 | * determining the maximum port number requires looking at | 2605 | * determining the maximum port number requires looking at |
@@ -2304,6 +2613,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
2304 | host->iomap = pcim_iomap_table(pdev); | 2613 | host->iomap = pcim_iomap_table(pdev); |
2305 | host->private_data = hpriv; | 2614 | host->private_data = hpriv; |
2306 | 2615 | ||
2616 | if (pi.flags & ATA_FLAG_EM) | ||
2617 | ahci_reset_em(host); | ||
2618 | |||
2307 | for (i = 0; i < host->n_ports; i++) { | 2619 | for (i = 0; i < host->n_ports; i++) { |
2308 | struct ata_port *ap = host->ports[i]; | 2620 | struct ata_port *ap = host->ports[i]; |
2309 | 2621 | ||
@@ -2314,6 +2626,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
2314 | /* set initial link pm policy */ | 2626 | /* set initial link pm policy */ |
2315 | ap->pm_policy = NOT_AVAILABLE; | 2627 | ap->pm_policy = NOT_AVAILABLE; |
2316 | 2628 | ||
2629 | /* set enclosure management message type */ | ||
2630 | if (ap->flags & ATA_FLAG_EM) | ||
2631 | ap->em_message_type = ahci_em_messages; | ||
2632 | |||
2633 | |||
2317 | /* disabled/not-implemented port */ | 2634 | /* disabled/not-implemented port */ |
2318 | if (!(hpriv->port_map & (1 << i))) | 2635 | if (!(hpriv->port_map & (1 << i))) |
2319 | ap->ops = &ata_dummy_port_ops; | 2636 | ap->ops = &ata_dummy_port_ops; |
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 57a43649a461..b578b11caa7b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c | |||
@@ -190,6 +190,85 @@ static void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) | |||
190 | scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq); | 190 | scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq); |
191 | } | 191 | } |
192 | 192 | ||
193 | static ssize_t | ||
194 | ata_scsi_em_message_store(struct device *dev, struct device_attribute *attr, | ||
195 | const char *buf, size_t count) | ||
196 | { | ||
197 | struct Scsi_Host *shost = class_to_shost(dev); | ||
198 | struct ata_port *ap = ata_shost_to_port(shost); | ||
199 | if (ap->ops->em_store && (ap->flags & ATA_FLAG_EM)) | ||
200 | return ap->ops->em_store(ap, buf, count); | ||
201 | return -EINVAL; | ||
202 | } | ||
203 | |||
204 | static ssize_t | ||
205 | ata_scsi_em_message_show(struct device *dev, struct device_attribute *attr, | ||
206 | char *buf) | ||
207 | { | ||
208 | struct Scsi_Host *shost = class_to_shost(dev); | ||
209 | struct ata_port *ap = ata_shost_to_port(shost); | ||
210 | |||
211 | if (ap->ops->em_show && (ap->flags & ATA_FLAG_EM)) | ||
212 | return ap->ops->em_show(ap, buf); | ||
213 | return -EINVAL; | ||
214 | } | ||
215 | DEVICE_ATTR(em_message, S_IRUGO | S_IWUGO, | ||
216 | ata_scsi_em_message_show, ata_scsi_em_message_store); | ||
217 | EXPORT_SYMBOL_GPL(dev_attr_em_message); | ||
218 | |||
219 | static ssize_t | ||
220 | ata_scsi_em_message_type_show(struct device *dev, struct device_attribute *attr, | ||
221 | char *buf) | ||
222 | { | ||
223 | struct Scsi_Host *shost = class_to_shost(dev); | ||
224 | struct ata_port *ap = ata_shost_to_port(shost); | ||
225 | |||
226 | return snprintf(buf, 23, "%d\n", ap->em_message_type); | ||
227 | } | ||
228 | DEVICE_ATTR(em_message_type, S_IRUGO, | ||
229 | ata_scsi_em_message_type_show, NULL); | ||
230 | EXPORT_SYMBOL_GPL(dev_attr_em_message_type); | ||
231 | |||
232 | static ssize_t | ||
233 | ata_scsi_activity_show(struct device *dev, struct device_attribute *attr, | ||
234 | char *buf) | ||
235 | { | ||
236 | struct scsi_device *sdev = to_scsi_device(dev); | ||
237 | struct ata_port *ap = ata_shost_to_port(sdev->host); | ||
238 | struct ata_device *atadev = ata_scsi_find_dev(ap, sdev); | ||
239 | |||
240 | if (ap->ops->sw_activity_show && (ap->flags & ATA_FLAG_SW_ACTIVITY)) | ||
241 | return ap->ops->sw_activity_show(atadev, buf); | ||
242 | return -EINVAL; | ||
243 | } | ||
244 | |||
245 | static ssize_t | ||
246 | ata_scsi_activity_store(struct device *dev, struct device_attribute *attr, | ||
247 | const char *buf, size_t count) | ||
248 | { | ||
249 | struct scsi_device *sdev = to_scsi_device(dev); | ||
250 | struct ata_port *ap = ata_shost_to_port(sdev->host); | ||
251 | struct ata_device *atadev = ata_scsi_find_dev(ap, sdev); | ||
252 | enum sw_activity val; | ||
253 | int rc; | ||
254 | |||
255 | if (ap->ops->sw_activity_store && (ap->flags & ATA_FLAG_SW_ACTIVITY)) { | ||
256 | val = simple_strtoul(buf, NULL, 0); | ||
257 | switch (val) { | ||
258 | case OFF: case BLINK_ON: case BLINK_OFF: | ||
259 | rc = ap->ops->sw_activity_store(atadev, val); | ||
260 | if (!rc) | ||
261 | return count; | ||
262 | else | ||
263 | return rc; | ||
264 | } | ||
265 | } | ||
266 | return -EINVAL; | ||
267 | } | ||
268 | DEVICE_ATTR(sw_activity, S_IWUGO | S_IRUGO, ata_scsi_activity_show, | ||
269 | ata_scsi_activity_store); | ||
270 | EXPORT_SYMBOL_GPL(dev_attr_sw_activity); | ||
271 | |||
193 | static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, | 272 | static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, |
194 | void (*done)(struct scsi_cmnd *)) | 273 | void (*done)(struct scsi_cmnd *)) |
195 | { | 274 | { |
diff --git a/include/linux/libata.h b/include/linux/libata.h index 035f8e1cd0ac..5b247b8a6b3b 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
@@ -169,6 +169,7 @@ enum { | |||
169 | ATA_LFLAG_ASSUME_CLASS = ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB, | 169 | ATA_LFLAG_ASSUME_CLASS = ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB, |
170 | ATA_LFLAG_NO_RETRY = (1 << 5), /* don't retry this link */ | 170 | ATA_LFLAG_NO_RETRY = (1 << 5), /* don't retry this link */ |
171 | ATA_LFLAG_DISABLED = (1 << 6), /* link is disabled */ | 171 | ATA_LFLAG_DISABLED = (1 << 6), /* link is disabled */ |
172 | ATA_LFLAG_SW_ACTIVITY = (1 << 7), /* keep activity stats */ | ||
172 | 173 | ||
173 | /* struct ata_port flags */ | 174 | /* struct ata_port flags */ |
174 | ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */ | 175 | ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */ |
@@ -191,6 +192,10 @@ enum { | |||
191 | ATA_FLAG_AN = (1 << 18), /* controller supports AN */ | 192 | ATA_FLAG_AN = (1 << 18), /* controller supports AN */ |
192 | ATA_FLAG_PMP = (1 << 19), /* controller supports PMP */ | 193 | ATA_FLAG_PMP = (1 << 19), /* controller supports PMP */ |
193 | ATA_FLAG_IPM = (1 << 20), /* driver can handle IPM */ | 194 | ATA_FLAG_IPM = (1 << 20), /* driver can handle IPM */ |
195 | ATA_FLAG_EM = (1 << 21), /* driver supports enclosure | ||
196 | * management */ | ||
197 | ATA_FLAG_SW_ACTIVITY = (1 << 22), /* driver supports sw activity | ||
198 | * led */ | ||
194 | 199 | ||
195 | /* The following flag belongs to ap->pflags but is kept in | 200 | /* The following flag belongs to ap->pflags but is kept in |
196 | * ap->flags because it's referenced in many LLDs and will be | 201 | * ap->flags because it's referenced in many LLDs and will be |
@@ -446,6 +451,15 @@ enum link_pm { | |||
446 | MEDIUM_POWER, | 451 | MEDIUM_POWER, |
447 | }; | 452 | }; |
448 | extern struct device_attribute dev_attr_link_power_management_policy; | 453 | extern struct device_attribute dev_attr_link_power_management_policy; |
454 | extern struct device_attribute dev_attr_em_message_type; | ||
455 | extern struct device_attribute dev_attr_em_message; | ||
456 | extern struct device_attribute dev_attr_sw_activity; | ||
457 | |||
458 | enum sw_activity { | ||
459 | OFF, | ||
460 | BLINK_ON, | ||
461 | BLINK_OFF, | ||
462 | }; | ||
449 | 463 | ||
450 | #ifdef CONFIG_ATA_SFF | 464 | #ifdef CONFIG_ATA_SFF |
451 | struct ata_ioports { | 465 | struct ata_ioports { |
@@ -701,6 +715,7 @@ struct ata_port { | |||
701 | struct timer_list fastdrain_timer; | 715 | struct timer_list fastdrain_timer; |
702 | unsigned long fastdrain_cnt; | 716 | unsigned long fastdrain_cnt; |
703 | 717 | ||
718 | int em_message_type; | ||
704 | void *private_data; | 719 | void *private_data; |
705 | 720 | ||
706 | #ifdef CONFIG_ATA_ACPI | 721 | #ifdef CONFIG_ATA_ACPI |
@@ -792,6 +807,12 @@ struct ata_port_operations { | |||
792 | u8 (*bmdma_status)(struct ata_port *ap); | 807 | u8 (*bmdma_status)(struct ata_port *ap); |
793 | #endif /* CONFIG_ATA_SFF */ | 808 | #endif /* CONFIG_ATA_SFF */ |
794 | 809 | ||
810 | ssize_t (*em_show)(struct ata_port *ap, char *buf); | ||
811 | ssize_t (*em_store)(struct ata_port *ap, const char *message, | ||
812 | size_t size); | ||
813 | ssize_t (*sw_activity_show)(struct ata_device *dev, char *buf); | ||
814 | ssize_t (*sw_activity_store)(struct ata_device *dev, | ||
815 | enum sw_activity val); | ||
795 | /* | 816 | /* |
796 | * Obsolete | 817 | * Obsolete |
797 | */ | 818 | */ |