diff options
author | Steven J. Hill <sjhill@mips.com> | 2012-05-30 21:02:49 +0000 |
---|---|---|
committer | Steven J. Hill <sjhill@mips.com> | 2012-09-13 15:43:46 -0500 |
commit | 3070033a16edcc21688d5ea8967c89522f833862 (patch) | |
tree | 48d1a4601dd4750d103bc0b02ff2494d75220321 /arch/mips | |
parent | 006a851b10a395955c153a145ad8241494d43688 (diff) |
MIPS: Add core files for MIPS SEAD-3 development platform.
More information about the SEAD-3 platform can be found at
<http://www.mips.com/products/development-kits/mips-sead-3/>
on MTI's site. Currently, the M14K family of cores is what
the SEAD-3 is utilised with.
Signed-off-by: Douglas Leung <douglas@mips.com>
Signed-off-by: Chris Dearman <chris@mips.com>
Signed-off-by: Steven J. Hill <sjhill@mips.com>
Diffstat (limited to 'arch/mips')
29 files changed, 2445 insertions, 0 deletions
diff --git a/arch/mips/include/asm/mach-sead3/cpu-feature-overrides.h b/arch/mips/include/asm/mach-sead3/cpu-feature-overrides.h new file mode 100644 index 00000000000..7f3e3f9bd23 --- /dev/null +++ b/arch/mips/include/asm/mach-sead3/cpu-feature-overrides.h @@ -0,0 +1,72 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003, 2004 Chris Dearman + * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) + */ +#ifndef __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H +#define __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H + + +/* + * CPU feature overrides for MIPS boards + */ +#ifdef CONFIG_CPU_MIPS32 +#define cpu_has_tlb 1 +#define cpu_has_4kex 1 +#define cpu_has_4k_cache 1 +/* #define cpu_has_fpu ? */ +/* #define cpu_has_32fpr ? */ +#define cpu_has_counter 1 +/* #define cpu_has_watch ? */ +#define cpu_has_divec 1 +#define cpu_has_vce 0 +/* #define cpu_has_cache_cdex_p ? */ +/* #define cpu_has_cache_cdex_s ? */ +/* #define cpu_has_prefetch ? */ +#define cpu_has_mcheck 1 +/* #define cpu_has_ejtag ? */ +#ifdef CONFIG_CPU_HAS_LLSC +#define cpu_has_llsc 1 +#else +#define cpu_has_llsc 0 +#endif +/* #define cpu_has_vtag_icache ? */ +/* #define cpu_has_dc_aliases ? */ +/* #define cpu_has_ic_fills_f_dc ? */ +#define cpu_has_nofpuex 0 +/* #define cpu_has_64bits ? */ +/* #define cpu_has_64bit_zero_reg ? */ +/* #define cpu_has_inclusive_pcaches ? */ +#define cpu_icache_snoops_remote_store 1 +#endif + +#ifdef CONFIG_CPU_MIPS64 +#define cpu_has_tlb 1 +#define cpu_has_4kex 1 +#define cpu_has_4k_cache 1 +/* #define cpu_has_fpu ? */ +/* #define cpu_has_32fpr ? */ +#define cpu_has_counter 1 +/* #define cpu_has_watch ? */ +#define cpu_has_divec 1 +#define cpu_has_vce 0 +/* #define cpu_has_cache_cdex_p ? */ +/* #define cpu_has_cache_cdex_s ? */ +/* #define cpu_has_prefetch ? */ +#define cpu_has_mcheck 1 +/* #define cpu_has_ejtag ? */ +#define cpu_has_llsc 1 +/* #define cpu_has_vtag_icache ? */ +/* #define cpu_has_dc_aliases ? */ +/* #define cpu_has_ic_fills_f_dc ? */ +#define cpu_has_nofpuex 0 +/* #define cpu_has_64bits ? */ +/* #define cpu_has_64bit_zero_reg ? */ +/* #define cpu_has_inclusive_pcaches ? */ +#define cpu_icache_snoops_remote_store 1 +#endif + +#endif /* __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H */ diff --git a/arch/mips/include/asm/mach-sead3/irq.h b/arch/mips/include/asm/mach-sead3/irq.h new file mode 100644 index 00000000000..652ea4c38cd --- /dev/null +++ b/arch/mips/include/asm/mach-sead3/irq.h @@ -0,0 +1,9 @@ +#ifndef __ASM_MACH_MIPS_IRQ_H +#define __ASM_MACH_MIPS_IRQ_H + +#define NR_IRQS 256 + + +#include_next <irq.h> + +#endif /* __ASM_MACH_MIPS_IRQ_H */ diff --git a/arch/mips/include/asm/mach-sead3/kernel-entry-init.h b/arch/mips/include/asm/mach-sead3/kernel-entry-init.h new file mode 100644 index 00000000000..3dfbd8e7947 --- /dev/null +++ b/arch/mips/include/asm/mach-sead3/kernel-entry-init.h @@ -0,0 +1,52 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Chris Dearman (chris@mips.com) + * Copyright (C) 2007 Mips Technologies, Inc. + */ +#ifndef __ASM_MACH_MIPS_KERNEL_ENTRY_INIT_H +#define __ASM_MACH_MIPS_KERNEL_ENTRY_INIT_H + + .macro kernel_entry_setup +#ifdef CONFIG_MIPS_MT_SMTC + mfc0 t0, CP0_CONFIG + bgez t0, 9f + mfc0 t0, CP0_CONFIG, 1 + bgez t0, 9f + mfc0 t0, CP0_CONFIG, 2 + bgez t0, 9f + mfc0 t0, CP0_CONFIG, 3 + and t0, 1<<2 + bnez t0, 0f +9 : + /* Assume we came from YAMON... */ + PTR_LA v0, 0x9fc00534 /* YAMON print */ + lw v0, (v0) + move a0, zero + PTR_LA a1, nonmt_processor + jal v0 + + PTR_LA v0, 0x9fc00520 /* YAMON exit */ + lw v0, (v0) + li a0, 1 + jal v0 + +1 : b 1b + + __INITDATA +nonmt_processor : + .asciz "SMTC kernel requires the MT ASE to run\n" + __FINIT +0 : +#endif + .endm + +/* + * Do SMP slave processor setup necessary before we can safely execute C code. + */ + .macro smp_slave_setup + .endm + +#endif /* __ASM_MACH_MIPS_KERNEL_ENTRY_INIT_H */ diff --git a/arch/mips/include/asm/mach-sead3/war.h b/arch/mips/include/asm/mach-sead3/war.h new file mode 100644 index 00000000000..7c6931d5f45 --- /dev/null +++ b/arch/mips/include/asm/mach-sead3/war.h @@ -0,0 +1,25 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org> + */ +#ifndef __ASM_MIPS_MACH_MIPS_WAR_H +#define __ASM_MIPS_MACH_MIPS_WAR_H + +#define R4600_V1_INDEX_ICACHEOP_WAR 0 +#define R4600_V1_HIT_CACHEOP_WAR 0 +#define R4600_V2_HIT_CACHEOP_WAR 0 +#define R5432_CP0_INTERRUPT_WAR 0 +#define BCM1250_M3_WAR 0 +#define SIBYTE_1956_WAR 0 +#define MIPS4K_ICACHE_REFILL_WAR 1 +#define MIPS_CACHE_SYNC_WAR 1 +#define TX49XX_ICACHE_INDEX_INV_WAR 0 +#define RM9000_CDEX_SMP_WAR 0 +#define ICACHE_REFILLS_WORKAROUND_WAR 1 +#define R10000_LLSC_WAR 0 +#define MIPS34K_MISSED_ITLB_WAR 0 + +#endif /* __ASM_MIPS_MACH_MIPS_WAR_H */ diff --git a/arch/mips/include/asm/mips-boards/sead3int.h b/arch/mips/include/asm/mips-boards/sead3int.h new file mode 100644 index 00000000000..d634d9a807f --- /dev/null +++ b/arch/mips/include/asm/mips-boards/sead3int.h @@ -0,0 +1,19 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000,2012 MIPS Technologies, Inc. All rights reserved. + * Douglas Leung <douglas@mips.com> + * Steven J. Hill <sjhill@mips.com> + */ +#ifndef _MIPS_SEAD3INT_H +#define _MIPS_SEAD3INT_H + +/* SEAD-3 GIC address space definitions. */ +#define GIC_BASE_ADDR 0x1b1c0000 +#define GIC_ADDRSPACE_SZ (128 * 1024) + +#define MIPS_GIC_IRQ_BASE (MIPS_CPU_IRQ_BASE + 0) + +#endif /* !(_MIPS_SEAD3INT_H) */ diff --git a/arch/mips/mti-sead3/Makefile b/arch/mips/mti-sead3/Makefile new file mode 100644 index 00000000000..626afeac438 --- /dev/null +++ b/arch/mips/mti-sead3/Makefile @@ -0,0 +1,19 @@ +# +# Carsten Langgaard, carstenl@mips.com +# Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. +# +# Copyright (C) 2008 Wind River Systems, Inc. +# written by Ralf Baechle <ralf@linux-mips.org> +# +obj-y := sead3-lcd.o sead3-cmdline.o \ + sead3-display.o sead3-init.o sead3-int.o \ + sead3-mtd.o sead3-net.o \ + sead3-memory.o sead3-platform.o \ + sead3-reset.o sead3-setup.o sead3-time.o + +obj-y += sead3-i2c-dev.o sead3-i2c.o \ + sead3-pic32-i2c-drv.o sead3-pic32-bus.o \ + leds-sead3.o sead3-leds.o + +obj-$(CONFIG_EARLY_PRINTK) += sead3-console.o +obj-$(CONFIG_USB_EHCI_HCD) += sead3-ehci.o diff --git a/arch/mips/mti-sead3/Platform b/arch/mips/mti-sead3/Platform new file mode 100644 index 00000000000..38709242714 --- /dev/null +++ b/arch/mips/mti-sead3/Platform @@ -0,0 +1,7 @@ +# +# MIPS SEAD-3 board +# +platform-$(CONFIG_MIPS_SEAD3) += mti-sead3/ +cflags-$(CONFIG_MIPS_SEAD3) += -I$(srctree)/arch/mips/include/asm/mach-sead3 +load-$(CONFIG_MIPS_SEAD3) += 0xffffffff80100000 +all-$(CONFIG_MIPS_SEAD3) := $(COMPRESSION_FNAME).srec diff --git a/arch/mips/mti-sead3/leds-sead3.c b/arch/mips/mti-sead3/leds-sead3.c new file mode 100644 index 00000000000..a95ac598520 --- /dev/null +++ b/arch/mips/mti-sead3/leds-sead3.c @@ -0,0 +1,128 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/io.h> + +#define DRVNAME "sead3-led" + +static struct platform_device *pdev; + +static void sead3_pled_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + pr_debug("sead3_pled_set\n"); + writel(value, (void __iomem *)0xBF000210); /* FIXME */ +} + +static void sead3_fled_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + pr_debug("sead3_fled_set\n"); + writel(value, (void __iomem *)0xBF000218); /* FIXME */ +} + +static struct led_classdev sead3_pled = { + .name = "sead3::pled", + .brightness_set = sead3_pled_set, +}; + +static struct led_classdev sead3_fled = { + .name = "sead3::fled", + .brightness_set = sead3_fled_set, +}; + +#ifdef CONFIG_PM +static int sead3_led_suspend(struct platform_device *dev, + pm_message_t state) +{ + led_classdev_suspend(&sead3_pled); + led_classdev_suspend(&sead3_fled); + return 0; +} + +static int sead3_led_resume(struct platform_device *dev) +{ + led_classdev_resume(&sead3_pled); + led_classdev_resume(&sead3_fled); + return 0; +} +#else +#define sead3_led_suspend NULL +#define sead3_led_resume NULL +#endif + +static int sead3_led_probe(struct platform_device *pdev) +{ + int ret; + + ret = led_classdev_register(&pdev->dev, &sead3_pled); + if (ret < 0) + return ret; + + ret = led_classdev_register(&pdev->dev, &sead3_fled); + if (ret < 0) + led_classdev_unregister(&sead3_pled); + + return ret; +} + +static int sead3_led_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&sead3_pled); + led_classdev_unregister(&sead3_fled); + return 0; +} + +static struct platform_driver sead3_led_driver = { + .probe = sead3_led_probe, + .remove = sead3_led_remove, + .suspend = sead3_led_suspend, + .resume = sead3_led_resume, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init sead3_led_init(void) +{ + int ret; + + ret = platform_driver_register(&sead3_led_driver); + if (ret < 0) + goto out; + + pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + platform_driver_unregister(&sead3_led_driver); + goto out; + } + +out: + return ret; +} + +static void __exit sead3_led_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&sead3_led_driver); +} + +module_init(sead3_led_init); +module_exit(sead3_led_exit); + +MODULE_AUTHOR("Kristian Kielhofner <kris@krisk.org>"); +MODULE_DESCRIPTION("SEAD3 LED driver"); +MODULE_LICENSE("GPL"); + diff --git a/arch/mips/mti-sead3/sead3-cmdline.c b/arch/mips/mti-sead3/sead3-cmdline.c new file mode 100644 index 00000000000..a2e6cec67f5 --- /dev/null +++ b/arch/mips/mti-sead3/sead3-cmdline.c @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/init.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> + +extern int prom_argc; +extern int *_prom_argv; + +/* + * YAMON (32-bit PROM) pass arguments and environment as 32-bit pointer. + * This macro take care of sign extension. + */ +#define prom_argv(index) ((char *)(long)_prom_argv[(index)]) + +char * __init prom_getcmdline(void) +{ + return &(arcs_cmdline[0]); +} + +void __init prom_init_cmdline(void) +{ + char *cp; + int actr; + + actr = 1; /* Always ignore argv[0] */ + + cp = &(arcs_cmdline[0]); + while (actr < prom_argc) { + strcpy(cp, prom_argv(actr)); + cp += strlen(prom_argv(actr)); + *cp++ = ' '; + actr++; + } + if (cp != &(arcs_cmdline[0])) { + /* get rid of trailing space */ + --cp; + *cp = '\0'; + } +} diff --git a/arch/mips/mti-sead3/sead3-console.c b/arch/mips/mti-sead3/sead3-console.c new file mode 100644 index 00000000000..b36739108a0 --- /dev/null +++ b/arch/mips/mti-sead3/sead3-console.c @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/init.h> +#include <linux/console.h> +#include <linux/serial_reg.h> +#include <linux/io.h> + +#define SEAD_UART1_REGS_BASE 0xbf000800 /* ttyS1 = DB9 port */ +#define SEAD_UART0_REGS_BASE 0xbf000900 /* ttyS0 = USB port */ +#define PORT(base_addr, offset) ((unsigned int __iomem *)(base_addr+(offset)*4)) + +static char console_port = 1; + +static inline unsigned int serial_in(int offset, unsigned int base_addr) +{ + return __raw_readl(PORT(base_addr, offset)) & 0xff; +} + +static inline void serial_out(int offset, int value, unsigned int base_addr) +{ + __raw_writel(value, PORT(base_addr, offset)); +} + +void __init prom_init_early_console(char port) +{ + console_port = port; +} + +int prom_putchar(char c) +{ + unsigned int base_addr; + + base_addr = console_port ? SEAD_UART1_REGS_BASE : SEAD_UART0_REGS_BASE; + + while ((serial_in(UART_LSR, base_addr) & UART_LSR_THRE) == 0) + ; + + serial_out(UART_TX, c, base_addr); + + return 1; +} diff --git a/arch/mips/mti-sead3/sead3-display.c b/arch/mips/mti-sead3/sead3-display.c new file mode 100644 index 00000000000..8308c7fc188 --- /dev/null +++ b/arch/mips/mti-sead3/sead3-display.c @@ -0,0 +1,78 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/timer.h> +#include <linux/io.h> +#include <asm/mips-boards/generic.h> +#include <asm/mips-boards/prom.h> + +static unsigned int display_count; +static unsigned int max_display_count; + +#define LCD_DISPLAY_POS_BASE 0x1f000400 +#define DISPLAY_LCDINSTRUCTION (0*2) +#define DISPLAY_LCDDATA (1*2) +#define DISPLAY_CPLDSTATUS (2*2) +#define DISPLAY_CPLDDATA (3*2) +#define LCD_SETDDRAM 0x80 +#define LCD_IR_BF 0x80 + +const char display_string[] = " LINUX ON SEAD3 "; + +static void scroll_display_message(unsigned long data); +static DEFINE_TIMER(mips_scroll_timer, scroll_display_message, HZ, 0); + +static void lcd_wait(unsigned int __iomem *display) +{ + /* Wait for CPLD state machine to become idle. */ + do { } while (__raw_readl(display + DISPLAY_CPLDSTATUS) & 1); + + do { + __raw_readl(display + DISPLAY_LCDINSTRUCTION); + + /* Wait for CPLD state machine to become idle. */ + do { } while (__raw_readl(display + DISPLAY_CPLDSTATUS) & 1); + } while (__raw_readl(display + DISPLAY_CPLDDATA) & LCD_IR_BF); +} + +void mips_display_message(const char *str) +{ + static unsigned int __iomem *display; + char ch; + int i; + + if (unlikely(display == NULL)) + display = ioremap_nocache(LCD_DISPLAY_POS_BASE, + (8 * sizeof(int))); + + for (i = 0; i < 16; i++) { + if (*str) + ch = *str++; + else + ch = ' '; + lcd_wait(display); + __raw_writel((LCD_SETDDRAM | i), + (display + DISPLAY_LCDINSTRUCTION)); + lcd_wait(display); + __raw_writel(ch, display + DISPLAY_LCDDATA); + } +} + +static void scroll_display_message(unsigned long data) +{ + mips_display_message(&display_string[display_count++]); + if (display_count == max_display_count) + display_count = 0; + mod_timer(&mips_scroll_timer, jiffies + HZ); +} + +void mips_scroll_message(void) +{ + del_timer_sync(&mips_scroll_timer); + max_display_count = strlen(display_string) + 1 - 16; + mod_timer(&mips_scroll_timer, jiffies + 1); +} diff --git a/arch/mips/mti-sead3/sead3-ehci.c b/arch/mips/mti-sead3/sead3-ehci.c new file mode 100644 index 00000000000..772fc056a92 --- /dev/null +++ b/arch/mips/mti-sead3/sead3-ehci.c @@ -0,0 +1,47 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> + +struct resource ehci_resources[] = { + { + .start = 0x1b200000, + .end = 0x1b200fff, + .flags = IORESOURCE_MEM + }, + { + .start = MIPS_CPU_IRQ_BASE + 2, + .flags = IORESOURCE_IRQ + } +}; + +u64 sead3_usbdev_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device ehci_device = { + .name = "sead3-ehci", + .id = 0, + .dev = { + .dma_mask = &sead3_usbdev_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32) + }, + .num_resources = ARRAY_SIZE(ehci_resources), + .resource = ehci_resources +}; + +static int __init ehci_init(void) +{ + return platform_device_register(&ehci_device); +} + +module_init(ehci_init); + +MODULE_AUTHOR("Chris Dearman <chris@mips.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EHCI probe driver for SEAD3"); diff --git a/arch/mips/mti-sead3/sead3-i2c-dev.c b/arch/mips/mti-sead3/sead3-i2c-dev.c new file mode 100644 index 00000000000..eca0b53a71d --- /dev/null +++ b/arch/mips/mti-sead3/sead3-i2c-dev.c @@ -0,0 +1,33 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/init.h> +#include <linux/i2c.h> + +static struct i2c_board_info __initdata sead3_i2c_devices[] = { + { + I2C_BOARD_INFO("adt7476", 0x2c), + .irq = 0, + }, + { + I2C_BOARD_INFO("m41t80", 0x68), + .irq = 0, + }, +}; + +static int __init sead3_i2c_init(void) +{ + int err; + + err = i2c_register_board_info(0, sead3_i2c_devices, + ARRAY_SIZE(sead3_i2c_devices)); + if (err < 0) + pr_err("sead3-i2c-dev: cannot register board I2C devices\n"); + return err; +} + +arch_initcall(sead3_i2c_init); diff --git a/arch/mips/mti-sead3/sead3-i2c-drv.c b/arch/mips/mti-sead3/sead3-i2c-drv.c new file mode 100644 index 00000000000..0375ee66bca --- /dev/null +++ b/arch/mips/mti-sead3/sead3-i2c-drv.c @@ -0,0 +1,405 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> + +#define PIC32_I2CxCON 0x0000 +#define PIC32_I2CCON_ON (1<<15) +#define PIC32_I2CCON_ACKDT (1<<5) +#define PIC32_I2CCON_ACKEN (1<<4) +#define PIC32_I2CCON_RCEN (1<<3) +#define PIC32_I2CCON_PEN (1<<2) +#define PIC32_I2CCON_RSEN (1<<1) +#define PIC32_I2CCON_SEN (1<<0) +#define PIC32_I2CxCONCLR 0x0004 +#define PIC32_I2CxCONSET 0x0008 +#define PIC32_I2CxSTAT 0x0010 +#define PIC32_I2CxSTATCLR 0x0014 +#define PIC32_I2CSTAT_ACKSTAT (1<<15) +#define PIC32_I2CSTAT_TRSTAT (1<<14) +#define PIC32_I2CSTAT_BCL (1<<10) +#define PIC32_I2CSTAT_IWCOL (1<<7) +#define PIC32_I2CSTAT_I2COV (1<<6) +#define PIC32_I2CxBRG 0x0040 +#define PIC32_I2CxTRN 0x0050 +#define PIC32_I2CxRCV 0x0060 + +static DEFINE_SPINLOCK(pic32_bus_lock); + +static void __iomem *bus_xfer = (void __iomem *)0xbf000600; +static void __iomem *bus_status = (void __iomem *)0xbf000060; + +#define DELAY() udelay(100) + +static inline unsigned int ioready(void) +{ + return readl(bus_status) & 1; +} + +static inline void wait_ioready(void) +{ + do { } while (!ioready()); +} + +static inline void wait_ioclear(void) +{ + do { } while (ioready()); +} + +static inline void check_ioclear(void) +{ + if (ioready()) { + do { + (void) readl(bus_xfer); + DELAY(); + } while (ioready()); + } +} + +static u32 pic32_bus_readl(u32 reg) +{ + unsigned long flags; + u32 status, val; + + spin_lock_irqsave(&pic32_bus_lock, flags); + + check_ioclear(); + writel((0x01 << 24) | (reg & 0x00ffffff), bus_xfer); + DELAY(); + wait_ioready(); + status = readl(bus_xfer); + DELAY(); + val = readl(bus_xfer); + wait_ioclear(); + + spin_unlock_irqrestore(&pic32_bus_lock, flags); + + return val; +} + +static void pic32_bus_writel(u32 val, u32 reg) +{ + unsigned long flags; + u32 status; + + spin_lock_irqsave(&pic32_bus_lock, flags); + + check_ioclear(); + writel((0x10 << 24) | (reg & 0x00ffffff), bus_xfer); + DELAY(); + writel(val, bus_xfer); + DELAY(); + wait_ioready(); + status = readl(bus_xfer); + wait_ioclear(); + + spin_unlock_irqrestore(&pic32_bus_lock, flags); +} + +struct pic32_i2c_platform_data { + u32 base; + struct i2c_adapter adap; + u32 xfer_timeout; + u32 ack_timeout; + u32 ctl_timeout; +}; + +static inline void pic32_i2c_start(struct pic32_i2c_platform_data *adap) +{ + pic32_bus_writel(PIC32_I2CCON_SEN, adap->base + PIC32_I2CxCONSET); +} + +static inline void pic32_i2c_stop(struct pic32_i2c_platform_data *adap) +{ + pic32_bus_writel(PIC32_I2CCON_PEN, adap->base + PIC32_I2CxCONSET); +} + +static inline void pic32_i2c_ack(struct pic32_i2c_platform_data *adap) +{ + pic32_bus_writel(PIC32_I2CCON_ACKDT, adap->base + PIC32_I2CxCONCLR); + pic32_bus_writel(PIC32_I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); +} + +static inline void pic32_i2c_nack(struct pic32_i2c_platform_data *adap) +{ + pic32_bus_writel(PIC32_I2CCON_ACKDT, adap->base + PIC32_I2CxCONSET); + pic32_bus_writel(PIC32_I2CCON_ACKEN, adap->base + PIC32_I2CxCONSET); +} + +static inline int pic32_i2c_idle(struct pic32_i2c_platform_data *adap) +{ + int i; + + for (i = 0; i < adap->ctl_timeout; i++) { + if (((pic32_bus_readl(adap->base + PIC32_I2CxCON) & + (PIC32_I2CCON_ACKEN | PIC32_I2CCON_RCEN | + PIC32_I2CCON_PEN | PIC32_I2CCON_RSEN | + PIC32_I2CCON_SEN)) == 0) && + ((pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & + (PIC32_I2CSTAT_TRSTAT)) == 0)) + return 0; + udelay(1); + } + return -ETIMEDOUT; +} + +static inline u32 pic32_i2c_master_write(struct pic32_i2c_platform_data *adap, + u32 byte) +{ + pic32_bus_writel(byte, adap->base + PIC32_I2CxTRN); + return pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & + PIC32_I2CSTAT_IWCOL; +} + +static inline u32 pic32_i2c_master_read(struct pic32_i2c_platform_data *adap) +{ + pic32_bus_writel(PIC32_I2CCON_RCEN, adap->base + PIC32_I2CxCONSET); + while (pic32_bus_readl(adap->base + PIC32_I2CxCON) & PIC32_I2CCON_RCEN) + ; + pic32_bus_writel(PIC32_I2CSTAT_I2COV, adap->base + PIC32_I2CxSTATCLR); + return pic32_bus_readl(adap->base + PIC32_I2CxRCV); +} + +static int pic32_i2c_address(struct pic32_i2c_platform_data *adap, + unsigned int addr, int rd) +{ + pic32_i2c_idle(adap); + pic32_i2c_start(adap); + pic32_i2c_idle(adap); + + addr <<= 1; + if (rd) + addr |= 1; + + if (pic32_i2c_master_write(adap, addr)) + return -EIO; + pic32_i2c_idle(adap); + if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & + PIC32_I2CSTAT_ACKSTAT) + return -EIO; + return 0; +} + +static int sead3_i2c_read(struct pic32_i2c_platform_data *adap, + unsigned char *buf, unsigned int len) +{ + u32 data; + int i; + + i = 0; + while (i < len) { + data = pic32_i2c_master_read(adap); + buf[i++] = data; + if (i < len) + pic32_i2c_ack(adap); + else + pic32_i2c_nack(adap); + } + + pic32_i2c_stop(adap); + pic32_i2c_idle(adap); + return 0; +} + +static int sead3_i2c_write(struct pic32_i2c_platform_data *adap, + unsigned char *buf, unsigned int len) +{ + int i; + u32 data; + + i = 0; + while (i < len) { + data = buf[i]; + if (pic32_i2c_master_write(adap, data)) + return -EIO; + pic32_i2c_idle(adap); + if (pic32_bus_readl(adap->base + PIC32_I2CxSTAT) & + PIC32_I2CSTAT_ACKSTAT) + return -EIO; + i++; + } + + pic32_i2c_stop(adap); + pic32_i2c_idle(adap); + return 0; +} + +static int sead3_pic32_platform_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct pic32_i2c_platform_data *adap = i2c_adap->algo_data; + struct i2c_msg *p; + int i, err = 0; + + for (i = 0; i < num; i++) { +#define __BUFSIZE 80 + int ii; + static char buf[__BUFSIZE]; + char *b = buf; + + p = &msgs[i]; + b += sprintf(buf, " [%d bytes]", p->len); + if ((p->flags & I2C_M_RD) == 0) { + for (ii = 0; ii < p->len; ii++) { + if (b < &buf[__BUFSIZE-4]) { + b += sprintf(b, " %02x", p->buf[ii]); + } else { + strcat(b, "..."); + break; + } + } + } + } + + for (i = 0; !err && i < num; i++) { + p = &msgs[i]; + err = pic32_i2c_address(adap, p->addr, p->flags & I2C_M_RD); + if (err || !p->len) |