summaryrefslogtreecommitdiff
path: root/bsp/mini2440/drivers/uart.c
blob: 5c83d1f2384bc8b817ae6f0c50e9914ee117f000 (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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-04-09     Jonne        Code refactoring for new bsp
 */

#include <stddef.h>
#include <rthw.h>
#include <rtdevice.h>
#include <board.h>

#define ULCON_OFS               0x00
#define UCON_OFS                0x04
#define UFCON_OFS               0x08
#define UMCON_OFS               0x0c
#define UTRSTAT_OFS             0x10
#define UERSTAT_OFS             0x14
#define UFSTAT_OFS              0x18
#define UMSTAT_OFS              0x1c
#define UTXH_OFS                0x20
#define URXH_OFS                0x24
#define UBRDIV_OFS              0x28

#define readl(addr)             (*(volatile unsigned long *)(addr))
#define writel(addr, value)     (*(volatile unsigned long *)(addr) = value)

#define PCLK_HZ                 50000000

struct hw_uart_device
{
    rt_uint32_t hw_base;
    rt_uint32_t irqno;
};

static rt_err_t s3c2440_serial_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct hw_uart_device *uart = serial->parent.user_data;

    writel(uart->hw_base + UBRDIV_OFS, PCLK_HZ / (cfg->baud_rate * 16));

    writel(uart->hw_base + ULCON_OFS, 0x03);// 8bit data, 1bit stop, No parity
    writel(uart->hw_base + UCON_OFS, 0x05);
    writel(uart->hw_base + UFCON_OFS, 0x00);
    writel(uart->hw_base + UMCON_OFS, 0x00);

    return RT_EOK;
}

static rt_err_t s3c2440_serial_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct hw_uart_device *uart;
    int mask;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct hw_uart_device *)serial->parent.user_data;

    if (uart->irqno == INTUART0)
    {
        mask = BIT_SUB_RXD0;
    }
    else if (uart->irqno == INTUART1)
    {
        mask = BIT_SUB_RXD1;
    }
    else
    {
        mask = BIT_SUB_RXD2;
    }

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable rx irq */
        INTSUBMSK |= mask;

        break;

    case RT_DEVICE_CTRL_SET_INT:
        /* enable rx irq */
        INTSUBMSK &= ~mask;
        break;
    }

    return RT_EOK;
}
static int s3c2440_putc(struct rt_serial_device *serial, char c)
{
    struct hw_uart_device *uart = serial->parent.user_data;

    while (!(readl(uart->hw_base + UTRSTAT_OFS) & (1 << 2)))
    {
    }

    writel(uart->hw_base + UTXH_OFS, c);

    return 0;


}
static int s3c2440_getc(struct rt_serial_device *serial)
{
    struct hw_uart_device *uart = serial->parent.user_data;
    int ch = -1;

    if (readl(uart->hw_base + UTRSTAT_OFS) & (1 << 0))
    {
        ch = readl(uart->hw_base + URXH_OFS) & 0x000000FF;
    }

    return ch;
}

static void rt_hw_uart_isr(int irqno, void *param)
{
    struct rt_serial_device *serial = (struct rt_serial_device *)param;

    rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);

    /*clear SUBSRCPND*/
    if (irqno == INTUART0)
    {
        SUBSRCPND = BIT_SUB_RXD0;
    }
    else if (irqno == INTUART1)
    {
        SUBSRCPND = BIT_SUB_RXD1;
    }
    else
    {
        SUBSRCPND = BIT_SUB_RXD2;
    }
}

static struct rt_uart_ops s3c2440_uart_ops =
{
    .configure = s3c2440_serial_configure,
    .control = s3c2440_serial_control,
    .putc = s3c2440_putc,
    .getc = s3c2440_getc
};


static struct rt_serial_device _serial0 =
{
    .ops = &s3c2440_uart_ops,
    .config = RT_SERIAL_CONFIG_DEFAULT,
    .serial_rx = NULL,
    .serial_tx = NULL
};
static struct hw_uart_device _hwserial0 =
{
    .hw_base = 0x50000000,
    .irqno = INTUART0
};

static struct rt_serial_device _serial1 =
{
    .ops = &s3c2440_uart_ops,
    .config = RT_SERIAL_CONFIG_DEFAULT,
    .serial_rx = NULL,
    .serial_tx = NULL
};
static struct hw_uart_device _hwserial1 =
{
    .hw_base = 0x50004000,
    .irqno = INTUART1
};

static struct rt_serial_device _serial2 =
{
    .ops = &s3c2440_uart_ops,
    .config = RT_SERIAL_CONFIG_DEFAULT,
    .serial_rx = NULL,
    .serial_tx = NULL
};
static struct hw_uart_device _hwserial2 =
{
    .hw_base = 0x50008000,
    .irqno = INTUART2
};


int rt_hw_uart_init(void)
{
    /* UART0  UART1 UART2 port configure */
    GPHCON |= 0xAAAA;
    /* PULLUP is disable */
    GPHUP |= 0xFFF;

    /* register UART0 device */
    rt_hw_serial_register(&_serial0, "uart0", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, &_hwserial0);
    rt_hw_interrupt_install(_hwserial0.irqno, rt_hw_uart_isr, &_serial0, "uart0");
    rt_hw_interrupt_umask(INTUART0);

    /* register UART1 device */
    rt_hw_serial_register(&_serial1, "uart1", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, &_hwserial1);
    rt_hw_interrupt_install(_hwserial1.irqno, rt_hw_uart_isr, &_serial1, "uart1");
    rt_hw_interrupt_umask(INTUART1);

    /* register UART2 device */
    rt_hw_serial_register(&_serial2, "uart2", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, &_hwserial2);
    rt_hw_interrupt_install(_hwserial2.irqno, rt_hw_uart_isr, &_serial2, "uart2");
    rt_hw_interrupt_umask(INTUART2);

    return RT_EOK;
}
INIT_BOARD_EXPORT(rt_hw_uart_init);