1/* 2 * udbg serial input/output routines for the Marvell MV64x60 (Discovery). 3 * 4 * Author: Dale Farnsworth <dale@farnsworth.org> 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 <asm/io.h> 13#include <asm/prom.h> 14#include <asm/udbg.h> 15 16#include <sysdev/mv64x60.h> 17 18#define MPSC_0_CR1_OFFSET 0x000c 19 20#define MPSC_0_CR2_OFFSET 0x0010 21#define MPSC_CHR_2_TCS (1 << 9) 22 23#define MPSC_0_CHR_10_OFFSET 0x0030 24 25#define MPSC_INTR_CAUSE_OFF_0 0x0004 26#define MPSC_INTR_CAUSE_OFF_1 0x000c 27#define MPSC_INTR_CAUSE_RCC (1<<6) 28 29static void __iomem *mpsc_base; 30static void __iomem *mpsc_intr_cause; 31 32static void mv64x60_udbg_putc(char c) 33{ 34 if (c == '\n') 35 mv64x60_udbg_putc('\r'); 36 37 while(in_le32(mpsc_base + MPSC_0_CR2_OFFSET) & MPSC_CHR_2_TCS) 38 ; 39 out_le32(mpsc_base + MPSC_0_CR1_OFFSET, c); 40 out_le32(mpsc_base + MPSC_0_CR2_OFFSET, MPSC_CHR_2_TCS); 41} 42 43static int mv64x60_udbg_testc(void) 44{ 45 return (in_le32(mpsc_intr_cause) & MPSC_INTR_CAUSE_RCC) != 0; 46} 47 48static int mv64x60_udbg_getc(void) 49{ 50 int cause = 0; 51 int c; 52 53 while (!mv64x60_udbg_testc()) 54 ; 55 56 c = in_8(mpsc_base + MPSC_0_CHR_10_OFFSET + 2); 57 out_8(mpsc_base + MPSC_0_CHR_10_OFFSET + 2, c); 58 out_le32(mpsc_intr_cause, cause & ~MPSC_INTR_CAUSE_RCC); 59 return c; 60} 61 62static int mv64x60_udbg_getc_poll(void) 63{ 64 if (!mv64x60_udbg_testc()) 65 return -1; 66 67 return mv64x60_udbg_getc(); 68} 69 70static void mv64x60_udbg_init(void) 71{ 72 struct device_node *np, *mpscintr, *stdout = NULL; 73 const char *path; 74 const phandle *ph; 75 struct resource r[2]; 76 const int *block_index; 77 int intr_cause_offset; 78 int err; 79 80 path = of_get_property(of_chosen, "linux,stdout-path", NULL); 81 if (!path) 82 return; 83 84 stdout = of_find_node_by_path(path); 85 if (!stdout) 86 return; 87 88 for_each_compatible_node(np, "serial", "marvell,mv64360-mpsc") { 89 if (np == stdout) 90 break; 91 } 92 93 of_node_put(stdout); 94 if (!np) 95 return; 96 97 block_index = of_get_property(np, "cell-index", NULL); 98 if (!block_index) 99 goto error; 100 101 switch (*block_index) { 102 case 0: 103 intr_cause_offset = MPSC_INTR_CAUSE_OFF_0; 104 break; 105 case 1: 106 intr_cause_offset = MPSC_INTR_CAUSE_OFF_1; 107 break; 108 default: 109 goto error; 110 } 111 112 err = of_address_to_resource(np, 0, &r[0]); 113 if (err) 114 goto error; 115 116 ph = of_get_property(np, "mpscintr", NULL); 117 mpscintr = of_find_node_by_phandle(*ph); 118 if (!mpscintr) 119 goto error; 120 121 err = of_address_to_resource(mpscintr, 0, &r[1]); 122 of_node_put(mpscintr); 123 if (err) 124 goto error; 125 126 of_node_put(np); 127 128 mpsc_base = ioremap(r[0].start, resource_size(&r[0])); 129 if (!mpsc_base) 130 return; 131 132 mpsc_intr_cause = ioremap(r[1].start, resource_size(&r[1])); 133 if (!mpsc_intr_cause) { 134 iounmap(mpsc_base); 135 return; 136 } 137 mpsc_intr_cause += intr_cause_offset; 138 139 udbg_putc = mv64x60_udbg_putc; 140 udbg_getc = mv64x60_udbg_getc; 141 udbg_getc_poll = mv64x60_udbg_getc_poll; 142 143 return; 144 145error: 146 of_node_put(np); 147} 148 149void mv64x60_init_early(void) 150{ 151 mv64x60_udbg_init(); 152} 153