1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  NS16550 UART driver			File: dev_ns16550.c
5    *
6    *  This is a console device driver for an NS16550 UART, either
7    *  on-board or as a PCI-device.  In the case of a PCI device,
8    *  our probe routine is called from the PCI probe code
9    *  over in dev_ns16550_pci.c
10    *
11    *  Author:  Mitch Lichtenberg (mpl@broadcom.com)
12    *
13    *********************************************************************
14    *
15    *  Copyright 2000,2001,2002,2003
16    *  Broadcom Corporation. All rights reserved.
17    *
18    *  This software is furnished under license and may be used and
19    *  copied only in accordance with the following terms and
20    *  conditions.  Subject to these conditions, you may download,
21    *  copy, install, use, modify and distribute modified or unmodified
22    *  copies of this software in source and/or binary form.  No title
23    *  or ownership is transferred hereby.
24    *
25    *  1) Any source code used, modified or distributed must reproduce
26    *     and retain this copyright notice and list of conditions
27    *     as they appear in the source file.
28    *
29    *  2) No right is granted to use any trade name, trademark, or
30    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
31    *     name may not be used to endorse or promote products derived
32    *     from this software without the prior written permission of
33    *     Broadcom Corporation.
34    *
35    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
36    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
37    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
38    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
39    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
40    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
41    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
42    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
43    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
44    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
46    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
47    *     THE POSSIBILITY OF SUCH DAMAGE.
48    ********************************************************************* */
49
50#if CFG_SIM && CFG_SIM_CONSOLE
51#include <typedefs.h>
52#include <cfe_osl.h>
53#endif	/* CFG_SIM && CFG_SIM_CONSOLE */
54
55#include "lib_types.h"
56#include "lib_malloc.h"
57#include "lib_printf.h"
58#include "cfe_iocb.h"
59#include "cfe_device.h"
60#include "cfe_ioctl.h"
61
62#include "lib_physio.h"
63
64#include "bsp_config.h"
65
66#include "ns16550.h"
67
68#ifdef BCM4704
69#define WRITECSR(softc, offset, value) do { \
70	phys_write8((softc)->uart_base + ((offset) << (softc)->reg_shift), (value)); \
71	phys_read8(0x18000000); \
72} while (0)
73#else
74#define WRITECSR(softc, offset, value) \
75	phys_write8((softc)->uart_base + ((offset) << (softc)->reg_shift), (value))
76#endif
77
78#define READCSR(softc, offset) \
79	phys_read8((softc)->uart_base + ((offset) << (softc)->reg_shift))
80
81static int ns16550_uart_open(cfe_devctx_t *ctx);
82static int ns16550_uart_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
83static int ns16550_uart_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat);
84static int ns16550_uart_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
85static int ns16550_uart_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
86static int ns16550_uart_close(cfe_devctx_t *ctx);
87
88void ns16550_uart_probe(cfe_driver_t *drv,
89			unsigned long probe_a, unsigned long probe_b,
90			void *probe_ptr);
91
92
93const cfe_devdisp_t ns16550_uart_dispatch = {
94    ns16550_uart_open,
95    ns16550_uart_read,
96    ns16550_uart_inpstat,
97    ns16550_uart_write,
98    ns16550_uart_ioctl,
99    ns16550_uart_close,
100    NULL,
101    NULL
102};
103
104const cfe_driver_t ns16550_uart = {
105    "NS16550 UART",
106    "uart",
107    CFE_DEV_SERIAL,
108    &ns16550_uart_dispatch,
109    ns16550_uart_probe
110};
111
112typedef struct ns16550_uart_s {
113    physaddr_t uart_base;
114    int uart_flowcontrol;
115    int baud_base;
116    int reg_shift;
117    int uart_speed;
118} ns16550_uart_t;
119
120
121/*
122 * NS16550-compatible UART.
123 * probe_a: physical address of UART
124 */
125
126void ns16550_uart_probe(cfe_driver_t *drv,
127			unsigned long probe_a, unsigned long probe_b,
128			void *probe_ptr)
129{
130    ns16550_uart_t *softc;
131    char descr[80];
132
133    softc = (ns16550_uart_t *) KMALLOC(sizeof(ns16550_uart_t),0);
134    if (softc) {
135	softc->uart_base = probe_a;
136	softc->baud_base = probe_b ? : NS16550_HZ;
137	softc->reg_shift = probe_ptr ? *(int*)probe_ptr : 0;
138	softc->uart_speed = CFG_SERIAL_BAUD_RATE;
139	softc->uart_flowcontrol = SERIAL_FLOW_NONE;
140	xsprintf(descr, "%s at 0x%X", drv->drv_description, (uint32_t)probe_a);
141
142	cfe_attach(drv, softc, NULL, descr);
143	}
144}
145
146#if !defined(MIPS33xx)
147
148#define DELAY(n) delay(n)
149extern int32_t _getticks(void);
150static void delay(int ticks)
151{
152    int32_t t;
153
154    t = _getticks() + ticks;
155    while (_getticks() < t)
156	; /* NULL LOOP */
157}
158#endif /* !MIPS33xx */
159
160static void ns16550_uart_setflow(ns16550_uart_t *softc)
161{
162    /* noop for now */
163}
164
165
166static int ns16550_uart_open(cfe_devctx_t *ctx)
167{
168    ns16550_uart_t *softc = ctx->dev_softc;
169    unsigned int brtc;
170
171    brtc = BRTC(softc->baud_base, softc->uart_speed);
172
173    WRITECSR(softc,R_UART_CFCR,CFCR_DLAB);
174    WRITECSR(softc,R_UART_DATA,brtc & 0xFF);
175    WRITECSR(softc,R_UART_IER,brtc>>8);
176    WRITECSR(softc,R_UART_CFCR,CFCR_8BITS);
177
178#if !defined(NS16550_NO_FLOW)
179
180    WRITECSR(softc,R_UART_CFCR,CFCR_DLAB);
181    WRITECSR(softc,R_UART_DATA,brtc & 0xFF);
182    WRITECSR(softc,R_UART_IER,brtc>>8);
183    WRITECSR(softc,R_UART_CFCR,CFCR_8BITS);
184#if !defined(_BCM94702_CPCI_)
185    WRITECSR(softc,R_UART_MCR,MCR_DTR | MCR_RTS | MCR_IENABLE);
186#endif
187    WRITECSR(softc,R_UART_IER,0);
188
189    WRITECSR(softc,R_UART_FIFO,FIFO_ENABLE);
190    DELAY(100);
191    WRITECSR(softc,R_UART_FIFO,
192	     FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1);
193    DELAY(100);
194
195    if ((READCSR(softc,R_UART_IIR) & IIR_FIFO_MASK) !=
196	IIR_FIFO_MASK) {
197	WRITECSR(softc,R_UART_FIFO,0);
198    }
199#endif /* !NS16550_NO_FLOW */
200
201    ns16550_uart_setflow(softc);
202
203    return 0;
204}
205
206static int ns16550_uart_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
207{
208    ns16550_uart_t *softc = ctx->dev_softc;
209    unsigned char *bptr;
210    int blen;
211
212    bptr = buffer->buf_ptr;
213    blen = buffer->buf_length;
214
215    while ((blen > 0) && (READCSR(softc,R_UART_LSR) & LSR_RXRDY)) {
216	*bptr++ = (READCSR(softc,R_UART_DATA) & 0xFF);
217	blen--;
218	}
219
220    buffer->buf_retlen = buffer->buf_length - blen;
221    return 0;
222}
223
224static int ns16550_uart_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat)
225{
226    ns16550_uart_t *softc = ctx->dev_softc;
227
228    inpstat->inp_status = (READCSR(softc,R_UART_LSR) & LSR_RXRDY) ? 1 : 0;
229
230    return 0;
231}
232
233#if CFG_SIM || CFG_BUFLOG
234#define LOG_BUF_LEN	(4096)
235#define LOG_BUF_MASK	(LOG_BUF_LEN-1)
236char log_buf[LOG_BUF_LEN];
237unsigned long log_start;
238#define KSEG1ADDR(a)	(((unsigned long)(a) & 0x1fffffff) | 0xa0000000)
239
240#define WRITEBUF(c) \
241do { \
242	*((char*)KSEG1ADDR(&log_buf[log_start])) = c; \
243	log_start = (log_start + 1) & LOG_BUF_MASK; \
244} \
245while (0)
246
247extern int logbuf_write(const char *str);
248extern void logbuf_dump(void);
249
250int logbuf_write(const char *str)
251{
252    int count = 0;
253    char c;
254
255    /* Convert CR to CRLF as we write things out */
256    while ((c = *str ++)) {
257        if (c == '\n') {
258		WRITEBUF('\r');
259		count ++;
260	}
261	WRITEBUF(c);
262	count ++;
263    }
264
265    return count;
266}
267
268void logbuf_dump(void)
269{
270    xprintf(log_buf);
271}
272#endif
273
274static int ns16550_uart_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
275{
276    ns16550_uart_t *softc = ctx->dev_softc;
277    unsigned char *bptr;
278    int blen;
279
280    bptr = buffer->buf_ptr;
281    blen = buffer->buf_length;
282    while ((blen > 0) && (READCSR(softc,R_UART_LSR) & LSR_TXRDY)) {
283#if CFG_SIM
284        WRITEBUF(*bptr);
285#endif
286	WRITECSR(softc,R_UART_DATA, *bptr++);
287	blen--;
288#if CFG_SIM && CFG_SIM_CONSOLE
289	OSL_DELAY(100);
290#endif	/* CFG_SIM && CFG_SIM_CONSOLE */
291	}
292
293    buffer->buf_retlen = buffer->buf_length - blen;
294    return 0;
295}
296
297static int ns16550_uart_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
298{
299    ns16550_uart_t *softc = ctx->dev_softc;
300
301    unsigned int *info = (unsigned int *) buffer->buf_ptr;
302
303    switch ((int)buffer->buf_ioctlcmd) {
304	case IOCTL_SERIAL_GETSPEED:
305	    *info = softc->uart_speed;
306	    break;
307	case IOCTL_SERIAL_SETSPEED:
308	    softc->uart_speed = *info;
309	    /* NYI */
310	    break;
311	case IOCTL_SERIAL_GETFLOW:
312	    *info = softc->uart_flowcontrol;
313	    break;
314	case IOCTL_SERIAL_SETFLOW:
315	    softc->uart_flowcontrol = *info;
316	    ns16550_uart_setflow(softc);
317	    break;
318	default:
319	    return -1;
320	}
321
322    return 0;
323}
324
325static int ns16550_uart_close(cfe_devctx_t *ctx)
326{
327    ns16550_uart_t *softc = ctx->dev_softc;
328
329    WRITECSR(softc,R_UART_MCR,0);
330
331    return 0;
332}
333
334
335