The Pedigree Project  0.1
arm_beagle/Serial.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 "Serial.h"
21 #include "pedigree/kernel/machine/Machine.h"
22 #include "pedigree/kernel/processor/PhysicalMemoryManager.h"
23 #include "pedigree/kernel/processor/VirtualAddressSpace.h"
24 
25 ArmBeagleSerial::ArmBeagleSerial() : m_Base(0), m_BaseRegion("beagle-uart")
26 {
27 }
28 
29 ArmBeagleSerial::~ArmBeagleSerial()
30 {
31 }
32 
33 void ArmBeagleSerial::setBase(uintptr_t nBaseAddr)
34 {
35  // Map in the base
36  if (!PhysicalMemoryManager::instance().allocateRegion(
37  m_BaseRegion, 1, PhysicalMemoryManager::continuous,
39  nBaseAddr))
40  {
41  // Failed to allocate the region!
42  return;
43  }
44 
45  // Set base MMIO address for UART and configure appropriately.
46  m_Base =
47  reinterpret_cast<volatile uint8_t *>(m_BaseRegion.virtualAddress());
48 
49  // Reset the UART
50  softReset();
51 
52  // Restore FIFO defaults
53  if (!setFifoDefaults())
54  {
55  m_Base = 0;
56  return;
57  }
58 
59  // Configure the UART protocol
60  if (!configureProtocol())
61  {
62  m_Base = 0;
63  return;
64  }
65 
66  // Disable hardware flow control
67  if (!disableFlowControl())
68  {
69  m_Base = 0;
70  return;
71  }
72 
73  // Install the IRQ handler
76 }
77 
78 void ArmBeagleSerial::interrupt(size_t nInterruptNumber, InterruptState &state)
79 {
80  uint8_t intStatus = m_Base[IIR_REG];
81  switch ((intStatus >> 1) & 0x1F)
82  {
83  case 0:
84  {
85  // Modem interrupt - reset by reading the modem status register
86  uint8_t c = m_Base[MSR_REG];
87  }
88  break;
89  case 1:
90  {
91  // THR interrupt - RX FIFO below threshold or THR empty
92  }
93  break;
94  case 2:
95  {
96  // RHR interrupt - data ready in the RX FIFO
97  while (m_Base[LSR_REG] & 0x1)
98  uint8_t c =
99  m_Base[RHR_REG];
100  }
101  break;
102  case 3:
103  {
104  // Receiver line status error
106  }
107  break;
108  case 6:
109  {
110  // RX timeout - caused by stale data in the RX FIFO.
111  // Reset by reading from the FIFO
112  uint8_t c = m_Base[RHR_REG];
113  }
114  break;
115  case 8:
116  {
117  // XOFF/special character
119  }
120  break;
121  case 16:
122  {
123  // CTS, RTS change of state from active to inactive
124  // Reset by the act of reading the IIR, as above
125  }
126  break;
127  }
128 }
129 
130 char ArmBeagleSerial::read()
131 {
132  // Read a character from the UART, if we have one configured.
133  if (m_Base)
134  {
135  while (!(m_Base[LSR_REG] & 0x1))
136  ;
137  return m_Base[RHR_REG];
138  }
139  else
140  return 0;
141 }
142 
143 char ArmBeagleSerial::readNonBlock()
144 {
145  // Same as above but don't block
146  if (m_Base)
147  {
148  if (!(m_Base[LSR_REG] & 0x1))
149  return 0;
150  return m_Base[RHR_REG];
151  }
152  else
153  return 0;
154 }
155 
156 void ArmBeagleSerial::write(char c)
157 {
158  // Write a character to the UART.
159  if (m_Base)
160  {
161  while (!(m_Base[LSR_REG] & 0x20))
162  ;
163  m_Base[THR_REG] = c;
164  }
165 }
166 
169 
171 {
172  if (!m_Base)
173  return;
174 
177  // 1. Initiate a software reset
178  m_Base[SYSC_REG] |= 0x2;
179 
180  // 2. Wait for the end of the reset operation
181  while (!(m_Base[SYSS_REG] & 0x1))
182  ;
183 }
184 
186 {
187  if (!m_Base)
188  return false;
189 
192  // 1. Switch to configuration mode B to access the EFR_REG register
193  unsigned char old_lcr_reg = m_Base[LCR_REG];
194  m_Base[LCR_REG] = 0xBF;
195 
196  // 2. Enable submode TCR_TLR to access TLR_REG (part 1 of 2)
197  unsigned char efr_reg = m_Base[EFR_REG];
198  unsigned char old_enhanced_en = efr_reg & 0x8;
199  if (!(efr_reg & 0x8)) // Set ENHANCED_EN (bit 4) if not set
200  efr_reg |= 0x8;
201  m_Base[EFR_REG] = efr_reg; // Write back to the register
202 
203  // 3. Switch to configuration mode A
204  m_Base[LCR_REG] = 0x80;
205 
206  // 4. Enable submode TCL_TLR to access TLR_REG (part 2 of 2)
207  unsigned char mcr_reg = m_Base[MCR_REG];
208  unsigned char old_tcl_tlr = mcr_reg & 0x20;
209  if (!(mcr_reg & 0x20))
210  mcr_reg |= 0x20;
211  m_Base[MCR_REG] = mcr_reg;
212 
213  // 5. Enable FIFO, load new FIFO triggers (part 1 of 3), and the new DMA
214  // mode (part 1 of 2)
215  m_Base[FCR_REG] =
216  1; // TX and RX FIFO triggers at 8 characters, no DMA mode
217 
218  // 6. Switch to configuration mode B to access EFR_REG
219  m_Base[LCR_REG] = 0xBF;
220 
221  // 7. Load new FIFO triggers (part 2 of 3)
222  m_Base[TLR_REG] = 0;
223 
224  // 8. Load new FIFO triggers (part 3 of 3) and the new DMA mode (part 2 of
225  // 2)
226  m_Base[SCR_REG] = 0;
227 
228  // 9. Restore the ENHANCED_EN value saved in step 2
229  if (!old_enhanced_en)
230  m_Base[EFR_REG] = m_Base[EFR_REG] ^ 0x8;
231 
232  // 10. Switch to configuration mode A to access the MCR_REG register
233  m_Base[LCR_REG] = 0x80;
234 
235  // 11. Restore the MCR_REG TCR_TLR value saved in step 4
236  if (!old_tcl_tlr)
237  m_Base[MCR_REG] = m_Base[MCR_REG] ^ 0x20;
238 
239  // 12. Restore the LCR_REG value stored in step 1
240  m_Base[LCR_REG] = old_lcr_reg;
241 
242  return true;
243 }
244 
246 {
247  if (!m_Base)
248  return false;
249 
252  // 1. Disable UART to access DLL_REG and DLH_REG
253  m_Base[MDR1_REG] = (m_Base[MDR1_REG] & ~0x7) | 0x7;
254 
255  // 2. Switch to configuration mode B to access the EFR_REG register
256  m_Base[LCR_REG] = 0xBF;
257 
258  // 3. Enable access to IER_REG
259  unsigned char efr_reg = m_Base[EFR_REG];
260  unsigned char old_enhanced_en = efr_reg & 0x8;
261  if (!(efr_reg & 0x8)) // Set ENHANCED_EN (bit 4) if not set
262  efr_reg |= 0x8;
263  m_Base[EFR_REG] = efr_reg; // Write back to the register
264 
265  // 4. Switch to operational mode to access the IER_REG register
266  m_Base[LCR_REG] = 0;
267 
268  // 5. Clear IER_REG
269  m_Base[IER_REG] = 0;
270 
271  // 6. Switch to configuration mode B to access DLL_REG and DLH_REG
272  m_Base[LCR_REG] = 0xBF;
273 
274  // 7. Load the new divisor value (looking for 115200 baud)
275  m_Base[0x0] = 0x1A; // divisor low byte
276  m_Base[0x4] = 0; // divisor high byte
277 
278  // 8. Switch to operational mode to access the IER_REG register
279  m_Base[LCR_REG] = 0;
280 
281  // 9. Load new interrupt configuration
282  m_Base[IER_REG] =
283  0x0D; // RHR interrupt. // No interrupts wanted at this stage
284 
285  // 10. Switch to configuration mode B to access the EFR_REG register
286  m_Base[LCR_REG] = 0xBF;
287 
288  // 11. Restore the ENHANCED_EN value saved in step 3
289  if (old_enhanced_en)
290  m_Base[EFR_REG] = m_Base[EFR_REG] ^ 0x8;
291 
292  // 12. Load the new protocol formatting (parity, stop bit, character length)
293  // and enter operational mode
294  m_Base[LCR_REG] = 0x3; // 8 bit characters, no parity, one stop bit
295 
296  // 13. Load the new UART mode
297  m_Base[MDR1_REG] = 0;
298 
299  return true;
300 }
301 
303 {
304  if (!m_Base)
305  return false;
306 
309  // 1. Switch to configuration mode A to access the MCR_REG register
310  unsigned char old_lcr_reg = m_Base[LCR_REG];
311  m_Base[LCR_REG] = 0x80;
312 
313  // 2. Enable submode TCR_TLR to access TCR_REG (part 1 of 2)
314  unsigned char mcr_reg = m_Base[MCR_REG];
315  unsigned char old_tcl_tlr = mcr_reg & 0x20;
316  if (!(mcr_reg & 0x20))
317  mcr_reg |= 0x20;
318  m_Base[MCR_REG] = mcr_reg;
319 
320  // 3. Switch to configuration mode B to access the EFR_REG register
321  m_Base[LCR_REG] = 0xBF;
322 
323  // 4. Enable submode TCR_TLR to access the TCR_REG register (part 2 of 2)
324  unsigned char efr_reg = m_Base[EFR_REG];
325  unsigned char old_enhanced_en = efr_reg & 0x8;
326  if (!(efr_reg & 0x8)) // Set ENHANCED_EN (bit 4) if not set
327  efr_reg |= 0x8;
328  m_Base[EFR_REG] = efr_reg; // Write back to the register
329 
330  // 5. Load new start and halt trigger values
331  m_Base[TCR_REG] = 0;
332 
333  // 6. Disable RX/TX hardware flow control mode, and restore the ENHANCED_EN
334  // values stored in step 4
335  m_Base[EFR_REG] = 0;
336 
337  // 7. Switch to configuration mode A to access MCR_REG
338  m_Base[LCR_REG] = 0x80;
339 
340  // 8. Restore the MCR_REG TCR_TLR value stored in step 2
341  if (!old_tcl_tlr)
342  m_Base[MCR_REG] = m_Base[MCR_REG] ^ 0x20;
343 
344  // 9. Restore the LCR_REG value saved in step 1
345  m_Base[LCR_REG] = old_lcr_reg;
346 
347  return true;
348 }
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)=0
static PhysicalMemoryManager & instance()
void softReset()
Perform a software reset of this UART.
virtual void interrupt(size_t nInterruptNumber, InterruptState &state)
bool setFifoDefaults()
Reset the FIFOs and DMA to default values.
virtual void setBase(uintptr_t nBaseAddr)
bool disableFlowControl()
Disables hardware flow control on the UART.
static InterruptManager & instance()