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
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
51#include "cfe.h"
52#include "lib_physio.h"
53
54#include "ns16550.h"
55
56#if defined(BCM47XX) && ENDIAN_BIG
57#define READREG(sc,r) phys_read8((sc)->uart_base+((r)^0x3))
58#define WRITEREG(sc,r,v) phys_write8((sc)->uart_base+((r)^0x3),(v))
59#else
60#define READREG(sc,r) phys_read8((sc)->uart_base+(r))
61#define WRITEREG(sc,r,v) phys_write8((sc)->uart_base+(r),(v))
62#endif
63
64#define READCSR  READREG
65#define WRITECSR WRITEREG
66
67/* Workarounds */
68#if defined(BCM4310)
69/* PR9562: Reads from MIPS to UART are unreliable */
70/* PR15083 WAR: Use dummy variables */
71#undef  READCSR
72#define READCSR(sc,r) (READREG((sc),R_UART_SCR), READREG((sc),(r)))
73#endif
74#if defined(BCM4704) || defined(BCM5365) || defined(BCM5836)
75/* PR13509 WAR: Read SB location after UART write */
76#undef  WRITECSR
77#define WRITECSR(sc,r,v) \
78     do { WRITEREG((sc),(r),(v)); phys_read8(0x18000000); } while (0)
79#endif
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 uart_speed;
117} ns16550_uart_t;
118
119
120/*
121 * NS16550-compatible UART.
122 * probe_a: physical address of UART
123 */
124
125void ns16550_uart_probe(cfe_driver_t *drv,
126			unsigned long probe_a, unsigned long probe_b,
127			void *probe_ptr)
128{
129    ns16550_uart_t *softc;
130    char descr[80];
131
132    softc = (ns16550_uart_t *) KMALLOC(sizeof(ns16550_uart_t),0);
133    if (softc) {
134	softc->uart_base = probe_a;
135	softc->baud_base = probe_b ? probe_b : NS16550_HZ;
136	softc->uart_speed = CFG_SERIAL_BAUD_RATE;
137	softc->uart_flowcontrol = SERIAL_FLOW_NONE;
138	xsprintf(descr, "%s at 0x%X", drv->drv_description, (uint32_t)probe_a);
139
140	cfe_attach(drv, softc, NULL, descr);
141	}
142}
143
144#if !defined(MIPS33xx)
145
146#define DELAY(n) delay(n)
147extern int32_t _getticks(void);
148static void delay(int ticks)
149{
150    int32_t t;
151
152    t = _getticks() + ticks;
153    while (_getticks() < t)
154	; /* NULL LOOP */
155}
156#endif /* !MIPS33xx */
157
158static void ns16550_uart_setflow(ns16550_uart_t *softc)
159{
160    /* noop for now */
161}
162
163
164static int ns16550_uart_open(cfe_devctx_t *ctx)
165{
166    ns16550_uart_t *softc = ctx->dev_softc;
167    unsigned int brtc;
168
169    brtc = BRTC(softc->baud_base, softc->uart_speed);
170
171    WRITECSR(softc,R_UART_CFCR,CFCR_DLAB);
172    WRITECSR(softc,R_UART_DATA,brtc & 0xFF);
173    WRITECSR(softc,R_UART_IER,brtc>>8);
174    WRITECSR(softc,R_UART_CFCR,CFCR_8BITS);
175
176#if !defined(NS16550_NO_FLOW)
177
178#if !defined(_BCM94702_CPCI_)
179    WRITECSR(softc,R_UART_MCR,MCR_DTR | MCR_RTS | MCR_IENABLE);
180#endif
181    WRITECSR(softc,R_UART_IER,0);
182
183    WRITECSR(softc,R_UART_FIFO,FIFO_ENABLE);
184    DELAY(100);
185    WRITECSR(softc,R_UART_FIFO,
186	     FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1);
187    DELAY(100);
188
189    if ((READCSR(softc,R_UART_IIR) & IIR_FIFO_MASK) !=
190	IIR_FIFO_MASK) {
191	WRITECSR(softc,R_UART_FIFO,0);
192    }
193#endif /* !NS16550_NO_FLOW */
194
195    ns16550_uart_setflow(softc);
196
197    return 0;
198}
199
200static int ns16550_uart_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
201{
202    ns16550_uart_t *softc = ctx->dev_softc;
203    hsaddr_t bptr;
204    int blen;
205    uint8_t b;
206
207    bptr = buffer->buf_ptr;
208    blen = buffer->buf_length;
209
210    while ((blen > 0) && (READCSR(softc,R_UART_LSR) & LSR_RXRDY)) {
211	b = (READCSR(softc,R_UART_DATA) & 0xFF);
212	hs_write8(bptr,b);
213	bptr++;
214	blen--;
215	}
216
217    buffer->buf_retlen = buffer->buf_length - blen;
218    return 0;
219}
220
221static int ns16550_uart_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat)
222{
223    ns16550_uart_t *softc = ctx->dev_softc;
224
225    inpstat->inp_status = (READCSR(softc,R_UART_LSR) & LSR_RXRDY) ? 1 : 0;
226
227    return 0;
228}
229
230static int ns16550_uart_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
231{
232    ns16550_uart_t *softc = ctx->dev_softc;
233    hsaddr_t bptr;
234    uint8_t b;
235    int blen;
236
237    bptr = buffer->buf_ptr;
238    blen = buffer->buf_length;
239    while ((blen > 0) && (READCSR(softc,R_UART_LSR) & LSR_TXRDY)) {
240	b = hs_read8(bptr);
241	bptr++;
242	WRITECSR(softc,R_UART_DATA, b);
243	blen--;
244	}
245
246    buffer->buf_retlen = buffer->buf_length - blen;
247    return 0;
248}
249
250static int ns16550_uart_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
251{
252    ns16550_uart_t *softc = ctx->dev_softc;
253    unsigned int info;
254
255    switch ((int)buffer->buf_ioctlcmd) {
256	case IOCTL_SERIAL_GETSPEED:
257	    info = softc->uart_speed;
258	    hs_memcpy_to_hs(buffer->buf_ptr,&info,sizeof(info));
259	    break;
260	case IOCTL_SERIAL_SETSPEED:
261	    hs_memcpy_from_hs(&info,buffer->buf_ptr,sizeof(info));
262	    softc->uart_speed = info;
263	    /* NYI */
264	    break;
265	case IOCTL_SERIAL_GETFLOW:
266	    info = softc->uart_flowcontrol;
267	    hs_memcpy_to_hs(buffer->buf_ptr,&info,sizeof(info));
268	    break;
269	case IOCTL_SERIAL_SETFLOW:
270	    hs_memcpy_from_hs(&info,buffer->buf_ptr,sizeof(info));
271	    softc->uart_flowcontrol = info;
272	    ns16550_uart_setflow(softc);
273	    break;
274	default:
275	    return -1;
276	}
277
278    return 0;
279}
280
281static int ns16550_uart_close(cfe_devctx_t *ctx)
282{
283    ns16550_uart_t *softc = ctx->dev_softc;
284
285    WRITECSR(softc,R_UART_MCR,0);
286
287    return 0;
288}
289
290
291