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/mti-sead3 | |
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/mti-sead3')
24 files changed, 2268 insertions, 0 deletions
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) + continue; + if (p->flags & I2C_M_RD) + err = sead3_i2c_read(adap, p->buf, p->len); + else + err = sead3_i2c_write(adap, p->buf, p->len); + } + + /* Return the number of messages processed, or the error code. */ + if (err == 0) + err = num; + + return err; +} + +static u32 sead3_pic32_platform_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sead3_platform_algo = { + .master_xfer = sead3_pic32_platform_xfer, + .functionality = sead3_pic32_platform_func, +}; + +static void sead3_i2c_platform_setup(struct pic32_i2c_platform_data *priv) +{ + pic32_bus_writel(500, priv->base + PIC32_I2CxBRG); + pic32_bus_writel(PIC32_I2CCON_ON, priv->base + PIC32_I2CxCONCLR); + pic32_bus_writel(PIC32_I2CCON_ON, priv->base + PIC32_I2CxCONSET); + pic32_bus_writel(PIC32_I2CSTAT_BCL | PIC32_I2CSTAT_IWCOL, + priv->base + PIC32_I2CxSTATCLR); +} + +static int __devinit sead3_i2c_platform_probe(struct platform_device *pdev) +{ + struct pic32_i2c_platform_data *priv; + struct resource *r; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENODEV; + goto out; + } + + priv = kzalloc(sizeof(struct pic32_i2c_platform_data), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto out; + } + + priv->base = r->start; + if (!priv->base) { + ret = -EBUSY; + goto out_mem; + } + + priv->xfer_timeout = 200; + priv->ack_timeout = 200; + priv->ctl_timeout = 200; + + priv->adap.nr = pdev->id; + priv->adap.algo = &sead3_platform_algo; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &pdev->dev; + strlcpy(priv->adap.name, "SEAD3 PIC32", sizeof(priv->adap.name)); + + sead3_i2c_platform_setup(priv); + + ret = i2c_add_numbered_adapter(&priv->adap); + if (ret == 0) { + platform_set_drvdata(pdev, priv); + return 0; + } + +out_mem: + kfree(priv); +out: + return ret; +} + +static int __devexit sead3_i2c_platform_remove(struct platform_device *pdev) +{ + struct pic32_i2c_platform_data *priv = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&priv->adap); + kfree(priv); + return 0; +} + +#ifdef CONFIG_PM +static int sead3_i2c_platform_suspend(struct platform_device *pdev, + pm_message_t state) +{ + dev_dbg(&pdev->dev, "i2c_platform_disable\n"); + return 0; +} + +static int sead3_i2c_platform_resume(struct platform_device *pdev) +{ + struct pic32_i2c_platform_data *priv = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "sead3_i2c_platform_setup\n"); + sead3_i2c_platform_setup(priv); + + return 0; +} +#else +#define sead3_i2c_platform_suspend NULL +#define sead3_i2c_platform_resume NULL +#endif + +static struct platform_driver sead3_i2c_platform_driver = { + .driver = { + .name = "sead3-i2c", + .owner = THIS_MODULE, + }, + .probe = sead3_i2c_platform_probe, + .remove = __devexit_p(sead3_i2c_platform_remove), + .suspend = sead3_i2c_platform_suspend, + .resume = sead3_i2c_platform_resume, +}; + +static int __init sead3_i2c_platform_init(void) +{ + return platform_driver_register(&sead3_i2c_platform_driver); +} +module_init(sead3_i2c_platform_init); + +static void __exit sead3_i2c_platform_exit(void) +{ + platform_driver_unregister(&sead3_i2c_platform_driver); +} +module_exit(sead3_i2c_platform_exit); + +MODULE_AUTHOR("Chris Dearman, MIPS Technologies INC."); +MODULE_DESCRIPTION("SEAD3 PIC32 I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/mips/mti-sead3/sead3-i2c.c b/arch/mips/mti-sead3/sead3-i2c.c new file mode 100644 index 00000000000..f70d5fc58ef --- /dev/null +++ b/arch/mips/mti-sead3/sead3-i2c.c @@ -0,0 +1,37 @@ +/* + * 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/init.h> +#include <linux/platform_device.h> +#include <irq.h> + +struct resource sead3_i2c_resources[] = { + { + .start = 0x805200, + .end = 0x8053ff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device sead3_i2c_device = { + .name = "sead3-i2c", + .id = 0, + .num_resources = ARRAY_SIZE(sead3_i2c_resources), + .resource = sead3_i2c_resources, +}; + +static int __init sead3_i2c_init(void) +{ + return platform_device_register(&sead3_i2c_device); +} + +module_init(sead3_i2c_init); + +MODULE_AUTHOR("Chris Dearman <chris@mips.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("I2C probe driver for SEAD3"); diff --git a/arch/mips/mti-sead3/sead3-init.c b/arch/mips/mti-sead3/sead3-init.c new file mode 100644 index 00000000000..a958cad6fff --- /dev/null +++ b/arch/mips/mti-sead3/sead3-init.c @@ -0,0 +1,91 @@ +/* + * 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/io.h> + +#include <asm/bootinfo.h> +#include <asm/cacheflush.h> +#include <asm/traps.h> +#include <asm/mips-boards/generic.h> +#include <asm/mips-boards/prom.h> + +extern void prom_init_early_console(char port); + +extern char except_vec_nmi; +extern char except_vec_ejtag_debug; + +int prom_argc; +int *_prom_argv, *_prom_envp; + +#define prom_envp(index) ((char *)(long)_prom_envp[(index)]) + +char *prom_getenv(char *envname) +{ + /* + * Return a pointer to the given environment variable. + * In 64-bit mode: we're using 64-bit pointers, but all pointers + * in the PROM structures are only 32-bit, so we need some + * workarounds, if we are running in 64-bit mode. + */ + int i, index = 0; + + i = strlen(envname); + + while (prom_envp(index)) { + if (strncmp(envname, prom_envp(index), i) == 0) + return prom_envp(index+1); + index += 2; + } + + return NULL; +} + +static void __init mips_nmi_setup(void) +{ + void *base; + + base = cpu_has_veic ? + (void *)(CAC_BASE + 0xa80) : + (void *)(CAC_BASE + 0x380); + memcpy(base, &except_vec_nmi, 0x80); + flush_icache_range((unsigned long)base, (unsigned long)base + 0x80); +} + +static void __init mips_ejtag_setup(void) +{ + void *base; + + base = cpu_has_veic ? + (void *)(CAC_BASE + 0xa00) : + (void *)(CAC_BASE + 0x300); + memcpy(base, &except_vec_ejtag_debug, 0x80); + flush_icache_range((unsigned long)base, (unsigned long)base + 0x80); +} + +void __init prom_init(void) +{ + prom_argc = fw_arg0; + _prom_argv = (int *) fw_arg1; + _prom_envp = (int *) fw_arg2; + + board_nmi_handler_setup = mips_nmi_setup; + board_ejtag_handler_setup = mips_ejtag_setup; + + prom_init_cmdline(); + prom_meminit(); +#ifdef CONFIG_EARLY_PRINTK + if ((strstr(prom_getcmdline(), "console=t |