The Pedigree Project  0.1
I2C.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "I2C.h"
21 #include "Prcm.h"
22 #include "pedigree/kernel/Log.h"
23 #include "pedigree/kernel/process/Semaphore.h"
24 #include "pedigree/kernel/processor/PhysicalMemoryManager.h"
25 #include "pedigree/kernel/processor/Processor.h"
26 #include "pedigree/kernel/processor/VirtualAddressSpace.h"
27 #include "pedigree/kernel/time/Time.h"
28 
29 I2C I2C::m_Instance[3];
30 
31 void I2C::initialise(uintptr_t baseAddr)
32 {
33  // Map in the base
34  if (!PhysicalMemoryManager::instance().allocateRegion(
35  m_MmioBase, 1, PhysicalMemoryManager::continuous,
37  baseAddr))
38  {
39  // Failed to allocate the region!
40  return;
41  }
42 
43  // Enable functional clocks for all three I2C modules (even though we're
44  // only initialising one now)
45  Prcm::instance().SetFuncClockCORE(1, 15, true);
46  Prcm::instance().SetFuncClockCORE(1, 16, true);
47  Prcm::instance().SetFuncClockCORE(1, 17, true);
48 
49  // Same for the interface clocks
50  Prcm::instance().SetIfaceClockCORE(1, 15, true);
51  Prcm::instance().SetIfaceClockCORE(1, 16, true);
52  Prcm::instance().SetIfaceClockCORE(1, 17, true);
53 
54  // Dump information about this module
55  volatile uint16_t *base =
56  reinterpret_cast<volatile uint16_t *>(m_MmioBase.virtualAddress());
57  NOTICE(
58  "I2C Module at " << baseAddr << " ("
59  << reinterpret_cast<uintptr_t>(
60  m_MmioBase.virtualAddress())
61  << "): Revision " << Dec
62  << ((base[I2C_REV] >> 4) & 0xF) << "."
63  << (base[I2C_REV] & 0xF) << Hex << ".");
64 
65  // Reset the module
66  base[I2C_SYSC] = 2;
67  base[I2C_CON] = 0x8000;
68  while (!(base[I2C_SYSS] & 1))
69  ;
70  base[I2C_SYSC] = 0;
71 
72  // Disable the module once again while we set it up
73  base[I2C_CON] = 0;
74 
75  // Set up a 100 kbps transfer rate
76  base[I2C_PSC] = 23; // 96 MHz / 23 = 4 MHz (looking for 100 kbps rate)
77  base[I2C_SCLL] = 13;
78  base[I2C_SCLH] = 15;
79 
80  // Configure the "own address"
81  base[I2C_OA0] = 1;
82 
83  // 1-byte FIFO
84  base[I2C_BUF] = 0;
85 
86  // Disable interrupts completely
87  base[I2C_IE] = 0;
88 
89  // Start the module
90  base[I2C_CON] = 0; // 0x8000;
91 
92  // Clear status etc
93  base[I2C_STAT] = 0xFFFF;
94  base[I2C_CNT] = 0;
95 }
96 
97 bool I2C::write(uint8_t addr, uint8_t reg, uint8_t data)
98 {
99  uint8_t buffer[] = {reg, data};
100  return transmit(addr, reinterpret_cast<uintptr_t>(buffer), 2);
101 }
102 
103 uint8_t I2C::read(uint8_t addr, uint8_t reg)
104 {
105  uint8_t buffer[] = {reg};
106  if (!transmit(addr, reinterpret_cast<uintptr_t>(buffer), 1))
107  return 0;
108  if (!receive(addr, reinterpret_cast<uintptr_t>(buffer), 1))
109  return 0;
110  return buffer[0];
111 }
112 
113 bool I2C::transmit(uint8_t addr, uintptr_t buffer, size_t len)
114 {
115  volatile uint16_t *base =
116  reinterpret_cast<volatile uint16_t *>(m_MmioBase.virtualAddress());
117  uint8_t *buf = reinterpret_cast<uint8_t *>(buffer);
118 
119  waitForBus();
120 
121  // Program the transfer
122  base[I2C_SA] = addr;
123  base[I2C_CNT] = len;
124  base[I2C_CON] = 0x8603; // Transmit
125 
126  // Perform the transfer itself
127  bool success = true;
128  while (1)
129  {
130  Time::delay(1 * Time::Multiplier::MILLISECOND);
131  uint16_t status = base[I2C_STAT];
132  if (status & 0x1)
133  {
134  // Arbitration lost
135  NOTICE("I2C: Arbitration lost");
136  success = false;
137  break;
138  }
139  else if (status & 0x2)
140  {
141  // NACK
142  NOTICE("I2C: NACK");
143  success = false;
144  break;
145  }
146  else if (status & 0x4)
147  {
148  // ARDY - transfer complete
149  break;
150  }
151  else if (status & 0x10)
152  {
153  // XRDY, transmit a byte
154  *reinterpret_cast<volatile uint8_t *>(&base[I2C_DATA]) = *buf++;
155  }
156 
157  Time::delay(50 * Time::Multiplier::MILLISECOND);
158  base[I2C_STAT] = status;
159  }
160 
161  base[I2C_STAT] = 0xFFFF;
162  base[I2C_CNT] = 0;
163 
164  return success;
165 }
166 
167 bool I2C::receive(uint8_t addr, uintptr_t buffer, size_t maxlen)
168 {
169  volatile uint16_t *base =
170  reinterpret_cast<volatile uint16_t *>(m_MmioBase.virtualAddress());
171  uint8_t *buf = reinterpret_cast<uint8_t *>(buffer);
172 
173  waitForBus();
174 
175  // Program the DMA transfer
176  base[I2C_SA] = addr;
177  base[I2C_CNT] = maxlen;
178  base[I2C_CON] = 0x8403; // No transmit
179 
180  // Perform the transfer itself
181  bool success = true;
182  while (1)
183  {
184  Time::delay(1 * Time::Multiplier::MILLISECOND);
185  uint16_t status = base[I2C_STAT];
186  if (status & 0x1)
187  {
188  // Arbitration lost
189  NOTICE("I2C: Arbitration lost");
190  success = false;
191  break;
192  }
193  else if (status & 0x2)
194  {
195  // NACK
196  NOTICE("I2C: NACK");
197  success = false;
198  break;
199  }
200  else if (status & 0x4)
201  {
202  // ARDY - transfer complete
203  break;
204  }
205  if (status & 0x8)
206  {
207  // RRDY, transmit a byte
208  *buf++ = *reinterpret_cast<volatile uint8_t *>(&base[I2C_DATA]);
209  }
210 
211  Time::delay(50 * Time::Multiplier::MILLISECOND);
212  base[I2C_STAT] = status;
213  }
214 
215  base[I2C_STAT] = 0xFFFF;
216  base[I2C_CNT] = 0;
217 
218  return success;
219 }
220 
221 void I2C::waitForBus()
222 {
223  volatile uint16_t *base =
224  reinterpret_cast<volatile uint16_t *>(m_MmioBase.virtualAddress());
225 
226  // Wait for the bus to be available
227  base[I2C_STAT] = 0xFFFF;
228  uint32_t status = 0;
229  while ((status = base[I2C_STAT]) & 0x1000)
230  {
231  base[I2C_STAT] = status;
232  Time::delay(50 * Time::Multiplier::MILLISECOND);
233  }
234  base[I2C_STAT] = 0xFFFF;
235 }
static PhysicalMemoryManager & instance()
void SetFuncClockCORE(size_t n, size_t clock, bool enabled)
Definition: Prcm.cc:166
#define NOTICE(text)
Definition: Log.h:74
Definition: Log.h:136
void SetIfaceClockCORE(size_t n, size_t clock, bool enabled)
Definition: Prcm.cc:200
Definition: I2C.h:26
void * virtualAddress() const
Definition: MemoryRegion.cc:39
Definition: Log.h:138