Здравствуйте, по работе пришлось разбираться с этой шиной и возникли некоторые вопросы. В ходе изучения шины у меня сложилось следующее представление о ней если я в чем-то ошибусь подправьте меня. Для работы используется южный мост, и перед началом работы надо понять включена ли она, для этого проверить биты в регистрах Host Configuration Register бит 0 и Chipset Configuration Registers-> Function Disable бит 3. Потом из конфигурационого пространства PCI считать значение SMBus Base и относительно него обращаться к регистрам контроллера SMBus. Но в интернете я нашел примеры только считывания SPD планок памяти, а вот примеров с другими устройствами нет. И возник вопрос как определить какие устройства еще подключены к шине, какие их адреса и как к ним обратиться. И я не понял как формируется запрос к какому либо устройству и в какие регистры что пишется. Этом вопрос вроде освещен в спецификации на шину, но не понятно(
Я, например, вот с этого начинал http://www.xlevel.ru/forum/YaBB.cgi?board=programming;action=display;num=1186920031 Запомни этот ник, Pavia. Вот этот чел уж точно занет все по этой теме. Ищи его творения и на этом форуме тоже...
+ полезно будет http://www.wasm.ru/forum/viewtopic.php?pid=388213#p388213 http://www.xlevel.ru/forum/YaBB.cgi?board=programming;action=display;num=1165931685
В общем для чтения SPD я написал следующую программу на C++. Только она все время выводит 26h. Почему пока не знаю... Если кому не лень проверьте код и алгоритм. Для ее рнаботы нужен драйвер ввода вывода, я использовал giveio.sys Код (Text): #include "stdafx.h" #define PCI_ADR 0xCF8 #define PCI_DATA 0xCFC #define RCBA_ADR 0x8000F8F0 //B00:D31:F00 offset f0h #define SMBus_REG 0x8000FB00 //B00:D31:F03 offset not const int CheckFunctionDisableRegister(); DWORD GetSMBusRegisterDataDword(int offset); DWORD GetSMBusRegisterDataByte(int offset); DWORD GetSMBusRegisterDataWord(int offset); void ClearSMBusStatusReg(int SMBusBase); DWORD ShowSMBusControllerInfo(); void ReadSpd(int SMBusBase); int inline openIO(){ HANDLE h; h = CreateFile(L"\\\\.\\giveio", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(h == INVALID_HANDLE_VALUE) { printf("Couldn't access giveio device\n"); return 1; } CloseHandle(h); return 0; } int _tmain(int argc, _TCHAR* argv[]) { if(openIO()) return -1; //для южного моста ich9 //получаем базовый адрес регистров конфигурации чипсета PCI Configuration Registers (LPC I/F—D31:F0) //для этого считываем регистр RCBA if (CheckFunctionDisableRegister() < 0) { printf("SMBus host disabled\n"); } DWORD SMBb = ShowSMBusControllerInfo(); ClearSMBusStatusReg(SMBb); _outp(0xEB, 1); ReadSpd(SMBb); } int CheckFunctionDisableRegister() { //получаем базовый адрес регистров конфигурации чипсета PCI Configuration Registers (LPC I/F—D31:F0) //для этого считываем регистр RCBA _outpd(PCI_ADR, RCBA_ADR); int RCBARegister = _inpd(PCI_DATA);//считали RCBA printf("RCBA register = 0x%08lX\n", RCBARegister); if (RCBARegister & 1 == 0) { return -2; //When set, enables the range specified in BA to be claimed as the Root Complex Register Block. } _asm { shr RCBARegister, 14 } printf("RCBA base address= 0x%08lX\n", RCBARegister); RCBARegister += 0x3418; //Function disable register //считываем FDR DWORD FDR = _inpd(RCBARegister); printf("FDR register = 0x%08lX\n", FDR); if (FDR & 0x00000008 != 0) { // return -3; //Выключен контроллер SMBus } return 1; } DWORD GetSMBusRegisterDataDword(int offset) { _outpd(PCI_ADR, SMBus_REG+offset); return _inpd(PCI_DATA); } DWORD GetSMBusRegisterDataWord(int offset) { _outpd(PCI_ADR, SMBus_REG+offset); DWORD b = _inpd(PCI_DATA); _asm{ push eax mov eax, b rol eax, 16 mov b, eax pop eax } return b; } DWORD GetSMBusRegisterDataByte( int offset ) { _outpd(PCI_ADR, SMBus_REG+offset); DWORD b = _inpd(PCI_DATA); _asm{ push eax mov eax, b bswap eax mov b, eax pop eax } return b; } DWORD ShowSMBusControllerInfo() { DWORD data = GetSMBusRegisterDataWord(0); printf("0x00 %08lX\n", data); data = GetSMBusRegisterDataWord(4); printf("0x04 %08lX\n", data); data = GetSMBusRegisterDataByte(8); printf("0x08 %08lX\n", data); data = GetSMBusRegisterDataDword(0xc); printf("0x0c %08lX\n", data); data = GetSMBusRegisterDataDword(0x10); printf("0x10 %08lX\n", data); data = GetSMBusRegisterDataDword(0x14); printf("0x14 %08lX\n", data); data = GetSMBusRegisterDataDword(0x18); printf("0x18 %08lX\n", data); data = GetSMBusRegisterDataDword(0x1c); printf("0x1c %08lX\n", data); data = GetSMBusRegisterDataDword(0x20); printf("0x20 %08lX\n", data); data = GetSMBusRegisterDataDword(0x24); printf("0x24 %08lX\n", data); data = GetSMBusRegisterDataDword(0x28); printf("0x28 %08lX\n", data); data = GetSMBusRegisterDataDword(0x2c); printf("0x2c %08lX\n", data); data = GetSMBusRegisterDataDword(0x30); printf("0x30 %08lX\n", data); data = GetSMBusRegisterDataDword(0x34); printf("0x34 %08lX\n", data); data = GetSMBusRegisterDataDword(0x38); printf("0x38 %08lX\n", data); data = GetSMBusRegisterDataDword(0x3c); printf("0x3c %08lX\n", data); data = GetSMBusRegisterDataByte(0x40); printf("0x40 %08lX\n\n", data); DWORD SMBusBaseAddress = GetSMBusRegisterDataDword(0x20); SMBusBaseAddress--; printf("SMBus Base Address = 0x%08lX\n", SMBusBaseAddress); DWORD SMBusPCICommandReg = GetSMBusRegisterDataWord(0x4); _asm shr SMBusPCICommandReg, 16 printf("SMBus PCICommandReg = 0x%08lX\n", SMBusPCICommandReg); DWORD SMBusHostConfigurationReg = GetSMBusRegisterDataByte(0x40); printf("SMBus Host Configuration Register = 0x%08lX\n", SMBusHostConfigurationReg); if (SMBusPCICommandReg & 0x1 == 0) { return -4; } if (SMBusHostConfigurationReg & 0x1 == 0) { return -5; } return SMBusBaseAddress; } void ClearSMBusStatusReg(int SMBusBase) { BYTE a; do { a = _inp(SMBusBase); //printf("SMBus IO Status reg = 0x%02lX\n", a); _outp(0xEB, a); _outp(SMBusBase, a); _outp(0xEB, a); a = a & 0xBF; } while (a != 0); } void ReadSpd(int SMBusBase) { //цикл по модулям dimm for(BYTE i = 0; i<4; i++) { printf("\nDIMM %X\n", i); //цикл по ячейкам SPD for (int j = 0; j< 256; j++) { ClearSMBusStatusReg(SMBusBase); if (j%16 == 0) { printf("\n"); } int Reg = SMBusBase + 4; _outp(Reg, 0xA1+i*2); Reg--; //+3 _outp(Reg, j); Reg--; //+2 _outp(Reg, 0x48); Reg = SMBusBase; //Начинаем ожидание готовности контроллера //Порядок проверок важен? BYTE a=0; /*Этот код взят из кода Pavia _asm{ xor cx,cx loc_E931C: mov edx, SMBusBase in al,dx out 0EBh,al ;{Задержка} test al,4 ; {ошибоки устройства} jnz loc_E932D test al,2 ; {Произошло прерывание} jz loc_E932B test al,80 ;{данные пришли} jnz loc_E932D loc_E932B: loop loc_E931C loc_E932D: out dx,al ;{+0 Пишем в регистор статуса, что обнуляет его} add dx,5 ;{+5 Регистр данных Date0} in al,dx mov a,al } printf("%02lX ", a);*/ BYTE Status; do { _outp(0xEB, 1); Status = _inp(Reg); _outp(0xEB, 1); /*if((Status & 4) != 0) { printf("bit DEV_ERR\n"); printf("Status reg: 0x%02lX\n\n", Status); break; } if((Status & 2) != 1) { printf("bit INTR error\n"); printf("Status reg: 0x%02lX\n\n", Status); break; }*/ } while (Status & 0x80 != 0x80); /* if((Status & 2) != 1) { break; }*/ BYTE data = _inp(SMBusBase + 5); printf("%02lX ", data); } _getch(); } }
ну раз все видел, тогда посмотри еще раз и разбери по полочкам модулёк с метки @ICH_SetCmdSMBus: до RET при этом DataSheet на твой ICH строго обязателен...
У меня этих даташитов, как ...)))) Как нибудь можно сравнить то, что возвращает прога с истинным значением? просто я вернул 256 байт и они похожи на правду, но несколько значений там смущают. Мне бы сравнить с неразобранной эталонной SPD, а как ее получить не знаю....
drem1lin Сэр, Вы меня удивляете до невозможности, её Богу! А что гугла нет? Чем НЕ устраивает дока по SPD структуре и тот же SpeedFAN? или CPU-Z? или EVEREST? или SiSoftware Sandra или HWinFO? Да в конце концов давно существует DocMemory SPD Reader 1.4???? Тем более, что он "в точку тот" ибо: "Limitations: Currently only supports only Intel and SiS DDR chips." Что ссылку дать? Ну вот тут ftp://majorgeeks.mirror.internode.on.net/memory/spdreaderv1_4.exe http://www.hwinfo.com/download32.html# http://www.hwinfo.com/files/hw32_358.zip http://www.overclockers.com/ctspd-memory-readout-utility/ Чем НЕ устраивает приведенный РАБОЧИЙ исходник? НЕ тем языком писаный?
Я вот что хочу уточнить, может ты заодно и подскажешь.. У тебя там приведен вариант кода от Pavia, он отличается от того на который я давал ссылку, вплане определения момента правильного приема данных контроллером от устройства по шине I2C. Так вот какой по твоему более правильный? И еще в коде Pavia этот момент определяется так (что вроде как не верно) Код (Text): test al,80 ;{данные пришли} jnz loc_E932D У тебя как то так Код (Text): while (Status & 0x80 != 0x80); А вообще я код на сях чуть хуже воспринимаю чем на asm.
Если не против я задам в твоем топике вопрос? Дело в том что у меня BIOS запрещает контроллер SMB перед загрузкой ОС и следовательно код для "голой" системы не работает, для Win все нормально. У тебя в функции CheckFunctionDisableRegister() это как раз проверяется. Так вот, что будет страшного если в этой функции в регистре FDR (Function disable register) очищать бит 7?
Protorus, это тот, который проверен для конкретного чипсета южного моста. Только детальная сверка даташитов, например ICH4, 6, 7, 9, 10, и конкретно в данном аспекте битов регистра "HST_STS—Host Status Register" помогут точно сделать вывод, что более... даже не то чтобы правильно, а что более совместимо, универсально для разных чипсетов, а значит и машин. Конкретный бит 7 это "Byte Done Status" и мой беглый взгляд на даташиты ICH6 и ICH10 существенных различий не замечает... Если так беспокоит программная реализация ожидания готовности, то вот мой дизасменный код одного из интеловских биосов по данному поводу: Код (Text): ............. xor cx, cx mov dx, 500h loc_E931C: in al, dx out 0EBh, al test al, 4 jnz loc_E932D test al, 2 jz loc_E932B test al, 80h jnz loc_E932D loc_E932B: loop loc_E931C loc_E932D: out dx, al mov dx, 507h in al, dx ; СОБСТВЕННО ЧТЕНИЕ ПРИНЯТОГО БАЙТА! pop cx ret а вот пример этого же места из некоторых сорцов биоса, которые уже несколько лет гуляют в нете и может быть даже на этом форуме ссылка была... Точно знаю, что это AWARD, так вот это дело: Код (Text): Chk_SMBus_READY: mov dx, SMBus_Port + 0 clc mov cx, 0x100 Chk_I2c_OK: in al, dx ;get status NEWIODELAY or al, al jz Clear_final test al, 4 jnz SMBus_Err test al, 1 ;busy ? jz Not_Smbusy ror ecx, 16 mov cx, 0x1000 @@: loop @B ror ecx, 16 Not_Smbusy: out dx,al loop Chk_I2c_OK SMBus_Err: out dx, al ;clear status NEWIODELAY in al, dx test al, 4 ;device error ? jnz SMBus_Err stc Clear_final: ret ну и еще 2 копейки по поводу правильности и дотошности подхода. Наиболее правильно с точки зрения именно ожидания, считаю это решение: Код (Text): mov cx, 800h loc_EAFCF: in al, dx out 0EBh, al out dx, al test al, 2 jnz loc_EAFE5 and al, 0BFh or al, al jz loc_EAFE5 test al, 4 jnz loc_EAFE3 loop loc_EAFCF loc_EAFE3: stc ret loc_EAFE5: clc ret собственно имеется ввиду это Код (Text): in al, dx out 0EBh, al out dx, al т.к. согласно даташитам биты нулятся обратной записью... но тут сразу они все нулятся после чтения, вне зависимости от дальнейшей паузы или ветвления или еще чего либо, но далее по ходу можно быть наверняка уверенным, что в данном регистре при новом его прочтении будут НАВЕРНЯКА правильные статусы. Ну вот и все, что я хотел молвить "...думайте сами, решайте сами, иметь или не иметь"
Код Pavia оказался более правильным, должно, кстати, быть так: Код (Text): while ((Status & 0x80) != 0x80); Мой код оказалось возвращает сначало какой-то левый байт в начале. То есть мой результат оказался смещенным на 1 байт.
Самое странное что эта проверка выдает что SMBus отключен, но при этом я спокойно его считываю. Вот мне тоже интересно получить ответ, как и на что он влияет. Да и правильно ли я адрес этого регистра получаю большой вопрос. VaStaNi можешь просветить? Если надо могу доработанную версию программы выложить, но пока без этого регистра
Спасибо за такой развернутый ответ. Тогда можешь посказать где в спецификации можно найти адреса других устройств подключенных к SMBus? Я читал спецификацию 2.0, там в конце приведена табличка, но я в ней ничего не понял( там все в основном ссылается к предыдущим спецификациям. Да и мне интересны свободные адреса.. Не знаешь где их посмотреть?
drem1lin Не хоте вас прерывать, вы так мирно беседовали. В ICH9 контроллер не отключается только PCI CFG запрещается. ICH10 не смотрел. По поводу адресов. В спецификации ты их не найдешь. Каждый производитель волен выбрать адреса по своему усмотрению. Но в стандарте часть за резервирована. Адрес там записан в битовом формате. Нулевой бит не показан так как он за адрес не отвечает, а только за запись или чтение. А другие адреса можно узнать из различных спецификаций. Но записывают его в разных форматах некоторые нулевой бит пишут и ставят 0. А некоторые его не записывают. К примеру генератор частот имеет 1101 0010 = D2h а если без нулевого бита то имеем 69h А еще в материках PIIX4 для пентиумов вторых он был реализован с ошибкой. В результате код которой выше приводит к зависанию. Надо менять команду чтения. SPD используют адреса 50h, 51h, 52h, 53h без младшего бита. 1010 000 1010 001 1010 010 1010 011 Теперь пройдемся по спецификации. Адреса без нулевого бита. 0001 000 =10h - это адрес хоста или ведущего контроллера именно его мы и программируем этот адрес используют подчиненные устройства если хотят что-то от хоста. Используется в ARP протоколе. 0001 100 =18h - это адрес тревоги. Если кому то из подчиненных устройств надо СРОЧНО передать сообщение он должен использовать адрес. Датчик должен вопить на этот адрес в случае перегрева. А вот сам датчик, а вернее Super IO висит на адресе 0101 111 =2Fh - адрес не устоявшейся. 1111 0XX признак 10-bit адреса мало кто поддерживает XX задает старшие биты адреса следующий байт оставшиеся. 1111 1XX зарезервированы 0001 001, 0001 010, 0001 011 для батареек можно встретить на ноутбуках и не только. Остальные адреса свободны. Хотя ряд хотят занять для IPMI.
Перечитал все посты несколько раз, и понял что ничего не понял.. Для drem1lin (из твоего кода) Код (Text): test al,80 ;{данные пришли} <- здесь 80d = 0x50 = 01010000b jnz loc_E932D Здесь вроде как у Pavia опечатка, т.к. по идее должно быть 0x80. Но если, вдруг, у Pavia это верно, то неверно у тебя. Чем отличается это Код (Text): while (Status & 0x80 != 0x80); и это Код (Text): while ((Status & 0x80) != 0x80); я не вижу. Я бит 7 (0x80) у себя не проверяю вообще, и вот почему. Во первых, на ICH8, он у меня никогда не устанавливается, может потому что используется протокол 010 = Byte Data. И твой код с while приведет у меня зависанию (странно что у тебя работает), код Pavia (в разных его вариантах) - нет, т.к. там используется счетчик. Во вторых, мой код так же используется на мосту ATI SB700, а здесь биты 7 - 5 вообще зарезервированы, а 4 - 0 такие же как на ICH8. Но, в любом случае результат чтения байта из eeprom будет не верный. Для Pavia (из твоего кода http://www.xlevel.ru/forum/YaBB.cgi?board=programming;action=display;num=1186920031) Код (Text): ... MOV CX, 0800h ; Время выполнения команды 0.0012207 секунд для передачи 40ит в 2 конца 32768 бит/с .Loop: NEWIODELAY IN AL, DX TEST AL, 2 JNZ .ok LOOP .Loop ADD DX,2 MOV AL,2 ; Тайм аут истек, выставляем Kill бит OUT DX,AL SUB DX,2 IN AL, DX .ok: NEWIODELAY OUT DX, AL ; Очищаем выставленные биты ADD DX, 5 ; Data0 IN AL, DX ... Является ли этот код минимальным и достаточным для определения момента окончания чтения хостом по шине I2С? Нужно ли выставлять бит KILL, если истек тайм-аут (в выше приведенных здесь вариантах, например из сорцов биоса) это не делается? Для VaStaNi Спасибо за варианты по поводу правильности и дотошности подхода. Кое-что попробую в своем коде, в частности сброс статусов в цикле и проверку на все брошенные биты в статусе.
По поводу Динамическое назначение адреса утройства — основано на уникальном идентификаторе устройства UDID (Unique Device Identifier) — 128-битной структуре, содержащей описание возможностей, версию, идентификаторы производителя, устройства, подсистемы и специфическую информацию. Чтение идентификатора выполняется ведущим устройством ARP по протоколу блочного чтения по «дежурному» адресу SMBus. Так вот, адреса всех устройств подключенных к SMBus можно определить опросом этих устройств (перебором всех возможных адресов). А вот можно ли как-то запросить у хоста (ведущего устройства ведающим «переучетом» всех устройств на шине) этот индификатор для устройсва по конкретному адресу? А уж из него узнать производителя устройства и его идентификатор.
В ICH9 контроллер не отключается только PCI CFG запрещается. Что означает, что PCI CFG запрещается? в ICH 6 вроде запрещается все. То есть по сути этот бит можно не смотреть? Спасибо за информацию по адресам.