blob: 0a991e6baf2ea54d9a7fe7b9feebcdfee38ed85d (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
/* or1k_uart.c -- UART implementation for OpenRISC 1000.
*
*Copyright (c) 2014 Authors
*
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
* Contributor Olof Kindgren <olof.kindgren@gmail.com>
*
* The authors hereby grant permission to use, copy, modify, distribute,
* and license this software and its documentation for any purpose, provided
* that existing copyright notices are retained in all copies and that this
* notice is included verbatim in any distributions. No written agreement,
* license, or royalty fee is required for any of the authorized uses.
* Modifications to this software may be copyrighted by their authors
* and need not follow the licensing terms described here, provided that
* the new terms are clearly indicated on the first page of each file where
* they apply.
*/
#include "include/or1k-support.h"
#include "or1k_uart.h"
#include <stdint.h>
// Register interface
#define RB _or1k_board_uart_base + 0 // Receiver Buffer (R)
#define THR _or1k_board_uart_base + 0 // Transmitter Holding Register (W)
#define IER _or1k_board_uart_base + 1 // Interrupt Enable Register (RW)
#define IIR _or1k_board_uart_base + 2 // Interrupt Identification Register (R)
#define FCR _or1k_board_uart_base + 2 // FIFO Control Register (W)
#define LCR _or1k_board_uart_base + 3 // Line Control Register (RW)
#define MCR _or1k_board_uart_base + 4 // Modem Control Register (W)
#define LSR _or1k_board_uart_base + 5 // Line Status Register (R)
#define MSR _or1k_board_uart_base + 6 // Modem Status Register (R)
// Divisor Register (Accessed when DLAB bit in LCR is set)
#define DLB1 _or1k_board_uart_base + 0 // Divisor Latch LSB (RW)
#define DLB2 _or1k_board_uart_base + 1 // Divisor Latch MSB (RW)
// Interrupt Enable Register bits
#define IER_RDAI 0 // Receiver Data Available Interrupt
#define IER_TEI 1 // Transmitter Holding Register Empty Interrupt
#define IER_RLSI 2 // Receiver Line Status Interrupt
#define IER_MSI 3 // Modem Status Interrupt
// Interrupt Identification Register Values
#define IIR_RLS 0xC6 // Receiver Line Status
#define IIR_RDA 0xC4 // Receiver Data Available
#define IIR_TO 0xCC // Timeout
#define IIR_THRE 0xC2 // Transmitter Holding Register Empty
#define IIT_MS 0xC0 // Modem Status
// FIFO Control Register bits
#define FCR_CLRRECV 0x1 // Clear receiver FIFO
#define FCR_CLRTMIT 0x2 // Clear transmitter FIFO
// FIFO Control Register bit 7-6 values
#define FCR_TRIG_1 0x0 // Trigger level 1 byte
#define FCR_TRIG_4 0x40 // Trigger level 4 bytes
#define FCR_TRIG_8 0x80 // Trigger level 8 bytes
#define FCR_TRIG_14 0xC0 // Trigger level 14 bytes
// Line Control Reigster values and bits
#define LCR_BPC_5 0x0 // 5 bits per character
#define LCR_BPC_6 0x1 // 6 bits per character
#define LCR_BPC_7 0x2 // 7 bits per character
#define LCR_BPC_8 0x3 // 8 bits per character
#define LCR_SB_1 0x0 // 1 stop bit
#define LCR_SB_2 0x4 // 1.5 stop bits (LCR_BPC_5) or 2 stop bits (else)
#define LCR_PE 0x8 // Parity Enabled
#define LCR_EPS 0x10 // Even Parity Select
#define LCR_SP 0x20 // Stick Parity
#define LCR_BC 0x40 // Break Control
#define LCR_DLA 0x80 // Divisor Latch Access
// Line Status Register
#define LSR_DR 0x0 // Data Ready
#define LSR_OE 0x2 // Overrun Error
#define LSR_PE 0x4 // Parity Error
#define LSR_FE 0x8 // Framing Error
#define LSR_BI 0x10 // Break Interrupt
#define LSR_TFE 0x20 // Transmitter FIFO Empty
#define LSR_TEI 0x40 // Transmitter Empty Indicator
/**
* The registered callback function
*/
void (*_or1k_uart_read_cb)(char c);
/**
* This is the interrupt handler that is registered for the callback
* function.
*/
void _or1k_uart_interrupt_handler(uint32_t data)
{
uint8_t iir = REG8(IIR);
// Check if this is a read fifo or timeout interrupt, bit 0
// indicates pending interrupt and the other bits are IIR_RDA
// or IIR_TO
if (!(iir & 0x1) || ((iir & 0xfe) != IIR_RDA) ||
((iir & 0xfe) != IIR_TO)) {
return;
}
// Read character and call callback function
_or1k_uart_read_cb(REG8(RB));
}
int _or1k_uart_init(void)
{
uint16_t divisor;
// Is uart present?
if (!_or1k_board_uart_base) {
return -1;
}
// Reset the callback function
_or1k_uart_read_cb = 0;
// Calculate and set divisor
divisor = _or1k_board_clk_freq / (_or1k_board_uart_baud * 16);
REG8(LCR) = LCR_DLA;
REG8(DLB1) = divisor & 0xff;
REG8(DLB2) = divisor >> 8;
// Set line control register:
// - 8 bits per character
// - 1 stop bit
// - No parity
// - Break disabled
// - Disallow access to divisor latch
REG8(LCR) = LCR_BPC_8;
// Reset FIFOs and set trigger level to 14 bytes
REG8(FCR) = FCR_CLRRECV | FCR_CLRTMIT | FCR_TRIG_14;
// Disable all interrupts
REG8(IER) = 0;
return 0;
}
void _or1k_uart_write(char c)
{
// Wait until FIFO is empty
while (!(REG8(LSR) & LSR_TFE)) {}
// Write character to device
REG8(THR) = c;
}
void or1k_uart_set_read_cb(void (*cb)(char c))
{
// Set callback function
_or1k_uart_read_cb = cb;
// Enable interrupt
REG8(IER) = 1 << IER_RDAI;
// Add the interrupt handler that calls the callback function
or1k_interrupt_handler_add(_or1k_board_uart_IRQ,
_or1k_uart_interrupt_handler, 0);
// Enable UART interrupt
or1k_interrupt_enable(_or1k_board_uart_IRQ);
}
|