| /* |
| * udbg serial input/output routines for the Marvell MV64x60 (Discovery). |
| * |
| * Author: Dale Farnsworth <dale@farnsworth.org> |
| * |
| * 2007 (c) MontaVista Software, Inc. This file is licensed under |
| * the terms of the GNU General Public License version 2. This program |
| * is licensed "as is" without any warranty of any kind, whether express |
| * or implied. |
| */ |
| |
| #include <asm/io.h> |
| #include <asm/prom.h> |
| #include <asm/udbg.h> |
| |
| #include <sysdev/mv64x60.h> |
| |
| #define MPSC_0_CR1_OFFSET 0x000c |
| |
| #define MPSC_0_CR2_OFFSET 0x0010 |
| #define MPSC_CHR_2_TCS (1 << 9) |
| |
| #define MPSC_0_CHR_10_OFFSET 0x0030 |
| |
| #define MPSC_INTR_CAUSE_OFF_0 0x0004 |
| #define MPSC_INTR_CAUSE_OFF_1 0x000c |
| #define MPSC_INTR_CAUSE_RCC (1<<6) |
| |
| static void __iomem *mpsc_base; |
| static void __iomem *mpsc_intr_cause; |
| |
| static void mv64x60_udbg_putc(char c) |
| { |
| if (c == '\n') |
| mv64x60_udbg_putc('\r'); |
| |
| while(in_le32(mpsc_base + MPSC_0_CR2_OFFSET) & MPSC_CHR_2_TCS) |
| ; |
| out_le32(mpsc_base + MPSC_0_CR1_OFFSET, c); |
| out_le32(mpsc_base + MPSC_0_CR2_OFFSET, MPSC_CHR_2_TCS); |
| } |
| |
| static int mv64x60_udbg_testc(void) |
| { |
| return (in_le32(mpsc_intr_cause) & MPSC_INTR_CAUSE_RCC) != 0; |
| } |
| |
| static int mv64x60_udbg_getc(void) |
| { |
| int cause = 0; |
| int c; |
| |
| while (!mv64x60_udbg_testc()) |
| ; |
| |
| c = in_8(mpsc_base + MPSC_0_CHR_10_OFFSET + 2); |
| out_8(mpsc_base + MPSC_0_CHR_10_OFFSET + 2, c); |
| out_le32(mpsc_intr_cause, cause & ~MPSC_INTR_CAUSE_RCC); |
| return c; |
| } |
| |
| static int mv64x60_udbg_getc_poll(void) |
| { |
| if (!mv64x60_udbg_testc()) |
| return -1; |
| |
| return mv64x60_udbg_getc(); |
| } |
| |
| static void mv64x60_udbg_init(void) |
| { |
| struct device_node *np, *mpscintr, *stdout = NULL; |
| const char *path; |
| const phandle *ph; |
| struct resource r[2]; |
| const int *block_index; |
| int intr_cause_offset; |
| int err; |
| |
| path = of_get_property(of_chosen, "linux,stdout-path", NULL); |
| if (!path) |
| return; |
| |
| stdout = of_find_node_by_path(path); |
| if (!stdout) |
| return; |
| |
| for (np = NULL; |
| (np = of_find_compatible_node(np, "serial", "marvell,mpsc")); ) |
| if (np == stdout) |
| break; |
| |
| of_node_put(stdout); |
| if (!np) |
| return; |
| |
| block_index = of_get_property(np, "block-index", NULL); |
| if (!block_index) |
| goto error; |
| |
| switch (*block_index) { |
| case 0: |
| intr_cause_offset = MPSC_INTR_CAUSE_OFF_0; |
| break; |
| case 1: |
| intr_cause_offset = MPSC_INTR_CAUSE_OFF_1; |
| break; |
| default: |
| goto error; |
| } |
| |
| err = of_address_to_resource(np, 0, &r[0]); |
| if (err) |
| goto error; |
| |
| ph = of_get_property(np, "mpscintr", NULL); |
| mpscintr = of_find_node_by_phandle(*ph); |
| if (!mpscintr) |
| goto error; |
| |
| err = of_address_to_resource(mpscintr, 0, &r[1]); |
| of_node_put(mpscintr); |
| if (err) |
| goto error; |
| |
| of_node_put(np); |
| |
| mpsc_base = ioremap(r[0].start, r[0].end - r[0].start + 1); |
| if (!mpsc_base) |
| return; |
| |
| mpsc_intr_cause = ioremap(r[1].start, r[1].end - r[1].start + 1); |
| if (!mpsc_intr_cause) { |
| iounmap(mpsc_base); |
| return; |
| } |
| mpsc_intr_cause += intr_cause_offset; |
| |
| udbg_putc = mv64x60_udbg_putc; |
| udbg_getc = mv64x60_udbg_getc; |
| udbg_getc_poll = mv64x60_udbg_getc_poll; |
| |
| return; |
| |
| error: |
| of_node_put(np); |
| } |
| |
| void mv64x60_init_early(void) |
| { |
| mv64x60_udbg_init(); |
| } |