1/*
2 * MPSC/UART driver for the Marvell mv64360, mv64460, ...
3 *
4 * Author: Mark A. Greer <mgreer@mvista.com>
5 *
6 * 2007 (c) MontaVista Software, Inc. This file is licensed under
7 * the terms of the GNU General Public License version 2. This program
8 * is licensed "as is" without any warranty of any kind, whether express
9 * or implied.
10 */
11
12#include <stdarg.h>
13#include <stddef.h>
14#include "types.h"
15#include "string.h"
16#include "stdio.h"
17#include "io.h"
18#include "ops.h"
19
20extern void udelay(long delay);
21
22#define MPSC_CHR_1		0x000c
23
24#define MPSC_CHR_2		0x0010
25#define MPSC_CHR_2_TA		(1<<7)
26#define MPSC_CHR_2_TCS		(1<<9)
27#define MPSC_CHR_2_RA		(1<<23)
28#define MPSC_CHR_2_CRD		(1<<25)
29#define MPSC_CHR_2_EH		(1<<31)
30
31#define MPSC_CHR_4		0x0018
32#define MPSC_CHR_4_Z		(1<<29)
33
34#define MPSC_CHR_5		0x001c
35#define MPSC_CHR_5_CTL1_INTR	(1<<12)
36#define MPSC_CHR_5_CTL1_VALID	(1<<15)
37
38#define MPSC_CHR_10		0x0030
39
40#define MPSC_INTR_CAUSE		0x0000
41#define MPSC_INTR_CAUSE_RCC	(1<<6)
42#define MPSC_INTR_MASK		0x0080
43
44#define SDMA_SDCM		0x0008
45#define SDMA_SDCM_AR		(1<<15)
46#define SDMA_SDCM_AT		(1<<31)
47
48static volatile char *mpsc_base;
49static volatile char *mpscintr_base;
50static u32 chr1, chr2;
51
52static int mpsc_open(void)
53{
54	chr1 = in_le32((u32 *)(mpsc_base + MPSC_CHR_1)) & 0x00ff0000;
55	chr2 = in_le32((u32 *)(mpsc_base + MPSC_CHR_2)) & ~(MPSC_CHR_2_TA
56			| MPSC_CHR_2_TCS | MPSC_CHR_2_RA | MPSC_CHR_2_CRD
57			| MPSC_CHR_2_EH);
58	out_le32((u32 *)(mpsc_base + MPSC_CHR_4), MPSC_CHR_4_Z);
59	out_le32((u32 *)(mpsc_base + MPSC_CHR_5),
60			MPSC_CHR_5_CTL1_INTR | MPSC_CHR_5_CTL1_VALID);
61	out_le32((u32 *)(mpsc_base + MPSC_CHR_2), chr2 | MPSC_CHR_2_EH);
62	return 0;
63}
64
65static void mpsc_putc(unsigned char c)
66{
67	while (in_le32((u32 *)(mpsc_base + MPSC_CHR_2)) & MPSC_CHR_2_TCS);
68
69	out_le32((u32 *)(mpsc_base + MPSC_CHR_1), chr1 | c);
70	out_le32((u32 *)(mpsc_base + MPSC_CHR_2), chr2 | MPSC_CHR_2_TCS);
71}
72
73static unsigned char mpsc_getc(void)
74{
75	u32 cause = 0;
76	unsigned char c;
77
78	while (!(cause & MPSC_INTR_CAUSE_RCC))
79		cause = in_le32((u32 *)(mpscintr_base + MPSC_INTR_CAUSE));
80
81	c = in_8((u8 *)(mpsc_base + MPSC_CHR_10 + 2));
82	out_8((u8 *)(mpsc_base + MPSC_CHR_10 + 2), c);
83	out_le32((u32 *)(mpscintr_base + MPSC_INTR_CAUSE),
84			cause & ~MPSC_INTR_CAUSE_RCC);
85
86	return c;
87}
88
89static u8 mpsc_tstc(void)
90{
91	return (u8)((in_le32((u32 *)(mpscintr_base + MPSC_INTR_CAUSE))
92				& MPSC_INTR_CAUSE_RCC) != 0);
93}
94
95static void mpsc_stop_dma(volatile char *sdma_base)
96{
97	out_le32((u32 *)(mpsc_base + MPSC_CHR_2),MPSC_CHR_2_TA | MPSC_CHR_2_RA);
98	out_le32((u32 *)(sdma_base + SDMA_SDCM), SDMA_SDCM_AR | SDMA_SDCM_AT);
99
100	while ((in_le32((u32 *)(sdma_base + SDMA_SDCM))
101				& (SDMA_SDCM_AR | SDMA_SDCM_AT)) != 0)
102		udelay(100);
103}
104
105static volatile char *mpsc_get_virtreg_of_phandle(void *devp, char *prop)
106{
107	void *v;
108	int n;
109
110	n = getprop(devp, prop, &v, sizeof(v));
111	if (n != sizeof(v))
112		goto err_out;
113
114	devp = find_node_by_linuxphandle((u32)v);
115	if (devp == NULL)
116		goto err_out;
117
118	n = getprop(devp, "virtual-reg", &v, sizeof(v));
119	if (n == sizeof(v))
120		return v;
121
122err_out:
123	return NULL;
124}
125
126int mpsc_console_init(void *devp, struct serial_console_data *scdp)
127{
128	void *v;
129	int n, reg_set;
130	volatile char *sdma_base;
131
132	n = getprop(devp, "virtual-reg", &v, sizeof(v));
133	if (n != sizeof(v))
134		goto err_out;
135	mpsc_base = v;
136
137	sdma_base = mpsc_get_virtreg_of_phandle(devp, "sdma");
138	if (sdma_base == NULL)
139		goto err_out;
140
141	mpscintr_base = mpsc_get_virtreg_of_phandle(devp, "mpscintr");
142	if (mpscintr_base == NULL)
143		goto err_out;
144
145	n = getprop(devp, "block-index", &v, sizeof(v));
146	if (n != sizeof(v))
147		goto err_out;
148	reg_set = (int)v;
149
150	mpscintr_base += (reg_set == 0) ? 0x4 : 0xc;
151
152	/* Make sure the mpsc ctlrs are shutdown */
153	out_le32((u32 *)(mpscintr_base + MPSC_INTR_CAUSE), 0);
154	out_le32((u32 *)(mpscintr_base + MPSC_INTR_CAUSE), 0);
155	out_le32((u32 *)(mpscintr_base + MPSC_INTR_MASK), 0);
156	out_le32((u32 *)(mpscintr_base + MPSC_INTR_MASK), 0);
157
158	mpsc_stop_dma(sdma_base);
159
160	scdp->open = mpsc_open;
161	scdp->putc = mpsc_putc;
162	scdp->getc = mpsc_getc;
163	scdp->tstc = mpsc_tstc;
164	scdp->close = NULL;
165
166	return 0;
167
168err_out:
169	return -1;
170}
171