| /* | |
| * 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_each_compatible_node(np, NULL, "marvell,mv64360-mpsc") { | |
| if (np == stdout) | |
| break; | |
| } | |
| of_node_put(stdout); | |
| if (!np) | |
| return; | |
| block_index = of_get_property(np, "cell-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, resource_size(&r[0])); | |
| if (!mpsc_base) | |
| return; | |
| mpsc_intr_cause = ioremap(r[1].start, resource_size(&r[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(); | |
| } |