diff options
author | Cástor Muñoz <cmvidal@gmail.com> | 2014-12-09 19:38:47 +0100 |
---|---|---|
committer | Cástor Muñoz <cmvidal@gmail.com> | 2015-10-07 06:15:04 +0200 |
commit | d20185ac96c4b50ed4de7098a101a31f2b140b82 (patch) | |
tree | 087c7d3f0160864a47bd02dbeb46371c8de01cf8 | |
parent | 32b455851103dea48444243352efdfd693cd3b6b (diff) | |
download | rockbox-d20185ac96c4b50ed4de7098a101a31f2b140b82.tar.gz rockbox-d20185ac96c4b50ed4de7098a101a31f2b140b82.zip |
iPod Classic: reads HDD S.M.A.R.T. data
Adds ata_read_smart() function to storage ATA driver, current
SMART data can be displayed and optionally written to hard
disk using System->Debug menu.
Change-Id: Ie8817bb311d5d956df2f0fbfaf554e2d53e89a93
-rw-r--r-- | apps/debug_menu.c | 202 | ||||
-rw-r--r-- | apps/gui/list.h | 4 | ||||
-rw-r--r-- | firmware/export/ata.h | 106 | ||||
-rw-r--r-- | firmware/export/config/ipod6g.h | 2 | ||||
-rw-r--r-- | firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c | 59 |
5 files changed, 373 insertions, 0 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c index e602b713c3..2b9702299b 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c | |||
@@ -1507,6 +1507,205 @@ static int disk_callback(int btn, struct gui_synclist *lists) | |||
1507 | #endif /* HAVE_ATA_DMA */ | 1507 | #endif /* HAVE_ATA_DMA */ |
1508 | return btn; | 1508 | return btn; |
1509 | } | 1509 | } |
1510 | |||
1511 | #ifdef HAVE_ATA_SMART | ||
1512 | static struct ata_smart_values *smart_data = NULL; | ||
1513 | |||
1514 | static const char * ata_smart_get_attr_name(unsigned char id) | ||
1515 | { | ||
1516 | switch (id) | ||
1517 | { | ||
1518 | case 1: return "Raw Read Error Rate"; | ||
1519 | case 2: return "Throughput Performance"; | ||
1520 | case 3: return "Spin-Up Time"; | ||
1521 | case 4: return "Start/Stop Count"; | ||
1522 | case 5: return "Reallocated Sector Count"; | ||
1523 | case 7: return "Seek Error Rate"; | ||
1524 | case 8: return "Seek Time Performance"; | ||
1525 | case 9: return "Power-On Hours Count"; | ||
1526 | case 10: return "Spin-Up Retry Count"; | ||
1527 | case 12: return "Power Cycle Count"; | ||
1528 | case 192: return "Power-Off Retract Count"; | ||
1529 | case 193: return "Load/Unload Cycle Count"; | ||
1530 | case 194: return "HDA Temperature"; | ||
1531 | case 196: return "Reallocated Event Count"; | ||
1532 | case 197: return "Current Pending Sector Count"; | ||
1533 | case 198: return "Uncorrectable Sector Count"; | ||
1534 | case 199: return "UltraDMA CRC Error Count"; | ||
1535 | case 220: return "Disk Shift"; | ||
1536 | case 222: return "Loaded Hours"; | ||
1537 | case 223: return "Load/Unload Retry Count"; | ||
1538 | case 224: return "Load Friction"; | ||
1539 | case 226: return "Load-In Time"; | ||
1540 | case 240: return "Transfer Error Rate"; /* Fujitsu */ | ||
1541 | /*case 240: return "Head Flying Hours";*/ | ||
1542 | default: return "Unknown Attribute"; | ||
1543 | } | ||
1544 | }; | ||
1545 | |||
1546 | static int ata_smart_get_attr_rawfmt(unsigned char id) | ||
1547 | { | ||
1548 | switch (id) | ||
1549 | { | ||
1550 | case 3: /* Spin-up time */ | ||
1551 | return RAWFMT_RAW16_OPT_AVG16; | ||
1552 | |||
1553 | case 5: /* Reallocated sector count */ | ||
1554 | case 196: /* Reallocated event count */ | ||
1555 | return RAWFMT_RAW16_OPT_RAW16; | ||
1556 | |||
1557 | case 190: /* Airflow Temperature */ | ||
1558 | case 194: /* HDA Temperature */ | ||
1559 | return RAWFMT_TEMPMINMAX; | ||
1560 | |||
1561 | default: | ||
1562 | return RAWFMT_RAW48; | ||
1563 | } | ||
1564 | }; | ||
1565 | |||
1566 | static int ata_smart_attr_to_string( | ||
1567 | struct ata_smart_attribute *attr, char *str, int size) | ||
1568 | { | ||
1569 | uint16_t w[3]; /* 3 words to store 6 bytes of raw data */ | ||
1570 | char buf[size]; /* temp string to store attribute data */ | ||
1571 | int len, slen; | ||
1572 | int id = attr->id; | ||
1573 | |||
1574 | if (id == 0) | ||
1575 | return 0; /* null attribute */ | ||
1576 | |||
1577 | /* align and convert raw data */ | ||
1578 | memcpy(w, attr->raw, 6); | ||
1579 | w[0] = letoh16(w[0]); | ||
1580 | w[1] = letoh16(w[1]); | ||
1581 | w[2] = letoh16(w[2]); | ||
1582 | |||
1583 | len = snprintf(buf, size, ": %u,%u ", attr->current, attr->worst); | ||
1584 | |||
1585 | switch (ata_smart_get_attr_rawfmt(id)) | ||
1586 | { | ||
1587 | case RAWFMT_RAW16_OPT_RAW16: | ||
1588 | len += snprintf(buf+len, size-len, "%u", w[0]); | ||
1589 | if ((w[1] || w[2]) && (len < size)) | ||
1590 | len += snprintf(buf+len, size-len, " %u %u", w[1],w[2]); | ||
1591 | break; | ||
1592 | |||
1593 | case RAWFMT_RAW16_OPT_AVG16: | ||
1594 | len += snprintf(buf+len, size-len, "%u", w[0]); | ||
1595 | if (w[1] && (len < size)) | ||
1596 | len += snprintf(buf+len, size-len, " Avg: %u", w[1]); | ||
1597 | break; | ||
1598 | |||
1599 | case RAWFMT_TEMPMINMAX: | ||
1600 | len += snprintf(buf+len, size-len, "%u -/+: %u/%u", w[0],w[1],w[2]); | ||
1601 | break; | ||
1602 | |||
1603 | case RAWFMT_RAW48: | ||
1604 | default: | ||
1605 | /* shows first 4 bytes of raw data as uint32 LE, | ||
1606 | and the ramaining 2 bytes as uint16 LE */ | ||
1607 | len += snprintf(buf+len, size-len, "%lu", letoh32(*((uint32_t*)w))); | ||
1608 | if (w[2] && (len < size)) | ||
1609 | len += snprintf(buf+len, size-len, " %u", w[2]); | ||
1610 | break; | ||
1611 | } | ||
1612 | /* ignore trailing \0 when truncated */ | ||
1613 | if (len >= size) len = size-1; | ||
1614 | |||
1615 | /* fill return string; when max. size is exceded: first truncate | ||
1616 | attribute name, then attribute data and finally attribute id */ | ||
1617 | slen = snprintf(str, size, "%d ", id); | ||
1618 | if (slen < size) { | ||
1619 | /* maximum space disponible for attribute name, | ||
1620 | including initial space separator */ | ||
1621 | int name_sz = size - (slen + len); | ||
1622 | if (name_sz > 1) { | ||
1623 | len = snprintf(str+slen, name_sz, " %s", | ||
1624 | ata_smart_get_attr_name(id)); | ||
1625 | if (len >= name_sz) len = name_sz-1; | ||
1626 | slen += len; | ||
1627 | } | ||
1628 | snprintf(str+slen, size-slen, "%s", buf); | ||
1629 | } | ||
1630 | |||
1631 | return 1; /* ok */ | ||
1632 | } | ||
1633 | |||
1634 | static bool ata_smart_dump(void) | ||
1635 | { | ||
1636 | int fd; | ||
1637 | |||
1638 | fd = creat("/smart_data.bin", 0666); | ||
1639 | if(fd >= 0) | ||
1640 | { | ||
1641 | write(fd, smart_data, sizeof(struct ata_smart_values)); | ||
1642 | close(fd); | ||
1643 | } | ||
1644 | |||
1645 | fd = creat("/smart_data.txt", 0666); | ||
1646 | if(fd >= 0) | ||
1647 | { | ||
1648 | int i; | ||
1649 | char buf[128]; | ||
1650 | for (i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) | ||
1651 | { | ||
1652 | if (ata_smart_attr_to_string( | ||
1653 | &smart_data->vendor_attributes[i], buf, sizeof(buf))) | ||
1654 | { | ||
1655 | write(fd, buf, strlen(buf)); | ||
1656 | write(fd, "\n", 1); | ||
1657 | } | ||
1658 | } | ||
1659 | close(fd); | ||
1660 | } | ||
1661 | |||
1662 | return false; | ||
1663 | } | ||
1664 | |||
1665 | static int ata_smart_callback(int btn, struct gui_synclist *lists) | ||
1666 | { | ||
1667 | (void)lists; | ||
1668 | |||
1669 | if (btn == ACTION_STD_CANCEL) | ||
1670 | { | ||
1671 | smart_data = NULL; | ||
1672 | return btn; | ||
1673 | } | ||
1674 | |||
1675 | /* read S.M.A.R.T. data only on first redraw */ | ||
1676 | if (!smart_data) | ||
1677 | { | ||
1678 | int i; | ||
1679 | char buf[SIMPLELIST_MAX_LINELENGTH]; | ||
1680 | smart_data = ata_read_smart(); | ||
1681 | simplelist_set_line_count(0); | ||
1682 | simplelist_addline("Id Name: Current,Worst Raw"); | ||
1683 | for (i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { | ||
1684 | if (ata_smart_attr_to_string( | ||
1685 | &smart_data->vendor_attributes[i], buf, sizeof(buf))) | ||
1686 | simplelist_addline(buf); | ||
1687 | } | ||
1688 | } | ||
1689 | |||
1690 | if (btn == ACTION_STD_CONTEXT) | ||
1691 | { | ||
1692 | ata_smart_dump(); | ||
1693 | splashf(HZ, "S.M.A.R.T. data dumped"); | ||
1694 | } | ||
1695 | |||
1696 | return btn; | ||
1697 | } | ||
1698 | |||
1699 | static bool dbg_ata_smart(void) | ||
1700 | { | ||
1701 | struct simplelist_info info; | ||
1702 | simplelist_info_init(&info, "S.M.A.R.T. Data [CONTEXT to dump]", 1, NULL); | ||
1703 | info.action_callback = ata_smart_callback; | ||
1704 | info.hide_selection = true; | ||
1705 | info.scroll_all = true; | ||
1706 | return simplelist_show_list(&info); | ||
1707 | } | ||
1708 | #endif /* HAVE_ATA_SMART */ | ||
1510 | #else /* No SD, MMC or ATA */ | 1709 | #else /* No SD, MMC or ATA */ |
1511 | static int disk_callback(int btn, struct gui_synclist *lists) | 1710 | static int disk_callback(int btn, struct gui_synclist *lists) |
1512 | { | 1711 | { |
@@ -2383,6 +2582,9 @@ static const struct { | |||
2383 | { "View disk info", dbg_disk_info }, | 2582 | { "View disk info", dbg_disk_info }, |
2384 | #if (CONFIG_STORAGE & STORAGE_ATA) | 2583 | #if (CONFIG_STORAGE & STORAGE_ATA) |
2385 | { "Dump ATA identify info", dbg_identify_info}, | 2584 | { "Dump ATA identify info", dbg_identify_info}, |
2585 | #ifdef HAVE_ATA_SMART | ||
2586 | { "View/Dump S.M.A.R.T. data", dbg_ata_smart}, | ||
2587 | #endif | ||
2386 | #endif | 2588 | #endif |
2387 | #endif | 2589 | #endif |
2388 | { "Metadata log", dbg_metadatalog }, | 2590 | { "Metadata log", dbg_metadatalog }, |
diff --git a/apps/gui/list.h b/apps/gui/list.h index 0f2f51a424..ef08a9e220 100644 --- a/apps/gui/list.h +++ b/apps/gui/list.h | |||
@@ -257,7 +257,11 @@ struct simplelist_info { | |||
257 | }; | 257 | }; |
258 | 258 | ||
259 | #define SIMPLELIST_MAX_LINES 32 | 259 | #define SIMPLELIST_MAX_LINES 32 |
260 | #ifdef HAVE_ATA_SMART | ||
261 | #define SIMPLELIST_MAX_LINELENGTH 48 | ||
262 | #else | ||
260 | #define SIMPLELIST_MAX_LINELENGTH 32 | 263 | #define SIMPLELIST_MAX_LINELENGTH 32 |
264 | #endif | ||
261 | 265 | ||
262 | /** The next three functions are used if the text is mostly static. | 266 | /** The next three functions are used if the text is mostly static. |
263 | These should be called in the action callback for the list. | 267 | These should be called in the action callback for the list. |
diff --git a/firmware/export/ata.h b/firmware/export/ata.h index 0bcb144e63..6f0d0b699f 100644 --- a/firmware/export/ata.h +++ b/firmware/export/ata.h | |||
@@ -25,6 +25,107 @@ | |||
25 | #include "config.h" /* for HAVE_MULTIVOLUME or not */ | 25 | #include "config.h" /* for HAVE_MULTIVOLUME or not */ |
26 | #include "mv.h" /* for IF_MV() and friends */ | 26 | #include "mv.h" /* for IF_MV() and friends */ |
27 | 27 | ||
28 | #ifdef HAVE_ATA_SMART | ||
29 | /* S.M.A.R.T. headers from smartmontools-5.42 */ | ||
30 | #define NUMBER_ATA_SMART_ATTRIBUTES 30 | ||
31 | |||
32 | struct ata_smart_attribute { | ||
33 | unsigned char id; | ||
34 | /* meaning of flag bits: see MACROS just below */ | ||
35 | /* WARNING: MISALIGNED! */ | ||
36 | unsigned short flags; | ||
37 | unsigned char current; | ||
38 | unsigned char worst; | ||
39 | unsigned char raw[6]; | ||
40 | unsigned char reserv; | ||
41 | } __attribute__((packed)); | ||
42 | |||
43 | /* MACROS to interpret the flags bits in the previous structure. */ | ||
44 | /* These have not been implemented using bitflags and a union, to make */ | ||
45 | /* it portable across bit/little endian and different platforms. */ | ||
46 | |||
47 | /* 0: Prefailure bit */ | ||
48 | |||
49 | /* From SFF 8035i Revision 2 page 19: Bit 0 (pre-failure/advisory bit) */ | ||
50 | /* - If the value of this bit equals zero, an attribute value less */ | ||
51 | /* than or equal to its corresponding attribute threshold indicates an */ | ||
52 | /* advisory condition where the usage or age of the device has */ | ||
53 | /* exceeded its intended design life period. If the value of this bit */ | ||
54 | /* equals one, an attribute value less than or equal to its */ | ||
55 | /* corresponding attribute threshold indicates a prefailure condition */ | ||
56 | /* where imminent loss of data is being predicted. */ | ||
57 | #define ATTRIBUTE_FLAGS_PREFAILURE(x) (x & 0x01) | ||
58 | |||
59 | /* 1: Online bit */ | ||
60 | |||
61 | /* From SFF 8035i Revision 2 page 19: Bit 1 (on-line data collection */ | ||
62 | /* bit) - If the value of this bit equals zero, then the attribute */ | ||
63 | /* value is updated only during off-line data collection */ | ||
64 | /* activities. If the value of this bit equals one, then the attribute */ | ||
65 | /* value is updated during normal operation of the device or during */ | ||
66 | /* both normal operation and off-line testing. */ | ||
67 | #define ATTRIBUTE_FLAGS_ONLINE(x) (x & 0x02) | ||
68 | |||
69 | /* The following are (probably) IBM's, Maxtors and Quantum's definitions for the */ | ||
70 | /* vendor-specific bits: */ | ||
71 | /* 2: Performance type bit */ | ||
72 | #define ATTRIBUTE_FLAGS_PERFORMANCE(x) (x & 0x04) | ||
73 | |||
74 | /* 3: Errorrate type bit */ | ||
75 | #define ATTRIBUTE_FLAGS_ERRORRATE(x) (x & 0x08) | ||
76 | |||
77 | /* 4: Eventcount bit */ | ||
78 | #define ATTRIBUTE_FLAGS_EVENTCOUNT(x) (x & 0x10) | ||
79 | |||
80 | /* 5: Selfpereserving bit */ | ||
81 | #define ATTRIBUTE_FLAGS_SELFPRESERVING(x) (x & 0x20) | ||
82 | |||
83 | /* 6-15: Reserved for future use */ | ||
84 | #define ATTRIBUTE_FLAGS_OTHER(x) ((x) & 0xffc0) | ||
85 | |||
86 | struct ata_smart_values | ||
87 | { | ||
88 | unsigned short int revnumber; | ||
89 | struct ata_smart_attribute vendor_attributes [NUMBER_ATA_SMART_ATTRIBUTES]; | ||
90 | unsigned char offline_data_collection_status; | ||
91 | unsigned char self_test_exec_status; | ||
92 | unsigned short int total_time_to_complete_off_line; | ||
93 | unsigned char vendor_specific_366; | ||
94 | unsigned char offline_data_collection_capability; | ||
95 | unsigned short int smart_capability; | ||
96 | unsigned char errorlog_capability; | ||
97 | unsigned char vendor_specific_371; | ||
98 | unsigned char short_test_completion_time; | ||
99 | unsigned char extend_test_completion_time; | ||
100 | unsigned char conveyance_test_completion_time; | ||
101 | unsigned char reserved_375_385[11]; | ||
102 | unsigned char vendor_specific_386_510[125]; | ||
103 | unsigned char chksum; | ||
104 | } __attribute__((packed)); | ||
105 | |||
106 | /* Raw attribute value print formats */ | ||
107 | enum ata_attr_raw_format | ||
108 | { | ||
109 | RAWFMT_DEFAULT, | ||
110 | RAWFMT_RAW8, | ||
111 | RAWFMT_RAW16, | ||
112 | RAWFMT_RAW48, | ||
113 | RAWFMT_HEX48, | ||
114 | RAWFMT_RAW64, | ||
115 | RAWFMT_HEX64, | ||
116 | RAWFMT_RAW16_OPT_RAW16, | ||
117 | RAWFMT_RAW16_OPT_AVG16, | ||
118 | RAWFMT_RAW24_DIV_RAW24, | ||
119 | RAWFMT_RAW24_DIV_RAW32, | ||
120 | RAWFMT_SEC2HOUR, | ||
121 | RAWFMT_MIN2HOUR, | ||
122 | RAWFMT_HALFMIN2HOUR, | ||
123 | RAWFMT_MSEC24_HOUR32, | ||
124 | RAWFMT_TEMPMINMAX, | ||
125 | RAWFMT_TEMP10X, | ||
126 | }; | ||
127 | #endif /* HAVE_ATA_SMART */ | ||
128 | |||
28 | struct storage_info; | 129 | struct storage_info; |
29 | 130 | ||
30 | void ata_enable(bool on); | 131 | void ata_enable(bool on); |
@@ -69,4 +170,9 @@ int ata_spinup_time(void); /* ticks */ | |||
69 | int ata_get_dma_mode(void); | 170 | int ata_get_dma_mode(void); |
70 | #endif /* HAVE_ATA_DMA */ | 171 | #endif /* HAVE_ATA_DMA */ |
71 | 172 | ||
173 | #ifdef HAVE_ATA_SMART | ||
174 | /* Returns current S.M.A.R.T. data */ | ||
175 | void* ata_read_smart(void); | ||
176 | #endif | ||
177 | |||
72 | #endif /* __ATA_H__ */ | 178 | #endif /* __ATA_H__ */ |
diff --git a/firmware/export/config/ipod6g.h b/firmware/export/config/ipod6g.h index 7e7025e157..0a40108699 100644 --- a/firmware/export/config/ipod6g.h +++ b/firmware/export/config/ipod6g.h | |||
@@ -201,6 +201,8 @@ | |||
201 | 201 | ||
202 | #define STORAGE_NEEDS_ALIGN | 202 | #define STORAGE_NEEDS_ALIGN |
203 | 203 | ||
204 | #define HAVE_ATA_SMART | ||
205 | |||
204 | /* define this if the device has larger sectors when accessed via USB */ | 206 | /* define this if the device has larger sectors when accessed via USB */ |
205 | /* (only relevant in disk.c, fat.c now always supports large virtual sectors) */ | 207 | /* (only relevant in disk.c, fat.c now always supports large virtual sectors) */ |
206 | //#define MAX_LOG_SECTOR_SIZE 4096 | 208 | //#define MAX_LOG_SECTOR_SIZE 4096 |
diff --git a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c index c629fd583a..53ec45ec6d 100644 --- a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c | |||
@@ -50,6 +50,9 @@ | |||
50 | /** static, private data **/ | 50 | /** static, private data **/ |
51 | static uint8_t ceata_taskfile[16] STORAGE_ALIGN_ATTR; | 51 | static uint8_t ceata_taskfile[16] STORAGE_ALIGN_ATTR; |
52 | static uint16_t ata_identify_data[0x100] STORAGE_ALIGN_ATTR; | 52 | static uint16_t ata_identify_data[0x100] STORAGE_ALIGN_ATTR; |
53 | #ifdef HAVE_ATA_SMART | ||
54 | static uint16_t ata_smart_data[0x100] STORAGE_ALIGN_ATTR; | ||
55 | #endif | ||
53 | static bool ceata; | 56 | static bool ceata; |
54 | static bool ata_swap; | 57 | static bool ata_swap; |
55 | static bool ata_lba48; | 58 | static bool ata_lba48; |
@@ -1211,6 +1214,62 @@ int ata_init(void) | |||
1211 | return 0; | 1214 | return 0; |
1212 | } | 1215 | } |
1213 | 1216 | ||
1217 | #ifdef HAVE_ATA_SMART | ||
1218 | static int ata_smart(uint16_t* buf) | ||
1219 | { | ||
1220 | mutex_lock(&ata_mutex); | ||
1221 | ata_power_up(); | ||
1222 | |||
1223 | if (ceata) | ||
1224 | { | ||
1225 | memset(ceata_taskfile, 0, 16); | ||
1226 | ceata_taskfile[0xc] = 0x4f; | ||
1227 | ceata_taskfile[0xd] = 0xc2; | ||
1228 | ceata_taskfile[0xe] = 0x40; /* Device/Head Register, bit6: 0->CHS, 1->LBA */ | ||
1229 | ceata_taskfile[0xf] = 0xb0; | ||
1230 | PASS_RC(ceata_wait_idle(), 3, 0); | ||
1231 | if (((uint8_t*)ata_identify_data)[54] != 'A') /* Model != aAmsung */ | ||
1232 | { | ||
1233 | ceata_taskfile[0x9] = 0xd8; /* SMART enable operations */ | ||
1234 | PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 1); | ||
1235 | PASS_RC(ceata_check_error(), 3, 2); | ||
1236 | } | ||
1237 | ceata_taskfile[0x9] = 0xd0; /* SMART read data */ | ||
1238 | PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 3); | ||
1239 | PASS_RC(ceata_rw_multiple_block(false, buf, 1, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 3, 4); | ||
1240 | } | ||
1241 | else | ||
1242 | { | ||
1243 | int i; | ||
1244 | uint32_t old = ATA_CFG; | ||
1245 | ATA_CFG |= BIT(6); /* 16bit big-endian */ | ||
1246 | PASS_RC(ata_wait_for_not_bsy(10000000), 3, 5); | ||
1247 | ata_write_cbr(&ATA_PIO_DAD, 0); | ||
1248 | ata_write_cbr(&ATA_PIO_FED, 0xd0); | ||
1249 | ata_write_cbr(&ATA_PIO_SCR, 0); | ||
1250 | ata_write_cbr(&ATA_PIO_LLR, 0); | ||
1251 | ata_write_cbr(&ATA_PIO_LMR, 0x4f); | ||
1252 | ata_write_cbr(&ATA_PIO_LHR, 0xc2); | ||
1253 | ata_write_cbr(&ATA_PIO_DVR, BIT(6)); | ||
1254 | ata_write_cbr(&ATA_PIO_CSD, 0xb0); | ||
1255 | PASS_RC(ata_wait_for_start_of_transfer(10000000), 3, 6); | ||
1256 | for (i = 0; i < 0x100; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR); | ||
1257 | ATA_CFG = old; | ||
1258 | } | ||
1259 | |||
1260 | ata_set_active(); | ||
1261 | mutex_unlock(&ata_mutex); | ||
1262 | |||
1263 | return 0; | ||
1264 | } | ||
1265 | |||
1266 | void* ata_read_smart(void) | ||
1267 | { | ||
1268 | ata_smart(ata_smart_data); | ||
1269 | return ata_smart_data; | ||
1270 | } | ||
1271 | #endif /* HAVE_ATA_SMART */ | ||
1272 | |||
1214 | #ifdef CONFIG_STORAGE_MULTI | 1273 | #ifdef CONFIG_STORAGE_MULTI |
1215 | static int ata_num_drives(int first_drive) | 1274 | static int ata_num_drives(int first_drive) |
1216 | { | 1275 | { |