i2c.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /*++
  2. Copyright (c) 2014 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. i2c.c
  9. Abstract:
  10. This module implements I2C bus support for OMAP3 and OMAP4 SoCs. This
  11. file should be removed when firmware enables this hardware.
  12. Author:
  13. Evan Green 12-Mar-2014
  14. Environment:
  15. Kernel
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <minoca/kernel/driver.h>
  21. #include "sdomap4.h"
  22. //
  23. // --------------------------------------------------------------------- Macros
  24. //
  25. #define OMAP_I2C_READ_REGISTER(_Register) \
  26. HlReadRegister32(OmapI2cBase + (_Register))
  27. #define OMAP_I2C_WRITE_REGISTER(_Register, _Value) \
  28. HlWriteRegister32(OmapI2cBase + (_Register), (_Value))
  29. //
  30. // ---------------------------------------------------------------- Definitions
  31. //
  32. #define OMAP44XX_L4_PER_BASE 0x48000000
  33. #define I2C_BASE (OMAP44XX_L4_PER_BASE + 0x70000)
  34. #define I2C_SIZE 0x1000
  35. #define I2C_BUSY_RETRY_COUNT 10000
  36. #define I2C_STATUS_RETRY_COUNT 10000
  37. //
  38. // Define the I2C timeout in seconds.
  39. //
  40. #define I2C_TIMEOUT 1
  41. //
  42. // Control register bit definitions.
  43. //
  44. #define OMAP_I2C_CONTROL_ENABLE (1 << 15)
  45. #define OMAP_I2C_CONTROL_MASTER (1 << 10)
  46. #define OMAP_I2C_CONTROL_TRANSMIT (1 << 9)
  47. #define OMAP_I2C_CONTROL_STOP_CONDITION (1 << 1)
  48. #define OMAP_I2C_CONTROL_START_CONDITION (1 << 0)
  49. //
  50. // Interrupt bit definitions.
  51. //
  52. #define OMAP_I2C_INTERRUPT_ARBITRATION_LOST (1 << 0)
  53. #define OMAP_I2C_INTERRUPT_NACK (1 << 1)
  54. #define OMAP_I2C_INTERRUPT_ACCESS_READY (1 << 2)
  55. #define OMAP_I2C_INTERRUPT_RECEIVE_READY (1 << 3)
  56. #define OMAP_I2C_INTERRUPT_TRANSMIT_READY (1 << 4)
  57. #define OMAP_I2C_STATUS_BUSY (1 << 12)
  58. //
  59. // ------------------------------------------------------ Data Type Definitions
  60. //
  61. typedef enum _OMAP_I2C_REGISTER {
  62. OmapI2cRevisionLow = 0x00,
  63. OmapI2cRevisionHigh = 0x04,
  64. OmapI2cSystemControl = 0x10,
  65. OmapI2cInterruptStatusRaw = 0x24,
  66. OmapI2cInterruptStatus = 0x28,
  67. OmapI2cInterruptEnableSet = 0x2C,
  68. OmapI2cInterruptEnableClear = 0x30,
  69. OmapI2cWakeupEnable = 0x34,
  70. OmapI2cDmaReceiveEnableSet = 0x38,
  71. OmapI2cDmaTransmitEnableSet = 0x3C,
  72. OmapI2cDmaReceiveEnableClear = 0x40,
  73. OmapI2cDmaTransmitEnableClear = 0x44,
  74. OmapI2cDmaReceiveWakeEnable = 0x48,
  75. OmapI2cDmaTransmitWakeEnable = 0x4C,
  76. OmapI2cInterruptEnableLegacy = 0x84,
  77. OmapI2cInterruptStatusLegacy = 0x88,
  78. OmapI2cSystemStatus = 0x90,
  79. OmapI2cBufferConfiguration = 0x94,
  80. OmapI2cCount = 0x98,
  81. OmapI2cData = 0x9C,
  82. OmapI2cControl = 0xA4,
  83. OmapI2cOwnAddress = 0xA8,
  84. OmapI2cSlaveAddress = 0xAC,
  85. OmapI2cPrescaler = 0xB0,
  86. OmapI2cClockLowTime = 0xB4,
  87. OmapI2cClockHighTime = 0xB8,
  88. OmapI2cSystemTest = 0xBC,
  89. OmapI2cBufferStatus = 0xC0,
  90. OmapI2cOwnAddress1 = 0xC4,
  91. OmapI2cOwnAddress2 = 0xC8,
  92. OmapI2cOwnAddress3 = 0xCC,
  93. OmapI2cActiveOwnAddress = 0xD0,
  94. OmapI2cClockBlockingEnable = 0xD4
  95. } OMAP_I2C_REGISTER, *POMAP_I2C_REGISTER;
  96. //
  97. // ----------------------------------------------- Internal Function Prototypes
  98. //
  99. KSTATUS
  100. OmapI2cWaitForBusyBit (
  101. VOID
  102. );
  103. KSTATUS
  104. OmapI2cWaitForEvent (
  105. ULONG Mask
  106. );
  107. //
  108. // -------------------------------------------------------------------- Globals
  109. //
  110. PVOID OmapI2cBase;
  111. //
  112. // ------------------------------------------------------------------ Functions
  113. //
  114. VOID
  115. OmapI2cInitialize (
  116. VOID
  117. )
  118. /*++
  119. Routine Description:
  120. This routine initializes the I2C device.
  121. Arguments:
  122. None.
  123. Return Value:
  124. None.
  125. --*/
  126. {
  127. ULONG Value;
  128. if (OmapI2cBase == NULL) {
  129. OmapI2cBase = MmMapPhysicalAddress(I2C_BASE,
  130. I2C_SIZE,
  131. TRUE,
  132. FALSE,
  133. TRUE);
  134. }
  135. ASSERT(OmapI2cBase != NULL);
  136. //
  137. // Set up the divisors.
  138. //
  139. OMAP_I2C_WRITE_REGISTER(OmapI2cPrescaler, 0);
  140. OMAP_I2C_WRITE_REGISTER(OmapI2cClockLowTime, 0x35);
  141. OMAP_I2C_WRITE_REGISTER(OmapI2cClockHighTime, 0x35);
  142. //
  143. // Take the i2c controller out of reset.
  144. //
  145. Value = OMAP_I2C_READ_REGISTER(OmapI2cControl);
  146. Value |= OMAP_I2C_CONTROL_ENABLE | OMAP_I2C_CONTROL_MASTER;
  147. OMAP_I2C_WRITE_REGISTER(OmapI2cControl, Value);
  148. //
  149. // Enable interrupts to be able to get their status.
  150. //
  151. Value = OMAP_I2C_INTERRUPT_NACK |
  152. OMAP_I2C_INTERRUPT_ACCESS_READY |
  153. OMAP_I2C_INTERRUPT_RECEIVE_READY |
  154. OMAP_I2C_INTERRUPT_TRANSMIT_READY;
  155. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptEnableLegacy, Value);
  156. HlBusySpin(1000);
  157. OmapI2cFlushData();
  158. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy, 0xFFFFFFFF);
  159. OMAP_I2C_WRITE_REGISTER(OmapI2cCount, 0);
  160. return;
  161. }
  162. VOID
  163. OmapI2cFlushData (
  164. VOID
  165. )
  166. /*++
  167. Routine Description:
  168. This routine flushes extraneous data out of the internal FIFOs.
  169. Arguments:
  170. None.
  171. Return Value:
  172. None.
  173. --*/
  174. {
  175. ULONG Status;
  176. while (TRUE) {
  177. Status = OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy);
  178. if ((Status & OMAP_I2C_INTERRUPT_RECEIVE_READY) != 0) {
  179. OMAP_I2C_READ_REGISTER(OmapI2cData);
  180. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
  181. OMAP_I2C_INTERRUPT_RECEIVE_READY);
  182. HlBusySpin(1000);
  183. } else {
  184. break;
  185. }
  186. }
  187. return;
  188. }
  189. KSTATUS
  190. OmapI2cWrite (
  191. UCHAR Chip,
  192. ULONG Address,
  193. ULONG AddressLength,
  194. PVOID Buffer,
  195. ULONG Length
  196. )
  197. /*++
  198. Routine Description:
  199. This routine writes the given buffer out to the given i2c device.
  200. Arguments:
  201. Chip - Supplies the device to write to.
  202. Address - Supplies the address.
  203. AddressLength - Supplies the width of the address. Valid values are zero
  204. through two.
  205. Buffer - Supplies the buffer containing the data to write.
  206. Length - Supplies the length of the buffer in bytes.
  207. Return Value:
  208. Status code.
  209. --*/
  210. {
  211. PUCHAR Bytes;
  212. ULONG DataIndex;
  213. KSTATUS Status;
  214. ULONG Value;
  215. ASSERT((Buffer != NULL) && (AddressLength <= 2) &&
  216. (Address + Length < 0x10000));
  217. Status = OmapI2cWaitForBusyBit();
  218. if (!KSUCCESS(Status)) {
  219. return Status;
  220. }
  221. OMAP_I2C_WRITE_REGISTER(OmapI2cCount, AddressLength + Length);
  222. OMAP_I2C_WRITE_REGISTER(OmapI2cSlaveAddress, Chip);
  223. Value = OMAP_I2C_CONTROL_ENABLE | OMAP_I2C_CONTROL_MASTER |
  224. OMAP_I2C_CONTROL_START_CONDITION |
  225. OMAP_I2C_CONTROL_STOP_CONDITION | OMAP_I2C_CONTROL_TRANSMIT;
  226. OMAP_I2C_WRITE_REGISTER(OmapI2cControl, Value);
  227. while (AddressLength != 0) {
  228. Status = OmapI2cWaitForEvent(OMAP_I2C_INTERRUPT_TRANSMIT_READY);
  229. if (!KSUCCESS(Status)) {
  230. goto I2cWriteEnd;
  231. }
  232. AddressLength -= 1;
  233. Value = (Address >> (AddressLength * BITS_PER_BYTE)) & 0xFF;
  234. OMAP_I2C_WRITE_REGISTER(OmapI2cData, Value);
  235. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
  236. OMAP_I2C_INTERRUPT_TRANSMIT_READY);
  237. }
  238. Bytes = Buffer;
  239. for (DataIndex = 0; DataIndex < Length; DataIndex += 1) {
  240. Status = OmapI2cWaitForEvent(OMAP_I2C_INTERRUPT_TRANSMIT_READY);
  241. if (!KSUCCESS(Status)) {
  242. goto I2cWriteEnd;
  243. }
  244. OMAP_I2C_WRITE_REGISTER(OmapI2cData, Bytes[DataIndex]);
  245. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
  246. OMAP_I2C_INTERRUPT_TRANSMIT_READY);
  247. }
  248. Status = STATUS_SUCCESS;
  249. I2cWriteEnd:
  250. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy, 0xFFFFFFFF);
  251. return Status;
  252. }
  253. KSTATUS
  254. OmapI2cRead (
  255. UCHAR Chip,
  256. ULONG Address,
  257. ULONG AddressLength,
  258. PVOID Buffer,
  259. ULONG Length
  260. )
  261. /*++
  262. Routine Description:
  263. This routine reads from the given i2c device into the given buffer.
  264. Arguments:
  265. Chip - Supplies the device to read from.
  266. Address - Supplies the address.
  267. AddressLength - Supplies the width of the address. Valid values are zero
  268. through two.
  269. Buffer - Supplies a pointer to the buffer where the read data will be
  270. returned.
  271. Length - Supplies the length of the buffer in bytes.
  272. Return Value:
  273. Status code.
  274. --*/
  275. {
  276. PUCHAR Bytes;
  277. ULONG DataIndex;
  278. ULONG InterruptStatus;
  279. ULONG Mask;
  280. KSTATUS Status;
  281. ULONG Value;
  282. ASSERT((Buffer != NULL) && (AddressLength <= 2) &&
  283. (Address + Length < 0x10000));
  284. Status = OmapI2cWaitForBusyBit();
  285. if (!KSUCCESS(Status)) {
  286. return Status;
  287. }
  288. OMAP_I2C_WRITE_REGISTER(OmapI2cCount, AddressLength);
  289. OMAP_I2C_WRITE_REGISTER(OmapI2cSlaveAddress, Chip);
  290. Value = OMAP_I2C_CONTROL_ENABLE | OMAP_I2C_CONTROL_MASTER |
  291. OMAP_I2C_CONTROL_START_CONDITION |
  292. OMAP_I2C_CONTROL_STOP_CONDITION | OMAP_I2C_CONTROL_TRANSMIT;
  293. OMAP_I2C_WRITE_REGISTER(OmapI2cControl, Value);
  294. Mask = OMAP_I2C_INTERRUPT_TRANSMIT_READY |
  295. OMAP_I2C_INTERRUPT_ACCESS_READY;
  296. while (TRUE) {
  297. Status = OmapI2cWaitForEvent(Mask);
  298. if (!KSUCCESS(Status)) {
  299. goto I2cReadEnd;
  300. }
  301. InterruptStatus = OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy);
  302. if (AddressLength != 0) {
  303. if ((InterruptStatus & OMAP_I2C_INTERRUPT_TRANSMIT_READY) != 0) {
  304. AddressLength -= 1;
  305. Value = (Address >> (AddressLength * BITS_PER_BYTE)) & 0xFF;
  306. OMAP_I2C_WRITE_REGISTER(OmapI2cData, Value);
  307. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
  308. OMAP_I2C_INTERRUPT_TRANSMIT_READY);
  309. }
  310. }
  311. if ((InterruptStatus & OMAP_I2C_INTERRUPT_ACCESS_READY) != 0) {
  312. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
  313. OMAP_I2C_INTERRUPT_ACCESS_READY);
  314. break;
  315. }
  316. }
  317. Status = OmapI2cWaitForBusyBit();
  318. if (!KSUCCESS(Status)) {
  319. goto I2cReadEnd;
  320. }
  321. OMAP_I2C_WRITE_REGISTER(OmapI2cSlaveAddress, Chip);
  322. OMAP_I2C_WRITE_REGISTER(OmapI2cCount, Length);
  323. Value = OMAP_I2C_CONTROL_ENABLE | OMAP_I2C_CONTROL_MASTER |
  324. OMAP_I2C_CONTROL_START_CONDITION |
  325. OMAP_I2C_CONTROL_STOP_CONDITION;
  326. OMAP_I2C_WRITE_REGISTER(OmapI2cControl, Value);
  327. Bytes = Buffer;
  328. Mask = OMAP_I2C_INTERRUPT_RECEIVE_READY |
  329. OMAP_I2C_INTERRUPT_ACCESS_READY;
  330. DataIndex = 0;
  331. while (DataIndex < Length) {
  332. Status = OmapI2cWaitForEvent(Mask);
  333. if (!KSUCCESS(Status)) {
  334. goto I2cReadEnd;
  335. }
  336. InterruptStatus = OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy);
  337. if ((InterruptStatus & OMAP_I2C_INTERRUPT_RECEIVE_READY) != 0) {
  338. Bytes[DataIndex] = OMAP_I2C_READ_REGISTER(OmapI2cData);
  339. DataIndex += 1;
  340. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
  341. OMAP_I2C_INTERRUPT_RECEIVE_READY);
  342. }
  343. if ((InterruptStatus & OMAP_I2C_INTERRUPT_ACCESS_READY) != 0) {
  344. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy,
  345. OMAP_I2C_INTERRUPT_ACCESS_READY);
  346. }
  347. }
  348. Status = STATUS_SUCCESS;
  349. I2cReadEnd:
  350. OMAP_I2C_WRITE_REGISTER(OmapI2cInterruptStatusLegacy, 0xFFFFFFFF);
  351. return Status;
  352. }
  353. //
  354. // --------------------------------------------------------- Internal Functions
  355. //
  356. KSTATUS
  357. OmapI2cWaitForBusyBit (
  358. VOID
  359. )
  360. /*++
  361. Routine Description:
  362. This routine waits for the busy bit to clear.
  363. Arguments:
  364. None.
  365. Return Value:
  366. Status code.
  367. --*/
  368. {
  369. ULONGLONG Timeout;
  370. Timeout = KeGetRecentTimeCounter() +
  371. (HlQueryTimeCounterFrequency() * I2C_TIMEOUT);
  372. do {
  373. if ((OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy) &
  374. OMAP_I2C_STATUS_BUSY) == 0) {
  375. return STATUS_SUCCESS;
  376. }
  377. } while (KeGetRecentTimeCounter() <= Timeout);
  378. return STATUS_TIMEOUT;
  379. }
  380. KSTATUS
  381. OmapI2cWaitForEvent (
  382. ULONG Mask
  383. )
  384. /*++
  385. Routine Description:
  386. This routine waits for the busy bit to clear.
  387. Arguments:
  388. Mask - Supplies the mask to wait to become non-zero.
  389. Return Value:
  390. Status code.
  391. --*/
  392. {
  393. ULONG Status;
  394. ULONGLONG Timeout;
  395. Timeout = KeGetRecentTimeCounter() +
  396. (HlQueryTimeCounterFrequency() * I2C_TIMEOUT);
  397. do {
  398. Status = OMAP_I2C_READ_REGISTER(OmapI2cInterruptStatusLegacy);
  399. if ((Status & Mask) != 0) {
  400. return STATUS_SUCCESS;
  401. }
  402. } while (KeGetRecentTimeCounter() <= Timeout);
  403. return STATUS_TIMEOUT;
  404. }